From 159c13e7b85b5ac5aa4f4e5f73081db3b07f98ca Mon Sep 17 00:00:00 2001 From: David Koloski Date: Sun, 11 Aug 2024 00:22:15 -0400 Subject: [PATCH] Update README, docs, and manifest --- .github/workflows/ci.yml | 10 +-- Cargo.toml | 21 ++--- README.md | 22 ++++-- bytecheck/Cargo.toml | 14 ++-- bytecheck/example.md | 77 +++++++++++++++++++ bytecheck/src/lib.rs | 148 +++++++----------------------------- bytecheck/src/uuid.rs | 4 +- bytecheck_derive/Cargo.toml | 9 ++- 8 files changed, 153 insertions(+), 152 deletions(-) create mode 100644 bytecheck/example.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50e981a..cd7ec49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ env: jobs: features: - name: Features / ${{ matrix.std }} ${{ matrix.simdutf8 }} ${{ matrix.external }} + name: Features / ${{ matrix.std }} ${{ matrix.derive }} ${{ matrix.external }} runs-on: ubuntu-latest strategy: fail-fast: false @@ -23,17 +23,17 @@ jobs: std: - '' - std - simdutf8: + derive: - '' - - simdutf8 + - derive external: - '' - - uuid + - simdutf8 uuid steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - run: cargo test --verbose --tests --no-default-features --features "${{ matrix.std }} ${{ matrix.simdutf8 }} ${{ matrix.external }}" + - run: cargo test --verbose --tests --no-default-features --features "${{ matrix.std }} ${{ matrix.derive }} ${{ matrix.external }}" toolchain: name: Toolchain / ${{ matrix.toolchain }} ${{ matrix.opt }} diff --git a/Cargo.toml b/Cargo.toml index a0be577..582e2bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,19 +11,22 @@ version = "0.8.0-alpha.9" authors = ["David Koloski "] edition = "2021" license = "MIT" -documentation = "https://docs.rs/bytecheck" +readme = "README.md" repository = "https://github.com/rkyv/bytecheck" +keywords = ["no_std", "validation", "serialization"] +categories = ["encoding", "no-std", "no-std::no-alloc"] [workspace.dependencies] -bytecheck = { version = "0.8.0-alpha.9", path = "bytecheck", default-features = false } -bytecheck_derive = { version = "0.8.0-alpha.9", path = "bytecheck_derive", default-features = false } -proc-macro2 = { version = "1.0", default-features = false } -ptr_meta = { version = "0.3.0-alpha.2", default-features = false } -rancor = { version = "0.1.0-alpha.9", default-features = false } +bytecheck = { version = "=0.8.0-alpha.9", path = "bytecheck", default-features = false } +bytecheck_derive = { version = "=0.8.0-alpha.9", path = "bytecheck_derive", default-features = false } +proc-macro2 = { version = "1", default-features = false } +ptr_meta = { version = "=0.3.0-alpha.3", default-features = false } +rancor = { version = "=0.1.0-alpha.9", default-features = false } simdutf8 = { version = "0.1", default-features = false } -syn = { version = "2.0", features = ["full"] } -uuid = { version = "1.4", default-features = false } -quote = { version = "1.0", default-features = false } +syn = { version = "2", default-features = false } +uuid = { version = "1", default-features = false } +quote = { version = "1", default-features = false } [patch.crates-io] +ptr_meta = { git = "https://github.com/rkyv/ptr_meta" } rancor = { git = "https://github.com/rkyv/rancor" } diff --git a/README.md b/README.md index bfa89e8..b7788aa 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,23 @@ -# bytecheck   [![Latest Version]][crates.io] [![License]][license path] +# `bytecheck` -[Latest Version]: https://img.shields.io/crates/v/bytecheck.svg +[![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] + +[crates.io badge]: https://img.shields.io/crates/v/bytecheck.svg [crates.io]: https://crates.io/crates/bytecheck -[License]: https://img.shields.io/badge/license-MIT-blue.svg -[license path]: https://github.com/djkoloski/bytecheck/blob/master/LICENSE +[docs badge]: https://img.shields.io/docsrs/bytecheck +[docs]: https://docs.rs/bytecheck +[license badge]: https://img.shields.io/badge/license-MIT-blue.svg +[license]: https://github.com/rkyv/bytecheck/blob/master/LICENSE + +bytecheck is a memory validation framework for Rust. + +## Documentation -bytecheck is a type validation framework for Rust. +- [bytecheck](https://docs.rs/bytecheck), a memory validation framework for Rust +- [bytecheck_derive](https://docs.rs/bytecheck_derive), the derive macro for + bytecheck -## bytecheck in action +## Example ```rust use bytecheck::{CheckBytes, check_bytes, rancor::Failure}; diff --git a/bytecheck/Cargo.toml b/bytecheck/Cargo.toml index e78f8f6..14524cb 100644 --- a/bytecheck/Cargo.toml +++ b/bytecheck/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "bytecheck" -description = "Derive macro for bytecheck" -documentation = "https://docs.rs/bytecheck" -keywords = ["bytecheck", "validation", "zero-copy", "rkyv"] -categories = ["encoding"] +description = "Memory validation framework for Rust" version.workspace = true -edition.workspace = true authors.workspace = true +edition.workspace = true license.workspace = true +readme.workspace = true repository.workspace = true +keywords.workspace = true +categories.workspace = true +documentation = "https://docs.rs/bytecheck" [dependencies] bytecheck_derive = { workspace = true, default-features = false } @@ -27,5 +28,6 @@ simdutf8 = { workspace = true, optional = true } uuid = { workspace = true, optional = true } [features] -default = ["simdutf8", "std"] +default = ["derive", "simdutf8", "std"] +derive = [] std = ["ptr_meta/std", "rancor/std", "simdutf8?/std", "uuid?/std"] diff --git a/bytecheck/example.md b/bytecheck/example.md new file mode 100644 index 0000000..b1ed7ac --- /dev/null +++ b/bytecheck/example.md @@ -0,0 +1,77 @@ +```rust +use bytecheck::{CheckBytes, check_bytes, rancor::Failure}; + +#[derive(CheckBytes, Debug)] +#[repr(C)] +struct Test { + a: u32, + b: char, + c: bool, +} + +#[repr(C, align(4))] +struct Aligned([u8; N]); + +macro_rules! bytes { + ($($byte:literal,)*) => { + (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() + }; + ($($byte:literal),*) => { + bytes!($($byte,)*) + }; +} + +// In this example, the architecture is assumed to be little-endian +#[cfg(target_endian = "little")] +unsafe { + // These are valid bytes for a `Test` + check_bytes::( + bytes![ + 0u8, 0u8, 0u8, 0u8, + 0x78u8, 0u8, 0u8, 0u8, + 1u8, 255u8, 255u8, 255u8, + ].cast() + ).unwrap(); + + // Changing the bytes for the u32 is OK, any bytes are a valid u32 + check_bytes::( + bytes![ + 42u8, 16u8, 20u8, 3u8, + 0x78u8, 0u8, 0u8, 0u8, + 1u8, 255u8, 255u8, 255u8, + ].cast() + ).unwrap(); + + // Characters outside the valid ranges are invalid + check_bytes::( + bytes![ + 0u8, 0u8, 0u8, 0u8, + 0x00u8, 0xd8u8, 0u8, 0u8, + 1u8, 255u8, 255u8, 255u8, + ].cast() + ).unwrap_err(); + check_bytes::( + bytes![ + 0u8, 0u8, 0u8, 0u8, + 0x00u8, 0x00u8, 0x11u8, 0u8, + 1u8, 255u8, 255u8, 255u8, + ].cast() + ).unwrap_err(); + + // 0 is a valid boolean value (false) but 2 is not + check_bytes::( + bytes![ + 0u8, 0u8, 0u8, 0u8, + 0x78u8, 0u8, 0u8, 0u8, + 0u8, 255u8, 255u8, 255u8, + ].cast() + ).unwrap(); + check_bytes::( + bytes![ + 0u8, 0u8, 0u8, 0u8, + 0x78u8, 0u8, 0u8, 0u8, + 2u8, 255u8, 255u8, 255u8, + ].cast() + ).unwrap_err(); +} +``` diff --git a/bytecheck/src/lib.rs b/bytecheck/src/lib.rs index b993395..fc9ebc5 100644 --- a/bytecheck/src/lib.rs +++ b/bytecheck/src/lib.rs @@ -1,6 +1,6 @@ //! # bytecheck //! -//! bytecheck is a type validation framework for Rust. +//! bytecheck is a memory validation framework for Rust. //! //! For some types, creating an invalid value immediately results in undefined //! behavior. This can cause some issues when trying to validate potentially @@ -23,115 +23,28 @@ //! The layouts of types may change between compiler versions, or even different //! compilations. To guarantee stable type layout between compilations, structs, //! enums, and unions can be annotated with `#[repr(C)]`, and enums specifically -//! can be annotated with `#[repr(int)]` or `#[repr(C, int)]` as well. -//! -//! See [the reference's page on type layout][reference] for more details. +//! can be annotated with `#[repr(int)]` or `#[repr(C, int)]` as well. See +//! [the reference's page on type layout][reference] for more details. //! //! [reference]: https://doc.rust-lang.org/reference/type-layout.html //! -//! ## Examples -//! -//! ``` -//! use bytecheck::{check_bytes, rancor::Failure, CheckBytes}; -//! -//! #[derive(CheckBytes, Debug)] -//! #[repr(C)] -//! struct Test { -//! a: u32, -//! b: char, -//! c: bool, -//! } -//! -//! #[repr(C, align(4))] -//! struct Aligned([u8; N]); -//! -//! macro_rules! bytes { -//! ($($byte:literal,)*) => { -//! (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() -//! }; -//! ($($byte:literal),*) => { -//! bytes!($($byte,)*) -//! }; -//! } -//! -//! // In this example, the architecture is assumed to be little-endian -//! #[cfg(target_endian = "little")] -//! unsafe { -//! // These are valid bytes for a `Test` -//! check_bytes::( -//! bytes![ -//! 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, -//! 255u8, -//! ] -//! .cast(), -//! ) -//! .unwrap(); -//! -//! // Changing the bytes for the u32 is OK, any bytes are a valid u32 -//! check_bytes::( -//! bytes![ -//! 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, -//! 255u8, 255u8, -//! ] -//! .cast(), -//! ) -//! .unwrap(); -//! -//! // Characters outside the valid ranges are invalid -//! check_bytes::( -//! bytes![ -//! 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, -//! 255u8, 255u8, -//! ] -//! .cast(), -//! ) -//! .unwrap_err(); -//! check_bytes::( -//! bytes![ -//! 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, -//! 255u8, 255u8, -//! ] -//! .cast(), -//! ) -//! .unwrap_err(); -//! -//! // 0 is a valid boolean value (false) but 2 is not -//! check_bytes::( -//! bytes![ -//! 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, -//! 255u8, -//! ] -//! .cast(), -//! ) -//! .unwrap(); -//! check_bytes::( -//! bytes![ -//! 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, -//! 255u8, -//! ] -//! .cast(), -//! ) -//! .unwrap_err(); -//! } -//! ``` -//! //! ## Features //! -//! - `std`: (Enabled by default) Enables standard library support. -//! -//! ## Crate support +//! - `derive`: Re-exports the macros from `bytecheck_derive`. Enabled by +//! default. +//! - `std`: Enables standard library support. Enabled by default. +//! - `simdutf8`: Uses the `simdutf8` crate to validate UTF-8 strings. Enabled +//! by default. //! -//! Some common crates need to be supported by bytecheck before an official -//! integration has been made. Support is provided by bytecheck for these -//! crates, but in the future crates should depend on bytecheck and provide -//! their own implementations. The crates that already have support provided by -//! bytecheck should work toward integrating the implementations into -//! themselves. +//! ### Crates //! -//! Crates supported by bytecheck: +//! Bytecheck provides integrations for some common crates by default. In the +//! future, crates should depend on bytecheck and provide their own integration. //! //! - [`uuid`](https://docs.rs/uuid) - +//! +//! ## Example +#![doc = include_str!("../example.md")] #![deny( future_incompatible, missing_docs, @@ -146,6 +59,7 @@ rustdoc::missing_crate_level_docs )] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg, doc_auto_cfg))] // Support for various common crates. These are primarily to get users off the // ground and build some momentum. @@ -156,7 +70,7 @@ // bytecheck support in the crate instead. #[cfg(feature = "uuid")] -pub mod uuid; +mod uuid; #[cfg(not(feature = "simdutf8"))] use core::str::from_utf8; @@ -169,20 +83,18 @@ use core::sync::atomic::{AtomicI32, AtomicU32}; #[cfg(target_has_atomic = "64")] use core::sync::atomic::{AtomicI64, AtomicU64}; use core::{ - cell::{Cell, UnsafeCell}, - fmt, - marker::{PhantomData, PhantomPinned}, - mem::ManuallyDrop, - num::{ + cell::{Cell, UnsafeCell}, ffi::CStr, fmt, marker::{PhantomData, PhantomPinned}, mem::ManuallyDrop, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, - }, - ops, ptr, + }, ops, ptr }; +#[cfg(feature = "derive")] pub use bytecheck_derive::CheckBytes; pub use rancor; -use rancor::{fail, Fallible, ResultExt as _, Source, Strategy, Trace}; +use rancor::{ + fail, Fallible, ResultExt as _, Source, StdError, Strategy, Trace, +}; #[cfg(feature = "simdutf8")] use simdutf8::basic::from_utf8; @@ -424,8 +336,7 @@ impl fmt::Display for BoolCheckError { } } -#[cfg(feature = "std")] -impl std::error::Error for BoolCheckError {} +impl StdError for BoolCheckError {} // SAFETY: A bool is a one byte value that must either be 0 or 1. `check_bytes` // only returns `Ok` if `value` is 0 or 1. @@ -618,7 +529,7 @@ where value: *const Self, context: &mut C, ) -> Result<(), C::Error> { - let (data_address, len) = ptr_meta::PtrExt::to_raw_parts(value); + let (data_address, len) = ptr_meta::to_raw_parts(value); let base = data_address.cast::(); for index in 0..len { // SAFETY: The caller has guaranteed that `value` points to enough @@ -656,10 +567,9 @@ where } } -#[cfg(feature = "std")] // SAFETY: `check_bytes` only returns `Ok` when the bytes constitute a valid // `CStr` per `CStr::from_bytes_with_nul`. -unsafe impl CheckBytes for std::ffi::CStr +unsafe impl CheckBytes for CStr where C: Fallible + ?Sized, C::Error: Source, @@ -674,7 +584,7 @@ where // and points to enough bytes for its `CStr`. Because a `u8` slice has // the same layout as a `CStr`, we can dereference it for validation. let slice = unsafe { &*slice_ptr }; - std::ffi::CStr::from_bytes_with_nul(slice).into_error()?; + CStr::from_bytes_with_nul(slice).into_error()?; Ok(()) } } @@ -738,8 +648,7 @@ impl fmt::Display for InvalidEnumDiscriminantError { } } -#[cfg(feature = "std")] -impl std::error::Error for InvalidEnumDiscriminantError where +impl StdError for InvalidEnumDiscriminantError where T: fmt::Debug + fmt::Display { } @@ -933,8 +842,7 @@ impl fmt::Display for NonZeroCheckError { } } -#[cfg(feature = "std")] -impl std::error::Error for NonZeroCheckError {} +impl StdError for NonZeroCheckError {} macro_rules! impl_nonzero { ($nonzero:ident, $underlying:ident) => { diff --git a/bytecheck/src/uuid.rs b/bytecheck/src/uuid.rs index 6754778..a684da8 100644 --- a/bytecheck/src/uuid.rs +++ b/bytecheck/src/uuid.rs @@ -1,5 +1,3 @@ -//! [`CheckBytes`] implementations for uuid. - use uuid::Uuid; use crate::{rancor::Fallible, CheckBytes}; @@ -14,7 +12,7 @@ unsafe impl CheckBytes for Uuid { } #[cfg(test)] -mod bytecheck_tests { +mod tests { use uuid::Uuid; use crate::{check_bytes, rancor::Infallible}; diff --git a/bytecheck_derive/Cargo.toml b/bytecheck_derive/Cargo.toml index 523d407..ce7f1b8 100644 --- a/bytecheck_derive/Cargo.toml +++ b/bytecheck_derive/Cargo.toml @@ -1,12 +1,15 @@ [package] name = "bytecheck_derive" description = "Derive macro for bytecheck" -documentation = "https://docs.rs/bytecheck_derive" version.workspace = true -edition.workspace = true authors.workspace = true +edition.workspace = true license.workspace = true +readme.workspace = true repository.workspace = true +keywords.workspace = true +categories.workspace = true +documentation = "https://docs.rs/bytecheck_derive" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,5 +18,5 @@ proc-macro = true [dependencies] proc-macro2.workspace = true -syn.workspace = true +syn = { workspace = true, features = ["clone-impls", "derive", "full", "parsing", "printing", "proc-macro"] } quote.workspace = true