Showing preview only (293K chars total). Download the full file or copy to clipboard to get everything.
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 <robinfreyler@web.de>"]
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
[](https://crates.io/crates/modular-bitfield)
[](https://github.com/modular-bitfield/modular-bitfield/actions/workflows/rust.yml)
[](https://docs.rs/modular-bitfield)
[](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 <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> 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<T, R, F, S>(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<T, R, F, S>(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<T, R, F, S>(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<F>(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<bool, InvalidBitPattern<u8>>` | 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<Self, OutOfBounds>` | 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> {
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<syn::Meta, syn::Token![,]> =
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<FieldConfig> {
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::<usize>()?, 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<ConfigValue<usize>>,
pub bits: Option<ConfigValue<usize>>,
pub filled: Option<ConfigValue<bool>>,
pub repr: Option<ConfigValue<ReprKind>>,
pub derive_debug: Option<ConfigValue<()>>,
pub derive_specifier: Option<ConfigValue<()>>,
pub retained_attributes: Vec<syn::Attribute>,
pub field_configs: HashMap<usize, ConfigValue<FieldConfig>>,
}
/// 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<T> {
/// The actual value of the config.
pub value: T,
/// The originating span of the config.
pub span: Span,
}
impl<T> ConfigValue<T> {
/// 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<T>(name: &str, span: Span, previous: &ConfigValue<T>) -> syn::Error
where
T: core::fmt::Debug + 'static,
{
if TypeId::of::<T>() == 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<TokenStream2> {
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<Self::Bytes, ::modular_bitfield::error::OutOfBounds> {
::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<Self::InOut, ::modular_bitfield::error::InvalidBitPattern<Self::Bytes>>
{
// 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(<Self::Bytes>::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<TokenStream2> {
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
/// <B8 as ::modular_bitfield::Specifier>::BITS +
/// <B8 as ::modular_bitfield::Specifier>::BITS +
/// <B8 as ::modular_bitfield::Specifier>::BITS +
/// <bool as ::modular_bitfield::Specifier>::BITS +
/// <B7 as ::modular_bitfield::Specifier>::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::<Punctuated<syn::Expr, Token![+]>>()
.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<TokenStream2> {
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<TokenStream2> {
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 {
<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<Self, ::modular_bitfield::error::OutOfBounds> {
#[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, Self::Error> {
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<TokenStream2> {
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<TokenStream2> {
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, ::modular_bitfield::error::OutOfBounds> {
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<syn::Expr, syn::Token![+]>,
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::<syn::Expr, Token![+]>::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<syn::Attribute>,
/// An encountered `#[bits = N]` attribute on a field.
pub bits: Option<ConfigValue<usize>>,
/// An encountered `#[skip]` attribute on a field.
pub skip: Option<ConfigValue<SkipWhich>>,
}
/// 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 = (usize, &syn::Field)> {
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<Item = FieldInfo<'a>> {
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<TokenStream2> {
let input = syn::parse2::<syn::ItemStruct>(input)?;
let params = syn::parse2::<ParamArgs>(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<syn::MetaNameValue>,
}
impl syn::parse::Parse for ParamArgs {
fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
let punctuated = <syn::punctuated::Punctuated<_, syn::Token![,]>>::parse_terminated(input)?;
Ok(Self {
args: punctuated.into_iter().collect(),
})
}
}
impl IntoIterator for ParamArgs {
type Item = syn::MetaNameValue;
type IntoIter = std::vec::IntoIter<syn::MetaNameValue>;
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<F>(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::<usize>().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<Item = syn::MetaNameValue> + '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<TokenStream2> {
let input = syn::parse2::<syn::DeriveInput>(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<usize>,
}
fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result<Attributes> {
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::<usize>()?)
} else {
return Err(format_err_spanned!(
attr,
"could not parse 'bits' attribute",
));
};
Ok(acc)
})?;
Ok(attributes)
}
fn generate_enum(input: &syn::ItemEnum) -> syn::Result<TokenStream2> {
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::<Vec<_>>();
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 <Self as ::modular_bitfield::Specifier>::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: <Self as ::modular_bitfield::Specifier>::InOut) -> ::core::result::Result<<Self as ::modular_bitfield::Specifier>::Bytes, ::modular_bitfield::error::OutOfBounds> {
::core::result::Result::Ok(input as <Self as ::modular_bitfield::Specifier>::Bytes)
}
#[inline]
fn from_bytes(bytes: <Self as ::modular_bitfield::Specifier>::Bytes) -> ::core::result::Result<<Self as ::modular_bitfield::Specifier>::InOut, ::modular_bitfield::error::InvalidBitPattern<<Self as ::modular_bitfield::Specifier>::Bytes>> {
match bytes {
#( #from_bytes_arms ),*
invalid_bytes => {
::core::result::Result::Err(
<::modular_bitfield::error::InvalidBitPattern<<Self as ::modular_bitfield::Specifier>::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<Self::Bytes, crate::OutOfBounds> {
if input <= #max_value {
Ok(input)
} else {
Err(crate::OutOfBounds)
}
}
#[inline]
fn from_bytes(bytes: Self::Bytes) -> Result<Self::InOut, crate::InvalidBitPattern<Self::Bytes>> {
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<Bytes> {
/// The invalid bits.
invalid_bytes: Bytes,
}
impl<Bytes> core::fmt::Display for InvalidBitPattern<Bytes>
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<Bytes> InvalidBitPattern<Bytes> {
/// 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<Self::Bytes, OutOfBounds>;
/// 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<Self::InOut, InvalidBitPattern<Self::Bytes>>;
}
/// 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 = <Self as SpecifierBytes>::Bytes;
fn bytes_into_array(bytes: Self::Bytes) -> Self::Array {
bytes.to_le_bytes()
}
fn array_into_bytes(bytes: Self::Array) -> Self::Bytes {
<BitCount<{::core::mem::size_of::<$prim>() * 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 = <Self as SpecifierBytes>::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 = <Self::Array>::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::<Self::Bytes>()];
result[0..($size / 8)].copy_from_slice(&array[..]);
<Self::Bytes>::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<T>(::core::marker::PhantomData<T>);
macro_rules! impl_total_size_for {
( $(($n:expr, $name:ident)),* ) => {
$(
pub enum $name {}
impl private::Sealed for TotalSize<BitCount<$n>> {}
impl private::Sealed for $name {}
impl RenameSizeType for TotalSize<BitCount<$n>> {
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
<Self::Size as RenameSizeType>::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
<Self::Size as RenameSizeType>::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<const N: usize>;
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<A>
where
<Self::CheckType as DispatchTrueFalse>::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
<Self::CheckType as DispatchTrueFalse>::Out: SpecifierHasAtMost128Bits,
{
type CheckType: DispatchTrueFalse;
}
pub trait CheckFillsUnalignedBits
where
<Self::CheckType as DispatchTrueFalse>::Out: FillsUnalignedBits,
{
type CheckType: DispatchTrueFalse;
}
pub trait FillsUnalignedBits {}
pub trait CheckDoesNotFillUnalignedBits
where
<Self::CheckType as DispatchTrueFalse>::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<Self::Bytes, OutOfBounds> {
Ok(input.into())
}
#[inline]
fn from_bytes(bytes: Self::Bytes) -> Result<Self::InOut, InvalidBitPattern<Self::Bytes>> {
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<Self::Bytes, OutOfBounds> {
Ok(input)
}
#[inline]
fn from_bytes(bytes: Self::Bytes) -> Result<Self::InOut, InvalidBitPattern<Self::Bytes>> {
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<T>() -> PushBuffer<<T as Specifier>::Bytes>
where
T: Specifier,
PushBuffer<T::Bytes>: Default,
{
<PushBuffer<<T as Specifier>::Bytes> as Default>::default()
}
#[doc(hidden)]
#[inline]
#[must_use]
pub fn read_specifier<T>(bytes: &[u8], offset: usize) -> <T as Specifier>::Bytes
where
T: Specifier,
PushBuffer<T::Bytes>: Default + PushBits,
{
let end = offset + <T as Specifier>::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::<T>();
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(<T as Specifier>::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<T>(bytes: &mut [u8], offset: usize, new_val: <T as Specifier>::Bytes)
where
T: Specifier,
PopBuffer<T::Bytes>: PopBits,
{
let end = offset + <T as Specifier>::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 = <PopBuffer<T::Bytes>>::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<T> {
bytes: T,
}
impl<T> PopBuffer<T> {
/// Creates a new pop buffer from the given bytes.
#[inline]
pub(super) fn from_bytes(bytes: T) -> Self {
Self { bytes }
}
}
impl Sealed for PopBuffer<u8> {}
impl PopBits for PopBuffer<u8> {
#[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<T> {
bytes: T,
}
impl<T> PushBuffer<T> {
/// 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!(<SignIntegerShort as Specifier>::BITS, 32);
}
#[test]
fn bits_non_filled_2() {
#[bitfield(bits = 32, filled = false)]
#[derive(Specifier)]
pub struct SignIntegerLong {
sign: bool,
value: B30,
}
assert_eq!(<SignIntegerLong as Specifier>::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!(<Status as Specifier>::BITS, 2);
assert_eq!(<Header as Specifier>::BITS, 4);
assert_eq!(<PackedData as Specifier>::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!(<Header as Specifier>::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::<Base>(), 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::<Header>(), 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::<RedirectionTableEntry>(), 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::<RedirectionTableEntry>(), 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!(<Header as Specifier>::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!(
<FilledSpecifier as Specifier>::from_bytes(0x0003_0201),
Ok(value)
);
assert_eq!(
<FilledSpecifier as Specifier>::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<T: From<u64>>(&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 = My
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
SYMBOL INDEX (388 symbols across 91 files)
FILE: benches/benchmarks.rs
type Color (line 12) | pub struct Color {
type SingleBitsInSingleByte (line 20) | pub struct SingleBitsInSingleByte {
type TwoHalfBytes (line 32) | pub struct TwoHalfBytes {
type SingleBitAndRest (line 38) | pub struct SingleBitAndRest {
type B7B1 (line 44) | pub struct B7B1 {
type B3B1B4 (line 50) | pub struct B3B1B4 {
type TwoHalfWords (line 57) | pub struct TwoHalfWords {
type B6B12B6 (line 63) | pub struct B6B12B6 {
type B6B36B6 (line 70) | pub struct B6B36B6 {
type Complex (line 77) | pub struct Complex {
function bench_set_variants (line 85) | fn bench_set_variants() {
function bench_get_variants (line 170) | fn bench_get_variants() {
function main (line 251) | fn main() {
FILE: benches/cmp_bitfield_crate.rs
type ModularBitfield (line 29) | pub struct ModularBitfield {
function main (line 112) | fn main() {
FILE: benches/cmp_handwritten.rs
function main (line 94) | fn main() {
FILE: benches/utils/handwritten.rs
type Generated (line 32) | pub struct Generated {
type Handwritten (line 48) | pub struct Handwritten {
method new (line 55) | pub fn new() -> Self {
method a (line 60) | pub fn a(&self) -> u16 {
method set_a (line 65) | pub fn set_a(&mut self, new_val: u16) {
method b (line 73) | pub fn b(&self) -> u8 {
method set_b (line 78) | pub fn set_b(&mut self, new_val: u8) {
method c (line 84) | pub fn c(&self) -> u16 {
method set_c (line 93) | pub fn set_c(&mut self, new_val: u16) {
method d (line 101) | pub fn d(&self) -> u8 {
method set_d (line 106) | pub fn set_d(&mut self, new_val: u8) {
method e (line 111) | pub fn e(&self) -> u8 {
method set_e (line 116) | pub fn set_e(&mut self, new_val: u8) {
method f (line 122) | pub fn f(&self) -> u32 {
method set_f (line 127) | pub fn set_f(&mut self, new_val: u32) {
FILE: benches/utils/mod.rs
function bench (line 8) | pub fn bench<T, R, F, S>(label: &'static str, setup: S, closure: F, comp...
function compare (line 26) | pub fn compare<T, R, F, S>(label: &'static str, setup: S, closure: F)
function one_shot (line 35) | pub fn one_shot<T, R, F, S>(label: &'static str, setup: S, closure: F)
function repeat (line 48) | pub fn repeat<F>(mut f: F)
FILE: impl/src/bitfield/analyse.rs
type Error (line 11) | type Error = syn::Error;
method try_from (line 13) | fn try_from((config, item_struct): (&mut Config, syn::ItemStruct)) -> Re...
method ensure_has_fields (line 25) | fn ensure_has_fields(item_struct: &syn::ItemStruct) -> Result<()> {
method ensure_valid_generics (line 40) | fn ensure_valid_generics(item_struct: &syn::ItemStruct) -> Result<()> {
method extract_repr_attribute (line 53) | fn extract_repr_attribute(attr: &syn::Attribute, config: &mut Config) ->...
method extract_derive_debug_attribute (line 106) | fn extract_derive_debug_attribute(attr: &syn::Attribute, config: &mut Co...
method extract_attributes (line 143) | fn extract_attributes(attributes: &[syn::Attribute], config: &mut Config...
method analyse_config_for_fields (line 157) | fn analyse_config_for_fields(item_struct: &syn::ItemStruct, config: &mut...
method extract_field_config (line 167) | fn extract_field_config(field: &syn::Field) -> Result<FieldConfig> {
function retain_repr_arguments (line 243) | fn retain_repr_arguments() {
FILE: impl/src/bitfield/config.rs
type Config (line 10) | pub struct Config {
method filled_enabled (line 73) | pub fn filled_enabled(&self) -> bool {
method ensure_no_bits_and_repr_conflict (line 77) | fn ensure_no_bits_and_repr_conflict(&self) -> Result<()> {
method ensure_no_bits_and_bytes_conflict (line 95) | fn ensure_no_bits_and_bytes_conflict(&self) -> Result<()> {
method ensure_no_repr_and_filled_conflict (line 122) | pub fn ensure_no_repr_and_filled_conflict(&self) -> Result<()> {
method ensure_no_conflicts (line 147) | pub fn ensure_no_conflicts(&self) -> Result<()> {
method raise_duplicate_error (line 155) | fn raise_duplicate_error<T>(name: &str, span: Span, previous: &ConfigV...
method bytes (line 181) | pub fn bytes(&mut self, value: usize, span: Span) -> Result<()> {
method bits (line 194) | pub fn bits(&mut self, value: usize, span: Span) -> Result<()> {
method filled (line 207) | pub fn filled(&mut self, value: bool, span: Span) -> Result<()> {
method repr (line 220) | pub fn repr(&mut self, value: ReprKind, span: Span) -> Result<()> {
method derive_debug (line 235) | pub fn derive_debug(&mut self, span: Span) -> Result<()> {
method derive_specifier (line 254) | pub fn derive_specifier(&mut self, span: Span) -> Result<()> {
method push_retained_attribute (line 269) | pub fn push_retained_attribute(&mut self, retained_attr: syn::Attribut...
method field_config (line 281) | pub fn field_config(&mut self, index: usize, span: Span, config: Field...
type ReprKind (line 23) | pub enum ReprKind {
method bits (line 38) | pub fn bits(self) -> usize {
method fmt (line 50) | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
type ConfigValue (line 57) | pub struct ConfigValue<T> {
function new (line 66) | pub fn new(value: T, span: Span) -> Self {
FILE: impl/src/bitfield/expand.rs
method expand (line 12) | pub fn expand(&self, config: &Config) -> TokenStream2 {
method generate_specifier_impl (line 42) | pub fn generate_specifier_impl(&self, config: &Config) -> Option<TokenSt...
method generate_debug_impl (line 99) | pub fn generate_debug_impl(&self, config: &Config) -> Option<TokenStream...
method generate_bitfield_size (line 182) | fn generate_bitfield_size(&self) -> TokenStream2 {
method generate_target_or_actual_bitfield_size (line 198) | fn generate_target_or_actual_bitfield_size(&self, config: &Config) -> To...
method generate_filled_check_for_unaligned_bits (line 215) | fn generate_filled_check_for_unaligned_bits(
method generate_filled_check_for_aligned_bits (line 247) | fn generate_filled_check_for_aligned_bits(&self, config: &Config) -> Tok...
method generate_check_for_filled (line 274) | fn generate_check_for_filled(&self, config: &Config) -> TokenStream2 {
method next_divisible_by_8 (line 284) | fn next_divisible_by_8(value: &TokenStream2) -> TokenStream2 {
method generate_struct (line 295) | fn generate_struct(&self, config: &Config) -> TokenStream2 {
method generate_constructor (line 313) | fn generate_constructor(&self, config: &Config) -> TokenStream2 {
method expand_optional_bytes_check (line 335) | fn expand_optional_bytes_check(&self, config: &Config) -> Option<TokenSt...
method expand_repr_from_impls_and_checks (line 353) | fn expand_repr_from_impls_and_checks(&self, config: &Config) -> Option<T...
method expand_byte_conversion_impls (line 402) | fn expand_byte_conversion_impls(&self, config: &Config) -> TokenStream2 {
method expand_bits_checks_for_field (line 487) | fn expand_bits_checks_for_field(field_info: FieldInfo<'_>) -> TokenStrea...
method expand_getters_for_field (line 507) | fn expand_getters_for_field(
method expand_setters_for_field (line 573) | fn expand_setters_for_field(
method expand_getters_and_setters_for_field (line 685) | fn expand_getters_and_setters_for_field(
method expand_getters_and_setters (line 708) | fn expand_getters_and_setters(&self, config: &Config) -> TokenStream2 {
FILE: impl/src/bitfield/field_config.rs
type FieldConfig (line 6) | pub struct FieldConfig {
method retain_attr (line 52) | pub fn retain_attr(&mut self, attr: syn::Attribute) {
method bits (line 61) | pub fn bits(&mut self, amount: usize, span: Span) -> Result<(), syn::E...
method skip (line 95) | pub fn skip(&mut self, which: SkipWhich, span: Span) -> Result<(), syn...
method skip_setters (line 124) | pub fn skip_setters(&self) -> bool {
method skip_getters (line 131) | pub fn skip_getters(&self) -> bool {
type SkipWhich (line 17) | pub enum SkipWhich {
method skip_getters (line 40) | pub fn skip_getters(self) -> bool {
method skip_setters (line 45) | pub fn skip_setters(self) -> bool {
FILE: impl/src/bitfield/field_info.rs
type FieldInfo (line 4) | pub struct FieldInfo<'a> {
function new (line 15) | pub fn new(id: usize, field: &'a syn::Field, config: FieldConfig) -> Self {
function ident_frag (line 24) | pub fn ident_frag(&self) -> &dyn quote::IdentFragment {
function name (line 32) | pub fn name(&self) -> String {
function ident_as_string (line 37) | pub fn ident_as_string(field: &'a syn::Field, index: usize) -> String {
method fields (line 49) | pub fn fields(item_struct: &syn::ItemStruct) -> impl Iterator<Item = (us...
method field_infos (line 56) | pub fn field_infos<'a, 'b: 'a>(
FILE: impl/src/bitfield/mod.rs
function analyse_and_expand (line 14) | pub fn analyse_and_expand(args: TokenStream2, input: TokenStream2) -> To...
function analyse_and_expand_or_error (line 26) | fn analyse_and_expand_or_error(args: TokenStream2, input: TokenStream2) ...
type BitfieldStruct (line 36) | struct BitfieldStruct {
function raise_skip_error (line 41) | fn raise_skip_error(skip_params: &str, span: Span, previous: Span) -> Re...
FILE: impl/src/bitfield/params.rs
type ParamArgs (line 6) | pub struct ParamArgs {
method parse (line 11) | fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
type Item (line 20) | type Item = syn::MetaNameValue;
type IntoIter (line 21) | type IntoIter = std::vec::IntoIter<syn::MetaNameValue>;
method into_iter (line 23) | fn into_iter(self) -> Self::IntoIter {
method feed_int_param (line 30) | fn feed_int_param<F>(name_value: &syn::MetaNameValue, name: &str, on_suc...
method feed_bytes_param (line 63) | fn feed_bytes_param(&mut self, name_value: &syn::MetaNameValue) -> Resul...
method feed_bits_param (line 68) | fn feed_bits_param(&mut self, name_value: &syn::MetaNameValue) -> Result...
method feed_filled_param (line 73) | fn feed_filled_param(&mut self, name_value: &syn::MetaNameValue) -> Resu...
method feed_params (line 97) | pub fn feed_params<'a, P>(&mut self, params: P) -> Result<()>
FILE: impl/src/bitfield_specifier.rs
function generate (line 5) | pub fn generate(input: TokenStream2) -> TokenStream2 {
function generate_or_error (line 12) | fn generate_or_error(input: TokenStream2) -> syn::Result<TokenStream2> {
type Attributes (line 34) | struct Attributes {
function parse_attrs (line 38) | fn parse_attrs(attrs: &[syn::Attribute]) -> syn::Result<Attributes> {
function generate_enum (line 67) | fn generate_enum(input: &syn::ItemEnum) -> syn::Result<TokenStream2> {
FILE: impl/src/define_specifiers.rs
function generate (line 4) | pub fn generate(_input: TokenStream2) -> TokenStream2 {
function generate_specifier_for (line 11) | fn generate_specifier_for(bits: usize) -> TokenStream2 {
FILE: impl/src/errors.rs
type CombineError (line 52) | pub trait CombineError {
method into_combine (line 54) | fn into_combine(self, another: syn::Error) -> Self;
method into_combine (line 58) | fn into_combine(mut self, another: syn::Error) -> Self {
FILE: impl/src/lib.rs
function define_specifiers (line 17) | pub fn define_specifiers(input: TokenStream) -> TokenStream {
function bitfield (line 22) | pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
function specifier (line 27) | pub fn specifier(input: TokenStream) -> TokenStream {
function ui_code_coverage (line 33) | fn ui_code_coverage() {
FILE: src/error.rs
type OutOfBounds (line 7) | pub struct OutOfBounds;
method fmt (line 10) | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
type InvalidBitPattern (line 17) | pub struct InvalidBitPattern<Bytes> {
function fmt (line 26) | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
function new (line 38) | pub fn new(invalid_bytes: Bytes) -> Self {
function invalid_bytes (line 44) | pub fn invalid_bytes(self) -> Bytes {
FILE: src/lib.rs
type Specifier (line 40) | pub trait Specifier {
constant BITS (line 42) | const BITS: usize;
method into_bytes (line 61) | fn into_bytes(input: Self::InOut) -> Result<Self::Bytes, OutOfBounds>;
method from_bytes (line 70) | fn from_bytes(bytes: Self::Bytes) -> Result<Self::InOut, InvalidBitPat...
FILE: src/private/array_bytes_conv.rs
type ArrayBytesConversion (line 3) | pub trait ArrayBytesConversion {
method bytes_into_array (line 7) | fn bytes_into_array(bytes: Self::Bytes) -> Self::Array;
method array_into_bytes (line 8) | fn array_into_bytes(bytes: Self::Array) -> Self::Bytes;
FILE: src/private/checks.rs
type Sealed (line 3) | pub trait Sealed {}
type TotalSizeIsMultipleOfEightBits (line 17) | pub trait TotalSizeIsMultipleOfEightBits: private::Sealed {}
type TotalSizeIsNotMultipleOfEightBits (line 21) | pub trait TotalSizeIsNotMultipleOfEightBits: private::Sealed {}
type RenameSizeType (line 24) | pub trait RenameSizeType: private::Sealed {
type TotalSize (line 29) | pub struct TotalSize<T>(::core::marker::PhantomData<T>);
type CheckTotalSizeMultipleOf8 (line 67) | pub trait CheckTotalSizeMultipleOf8
type CheckTotalSizeIsNotMultipleOf8 (line 76) | pub trait CheckTotalSizeIsNotMultipleOf8
type DiscriminantInRange (line 85) | pub trait DiscriminantInRange: private::Sealed {}
type SpecifierHasAtMost128Bits (line 90) | pub trait SpecifierHasAtMost128Bits: private::Sealed {}
type True (line 97) | pub enum True {}
type False (line 104) | pub enum False {}
type DispatchTrueFalse (line 113) | pub trait DispatchTrueFalse: private::Sealed {
type Out (line 122) | type Out = False;
type Out (line 127) | type Out = True;
type BitCount (line 118) | pub struct BitCount<const N: usize>;
type CheckDiscriminantInRange (line 133) | pub trait CheckDiscriminantInRange<A>
type CheckSpecifierHasAtMost128Bits (line 142) | pub trait CheckSpecifierHasAtMost128Bits
type CheckFillsUnalignedBits (line 149) | pub trait CheckFillsUnalignedBits
type FillsUnalignedBits (line 156) | pub trait FillsUnalignedBits {}
type CheckDoesNotFillUnalignedBits (line 158) | pub trait CheckDoesNotFillUnalignedBits
type DoesNotFillUnalignedBits (line 165) | pub trait DoesNotFillUnalignedBits {}
FILE: src/private/impls.rs
constant BITS (line 7) | const BITS: usize = 1;
type Bytes (line 8) | type Bytes = u8;
type InOut (line 9) | type InOut = bool;
method into_bytes (line 12) | fn into_bytes(input: Self::InOut) -> Result<Self::Bytes, OutOfBounds> {
method from_bytes (line 17) | fn from_bytes(bytes: Self::Bytes) -> Result<Self::InOut, InvalidBitPatte...
FILE: src/private/proc.rs
function push_buffer (line 8) | fn push_buffer<T>() -> PushBuffer<<T as Specifier>::Bytes>
function read_specifier (line 19) | pub fn read_specifier<T>(bytes: &[u8], offset: usize) -> <T as Specifier...
function write_specifier (line 67) | pub fn write_specifier<T>(bytes: &mut [u8], offset: usize, new_val: <T a...
FILE: src/private/push_pop.rs
type PopBuffer (line 4) | pub struct PopBuffer<T> {
function from_bytes (line 11) | pub(super) fn from_bytes(bytes: T) -> Self {
method pop_bits (line 20) | fn pop_bits(&mut self, amount: u32) -> u8 {
type PushBuffer (line 59) | pub struct PushBuffer<T> {
function into_bytes (line 66) | pub(super) fn into_bytes(self) -> T {
FILE: src/private/traits.rs
type PushBits (line 9) | pub trait PushBits: checks::private::Sealed {
method push_bits (line 10) | fn push_bits(&mut self, amount: u32, bits: u8);
type PopBits (line 19) | pub trait PopBits: checks::private::Sealed {
method pop_bits (line 20) | fn pop_bits(&mut self, amount: u32) -> u8;
type SpecifierBytes (line 25) | pub trait SpecifierBytes: checks::private::Sealed {
type IsU8Compatible (line 30) | pub trait IsU8Compatible: checks::private::Sealed {}
type IsU16Compatible (line 31) | pub trait IsU16Compatible: checks::private::Sealed {}
type IsU32Compatible (line 32) | pub trait IsU32Compatible: checks::private::Sealed {}
type IsU64Compatible (line 33) | pub trait IsU64Compatible: checks::private::Sealed {}
type IsU128Compatible (line 34) | pub trait IsU128Compatible: checks::private::Sealed {}
FILE: tests/bitfield/bits_param.rs
function bits_non_filled_1 (line 6) | fn bits_non_filled_1() {
function bits_non_filled_2 (line 18) | fn bits_non_filled_2() {
function complex_use_case (line 30) | fn complex_use_case() {
function low_bits_filled (line 64) | fn low_bits_filled() {
function valid_use_1 (line 78) | fn valid_use_1() {
function valid_use_2 (line 87) | fn valid_use_2() {
function valid_use_3 (line 97) | fn valid_use_3() {
function valid_use_4 (line 107) | fn valid_use_4() {
FILE: tests/bitfield/bytes_param.rs
function valid_bitfield (line 6) | fn valid_bitfield() {
function valid_specifier_bitfield (line 20) | fn valid_specifier_bitfield() {
FILE: tests/bitfield/derive_bitfield_specifier.rs
function enums (line 51) | fn enums() {
function name_conflict (line 114) | fn name_conflict() {
function optional_discriminant (line 138) | fn optional_discriminant() {
FILE: tests/bitfield/derive_debug.rs
function print_invalid_bits (line 8) | fn print_invalid_bits() {
function respects_other_derives (line 45) | fn respects_other_derives() {
function valid_use_2 (line 66) | fn valid_use_2() {
function valid_use_specifier (line 100) | fn valid_use_specifier() {
function valid_use (line 124) | fn valid_use() {
function valid_use_tuple (line 143) | fn valid_use_tuple() {
FILE: tests/bitfield/derive_specifier.rs
function struct_in_struct (line 6) | fn struct_in_struct() {
function unfilled_from_bytes (line 32) | fn unfilled_from_bytes() {
function valid_use (line 58) | fn valid_use() {
FILE: tests/bitfield/filled_param.rs
function valid_bitfield_1 (line 6) | fn valid_bitfield_1() {
function valid_bitfield_2 (line 15) | fn valid_bitfield_2() {
function valid_bitfield_specifier_1 (line 24) | fn valid_bitfield_specifier_1() {
function valid_bitfield_specifier_2 (line 35) | fn valid_bitfield_specifier_2() {
FILE: tests/bitfield/mod.rs
function accessors (line 15) | fn accessors() {
function accessor_signatures (line 40) | fn accessor_signatures() {
function accessors_edge (line 98) | fn accessors_edge() {
function tuple_structs (line 134) | fn tuple_structs() {
function bool_specifier (line 154) | fn bool_specifier() {
function checked_setters (line 165) | fn checked_setters() {
function errors (line 226) | fn errors() {
function manual_reset (line 249) | fn manual_reset() {
function u128_specifier (line 287) | fn u128_specifier() {
function byte_conversions (line 325) | fn byte_conversions() {
function within_single_byte (line 359) | fn within_single_byte() {
function get_spanning_data (line 426) | fn get_spanning_data() {
function raw_identifiers (line 455) | fn raw_identifiers() {
function with_setter (line 477) | fn with_setter() {
function primitives_as_specifiers (line 501) | fn primitives_as_specifiers() {
function single_bit_enum (line 517) | fn single_bit_enum() {
function generic (line 544) | fn generic() {
function impl_from_trait (line 552) | fn impl_from_trait() {
function impl_try_from_trait (line 566) | fn impl_try_from_trait() {
FILE: tests/bitfield/no_implicit_prelude.rs
type OverrideLanguagePrelude (line 6) | struct OverrideLanguagePrelude;
function no_implicit_prelude (line 20) | fn no_implicit_prelude() {
function no_implicit_prelude_2 (line 37) | fn no_implicit_prelude_2() {
FILE: tests/bitfield/regressions.rs
function deny_elided_lifetime (line 6) | fn deny_elided_lifetime() {
function regression_issue_8 (line 17) | fn regression_issue_8() {
function regression_v0_11 (line 66) | fn regression_v0_11() {
function regression_issue_132 (line 90) | fn regression_issue_132() {
FILE: tests/bitfield/repr.rs
function complex_use (line 6) | fn complex_use() {
function generic_repr (line 63) | fn generic_repr() {
function multiple_valid_reprs_1 (line 72) | fn multiple_valid_reprs_1() {
function multiple_valid_reprs_2 (line 82) | fn multiple_valid_reprs_2() {
function valid_cond_use (line 93) | fn valid_cond_use() {
FILE: tests/bitfield/skip.rs
function double_wildcards_1 (line 8) | fn double_wildcards_1() {
function double_wildcards_2 (line 23) | fn double_wildcards_2() {
function skip_default (line 38) | fn skip_default() {
function skip_getters_and_setters_1 (line 61) | fn skip_getters_and_setters_1() {
function skip_getters_and_setters_2 (line 84) | fn skip_getters_and_setters_2() {
function skip_getters (line 110) | fn skip_getters() {
function skip_setters (line 141) | fn skip_setters() {
function skip_with_debug (line 170) | fn skip_with_debug() {
FILE: tests/panic_tests.rs
type EdgeCaseBytes (line 6) | pub struct EdgeCaseBytes {
function invalid_access_a (line 15) | fn invalid_access_a() {
function invalid_access_b (line 22) | fn invalid_access_b() {
function invalid_access_c (line 29) | fn invalid_access_c() {
function invalid_access_d (line 36) | fn invalid_access_d() {
FILE: tests/ui.rs
function ui_trybuild (line 5) | fn ui_trybuild() {
FILE: tests/ui/access_test.rs
type ColorEntry (line 5) | pub struct ColorEntry {
function main (line 12) | fn main() {
FILE: tests/ui/bitfield_attribute_wrong.rs
type Foo (line 4) | struct Foo {
function main (line 8) | fn main() {}
FILE: tests/ui/bits_attribute_wrong.rs
type TriggerMode (line 4) | pub enum TriggerMode {
type MismatchedTypes (line 10) | pub struct MismatchedTypes {
constant NOT_A_LITERAL (line 16) | const NOT_A_LITERAL: u32 = 9;
type InvalidValueType (line 19) | pub struct InvalidValueType {
type DuplicateAttribute (line 26) | pub struct DuplicateAttribute {
type NotANameValue (line 34) | pub struct NotANameValue {
function main (line 40) | fn main() {}
FILE: tests/ui/bits_param/conflicting_params.rs
type SignInteger (line 4) | pub struct SignInteger {
function main (line 9) | fn main() {}
FILE: tests/ui/bits_param/conflicting_repr.rs
type SignInteger (line 5) | pub struct SignInteger {
function main (line 10) | fn main() {}
FILE: tests/ui/bits_param/duplicate_param_1.rs
type SignInteger (line 4) | pub struct SignInteger {
function main (line 9) | fn main() {}
FILE: tests/ui/bits_param/duplicate_param_2.rs
type SignInteger (line 4) | pub struct SignInteger {
function main (line 9) | fn main() {}
FILE: tests/ui/bits_param/invalid_param_type.rs
constant NOT_LITERAL (line 3) | const NOT_LITERAL: u32 = 32;
type SignInteger (line 6) | pub struct SignInteger {
function main (line 11) | fn main() {}
FILE: tests/ui/bits_param/invalid_param_value_1.rs
type SignInteger (line 4) | pub struct SignInteger {
function main (line 9) | fn main() {}
FILE: tests/ui/bits_param/invalid_param_value_2.rs
type SignInteger (line 4) | pub struct SignInteger {
function main (line 9) | fn main() {}
FILE: tests/ui/bits_param/missing_param_value.rs
type Missing (line 4) | pub struct Missing {
type WrongMetaType (line 10) | pub struct WrongMetaType {
function main (line 15) | fn main() {}
FILE: tests/ui/bits_param/too_few_bits.rs
type SignInteger (line 4) | pub struct SignInteger {
function main (line 9) | fn main() {}
FILE: tests/ui/bits_param/too_many_bits.rs
type SignInteger (line 4) | pub struct SignInteger {
function main (line 9) | fn main() {}
FILE: tests/ui/bytes_param/duplicate_parameters.rs
type Base (line 5) | pub struct Base {
function main (line 12) | fn main() {}
FILE: tests/ui/bytes_param/fewer_bytes_than_expected.rs
type Base (line 5) | pub struct Base {
function main (line 12) | fn main() {
FILE: tests/ui/bytes_param/invalid_int_value.rs
type Base (line 5) | pub struct Base {
function main (line 12) | fn main() {}
FILE: tests/ui/bytes_param/invalid_type.rs
type Base (line 5) | pub struct Base {
function main (line 12) | fn main() {}
FILE: tests/ui/bytes_param/more_bytes_than_expected.rs
type Base (line 5) | pub struct Base {
function main (line 12) | fn main() {
FILE: tests/ui/derive_bitfield_specifier/invalid_bits_attribute.rs
type TooManyAttrs (line 6) | enum TooManyAttrs {
type NotAnInt (line 13) | enum NotAnInt {
function main (line 18) | fn main() {}
FILE: tests/ui/derive_bitfield_specifier/non_power_of_two.rs
type Bad (line 11) | pub enum Bad {
function main (line 17) | fn main() {}
FILE: tests/ui/derive_bitfield_specifier/variant_out_of_range.rs
constant F (line 6) | const F: isize = 1;
type DeliveryMode (line 9) | pub enum DeliveryMode {
function main (line 20) | fn main() {}
FILE: tests/ui/derive_debug/duplicate_derive_debug.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/derive_debug/duplicate_derive_debug_2.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/derive_specifier/duplicate_derive_1.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/derive_specifier/duplicate_derive_2.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/derive_specifier/out_of_bounds.rs
type Header (line 5) | pub struct Header {
function main (line 10) | fn main() {}
FILE: tests/ui/empty.rs
type Unit (line 4) | struct Unit;
type Named (line 7) | struct Named {}
type Unnamed (line 10) | struct Unnamed();
function main (line 12) | fn main() {}
FILE: tests/ui/filled_param/duplicate_parameters.rs
type Base (line 5) | pub struct Base {
function main (line 12) | fn main() {}
FILE: tests/ui/filled_param/invalid_bool_value.rs
type Base (line 5) | pub struct Base {
function main (line 12) | fn main() {}
FILE: tests/ui/filled_param/invalid_specified_as_filled.rs
type UnfilledBitfield (line 5) | pub struct UnfilledBitfield {
function main (line 10) | fn main() {}
FILE: tests/ui/filled_param/invalid_specified_as_unfilled.rs
type UnfilledBitfield (line 5) | pub struct UnfilledBitfield {
function main (line 10) | fn main() {}
FILE: tests/ui/generic.rs
type Generic (line 4) | struct Generic<T> {
function main (line 8) | fn main() {}
FILE: tests/ui/invalid_struct_specifier.rs
type InvalidStructSpecifier (line 4) | pub struct InvalidStructSpecifier {
function main (line 10) | fn main() {}
FILE: tests/ui/invalid_union_specifier.rs
function main (line 10) | fn main() {}
FILE: tests/ui/multiple_of_8bits.rs
type A (line 48) | type A = B1;
type B (line 49) | type B = B3;
type C (line 50) | type C = B4;
type D (line 51) | type D = B23;
type NotQuiteFourBytes (line 54) | pub struct NotQuiteFourBytes {
function main (line 61) | fn main() {}
FILE: tests/ui/regressions/invalid_bits_field_attr.rs
type SignInt (line 5) | pub struct SignInt {
function main (line 12) | fn main() {}
FILE: tests/ui/repr/conflicting_ignored_reprs.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/repr/duplicate_repr_1.rs
type SignedInt (line 6) | pub struct SignedInt {
function main (line 11) | fn main() {}
FILE: tests/ui/repr/duplicate_repr_2.rs
type SignedInt (line 6) | pub struct SignedInt {
function main (line 11) | fn main() {}
FILE: tests/ui/repr/duplicate_repr_3.rs
type SignedInt (line 6) | pub struct SignedInt {
function main (line 11) | fn main() {}
FILE: tests/ui/repr/invalid_repr_1.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/repr/invalid_repr_2.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/repr/invalid_repr_unfilled.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/repr/invalid_repr_width_1.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/repr/invalid_repr_width_2.rs
type SignedInt (line 5) | pub struct SignedInt {
function main (line 10) | fn main() {}
FILE: tests/ui/skip/duplicate_attr.rs
type TwoExplicitAll (line 4) | pub struct TwoExplicitAll {
type TwoExplicitListAll (line 12) | pub struct TwoExplicitListAll {
type TwoExplicitGetters (line 20) | pub struct TwoExplicitGetters {
type TwoExplicitListGetters (line 28) | pub struct TwoExplicitListGetters {
type ImplicitExplicitGetters (line 35) | pub struct ImplicitExplicitGetters {
type ExplicitImplicitGetters (line 43) | pub struct ExplicitImplicitGetters {
type TwoExplicitSetters (line 51) | pub struct TwoExplicitSetters {
type TwoExplicitListSetters (line 59) | pub struct TwoExplicitListSetters {
type ImplicitExplicitSetters (line 66) | pub struct ImplicitExplicitSetters {
type ExplicitImplicitSetters (line 74) | pub struct ExplicitImplicitSetters {
function main (line 81) | fn main() {}
FILE: tests/ui/skip/invalid_specifier.rs
type InvalidSpecifier (line 4) | pub struct InvalidSpecifier {
type InvalidFormat (line 16) | pub struct InvalidFormat {
function main (line 27) | fn main() {}
FILE: tests/ui/skip/use_skipped_getter_1.rs
type Sparse (line 5) | pub struct Sparse {
function main (line 11) | fn main() {
FILE: tests/ui/skip/use_skipped_getter_2.rs
type Sparse (line 5) | pub struct Sparse {
function main (line 11) | fn main() {
FILE: tests/ui/skip/use_skipped_getter_3.rs
type Sparse (line 5) | pub struct Sparse {
function main (line 11) | fn main() {
FILE: tests/ui/skip/use_skipped_setter_1.rs
type Sparse (line 5) | pub struct Sparse {
function main (line 11) | fn main() {
FILE: tests/ui/skip/use_skipped_setter_2.rs
type Sparse (line 5) | pub struct Sparse {
function main (line 11) | fn main() {
FILE: tests/ui/skip/use_skipped_setter_3.rs
type Sparse (line 5) | pub struct Sparse {
function main (line 11) | fn main() {
FILE: tests/ui/unused_must_use.rs
type Foo (line 6) | struct Foo {
function main (line 10) | fn main() {
Condensed preview — 158 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (283K chars).
[
{
"path": ".github/workflows/rust.yml",
"chars": 4132,
"preview": "name: Rust CI\n\non:\n pull_request:\n push:\n branches:\n - master\n - '[0-9]+.[0-9]+'\n\nenv:\n CARGO_TERM_COLOR"
},
{
"path": ".gitignore",
"chars": 380,
"preview": "# Ignore build artifacts from the local tests sub-crate.\n/target/\n/impl/target/\n\n# Ignore backup files creates by cargo "
},
{
"path": "Cargo.toml",
"chars": 1366,
"preview": "[workspace]\nmembers = [\n \"impl\"\n]\ndefault-members = [\"\", \"impl\"]\nresolver = \"2\"\n\n[workspace.package]\nauthors = [\"Robi"
},
{
"path": "LICENSE-APACHE",
"chars": 10847,
"preview": " Apache License\n Version 2.0, January 2004\n http"
},
{
"path": "LICENSE-MIT",
"chars": 1023,
"preview": "Permission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentati"
},
{
"path": "README.md",
"chars": 9414,
"preview": "# Modular Bitfields for Rust\n\n[](https://crates.io/crates"
},
{
"path": "benches/benchmarks.rs",
"chars": 6221,
"preview": "#![allow(dead_code)]\n\nmod utils;\n\nuse modular_bitfield::{\n bitfield,\n specifiers::{B12, B13, B16, B3, B32, B36, B4"
},
{
"path": "benches/cmp_bitfield_crate.rs",
"chars": 3141,
"preview": "//! In this benchmark we compare our `modular_bitfield` crate generated bitfields\n//! with the ones generated by the pop"
},
{
"path": "benches/cmp_handwritten.rs",
"chars": 3113,
"preview": "//! In this benchmark we compare the macro generated code for\n//! setters and getters to some hand-written code for the "
},
{
"path": "benches/utils/handwritten.rs",
"chars": 5425,
"preview": "//! In this benchmark we compare the macro generated code for\n//! setters and getters to some hand-written code for the "
},
{
"path": "benches/utils/mod.rs",
"chars": 1431,
"preview": "#![allow(dead_code)]\n\npub mod handwritten;\n\npub use tiny_bench::black_box;\n\n/// Runs a benchmark without extremely slow "
},
{
"path": "docs/bitfield.md",
"chars": 7426,
"preview": "Applicable to structs to turn their fields into compact bitfields.\n\n# Generated API\n\nBy default this generates the follo"
},
{
"path": "docs/bitfield_specifier.md",
"chars": 2131,
"preview": "Derive macro generating an impl of the trait [`Specifier`].\n\nThis macro can be used on all unit enums and structs annota"
},
{
"path": "docs/index.md",
"chars": 11570,
"preview": "Provides macros to support bitfield structs allowing for modular use of bit-enums.\n\nThe mainly provided macros are [`#[b"
},
{
"path": "impl/Cargo.toml",
"chars": 561,
"preview": "[package]\nname = \"modular-bitfield-impl\"\ndescription = \"Derive macro for modular-bitfield\"\nauthors.workspace = true\ndocu"
},
{
"path": "impl/src/bitfield/analyse.rs",
"chars": 10846,
"preview": "use super::{\n config::{Config, ReprKind},\n field_config::{FieldConfig, SkipWhich},\n raise_skip_error, BitfieldS"
},
{
"path": "impl/src/bitfield/config.rs",
"chars": 9846,
"preview": "use super::field_config::FieldConfig;\nuse crate::errors::CombineError;\nuse core::any::TypeId;\nuse proc_macro2::Span;\nuse"
},
{
"path": "impl/src/bitfield/expand.rs",
"chars": 30389,
"preview": "use super::{\n config::{Config, ReprKind},\n field_info::FieldInfo,\n BitfieldStruct,\n};\nuse proc_macro2::TokenStr"
},
{
"path": "impl/src/bitfield/field_config.rs",
"chars": 4748,
"preview": "use super::{config::ConfigValue, raise_skip_error};\nuse crate::errors::CombineError;\nuse proc_macro2::Span;\n\n#[derive(De"
},
{
"path": "impl/src/bitfield/field_info.rs",
"chars": 2160,
"preview": "use super::{field_config::FieldConfig, BitfieldStruct, Config};\n\n/// Compactly stores all shared and useful information "
},
{
"path": "impl/src/bitfield/mod.rs",
"chars": 1657,
"preview": "mod analyse;\nmod config;\nmod expand;\nmod field_config;\nmod field_info;\nmod params;\n\nuse self::{config::Config, params::P"
},
{
"path": "impl/src/bitfield/params.rs",
"chars": 4038,
"preview": "use super::config::Config;\nuse proc_macro2::Span;\nuse syn::{parse::Result, spanned::Spanned};\n\n/// The parameters given "
},
{
"path": "impl/src/bitfield_specifier.rs",
"chars": 5835,
"preview": "use proc_macro2::TokenStream as TokenStream2;\nuse quote::quote_spanned;\nuse syn::spanned::Spanned as _;\n\npub fn generate"
},
{
"path": "impl/src/define_specifiers.rs",
"chars": 2248,
"preview": "use proc_macro2::TokenStream as TokenStream2;\nuse quote::{format_ident, quote};\n\npub fn generate(_input: TokenStream2) -"
},
{
"path": "impl/src/errors.rs",
"chars": 2089,
"preview": "/// Creates a [`syn::Error`] with the format message and infers the\n/// [`Span`](`proc_macro2::Span`) using [`ToTokens`]"
},
{
"path": "impl/src/lib.rs",
"chars": 1528,
"preview": "#![recursion_limit = \"256\"]\n#![forbid(unsafe_code)]\n#![warn(clippy::pedantic, rust_2018_idioms)]\n\n#[macro_use]\nmod error"
},
{
"path": "release.sh",
"chars": 6514,
"preview": "#!/usr/bin/env bash\n\nset -ueo pipefail\n\nDEFAULT_REPO=modular-bitfield/modular-bitfield\nDEFAULT_BRANCH=master\n\nusage() {\n"
},
{
"path": "src/error.rs",
"chars": 1204,
"preview": "//! Errors that can occur while operating on modular bitfields.\n\nuse core::fmt::Debug;\n\n/// The given value was out of r"
},
{
"path": "src/lib.rs",
"chars": 2766,
"preview": "#![doc = include_str!(\"../docs/index.md\")]\n#![no_std]\n#![forbid(unsafe_code)]\n#![warn(clippy::pedantic, missing_docs, ru"
},
{
"path": "src/private/array_bytes_conv.rs",
"chars": 2181,
"preview": "use crate::private::{checks::BitCount, SpecifierBytes};\n\npub trait ArrayBytesConversion {\n type Array;\n type Bytes"
},
{
"path": "src/private/checks.rs",
"chars": 4764,
"preview": "pub(crate) mod private {\n /// Prevents internal traits from being implemented from dependencies.\n pub trait Sealed"
},
{
"path": "src/private/impls.rs",
"chars": 1324,
"preview": "use crate::{\n error::{InvalidBitPattern, OutOfBounds},\n Specifier,\n};\n\nimpl Specifier for bool {\n const BITS: u"
},
{
"path": "src/private/mod.rs",
"chars": 454,
"preview": "mod array_bytes_conv;\npub mod checks;\nmod impls;\nmod proc;\nmod push_pop;\nmod traits;\n\npub mod static_assertions {\n pu"
},
{
"path": "src/private/proc.rs",
"chars": 4013,
"preview": "use crate::{\n private::{PopBits, PopBuffer, PushBits, PushBuffer},\n Specifier,\n};\n\n/// Creates a new push buffer w"
},
{
"path": "src/private/push_pop.rs",
"chars": 3096,
"preview": "use crate::private::{checks::private::Sealed, PopBits, PushBits};\n\n/// A bit buffer that allows to pop bits from it.\npub"
},
{
"path": "src/private/traits.rs",
"chars": 1271,
"preview": "use super::checks;\n\n/// Helper trait for underlying primitives handling of bitfields.\n///\n/// # Note\n///\n/// Must not an"
},
{
"path": "tests/bitfield/bits_param.rs",
"chars": 2149,
"preview": "//! Tests for `#[bitfield(bits = N)]`\n\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn bits_non_filled_1() {\n #[bitfiel"
},
{
"path": "tests/bitfield/bytes_param.rs",
"chars": 652,
"preview": "//! Tests for `bytes = N` #[bitfield] parameter\n\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn valid_bitfield() {\n //"
},
{
"path": "tests/bitfield/derive_bitfield_specifier.rs",
"chars": 5239,
"preview": "//! Tests specific to the `#[derive(Specifier)]` proc. macro\n\nuse modular_bitfield::prelude::*;\n\n// For some bitfield me"
},
{
"path": "tests/bitfield/derive_debug.rs",
"chars": 3826,
"preview": "//! Tests for `#[derive(Debug)]`\n\nextern crate alloc;\nuse alloc::format;\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn p"
},
{
"path": "tests/bitfield/derive_specifier.rs",
"chars": 1576,
"preview": "//! Tests for `#[derive(Specifier)]` using `#[bitfield]`\n\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn struct_in_struct"
},
{
"path": "tests/bitfield/filled_param.rs",
"chars": 1286,
"preview": "//! Tests for `filled: bool` #[bitfield] parameter\n\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn valid_bitfield_1() {\n "
},
{
"path": "tests/bitfield/mod.rs",
"chars": 14143,
"preview": "mod bits_param;\nmod bytes_param;\nmod derive_bitfield_specifier;\nmod derive_debug;\nmod derive_specifier;\nmod filled_param"
},
{
"path": "tests/bitfield/no_implicit_prelude.rs",
"chars": 1237,
"preview": "//! Checks that no implicit paths are generated by the `#[bitfield]` proc. macro\n//! and `#[derive(Specifier)]` derive m"
},
{
"path": "tests/bitfield/regressions.rs",
"chars": 2009,
"preview": "//! Tests for regressions found in published versions\n\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn deny_elided_lifetim"
},
{
"path": "tests/bitfield/repr.rs",
"chars": 3250,
"preview": "//! Tests for `#[repr(uN)]` and `#[cfg_attr(cond, repr(uN))]`\n\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn complex_use"
},
{
"path": "tests/bitfield/skip.rs",
"chars": 3911,
"preview": "//! Tests for `#[skip(..)]`\n\nextern crate alloc;\nuse alloc::format;\nuse modular_bitfield::prelude::*;\n\n#[test]\nfn double"
},
{
"path": "tests/lib.rs",
"chars": 128,
"preview": "#![no_std]\n#![deny(elided_lifetimes_in_paths)]\n#![warn(clippy::pedantic, rust_2018_idioms)]\n#![allow(dead_code)]\n\nmod bi"
},
{
"path": "tests/panic_tests.rs",
"chars": 894,
"preview": "#![allow(dead_code)]\n\nuse modular_bitfield::prelude::*;\n\n#[bitfield]\npub struct EdgeCaseBytes {\n a: B9,\n b: B6,\n "
},
{
"path": "tests/ui/access_test.rs",
"chars": 403,
"preview": "mod inner {\n use modular_bitfield::prelude::*;\n #[bitfield]\n #[derive(Copy, Clone, Eq, PartialEq, Default)]\n "
},
{
"path": "tests/ui/access_test.stderr",
"chars": 204,
"preview": "error[E0624]: method `a` is private\n --> tests/ui/access_test.rs:14:15\n |\n 6 | a: B5,\n | - private "
},
{
"path": "tests/ui/bitfield_attribute_wrong.rs",
"chars": 114,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(unknown_attribute = 0)]\nstruct Foo {\n value: u8,\n}\n\nfn main() {}\n"
},
{
"path": "tests/ui/bitfield_attribute_wrong.stderr",
"chars": 176,
"preview": "error: encountered unsupported #[bitfield] attribute\n --> tests/ui/bitfield_attribute_wrong.rs:3:12\n |\n3 | #[bitfield(u"
},
{
"path": "tests/ui/bits_attribute_wrong.rs",
"chars": 627,
"preview": "use modular_bitfield::prelude::*;\n\n#[derive(Specifier, Debug)]\npub enum TriggerMode {\n Edge = 0,\n Level = 1,\n}\n\n#["
},
{
"path": "tests/ui/bits_attribute_wrong.stderr",
"chars": 871,
"preview": "error: encountered invalid value type for #[bits = N]\n --> tests/ui/bits_attribute_wrong.rs:20:14\n |\n20 | #[bits "
},
{
"path": "tests/ui/bits_param/conflicting_params.rs",
"chars": 142,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = 16, bytes = 4)]\npub struct SignInteger {\n sign: bool,\n value:"
},
{
"path": "tests/ui/bits_param/conflicting_params.stderr",
"chars": 648,
"preview": "error: encountered conflicting `bits = 16` and `bytes = 4` parameters\n --> tests/ui/bits_param/conflicting_params.rs:3:1"
},
{
"path": "tests/ui/bits_param/conflicting_repr.rs",
"chars": 144,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = 16)]\n#[repr(u32)]\npub struct SignInteger {\n sign: bool,\n valu"
},
{
"path": "tests/ui/bits_param/conflicting_repr.stderr",
"chars": 572,
"preview": "error: encountered conflicting `bits = 16` and #[repr(u32)] parameters\n --> tests/ui/bits_param/conflicting_repr.rs:3:1\n"
},
{
"path": "tests/ui/bits_param/duplicate_param_1.rs",
"chars": 142,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = 32, bits = 32)]\npub struct SignInteger {\n sign: bool,\n value:"
},
{
"path": "tests/ui/bits_param/duplicate_param_1.stderr",
"chars": 343,
"preview": "error: encountered duplicate `bits` parameter: duplicate set to 32\n --> tests/ui/bits_param/duplicate_param_1.rs:3:23\n "
},
{
"path": "tests/ui/bits_param/duplicate_param_2.rs",
"chars": 142,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = 32, bits = 16)]\npub struct SignInteger {\n sign: bool,\n value:"
},
{
"path": "tests/ui/bits_param/duplicate_param_2.stderr",
"chars": 343,
"preview": "error: encountered duplicate `bits` parameter: duplicate set to 32\n --> tests/ui/bits_param/duplicate_param_2.rs:3:23\n "
},
{
"path": "tests/ui/bits_param/invalid_param_type.rs",
"chars": 170,
"preview": "use modular_bitfield::prelude::*;\n\nconst NOT_LITERAL: u32 = 32;\n\n#[bitfield(bits = NOT_LITERAL)]\npub struct SignInteger "
},
{
"path": "tests/ui/bits_param/invalid_param_type.stderr",
"chars": 201,
"preview": "error: encountered invalid value argument for #[bitfield] `bits` parameter\n --> tests/ui/bits_param/invalid_param_type.r"
},
{
"path": "tests/ui/bits_param/invalid_param_value_1.rs",
"chars": 133,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = true)]\npub struct SignInteger {\n sign: bool,\n value: B31,\n}\n\n"
},
{
"path": "tests/ui/bits_param/invalid_param_value_1.stderr",
"chars": 190,
"preview": "error: encountered invalid value argument for #[bitfield] `bits` parameter\n --> tests/ui/bits_param/invalid_param_value_"
},
{
"path": "tests/ui/bits_param/invalid_param_value_2.rs",
"chars": 131,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = -1)]\npub struct SignInteger {\n sign: bool,\n value: B31,\n}\n\nfn"
},
{
"path": "tests/ui/bits_param/invalid_param_value_2.stderr",
"chars": 208,
"preview": "error: encountered malformatted integer value for `bits` parameter: invalid digit found in string\n --> tests/ui/bits_par"
},
{
"path": "tests/ui/bits_param/missing_param_value.rs",
"chars": 206,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits)]\npub struct Missing {\n sign: bool,\n value: B31,\n}\n\n#[bitfield("
},
{
"path": "tests/ui/bits_param/missing_param_value.stderr",
"chars": 378,
"preview": "error: expected `=`\n --> tests/ui/bits_param/missing_param_value.rs:3:1\n |\n3 | #[bitfield(bits)]\n | ^^^^^^^^^^^^^^^^^\n"
},
{
"path": "tests/ui/bits_param/too_few_bits.rs",
"chars": 131,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = 16)]\npub struct SignInteger {\n sign: bool,\n value: B31,\n}\n\nfn"
},
{
"path": "tests/ui/bits_param/too_few_bits.stderr",
"chars": 1799,
"preview": "error[E0277]: the trait bound `False: FillsUnalignedBits` is not satisfied\n --> tests/ui/bits_param/too_few_bits.rs:4:1\n"
},
{
"path": "tests/ui/bits_param/too_many_bits.rs",
"chars": 131,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(bits = 33)]\npub struct SignInteger {\n sign: bool,\n value: B31,\n}\n\nfn"
},
{
"path": "tests/ui/bits_param/too_many_bits.stderr",
"chars": 1801,
"preview": "error[E0277]: the trait bound `False: FillsUnalignedBits` is not satisfied\n --> tests/ui/bits_param/too_many_bits.rs:4:1"
},
{
"path": "tests/ui/bytes_param/duplicate_parameters.rs",
"chars": 191,
"preview": "use modular_bitfield::prelude::*;\n\n// There are 2 duplicate bytes parameters.\n#[bitfield(bytes = 4, bytes = 4)]\npub stru"
},
{
"path": "tests/ui/bytes_param/duplicate_parameters.stderr",
"chars": 354,
"preview": "error: encountered duplicate `bytes` parameter: duplicate set to 4\n --> tests/ui/bytes_param/duplicate_parameters.rs:4:2"
},
{
"path": "tests/ui/bytes_param/fewer_bytes_than_expected.rs",
"chars": 228,
"preview": "use modular_bitfield::prelude::*;\n\n// Requires 3 bytes in total instead of 4.\n#[bitfield(bytes = 4)]\npub struct Base {\n "
},
{
"path": "tests/ui/bytes_param/fewer_bytes_than_expected.stderr",
"chars": 470,
"preview": "error[E0512]: cannot transmute between types of different sizes, or dependently-sized types\n --> tests/ui/bytes_param/fe"
},
{
"path": "tests/ui/bytes_param/invalid_int_value.rs",
"chars": 213,
"preview": "use modular_bitfield::prelude::*;\n\n// The integer value cannot be parsed into a `usize` since it is negative.\n#[bitfield"
},
{
"path": "tests/ui/bytes_param/invalid_int_value.stderr",
"chars": 208,
"preview": "error: encountered malformatted integer value for `bytes` parameter: invalid digit found in string\n --> tests/ui/bytes_p"
},
{
"path": "tests/ui/bytes_param/invalid_type.rs",
"chars": 201,
"preview": "use modular_bitfield::prelude::*;\n\n// The bytes parameter is required to have an integer value.\n#[bitfield(bytes = true)"
},
{
"path": "tests/ui/bytes_param/invalid_type.stderr",
"chars": 185,
"preview": "error: encountered invalid value argument for #[bitfield] `bytes` parameter\n --> tests/ui/bytes_param/invalid_type.rs:4:"
},
{
"path": "tests/ui/bytes_param/more_bytes_than_expected.rs",
"chars": 229,
"preview": "use modular_bitfield::prelude::*;\n\n// Requires 6 bytes in total instead of 4.\n#[bitfield(bytes = 4)]\npub struct Base {\n "
},
{
"path": "tests/ui/bytes_param/more_bytes_than_expected.stderr",
"chars": 469,
"preview": "error[E0512]: cannot transmute between types of different sizes, or dependently-sized types\n --> tests/ui/bytes_param/mo"
},
{
"path": "tests/ui/derive_bitfield_specifier/invalid_bits_attribute.rs",
"chars": 224,
"preview": "use modular_bitfield::prelude::*;\n\n#[derive(Specifier)]\n#[bits = 1]\n#[bits = 1]\nenum TooManyAttrs {\n Zero = 0,\n On"
},
{
"path": "tests/ui/derive_bitfield_specifier/invalid_bits_attribute.stderr",
"chars": 317,
"preview": "error: More than one 'bits' attribute is not permitted\n --> tests/ui/derive_bitfield_specifier/invalid_bits_attribute.rs"
},
{
"path": "tests/ui/derive_bitfield_specifier/non_power_of_two.rs",
"chars": 433,
"preview": "// Bitfield enums with a number of variants other than a power of two should\n// fail to compile.\n//\n// (Or, if you imple"
},
{
"path": "tests/ui/derive_bitfield_specifier/non_power_of_two.stderr",
"chars": 225,
"preview": "error: #[derive(Specifier)] expected a number of variants which is a power of 2, specify #[bits = 2] if that was your in"
},
{
"path": "tests/ui/derive_bitfield_specifier/variant_out_of_range.rs",
"chars": 337,
"preview": "// Bitfield enums with any discriminant (implicit or explicit) outside of the\n// range 0..2^BITS should fail to compile."
},
{
"path": "tests/ui/derive_bitfield_specifier/variant_out_of_range.stderr",
"chars": 1893,
"preview": "error[E0277]: the trait bound `False: DiscriminantInRange` is not satisfied\n --> tests/ui/derive_bitfield_specifier/var"
},
{
"path": "tests/ui/derive_debug/duplicate_derive_debug.rs",
"chars": 152,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug)] #[derive(Debug)]\npub struct SignedInt {\n sign: bool,\n"
},
{
"path": "tests/ui/derive_debug/duplicate_derive_debug.stderr",
"chars": 364,
"preview": "error: encountered duplicate `#[derive(Debug)]` parameter\n --> tests/ui/derive_debug/duplicate_derive_debug.rs:4:27\n |\n"
},
{
"path": "tests/ui/derive_debug/duplicate_derive_debug_2.rs",
"chars": 142,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug, Debug)]\npub struct SignedInt {\n sign: bool,\n value:"
},
{
"path": "tests/ui/derive_debug/duplicate_derive_debug_2.stderr",
"chars": 338,
"preview": "error: encountered duplicate `#[derive(Debug)]` parameter\n --> tests/ui/derive_debug/duplicate_derive_debug_2.rs:4:17\n "
},
{
"path": "tests/ui/derive_specifier/duplicate_derive_1.rs",
"chars": 150,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Specifier, Specifier)]\npub struct SignedInt {\n sign: bool,\n "
},
{
"path": "tests/ui/derive_specifier/duplicate_derive_1.stderr",
"chars": 370,
"preview": "error: encountered duplicate `#[derive(Specifier)]` parameter\n --> tests/ui/derive_specifier/duplicate_derive_1.rs:4:21\n"
},
{
"path": "tests/ui/derive_specifier/duplicate_derive_2.rs",
"chars": 160,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Specifier)] #[derive(Specifier)]\npub struct SignedInt {\n sign"
},
{
"path": "tests/ui/derive_specifier/duplicate_derive_2.stderr",
"chars": 400,
"preview": "error: encountered duplicate `#[derive(Specifier)]` parameter\n --> tests/ui/derive_specifier/duplicate_derive_2.rs:4:31\n"
},
{
"path": "tests/ui/derive_specifier/out_of_bounds.rs",
"chars": 151,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(filled = false)]\n#[derive(Specifier, Debug)]\npub struct Header {\n a: B1"
},
{
"path": "tests/ui/derive_specifier/out_of_bounds.stderr",
"chars": 2501,
"preview": "error[E0277]: the trait bound `False: SpecifierHasAtMost128Bits` is not satisfied\n --> tests/ui/derive_specifier/out_of_"
},
{
"path": "tests/ui/empty.rs",
"chars": 134,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\nstruct Unit;\n\n#[bitfield]\nstruct Named {}\n\n#[bitfield]\nstruct Unnamed();\n"
},
{
"path": "tests/ui/empty.stderr",
"chars": 392,
"preview": "error: encountered invalid bitfield struct without fields\n --> tests/ui/empty.rs:4:1\n |\n4 | struct Unit;\n | ^^^^^^^^^^"
},
{
"path": "tests/ui/filled_param/duplicate_parameters.rs",
"chars": 202,
"preview": "use modular_bitfield::prelude::*;\n\n// There are 2 duplicate `filled` parameters.\n#[bitfield(filled = true, filled = true"
},
{
"path": "tests/ui/filled_param/duplicate_parameters.stderr",
"chars": 383,
"preview": "error: encountered duplicate `filled` parameter: duplicate set to true\n --> tests/ui/filled_param/duplicate_parameters.r"
},
{
"path": "tests/ui/filled_param/invalid_bool_value.rs",
"chars": 195,
"preview": "use modular_bitfield::prelude::*;\n\n// The boolean value cannot be parsed from a string.\n#[bitfield(filled = \"yes\")]\npub "
},
{
"path": "tests/ui/filled_param/invalid_bool_value.stderr",
"chars": 197,
"preview": "error: encountered invalid value argument for #[bitfield] `filled` parameter\n --> tests/ui/filled_param/invalid_bool_val"
},
{
"path": "tests/ui/filled_param/invalid_specified_as_filled.rs",
"chars": 219,
"preview": "use modular_bitfield::prelude::*;\n\n// The bitfield has exactly 15 bits and therefore is unfilled but not specified as su"
},
{
"path": "tests/ui/filled_param/invalid_specified_as_filled.stderr",
"chars": 1986,
"preview": "error[E0277]: the trait bound `SevenMod8: TotalSizeIsMultipleOfEightBits` is not satisfied\n --> tests/ui/filled_param/in"
},
{
"path": "tests/ui/filled_param/invalid_specified_as_unfilled.rs",
"chars": 218,
"preview": "use modular_bitfield::prelude::*;\n\n// The bitfield has exactly 16 bits and therefore is filled but not specified as such"
},
{
"path": "tests/ui/filled_param/invalid_specified_as_unfilled.stderr",
"chars": 2687,
"preview": "error[E0277]: the trait bound `ZeroMod8: TotalSizeIsNotMultipleOfEightBits` is not satisfied\n --> tests/ui/filled_param/"
},
{
"path": "tests/ui/generic.rs",
"chars": 120,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\nstruct Generic<T> {\n t: core::marker::PhantomData<T>,\n}\n\nfn main() {}\n"
},
{
"path": "tests/ui/generic.stderr",
"chars": 132,
"preview": "error: bitfield structs can only use const generics\n --> tests/ui/generic.rs:4:15\n |\n4 | struct Generic<T> {\n | "
},
{
"path": "tests/ui/invalid_struct_specifier.rs",
"chars": 143,
"preview": "use modular_bitfield::prelude::*;\n\n#[derive(Specifier)]\npub struct InvalidStructSpecifier {\n a: bool,\n b: B7,\n "
},
{
"path": "tests/ui/invalid_struct_specifier.stderr",
"chars": 154,
"preview": "error: structs are not supported as bitfield specifiers\n --> tests/ui/invalid_struct_specifier.rs:4:1\n |\n4 | pub struct"
},
{
"path": "tests/ui/invalid_union_specifier.rs",
"chars": 141,
"preview": "use modular_bitfield::prelude::*;\n\n#[derive(Specifier)]\npub union InvalidUnionSpecifier {\n a: bool,\n b: B7,\n c:"
},
{
"path": "tests/ui/invalid_union_specifier.stderr",
"chars": 150,
"preview": "error: unions are not supported as bitfield specifiers\n --> tests/ui/invalid_union_specifier.rs:4:1\n |\n4 | pub union In"
},
{
"path": "tests/ui/multiple_of_8bits.rs",
"chars": 2735,
"preview": "// Make it so that a bitfield with a size not a multiple of 8 bits will not\n// compile.\n//\n// Aim to make the error mess"
},
{
"path": "tests/ui/multiple_of_8bits.stderr",
"chars": 1973,
"preview": "error[E0277]: the trait bound `SevenMod8: TotalSizeIsMultipleOfEightBits` is not satisfied\n --> tests/ui/multiple_of_8b"
},
{
"path": "tests/ui/regressions/invalid_bits_field_attr.rs",
"chars": 195,
"preview": "\nuse modular_bitfield::prelude::*;\n\n#[bitfield]\npub struct SignInt {\n #[bits = 1] // This one is valid.\n sign: boo"
},
{
"path": "tests/ui/regressions/invalid_bits_field_attr.stderr",
"chars": 166,
"preview": "error: cannot find attribute `whoat_bits` in this scope\n --> $DIR/invalid_bits_field_attr.rs:8:7\n |\n8 | #[whoat_bit"
},
{
"path": "tests/ui/repr/conflicting_ignored_reprs.rs",
"chars": 209,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[repr(C, transparent, u32)] // The macro simply ignores `repr(C)` and `r"
},
{
"path": "tests/ui/repr/conflicting_ignored_reprs.stderr",
"chars": 239,
"preview": "error[E0692]: transparent struct cannot have other repr hints\n --> tests/ui/repr/conflicting_ignored_reprs.rs:4:8\n |\n4 "
},
{
"path": "tests/ui/repr/duplicate_repr_1.rs",
"chars": 144,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[repr(u32)]\n#[repr(u32)]\npub struct SignedInt {\n sign: bool,\n valu"
},
{
"path": "tests/ui/repr/duplicate_repr_1.stderr",
"chars": 288,
"preview": "error: encountered duplicate `#[repr(uN)]` parameter: duplicate set to #[repr(u32)]\n --> tests/ui/repr/duplicate_repr_1."
},
{
"path": "tests/ui/repr/duplicate_repr_2.rs",
"chars": 186,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[cfg_attr(not(test), repr(u32))]\n#[cfg_attr(not(test), repr(u32))]\npub s"
},
{
"path": "tests/ui/repr/duplicate_repr_2.stderr",
"chars": 372,
"preview": "error: encountered duplicate `#[repr(uN)]` parameter: duplicate set to #[repr(u32)]\n --> tests/ui/repr/duplicate_repr_2."
},
{
"path": "tests/ui/repr/duplicate_repr_3.rs",
"chars": 165,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[repr(u32)]\n#[cfg_attr(not(test), repr(u32))]\npub struct SignedInt {\n "
},
{
"path": "tests/ui/repr/duplicate_repr_3.stderr",
"chars": 330,
"preview": "error: encountered duplicate `#[repr(uN)]` parameter: duplicate set to #[repr(u32)]\n --> tests/ui/repr/duplicate_repr_3."
},
{
"path": "tests/ui/repr/invalid_repr_1.rs",
"chars": 135,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[repr(invalid)]\npub struct SignedInt {\n sign: bool,\n value: B31,\n}"
},
{
"path": "tests/ui/repr/invalid_repr_1.stderr",
"chars": 438,
"preview": "error[E0552]: unrecognized representation hint\n --> tests/ui/repr/invalid_repr_1.rs:4:8\n |\n4 | #[repr(invalid)]\n | "
},
{
"path": "tests/ui/repr/invalid_repr_2.rs",
"chars": 152,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[cfg_attr(all(), repr(invalid))]\npub struct SignedInt {\n sign: bool,\n"
},
{
"path": "tests/ui/repr/invalid_repr_2.stderr",
"chars": 472,
"preview": "error[E0552]: unrecognized representation hint\n --> tests/ui/repr/invalid_repr_2.rs:4:24\n |\n4 | #[cfg_attr(all(), repr("
},
{
"path": "tests/ui/repr/invalid_repr_unfilled.rs",
"chars": 147,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield(filled = false)]\n#[repr(u32)]\npub struct SignedInt {\n sign: bool,\n v"
},
{
"path": "tests/ui/repr/invalid_repr_unfilled.stderr",
"chars": 600,
"preview": "error: encountered conflicting `#[repr(u32)]` and `filled = false` parameters\n --> tests/ui/repr/invalid_repr_unfilled.r"
},
{
"path": "tests/ui/repr/invalid_repr_width_1.rs",
"chars": 148,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[repr(u16)] // Too few bits!\npub struct SignedInt {\n sign: bool,\n "
},
{
"path": "tests/ui/repr/invalid_repr_width_1.stderr",
"chars": 1008,
"preview": "error[E0277]: the trait bound `BitCount<32>: IsU16Compatible` is not satisfied\n --> tests/ui/repr/invalid_repr_width_1.r"
},
{
"path": "tests/ui/repr/invalid_repr_width_2.rs",
"chars": 149,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[repr(u64)] // Too many bits!\npub struct SignedInt {\n sign: bool,\n "
},
{
"path": "tests/ui/repr/invalid_repr_width_2.stderr",
"chars": 1011,
"preview": "error[E0277]: the trait bound `BitCount<32>: IsU64Compatible` is not satisfied\n --> tests/ui/repr/invalid_repr_width_2.r"
},
{
"path": "tests/ui/skip/duplicate_attr.rs",
"chars": 1190,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\npub struct TwoExplicitAll {\n #[skip]\n #[skip]\n unused_1: B15,\n "
},
{
"path": "tests/ui/skip/duplicate_attr.stderr",
"chars": 2849,
"preview": "error: encountered duplicate `#[skip]` attribute for field\n --> tests/ui/skip/duplicate_attr.rs:6:7\n |\n6 | #[skip]\n"
},
{
"path": "tests/ui/skip/invalid_specifier.rs",
"chars": 396,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\npub struct InvalidSpecifier {\n #[skip(invalid_specifier)]\n unused_1"
},
{
"path": "tests/ui/skip/invalid_specifier.stderr",
"chars": 332,
"preview": "error: encountered unknown or unsupported #[skip(..)] specifier\n --> tests/ui/skip/invalid_specifier.rs:5:12\n |\n5 | "
},
{
"path": "tests/ui/skip/use_skipped_getter_1.rs",
"chars": 277,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug)]\npub struct Sparse {\n #[skip(getters)]\n unused_1: B"
},
{
"path": "tests/ui/skip/use_skipped_getter_1.stderr",
"chars": 530,
"preview": "error[E0599]: no method named `unused_1` found for struct `Sparse` in the current scope\n --> tests/ui/skip/use_skipped_"
},
{
"path": "tests/ui/skip/use_skipped_getter_2.rs",
"chars": 261,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug)]\npub struct Sparse {\n #[skip(getters, setters)]\n un"
},
{
"path": "tests/ui/skip/use_skipped_getter_2.stderr",
"chars": 378,
"preview": "error[E0599]: no method named `unused_1` found for struct `Sparse` in the current scope\n --> tests/ui/skip/use_skipped_"
},
{
"path": "tests/ui/skip/use_skipped_getter_3.rs",
"chars": 269,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug)]\npub struct Sparse {\n #[skip(getters)] #[skip(setters)"
},
{
"path": "tests/ui/skip/use_skipped_getter_3.stderr",
"chars": 378,
"preview": "error[E0599]: no method named `unused_1` found for struct `Sparse` in the current scope\n --> tests/ui/skip/use_skipped_"
},
{
"path": "tests/ui/skip/use_skipped_setter_1.rs",
"chars": 274,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug)]\npub struct Sparse {\n #[skip(setters)]\n unused_1: B"
},
{
"path": "tests/ui/skip/use_skipped_setter_1.stderr",
"chars": 530,
"preview": "error[E0599]: no method named `set_unused_1` found for struct `Sparse` in the current scope\n --> tests/ui/skip/use_skip"
},
{
"path": "tests/ui/skip/use_skipped_setter_2.rs",
"chars": 248,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug)]\npub struct Sparse {\n #[skip(getters, setters)]\n un"
},
{
"path": "tests/ui/skip/use_skipped_setter_2.stderr",
"chars": 366,
"preview": "error[E0599]: no method named `set_unused_1` found for struct `Sparse` in the current scope\n --> tests/ui/skip/use_skip"
},
{
"path": "tests/ui/skip/use_skipped_setter_3.rs",
"chars": 256,
"preview": "use modular_bitfield::prelude::*;\n\n#[bitfield]\n#[derive(Debug)]\npub struct Sparse {\n #[skip(getters)] #[skip(setters)"
},
{
"path": "tests/ui/skip/use_skipped_setter_3.stderr",
"chars": 366,
"preview": "error[E0599]: no method named `set_unused_1` found for struct `Sparse` in the current scope\n --> tests/ui/skip/use_skip"
},
{
"path": "tests/ui/unused_must_use.rs",
"chars": 203,
"preview": "#![deny(unused_must_use)]\n\nuse modular_bitfield::prelude::*;\n\n#[bitfield]\nstruct Foo {\n a: B8,\n}\n\nfn main() {\n Foo"
},
{
"path": "tests/ui/unused_must_use.stderr",
"chars": 1239,
"preview": "error: unused return value of `Foo::new` that must be used\n --> tests/ui/unused_must_use.rs:11:5\n |\n11 | Foo::new"
},
{
"path": "tests/ui.rs",
"chars": 173,
"preview": "//! Tests for emitted diagnostics\n\n#[cfg(all(test, not(miri)))]\n#[test]\nfn ui_trybuild() {\n let t = trybuild::TestCas"
}
]
About this extraction
This page contains the full source code of the Robbepop/modular-bitfield GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 158 files (255.8 KB), approximately 72.6k tokens, and a symbol index with 388 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.