diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index 12dbd846..168a0c6b 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -23,7 +23,7 @@ jobs: echo `pwd`/mdbook >> $GITHUB_PATH - name: Test and Build Book run: | - rustup default nightly-2024-09-07 + rustup default nightly-2024-09-01 cargo clean --target-dir mdbook-target cargo build --all-features --target-dir mdbook-target mdbook test -L mdbook-target/debug/deps wtx-docs && mdbook build wtx-docs diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 405e4362..1351bbcf 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2024-09-07 + toolchain: nightly-2024-09-01 - uses: Swatinem/rust-cache@v2 - run: .scripts/autobahn-fuzzingclient.sh ci @@ -25,7 +25,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2024-09-07 + toolchain: nightly-2024-09-01 - uses: Swatinem/rust-cache@v2 - run: .scripts/autobahn-fuzzingserver.sh ci @@ -37,7 +37,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2024-09-07 + toolchain: nightly-2024-09-01 - uses: actions-rs/install@v0.1 with: crate: cargo-fuzz @@ -52,7 +52,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2024-09-07 + toolchain: nightly-2024-09-01 - uses: Swatinem/rust-cache@v2 - run: .scripts/h2spec.sh ci @@ -64,7 +64,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2024-09-07 + toolchain: nightly-2024-09-01 - uses: Swatinem/rust-cache@v2 - run: sudo apt install docker-compose -y - run: docker-compose -f .test-utils/docker-compose.yml up -d @@ -85,7 +85,7 @@ jobs: components: clippy, rustfmt override: true profile: minimal - toolchain: nightly-2024-09-07 + toolchain: nightly-2024-09-01 - uses: Swatinem/rust-cache@v2 - run: .scripts/internal-tests-0.sh @@ -102,6 +102,6 @@ jobs: components: clippy, rustfmt override: true profile: minimal - toolchain: nightly-2024-09-07 + toolchain: nightly-2024-09-01 - uses: Swatinem/rust-cache@v2 - run: .scripts/internal-tests-1.sh diff --git a/.scripts/internal-tests-0.sh b/.scripts/internal-tests-0.sh index 9c66cad5..b2610329 100755 --- a/.scripts/internal-tests-0.sh +++ b/.scripts/internal-tests-0.sh @@ -3,7 +3,7 @@ . "$(dirname "$0")/common.sh" --source-only $rt rustfmt -$rt clippy -Aclippy::panic_in_result_fn +$rt clippy -Aclippy::little-endian-bytes,-Aclippy::needless-return,-Aclippy::panic-in-result-fn cargo miri test -p wtx diff --git a/.scripts/internal-tests-1.sh b/.scripts/internal-tests-1.sh index 09b33b16..c2585894 100755 --- a/.scripts/internal-tests-1.sh +++ b/.scripts/internal-tests-1.sh @@ -6,7 +6,7 @@ cargo check --all-features --all-targets # WTX Docs -rustup default nightly-2024-09-07 +rustup default nightly-2024-09-01 cargo clean --target-dir mdbook-target cargo build --all-features --target-dir mdbook-target mdbook test -L mdbook-target/debug/deps wtx-docs \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ed27aefb..d873b0f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,9 +599,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libfuzzer-sys" @@ -1051,9 +1051,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1332,9 +1332,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -1633,9 +1633,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/README.md b/README.md index e9710867..e5ace4e7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![crates.io](https://img.shields.io/crates/v/wtx.svg)](https://crates.io/crates/wtx) [![Documentation](https://docs.rs/wtx/badge.svg)](https://docs.rs/wtx) [![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/c410-f3r/wtx/blob/main/LICENSE) -[![Rustc](https://img.shields.io/badge/rustc-nightly-blue")](https://blog.rust-lang.org/2023/12/28/Rust-1.80.0.html) +[![Rustc](https://img.shields.io/badge/rustc-1.82-lightgray")](https://blog.rust-lang.org/2023/12/28/Rust-1.80.0.html) A collection of different transport implementations and related tools focused primarily on web technologies. Contains the implementations of 5 IETF RFCs ([RFC6265](https://datatracker.ietf.org/doc/html/rfc6265), [RFC6455](https://datatracker.ietf.org/doc/html/rfc6455), [RFC7541](https://datatracker.ietf.org/doc/html/rfc7541), [RFC7692](https://datatracker.ietf.org/doc/html/rfc7692), [RFC9113](https://datatracker.ietf.org/doc/html/rfc9113)), 2 formal specifications ([gRPC](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md), [PostgreSQL](https://www.postgresql.org/docs/16/protocol.html)) and several other invented ideas. diff --git a/rust-toolchain b/rust-toolchain index eddff5d7..2f003195 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-09-07" +channel = "nightly-2024-09-01" components = ["clippy", "miri", "rustfmt", "rust-src"] profile = "minimal" diff --git a/wtx-instances/Cargo.toml b/wtx-instances/Cargo.toml index 24824880..794f94dc 100644 --- a/wtx-instances/Cargo.toml +++ b/wtx-instances/Cargo.toml @@ -108,7 +108,7 @@ pb-rs = { default-features = false, version = "0.10" } argon2 = { default-features = false, version = "0.5" } quick-protobuf = { default-features = false, version = "0.8" } rand_chacha = { default-features = false, version = "0.3" } -rand_core = { default-features = false, feature = ["getrandom"], version = "0.6" } +rand_core = { default-features = false, features = ["getrandom"], version = "0.6" } serde = { default-features = false, version = "1.0" } serde_json = { default-features = false, features = ["alloc"], version = "1.0" } tokio = { default-features = false, features = ["macros", "rt-multi-thread"], version = "1.0" } diff --git a/wtx-instances/http-server-framework-examples/http-server-framework-session.rs b/wtx-instances/http-server-framework-examples/http-server-framework-session.rs index 15703e9b..cf034649 100644 --- a/wtx-instances/http-server-framework-examples/http-server-framework-session.rs +++ b/wtx-instances/http-server-framework-examples/http-server-framework-session.rs @@ -30,7 +30,6 @@ use wtx::{ server_framework::{get, post, Router, ServerFrameworkBuilder, State, StateClean}, ReqResBuffer, ReqResData, SessionDecoder, SessionEnforcer, SessionTokio, StatusCode, }, - misc::Rng, pool::{PostgresRM, SimplePoolTokio}, }; @@ -62,9 +61,7 @@ async fn main() -> wtx::Result<()> { )?; let pool = Pool::new(4, PostgresRM::tokio("postgres://USER:PASSWORD@localhost/DB_NAME".into())); let mut rng = ChaCha20Rng::from_entropy(); - let mut key = [0; 16]; - rng.fill_slice(&mut key); - let (expired_sessions, session) = Session::builder(key, pool).build(); + let (expired_sessions, session) = Session::builder(pool).build_generating_key(&mut rng); tokio::spawn(async move { if let Err(err) = expired_sessions.await { eprintln!("{err}"); diff --git a/wtx-macros/tests/ui.rs b/wtx-macros/tests/ui.rs index 9399d5f9..45469133 100644 --- a/wtx-macros/tests/ui.rs +++ b/wtx-macros/tests/ui.rs @@ -1,3 +1,5 @@ +//! Ui + #[test] fn ui() { let t = trybuild::TestCases::new(); diff --git a/wtx/Cargo.toml b/wtx/Cargo.toml index 5d2dd91d..e088aa02 100644 --- a/wtx/Cargo.toml +++ b/wtx/Cargo.toml @@ -30,7 +30,7 @@ sha1 = { default-features = false, optional = true, version = "0.10" } sha2 = { default-features = false, optional = true, version = "0.10" } simdutf8 = { default-features = false, features = ["aarch64_neon"], optional = true, version = "0.1" } test-strategy = { default-features = false, optional = true, version = "0.4" } -tokio = { default-features = false, features = ["io-util", "net", "rt", "sync", "time"], optional = true, version = "1.0" } +tokio = { default-features = false, features = ["io-util", "net", "sync", "time"], optional = true, version = "1.0" } tokio-rustls = { default-features = false, features = ["ring"], optional = true, version = "0.26" } tracing = { default-features = false, features = ["attributes"], optional = true, version = "0.1" } tracing-subscriber = { default-features = false, features = ["env-filter", "fmt"], optional = true, version = "0.3" } @@ -113,6 +113,7 @@ license = "Apache-2.0" name = "wtx" readme = "README.md" repository = "https://github.com/c410-f3r/wtx" +rust-version = "1.82" version = "0.20.0" [package.metadata.docs.rs] diff --git a/wtx/src/data_transformation/format/graph_ql/graph_ql_request.rs b/wtx/src/data_transformation/format/graph_ql/graph_ql_request.rs index 5384bc4f..da0dfe38 100644 --- a/wtx/src/data_transformation/format/graph_ql/graph_ql_request.rs +++ b/wtx/src/data_transformation/format/graph_ql/graph_ql_request.rs @@ -1,4 +1,4 @@ -/// `GraphQL` request or operation, can be a query or a mutation. +/// `GraphQL` request/operation, can be a query or a mutation. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct GraphQlRequest { @@ -18,12 +18,13 @@ mod serde_json { data_transformation::{dnsn::SerdeJson, format::GraphQlRequest}, misc::Vector, }; + use serde::Serialize; impl crate::data_transformation::dnsn::Serialize for GraphQlRequest where - ON: serde::Serialize, - Q: serde::Serialize, - V: serde::Serialize, + ON: Serialize, + Q: Serialize, + V: Serialize, { #[inline] fn to_bytes(&mut self, bytes: &mut Vector, _: &mut SerdeJson) -> crate::Result<()> { diff --git a/wtx/src/database/client/postgres/tys.rs b/wtx/src/database/client/postgres/tys.rs index d7b4f08a..90921ba8 100644 --- a/wtx/src/database/client/postgres/tys.rs +++ b/wtx/src/database/client/postgres/tys.rs @@ -372,7 +372,7 @@ mod pg_numeric { curr_slice = local_rest; } _PgNumeric::Number { - digits: ArrayVector::from_parts(array, digits.into()), + digits: ArrayVector::from_parts(array, Some(digits.into())), scale, sign: Sign::try_from(sign)?, weight, @@ -659,8 +659,8 @@ mod rust_decimal { } digits.reverse(); - let after_decimal = usize::from(scale.wrapping_add(3) / 4); - let weight = digits.len().wrapping_sub(after_decimal).wrapping_sub(1) as i16; + let after_decimal = scale.wrapping_add(3) / 4; + let weight = digits.len().wrapping_sub(after_decimal.into()).wrapping_sub(1) as i16; while let Some(&0) = digits.last() { let _ = digits.pop(); diff --git a/wtx/src/http/server_framework.rs b/wtx/src/http/server_framework.rs index e266f69a..a8a19deb 100644 --- a/wtx/src/http/server_framework.rs +++ b/wtx/src/http/server_framework.rs @@ -37,7 +37,7 @@ pub use res_finalizer::ResFinalizer; pub use route_wrappers::{get, json, post, Get, Json, Post}; pub use router::Router; pub use server_framework_builder::ServerFrameworkBuilder; -pub use state::{State, StateClean}; +pub use state::{State, StateClean, StateGeneric}; /// Server #[derive(Debug)] @@ -53,14 +53,15 @@ where CA: Clone + ConnAux + Send + 'static, CAC: Clone + Fn() -> CA::Init + Send + 'static, E: From + Send + 'static, - P: Send + 'static, + P: PathManagement + Send + 'static, RA: ReqAux + Send + 'static, RAC: Clone + Fn() -> RA::Init + Send + 'static, REQM: ReqMiddleware + Send + 'static, RESM: ResMiddleware + Send + 'static, Arc>: Send, - Router: - PathManagement, + Router: Send, + for<'any> &'any Arc>: Send, + for<'any> &'any Router: Send, { /// Starts listening to incoming requests based on the given `host`. #[inline] diff --git a/wtx/src/http/server_framework/param_wrappers/path_owned.rs b/wtx/src/http/server_framework/param_wrappers/path_owned.rs index 34d6f33f..423b68be 100644 --- a/wtx/src/http/server_framework/param_wrappers/path_owned.rs +++ b/wtx/src/http/server_framework/param_wrappers/path_owned.rs @@ -1,6 +1,6 @@ use crate::{ http::{ - server_framework::{param_wrappers::manage_path, Endpoint, ResFinalizer, State, StateClean}, + server_framework::{param_wrappers::manage_path, Endpoint, ResFinalizer, StateGeneric}, ReqResDataMut, Request, StatusCode, }, misc::{FnFut, FnFutWrapper}, @@ -39,13 +39,13 @@ where } } -impl Endpoint - for FnFutWrapper<(State<'_, CA, RA, RRD>, PathOwned

), F> +impl Endpoint + for FnFutWrapper<(StateGeneric<'_, CA, RA, RRD, CLEAN>, PathOwned

), F> where E: From, P: FromStr, P::Err: Into, - F: for<'any> FnFut<(State<'any, CA, RA, RRD>, PathOwned

), Result = RES>, + F: for<'any> FnFut<(StateGeneric<'any, CA, RA, RRD, CLEAN>, PathOwned

), Result = RES>, RES: ResFinalizer, RRD: ReqResDataMut, { @@ -60,31 +60,6 @@ where let uri = req.rrd.uri(); let path = manage_path(path_defs, &uri).map_err(From::from)?; let path_owned = PathOwned(P::from_str(path).map_err(Into::into)?); - self.0.call((State::new(ca, ra, req), path_owned)).await.finalize_response(req) - } -} - -impl Endpoint - for FnFutWrapper<(StateClean<'_, CA, RA, RRD>, PathOwned

), F> -where - E: From, - P: FromStr, - P::Err: Into, - F: for<'any> FnFut<(StateClean<'any, CA, RA, RRD>, PathOwned

), Result = RES>, - RES: ResFinalizer, - RRD: ReqResDataMut, -{ - #[inline] - async fn call( - &self, - ca: &mut CA, - path_defs: (u8, &[(&'static str, u8)]), - ra: &mut RA, - req: &mut Request, - ) -> Result { - let uri = req.rrd.uri(); - let path = manage_path(path_defs, &uri).map_err(From::from)?; - let path_owned = PathOwned(P::from_str(path).map_err(Into::into)?); - self.0.call((StateClean::new(ca, ra, req), path_owned)).await.finalize_response(req) + self.0.call((StateGeneric::new(ca, ra, req), path_owned)).await.finalize_response(req) } } diff --git a/wtx/src/http/server_framework/param_wrappers/path_str.rs b/wtx/src/http/server_framework/param_wrappers/path_str.rs index 0df881de..2fc51ab5 100644 --- a/wtx/src/http/server_framework/param_wrappers/path_str.rs +++ b/wtx/src/http/server_framework/param_wrappers/path_str.rs @@ -1,6 +1,6 @@ use crate::{ http::{ - server_framework::{param_wrappers::manage_path, Endpoint, ResFinalizer, StateClean}, + server_framework::{param_wrappers::manage_path, Endpoint, ResFinalizer, StateGeneric}, Headers, ReqResDataMut, Request, StatusCode, }, misc::{FnFut, FnFutWrapper}, @@ -35,12 +35,15 @@ where } } -impl Endpoint - for FnFutWrapper<(StateClean<'_, CA, RA, (&mut RRD::Body, &mut Headers)>, PathStr<'_>), F> +impl Endpoint + for FnFutWrapper< + (StateGeneric<'_, CA, RA, (&mut RRD::Body, &mut Headers), CLEAN>, PathStr<'_>), + F, + > where E: From, F: for<'any> FnFut< - (StateClean<'any, CA, RA, (&'any mut RRD::Body, &'any mut Headers)>, PathStr<'any>), + (StateGeneric<'any, CA, RA, (&'any mut RRD::Body, &'any mut Headers), CLEAN>, PathStr<'any>), Result = RES, >, RES: ResFinalizer, @@ -57,6 +60,10 @@ where let (body, headers, uri) = req.rrd.parts_mut(); let mut new_req = Request::_new(req.method, (body, headers), req.version); let path = manage_path(path_defs, &uri).map_err(From::from)?; - self.0.call((StateClean::new(ca, ra, &mut new_req), PathStr(path))).await.finalize_response(req) + self + .0 + .call((StateGeneric::new(ca, ra, &mut new_req), PathStr(path))) + .await + .finalize_response(req) } } diff --git a/wtx/src/http/server_framework/res_finalizer.rs b/wtx/src/http/server_framework/res_finalizer.rs index 7177b1ec..976a6fc6 100644 --- a/wtx/src/http/server_framework/res_finalizer.rs +++ b/wtx/src/http/server_framework/res_finalizer.rs @@ -19,24 +19,26 @@ where } } -impl<'any, CA, E, RA, RRD> ResFinalizer for (StateClean<'any, CA, RA, RRD>, StatusCode) +impl ResFinalizer for (StateClean<'_, CA, RA, RRD>, StatusCode) where E: From, RRD: ReqResDataMut, { #[inline] fn finalize_response(self, _: &mut Request) -> Result { + self.0.req.rrd.clear(); Ok(self.1) } } -impl<'any, CA, E, RA, RRD> ResFinalizer for StateClean<'any, CA, RA, RRD> +impl ResFinalizer for StateClean<'_, CA, RA, RRD> where E: From, RRD: ReqResDataMut, { #[inline] fn finalize_response(self, _: &mut Request) -> Result { + self.req.rrd.clear(); Ok(StatusCode::Ok) } } diff --git a/wtx/src/http/server_framework/state.rs b/wtx/src/http/server_framework/state.rs index 327d3d24..e7b5afc9 100644 --- a/wtx/src/http/server_framework/state.rs +++ b/wtx/src/http/server_framework/state.rs @@ -6,48 +6,19 @@ use crate::{ misc::{FnFut, FnFutWrapper}, }; -/// State of a connection -#[derive(Debug)] -pub struct State<'any, CA, RA, RRD> { - /// Connection auxiliary - pub ca: &'any mut CA, - /// Request auxiliary - pub ra: &'any mut RA, - /// Request/Response Data - pub req: &'any mut Request, -} - -impl<'any, CA, RA, RRD> State<'any, CA, RA, RRD> { - #[inline] - pub(crate) fn new(ca: &'any mut CA, ra: &'any mut RA, req: &'any mut Request) -> Self { - Self { ca, ra, req } - } -} - -impl Endpoint for FnFutWrapper<(State<'_, CA, RA, RRD>,), F> -where - F: for<'any> FnFut<(State<'any, CA, RA, RRD>,), Result = RES>, - RES: ResFinalizer, - RRD: ReqResDataMut, -{ - #[inline] - async fn call( - &self, - ca: &mut CA, - _: (u8, &[(&'static str, u8)]), - ra: &mut RA, - req: &mut Request, - ) -> Result { - self.0.call((State::new(ca, ra, req),)).await.finalize_response(req) - } -} +/// [`StateGeneric`] with original content +pub type State<'any, CA, RA, RRD> = StateGeneric<'any, CA, RA, RRD, false>; +/// [`StateGeneric`] with cleaned content +pub type StateClean<'any, CA, RA, RRD> = StateGeneric<'any, CA, RA, RRD, true>; /// State of a connection /// -/// If used in an endpoint's argument, request data is cleared. If used as the return type of an -/// endpoint, response data is cleared. +/// # If `CLEAN` is true +/// +/// When used in an endpoint's argument, request data is automatically cleaned. When used as the return type of an +/// endpoint, response data is automatically cleaned. #[derive(Debug)] -pub struct StateClean<'any, CA, RA, RRD> { +pub struct StateGeneric<'any, CA, RA, RRD, const CLEAN: bool> { /// Connection auxiliary pub ca: &'any mut CA, /// Request auxiliary @@ -56,21 +27,24 @@ pub struct StateClean<'any, CA, RA, RRD> { pub req: &'any mut Request, } -impl<'any, CA, RA, RRD> StateClean<'any, CA, RA, RRD> +impl<'any, CA, RA, RRD, const CLEAN: bool> StateGeneric<'any, CA, RA, RRD, CLEAN> where RRD: ReqResDataMut, { + /// Creates an instance with erased `RRD` data if `CLEAN` is true. #[inline] - pub(crate) fn new(ca: &'any mut CA, ra: &'any mut RA, req: &'any mut Request) -> Self { - req.rrd.clear(); + pub fn new(ca: &'any mut CA, ra: &'any mut RA, req: &'any mut Request) -> Self { + if CLEAN { + req.rrd.clear(); + } Self { ca, ra, req } } } -impl Endpoint - for FnFutWrapper<(StateClean<'_, CA, RA, RRD>,), F> +impl Endpoint + for FnFutWrapper<(StateGeneric<'_, CA, RA, RRD, CLEAN>,), F> where - F: for<'any> FnFut<(StateClean<'any, CA, RA, RRD>,), Result = RES>, + F: for<'any> FnFut<(StateGeneric<'any, CA, RA, RRD, CLEAN>,), Result = RES>, RES: ResFinalizer, RRD: ReqResDataMut, { @@ -82,13 +56,6 @@ where ra: &mut RA, req: &mut Request, ) -> Result { - self.0.call((StateClean::new(ca, ra, req),)).await.finalize_response(req) - } -} - -impl<'any, CA, RA, RRD> From> for StateClean<'any, CA, RA, RRD> { - #[inline] - fn from(from: State<'any, CA, RA, RRD>) -> Self { - Self { ca: from.ca, ra: from.ra, req: from.req } + self.0.call((StateGeneric::new(ca, ra, req),)).await.finalize_response(req) } } diff --git a/wtx/src/http/session.rs b/wtx/src/http/session.rs index 8a1d0102..f02a72a0 100644 --- a/wtx/src/http/session.rs +++ b/wtx/src/http/session.rs @@ -47,8 +47,8 @@ where { /// Allows the specification of custom parameters. #[inline] - pub fn builder(key: SessionKey, store: SS) -> SessionBuilder { - SessionBuilder::new(key, store) + pub fn builder(store: SS) -> SessionBuilder { + SessionBuilder::new(store) } /// Removes the session from the store and also modifies headers. diff --git a/wtx/src/http/session/session_builder.rs b/wtx/src/http/session/session_builder.rs index 811f108e..bd788efe 100644 --- a/wtx/src/http/session/session_builder.rs +++ b/wtx/src/http/session/session_builder.rs @@ -4,7 +4,7 @@ use crate::{ session::{SessionInner, SessionKey}, Session, SessionStore, }, - misc::{sleep, Lock, Vector}, + misc::{sleep, Lock, Rng, Vector}, }; use chrono::{DateTime, Utc}; use core::{future::Future, marker::PhantomData, time::Duration}; @@ -14,13 +14,12 @@ use core::{future::Future, marker::PhantomData, time::Duration}; pub struct SessionBuilder { pub(crate) cookie_def: CookieGeneric<&'static [u8], Vector>, pub(crate) inspection_interval: Duration, - pub(crate) key: SessionKey, pub(crate) store: SS, } impl SessionBuilder { #[inline] - pub(crate) const fn new(key: SessionKey, store: SS) -> Self { + pub(crate) const fn new(store: SS) -> Self { Self { cookie_def: CookieGeneric { domain: &[], @@ -34,24 +33,52 @@ impl SessionBuilder { value: Vector::new(), }, inspection_interval: Duration::from_secs(60 * 30), - key, store, } } + /// Creates a new [`Session`] with a random generated key. It is up to the caller to provide + /// a good RNG. + /// + /// The returned [`Future`] is responsible for deleting expired sessions at an interval defined by + /// [`Self::inspection_interval`] and should be called in a separated task. + /// + /// If the backing store already has a system that automatically removes outdated sessions like + /// SQL triggers, then the [`Future`] can be ignored. + #[inline] + pub fn build_generating_key( + self, + rng: &mut RNG, + ) -> (impl Future>, Session) + where + E: From, + L: Lock>, + RNG: Rng, + SS: Clone + SessionStore, + { + let mut key = [0; 16]; + rng.fill_slice(&mut key); + Self::build_with_key(self, key) + } + + /// Creates a new [`Session`] with the provided `key`. + /// /// The returned [`Future`] is responsible for deleting expired sessions at an interval defined by /// [`Self::inspection_interval`] and should be called in a separated task. /// /// If the backing store already has a system that automatically removes outdated sessions like /// SQL triggers, then the [`Future`] can be ignored. #[inline] - pub fn build(self) -> (impl Future>, Session) + pub fn build_with_key( + self, + key: SessionKey, + ) -> (impl Future>, Session) where E: From, L: Lock>, SS: Clone + SessionStore, { - let Self { cookie_def, inspection_interval, key, store } = self; + let Self { cookie_def, inspection_interval, store } = self; let mut local_store = store.clone(); ( async move { diff --git a/wtx/src/http2.rs b/wtx/src/http2.rs index 98630480..f6c5f264 100644 --- a/wtx/src/http2.rs +++ b/wtx/src/http2.rs @@ -52,7 +52,7 @@ use crate::{ }, misc::{ AtomicWaker, ConnectionState, Either, LeaseMut, Lock, PartitionedFilledBuffer, RefCounter, - StreamReader, StreamWriter, Usize, + StreamReader, StreamWriter, Usize, NOOP_WAKER, }, }; use alloc::sync::Arc; @@ -64,7 +64,7 @@ use core::{ mem, pin::pin, sync::atomic::{AtomicBool, Ordering}, - task::{Poll, Waker}, + task::Poll, }; pub(crate) use data_frame::DataFrame; pub(crate) use frame_init::{FrameInit, FrameInitTy}; @@ -337,7 +337,7 @@ where StreamControlRecvParams { is_stream_open: true, stream_state: StreamState::Idle, - waker: Waker::noop().clone(), + waker: NOOP_WAKER.clone(), windows: Windows::initial(hdpm.hp, hdpm.hps), }, )); diff --git a/wtx/src/http2/hpack_static_headers.rs b/wtx/src/http2/hpack_static_headers.rs index 6b9cb082..c53f2a9e 100644 --- a/wtx/src/http2/hpack_static_headers.rs +++ b/wtx/src/http2/hpack_static_headers.rs @@ -13,7 +13,7 @@ pub(crate) struct HpackStaticRequestHeaders<'bytes> { pub(crate) scheme: &'bytes [u8], } -impl<'bytes> HpackStaticRequestHeaders<'bytes> { +impl HpackStaticRequestHeaders<'_> { pub(crate) const EMPTY: Self = Self { authority: &[], method: None, path: &[], protocol: None, scheme: &[] }; diff --git a/wtx/src/http2/process_receipt_frame_ty.rs b/wtx/src/http2/process_receipt_frame_ty.rs index 2c58dbbc..d95a4fdb 100644 --- a/wtx/src/http2/process_receipt_frame_ty.rs +++ b/wtx/src/http2/process_receipt_frame_ty.rs @@ -9,7 +9,7 @@ use crate::{ DataFrame, FrameInit, HpackDecoder, Http2Error, Http2ErrorCode, Http2Params, ResetStreamFrame, Scrp, Sorp, StreamOverallRecvParams, StreamState, UriBuffer, WindowUpdateFrame, Windows, U31, }, - misc::{AtomicWaker, LeaseMut, PartitionedFilledBuffer, StreamReader, StreamWriter}, + misc::{AtomicWaker, LeaseMut, PartitionedFilledBuffer, StreamReader, StreamWriter, NOOP_WAKER}, }; use alloc::collections::VecDeque; use core::{marker::PhantomData, sync::atomic::AtomicBool, task::Waker}; @@ -32,7 +32,7 @@ pub(crate) struct ProcessReceiptFrameTy<'instance, RRB, SR, SW> { pub(crate) uri_buffer: &'instance mut UriBuffer, } -impl<'instance, RRB, SR, SW> ProcessReceiptFrameTy<'instance, RRB, SR, SW> +impl ProcessReceiptFrameTy<'_, RRB, SR, SW> where RRB: LeaseMut, SR: StreamReader, @@ -166,7 +166,7 @@ where rrb, status_code: StatusCode::Ok, stream_state, - waker: Waker::noop().clone(), + waker: NOOP_WAKER.clone(), windows: Windows::initial(self.hp, self.hps), }, )); diff --git a/wtx/src/lib.rs b/wtx/src/lib.rs index b5fb7c21..15e8db8b 100644 --- a/wtx/src/lib.rs +++ b/wtx/src/lib.rs @@ -2,7 +2,7 @@ #![cfg_attr(feature = "_bench", allow(soft_unstable))] #![cfg_attr(feature = "_bench", feature(test))] #![doc = include_str!("../README.md")] -#![feature(macro_metavar_expr, noop_waker, return_type_notation, strict_provenance)] +#![cfg_attr(feature = "tokio", feature(return_type_notation))] #![no_std] extern crate alloc; diff --git a/wtx/src/misc.rs b/wtx/src/misc.rs index 593be6ad..8c26b663 100644 --- a/wtx/src/misc.rs +++ b/wtx/src/misc.rs @@ -18,6 +18,7 @@ mod iter_wrapper; mod lease; mod lock; mod mem_transfer; +mod noop_waker; mod optimization; mod partitioned_filled_buffer; mod query_writer; @@ -41,7 +42,7 @@ mod vector; pub use self::tokio_rustls::{TokioRustlsAcceptor, TokioRustlsConnector}; pub use array_chunks::{ArrayChunks, ArrayChunksMut}; pub use array_string::{ArrayString, ArrayStringError}; -pub use array_vector::{ArrayVector, ArrayVectorError}; +pub use array_vector::{ArrayVector, ArrayVectorError, IntoIter}; pub use atomic_waker::AtomicWaker; pub use blocks_queue::{Block, BlocksQueue, BlocksQueueError}; pub use bytes_fmt::BytesFmt; @@ -57,6 +58,7 @@ pub use incomplete_utf8_char::{CompletionErr, IncompleteUtf8Char}; pub use iter_wrapper::IterWrapper; pub use lease::{Lease, LeaseMut}; pub use lock::{Lock, SyncLock}; +pub use noop_waker::NOOP_WAKER; pub use optimization::*; pub use query_writer::QueryWriter; pub use queue::{Queue, QueueError}; diff --git a/wtx/src/misc/array_vector.rs b/wtx/src/misc/array_vector.rs index 75119c15..de3c2682 100644 --- a/wtx/src/misc/array_vector.rs +++ b/wtx/src/misc/array_vector.rs @@ -1,10 +1,10 @@ +#![expect(clippy::mem_forget, reason = "out-of-bounds elements are manually dropped")] + use crate::misc::{char_slice, Lease, LeaseMut, Usize}; use core::{ - array, cmp::Ordering, fmt::{self, Debug, Formatter}, - iter, - mem::{needs_drop, MaybeUninit}, + mem::{self, needs_drop, MaybeUninit}, ops::{Deref, DerefMut}, ptr, slice, }; @@ -21,59 +21,87 @@ pub enum ArrayVectorError { } /// A wrapper around the std's vector with some additional methods to manipulate copyable data. -pub struct ArrayVector { +pub struct ArrayVector { len: u32, - data: [MaybeUninit; N], + data: [MaybeUninit; N], } -impl ArrayVector { - /// Constructs a new instance reusing any `data` elements delimited by `len`. +impl ArrayVector { + const _INSTANCE_CHECK: () = { + assert!(N <= Usize::from_u32(u32::MAX).into_usize()); + }; + const N_U32: u32 = { + let [a, b, c, d, ..] = Usize::from_usize(N).into_u64().to_le_bytes(); + u32::from_le_bytes([a, b, c, d]) + }; + const NEEDS_DROP: bool = needs_drop::(); + + /// Constructs a new instance from a fully initialized array. #[inline] - pub fn from_array(array: [D; M]) -> Self { - const { - assert!(M <= N); - } + pub fn from_array(array: [T; N]) -> Self { let mut this = Self::new(); - for elem in array { - let _rslt = this.push(elem); + this.len = Self::N_U32; + // SAFETY: The inner `data` as well as the provided `array` have the same layout in different + // memory regions + unsafe { + ptr::copy_nonoverlapping(array.as_ptr(), this.as_mut_ptr(), N); } + mem::forget(array); this } - /// Constructs a new instance reusing any `data` elements delimited by `len`. - #[expect(clippy::should_implement_trait, reason = "The std trait ins infallible")] + /// Constructs a new instance from an iterator. + /// + /// The iterator is capped at `N`. + #[expect(clippy::should_implement_trait, reason = "The std trait is infallible")] #[inline] - pub fn from_iter(iter: impl IntoIterator) -> Result { + pub fn from_iter(iter: impl IntoIterator) -> Result { let mut this = Self::new(); for elem in iter.into_iter().take(N) { - this.push(elem)?; + let _rslt = this.push(elem); } Ok(this) } - /// Constructs a new instance reusing any `data` elements delimited by `len`. - #[expect(clippy::needless_pass_by_value, reason = "false positive")] + /// Constructs a new instance reusing `data` elements optionally delimited by `len`. + /// + /// The actual length will be the smallest value among `M`, `N` and `len` #[inline] - pub fn from_parts(data: [D; N], len: u32) -> Self { - Self::instance_check(); - let n = Self::instance_u32(); - Self { - len: if len > n { n } else { len }, - // SAFETY: `data` is fully initialized within `u32` bounds and `D` does not implement `Drop` - data: unsafe { ptr::from_ref::<[D; N]>(&data).cast::<[MaybeUninit; N]>().read() }, + pub fn from_parts(mut data: [T; M], len: Option) -> Self { + let mut actual_len = u32::try_from(M).unwrap_or(u32::MAX).min(Self::N_U32); + if let Some(elem) = len { + actual_len = actual_len.min(elem); + } + let actual_len_usize = Usize::from_u32(actual_len).into_usize(); + let mut this = Self::new(); + this.len = actual_len; + // SAFETY: The inner `data` as well as the provided `data` have the same layout in different + // memory regions + unsafe { + ptr::copy_nonoverlapping(data.as_ptr(), this.as_mut_ptr(), actual_len_usize); } + if Self::NEEDS_DROP { + let diff_opt = data.len().checked_sub(actual_len_usize); + if let Some(diff @ 1..=usize::MAX) = diff_opt { + // SAFETY: Indices are within bounds + unsafe { + drop_elements(actual_len, diff, data.as_mut_ptr()); + } + }; + } + mem::forget(data); + this } /// Constructs a new empty instance. #[inline] pub const fn new() -> Self { - Self::instance_check(); Self { len: 0, data: [const { MaybeUninit::uninit() }; N] } } /// Extracts a slice containing the entire vector. #[inline] - pub const fn as_slice(&self) -> &[D] { + pub const fn as_slice(&self) -> &[T] { // SAFETY: `len` ensures initialized elements unsafe { slice::from_raw_parts(self.as_ptr(), Usize::from_u32(self.len).into_usize()) } } @@ -81,16 +109,13 @@ impl ArrayVector { /// The number of elements that can be stored. #[inline] pub const fn capacity(&self) -> u32 { - const { - let [_, _, _, _, a, b, c, d] = Usize::from_usize(N).into_u64().to_be_bytes(); - u32::from_be_bytes([a, b, c, d]) - } + Self::N_U32 } /// Clears the vector, removing all values. #[inline] pub fn clear(&mut self) { - self.len = 0; + self.truncate(0); } /// Iterates over the slice `other`, copies each element, and then appends @@ -98,7 +123,7 @@ impl ArrayVector { #[inline] pub fn extend_from_iter( &mut self, - iter: impl IntoIterator, + iter: impl IntoIterator, ) -> Result<(), ArrayVectorError> { for elem in iter { self.push(elem)?; @@ -108,7 +133,7 @@ impl ArrayVector { /// Return the inner fixed size array, if the capacity is full. #[inline] - pub fn into_inner(self) -> Result<[D; N], ArrayVectorError> { + pub fn into_inner(self) -> Result<[T; N], ArrayVectorError> { if Usize::from_u32(self.len).into_usize() >= N { // SAFETY: All elements are initialized Ok(unsafe { ptr::read(self.data.as_ptr().cast()) }) @@ -117,14 +142,21 @@ impl ArrayVector { } } + /// Returns the number of elements in the vector, also referred to as its ‘length’. + #[inline] + pub const fn len(&self) -> u32 { + self.len + } + /// Shortens the vector, removing the last element. #[inline] - pub fn pop(&mut self) -> bool { - if let Some(elem) = self.len.checked_sub(1) { - self.len = elem; - true + pub fn pop(&mut self) -> Option { + if let Some(new_len) = self.len.checked_sub(1) { + self.len = new_len; + // SAFETY: `new_len` is within bounds + Some(unsafe { self.get_owned(new_len) }) } else { - false + None } } @@ -136,63 +168,79 @@ impl ArrayVector { /// Appends an element to the back of the collection. #[inline] - pub fn push(&mut self, value: D) -> Result<(), ArrayVectorError> { - let Some(elem) = self.data.get_mut(*Usize::from(self.len)) else { - return Err(ArrayVectorError::PushOverflow); - }; - *elem = MaybeUninit::new(value); - self.len = self.len.wrapping_add(1); - Ok(()) + pub fn push(&mut self, value: T) -> Result<(), ArrayVectorError> { + self.do_push(value).map_err(|_err| ArrayVectorError::PushOverflow) } /// Shortens the vector, keeping the first `len` elements. #[inline] - pub fn truncate(&mut self, len: u32) { - self.len = len.min(self.capacity()); + pub fn truncate(&mut self, new_len: u32) { + let len = self.len; + let Some(diff @ 1..=u32::MAX) = len.checked_sub(new_len) else { + return; + }; + self.len = new_len; + if Self::NEEDS_DROP { + // SAFETY: Indices are within bounds + unsafe { + drop_elements(new_len, *Usize::from(diff), self.as_mut_ptr()); + } + } } #[inline] - const fn as_ptr(&self) -> *const D { - self.data.as_ptr().cast() + fn as_mut_ptr(&mut self) -> *mut T { + self.data.as_mut_ptr().cast() } #[inline] - fn as_mut_ptr(&mut self) -> *mut D { - self.data.as_mut_ptr().cast() + const fn as_ptr(&self) -> *const T { + self.data.as_ptr().cast() } #[inline] - const fn instance_check() { - const { - assert!(N <= Usize::from_u32(u32::MAX).into_usize() && !needs_drop::()); + fn do_push(&mut self, value: T) -> Result<(), T> { + let len = self.len; + if len >= Self::N_U32 { + return Err(value); } + // SAFETY: `len` is within `N` bounds + let dst = unsafe { self.data.as_mut_ptr().add(Usize::from_u32(len).into_usize()) }; + // SAFETY: `dst` points to valid uninitialized memory + unsafe { + ptr::write(dst, MaybeUninit::new(value)); + } + self.len = len.wrapping_add(1); + Ok(()) } #[inline] - const fn instance_u32() -> u32 { - const { - let [_, _, _, _, a, b, c, d] = Usize::from_usize(N).into_u64().to_be_bytes(); - u32::from_be_bytes([a, b, c, d]) - } + unsafe fn get_owned(&mut self, idx: u32) -> T { + // SAFETY: It is up to the caller to provide a valid index + let src = unsafe { self.data.as_ptr().add(Usize::from_u32(idx).into_usize()) }; + // SAFETY: If the index is valid, then the element exists + let elem = unsafe { ptr::read(src) }; + // SAFETY: If the index is valid, then the element is initialized + unsafe { elem.assume_init() } } } -impl ArrayVector +impl ArrayVector where - D: Clone, + T: Clone, { /// Creates a new instance with the copyable elements of `slice`. #[inline] - pub fn from_cloneable_slice(slice: &[D]) -> Result { + pub fn from_cloneable_slice(slice: &[T]) -> Result { let mut this = Self::new(); this.extend_from_cloneable_slice(slice)?; Ok(this) } - /// Iterates over the slice `other`, copies each element, and then appends + /// Iterates over the slice `other`, clones each element and then appends /// it to this vector. The `other` slice is traversed in-order. #[inline] - pub fn extend_from_cloneable_slice(&mut self, other: &[D]) -> Result<(), ArrayVectorError> { + pub fn extend_from_cloneable_slice(&mut self, other: &[T]) -> Result<(), ArrayVectorError> { for elem in other { self.push(elem.clone())?; } @@ -200,41 +248,55 @@ where } } -impl ArrayVector +impl ArrayVector where - D: Copy, + T: Copy, { /// Creates a new instance with the copyable elements of `slice`. #[inline] - pub fn from_copyable_slice(slice: &[D]) -> Result { + pub fn from_copyable_slice(slice: &[T]) -> Result { let mut this = Self::new(); this.extend_from_copyable_slice(slice)?; Ok(this) } - /// Iterates over the slice `other`, copies each element, and then appends + /// Iterates over the slice `other`, copies each element and then appends /// it to this vector. The `other` slice is traversed in-order. #[inline] - pub fn extend_from_copyable_slice(&mut self, other: &[D]) -> Result<(), ArrayVectorError> { - let this_len_u32 = self.len; - let other_len = other.len(); - let Some(len_u32) = u32::try_from(other_len).ok().filter(|el| self.remaining() >= *el) else { + pub fn extend_from_copyable_slice(&mut self, other: &[T]) -> Result<(), ArrayVectorError> { + let len = self.len; + let other_len_usize = other.len(); + let other_len_u32 = 'block: { + if let Some(other_len_u32) = Usize::from_usize(other_len_usize).into_u32() { + if other_len_u32 <= self.remaining() { + break 'block other_len_u32; + } + } return Err(ArrayVectorError::ExtendFromSliceOverflow); }; // SAFETY: The above check ensures bounds - let dst = unsafe { self.as_mut_ptr().add(*Usize::from_u32(this_len_u32)) }; + let dst = unsafe { self.as_mut_ptr().add(Usize::from_u32(len).into_usize()) }; // SAFETY: Parameters are valid unsafe { - ptr::copy_nonoverlapping(other.as_ptr(), dst, other_len); + ptr::copy_nonoverlapping(other.as_ptr(), dst, other_len_usize); + } + self.len = len.wrapping_add(other_len_u32); + Ok(()) + } + + /// Appends an copyable element to the back of the collection. + #[inline] + pub fn push_copyable(&mut self, value: T) -> Result<(), ArrayVectorError> { + if self.do_push(value).is_err() { + return Err(ArrayVectorError::PushOverflow); } - self.len = self.len.wrapping_add(len_u32); Ok(()) } } -impl Clone for ArrayVector +impl Clone for ArrayVector where - D: Clone, + T: Clone, { #[inline] fn clone(&self) -> Self { @@ -244,9 +306,9 @@ where } } -impl Debug for ArrayVector +impl Debug for ArrayVector where - D: Debug, + T: Debug, { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -254,15 +316,15 @@ where } } -impl Default for ArrayVector { +impl Default for ArrayVector { #[inline] fn default() -> Self { Self::new() } } -impl Deref for ArrayVector { - type Target = [D]; +impl Deref for ArrayVector { + type Target = [T]; #[inline] fn deref(&self) -> &Self::Target { @@ -270,7 +332,7 @@ impl Deref for ArrayVector { } } -impl DerefMut for ArrayVector { +impl DerefMut for ArrayVector { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { // SAFETY: `len` ensures initialized elements @@ -278,29 +340,33 @@ impl DerefMut for ArrayVector { } } -impl Eq for ArrayVector where D: Eq {} +impl Drop for ArrayVector { + #[inline] + fn drop(&mut self) { + if Self::NEEDS_DROP { + self.clear(); + } + } +} + +impl Eq for ArrayVector where T: Eq {} -impl IntoIterator for ArrayVector { - type IntoIter = - iter::Map, N>>, fn(MaybeUninit) -> D>; - type Item = D; +impl IntoIterator for ArrayVector { + type IntoIter = IntoIter; + type Item = T; #[inline] fn into_iter(self) -> Self::IntoIter { - fn map(elem: MaybeUninit) -> D { - // SAFETY: Only maps initialized elements - unsafe { elem.assume_init() } - } - self.data.into_iter().take(*Usize::from(self.len)).map(map) + IntoIter { idx: 0, data: self } } } -impl<'any, D, const N: usize> IntoIterator for &'any ArrayVector +impl<'any, T, const N: usize> IntoIterator for &'any ArrayVector where - D: 'any, + T: 'any, { - type IntoIter = slice::Iter<'any, D>; - type Item = &'any D; + type IntoIter = slice::Iter<'any, T>; + type Item = &'any T; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -308,12 +374,12 @@ where } } -impl<'any, D, const N: usize> IntoIterator for &'any mut ArrayVector +impl<'any, T, const N: usize> IntoIterator for &'any mut ArrayVector where - D: 'any, + T: 'any, { - type IntoIter = slice::IterMut<'any, D>; - type Item = &'any mut D; + type IntoIter = slice::IterMut<'any, T>; + type Item = &'any mut T; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -321,23 +387,23 @@ where } } -impl Lease<[D]> for ArrayVector { +impl Lease<[T]> for ArrayVector { #[inline] - fn lease(&self) -> &[D] { + fn lease(&self) -> &[T] { self } } -impl LeaseMut<[D]> for ArrayVector { +impl LeaseMut<[T]> for ArrayVector { #[inline] - fn lease_mut(&mut self) -> &mut [D] { + fn lease_mut(&mut self) -> &mut [T] { self } } -impl PartialEq for ArrayVector +impl PartialEq for ArrayVector where - D: PartialEq, + T: PartialEq, { #[inline] fn eq(&self, other: &Self) -> bool { @@ -345,19 +411,19 @@ where } } -impl PartialEq<[D]> for ArrayVector +impl PartialEq<[T]> for ArrayVector where - D: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &[D]) -> bool { + fn eq(&self, other: &[T]) -> bool { **self == *other } } -impl PartialOrd for ArrayVector +impl PartialOrd for ArrayVector where - D: PartialOrd, + T: PartialOrd, { #[inline] fn ge(&self, other: &Self) -> bool { @@ -385,9 +451,9 @@ where } } -impl Ord for ArrayVector +impl Ord for ArrayVector where - D: Ord, + T: Ord, { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -395,10 +461,10 @@ where } } -impl From<[D; N]> for ArrayVector { +impl From<[T; N]> for ArrayVector { #[inline] - fn from(from: [D; N]) -> Self { - Self::from_parts(from, Self::instance_u32()) + fn from(from: [T; N]) -> Self { + Self::from_parts(from, None) } } @@ -429,6 +495,82 @@ impl std::io::Write for ArrayVector { } } +/// A by-value array iterator. +#[derive(Debug)] +pub struct IntoIter { + idx: u32, + data: ArrayVector, +} + +impl IntoIter { + const NEEDS_DROP: bool = needs_drop::(); +} + +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + let Some(diff @ 1..=u32::MAX) = self.data.len.checked_sub(1) else { + return None; + }; + self.data.len = diff; + // SAFETY: `diff` is within bounds + Some(unsafe { self.data.get_owned(diff) }) + } +} + +impl Drop for IntoIter { + #[inline] + fn drop(&mut self) { + let idx = self.idx; + let len = self.data.len; + self.data.len = 0; + if Self::NEEDS_DROP { + let diff = len.wrapping_sub(idx); + if diff > 0 { + // SAFETY: Indices are within bounds + unsafe { + drop_elements(idx, *Usize::from(diff), self.data.as_mut_ptr()); + } + } + } + } +} + +impl ExactSizeIterator for IntoIter {} + +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + if self.idx >= self.data.len { + return None; + } + let idx = self.idx; + self.idx = idx.wrapping_add(1); + // SAFETY: `idx` is within bounds + Some(unsafe { self.data.get_owned(idx) }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = *Usize::from(self.data.len.wrapping_sub(self.idx)); + (len, Some(len)) + } +} + +#[inline] +unsafe fn drop_elements(begin: u32, len: usize, ptr: *mut T) { + // SAFETY: It is up to the caller to provide a valid pointer with a valid index + let data = unsafe { ptr.add(*Usize::from(begin)) }; + // SAFETY: It is up to the caller to provide a valid length + let elements = unsafe { slice::from_raw_parts_mut(data, len) }; + // SAFETY: It is up to the caller to provide parameters that can lead to droppable elements + unsafe { + ptr::drop_in_place(elements); + } +} + #[cfg(feature = "arbitrary")] mod arbitrary { use crate::misc::{ArrayVector, Usize}; @@ -464,22 +606,22 @@ mod serde { Deserialize, Deserializer, Serialize, Serializer, }; - impl<'de, D, const N: usize> Deserialize<'de> for ArrayVector + impl<'de, T, const N: usize> Deserialize<'de> for ArrayVector where - D: Deserialize<'de>, + T: Deserialize<'de>, { #[inline] fn deserialize(deserializer: DE) -> Result where DE: Deserializer<'de>, { - struct ArrayVisitor(PhantomData); + struct ArrayVisitor(PhantomData); - impl<'de, D, const N: usize> Visitor<'de> for ArrayVisitor + impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor where - D: Deserialize<'de>, + T: Deserialize<'de>, { - type Value = ArrayVector; + type Value = ArrayVector; #[inline] fn expecting(&self, formatter: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { @@ -493,7 +635,7 @@ mod serde { { let mut this = ArrayVector::new(); for elem in &mut this { - *elem = seq.next_element::()?.ok_or_else(|| { + *elem = seq.next_element::()?.ok_or_else(|| { de::Error::invalid_length(N, &"Array need more data to be constructed") })?; } @@ -501,13 +643,13 @@ mod serde { } } - deserializer.deserialize_tuple(N, ArrayVisitor::(PhantomData)) + deserializer.deserialize_tuple(N, ArrayVisitor::(PhantomData)) } } - impl Serialize for ArrayVector + impl Serialize for ArrayVector where - D: Serialize, + T: Serialize, { #[inline] fn serialize(&self, serializer: S) -> Result diff --git a/wtx/src/misc/bytes_fmt.rs b/wtx/src/misc/bytes_fmt.rs index e1eff25a..a886fb6f 100644 --- a/wtx/src/misc/bytes_fmt.rs +++ b/wtx/src/misc/bytes_fmt.rs @@ -11,7 +11,7 @@ pub struct BytesFmt<'bytes>( pub &'bytes [u8], ); -impl<'bytes> Display for BytesFmt<'bytes> { +impl Display for BytesFmt<'_> { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { for elem in self.0.iter().copied() { diff --git a/wtx/src/misc/noop_waker.rs b/wtx/src/misc/noop_waker.rs new file mode 100644 index 00000000..74872ac6 --- /dev/null +++ b/wtx/src/misc/noop_waker.rs @@ -0,0 +1,20 @@ +use core::{ + ptr, + task::{RawWaker, RawWakerVTable, Waker}, +}; + +const VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); + +// FIXME(STABLE): noop_waker +/// A waker that does nothing. +pub static NOOP_WAKER: Waker = { + let raw = RawWaker::new(ptr::null(), &VTABLE); + // SAFETY: Contract is upheld + unsafe { Waker::from_raw(raw) } +}; + +unsafe fn noop(_: *const ()) {} + +unsafe fn noop_clone(_: *const ()) -> RawWaker { + RawWaker::new(ptr::null(), &VTABLE) +} diff --git a/wtx/src/misc/rng/seed.rs b/wtx/src/misc/rng/seed.rs index eed29a0d..73e3fac3 100644 --- a/wtx/src/misc/rng/seed.rs +++ b/wtx/src/misc/rng/seed.rs @@ -22,7 +22,9 @@ pub fn simple_seed() -> u64 { } let elem = Box::new(Foo { _bar: 1, _baz: 2 }); - let ptr_addr = ptr::addr_of!(elem).addr(); + // FIXME(STABLE): strict_provenance + // SAFETY: Memory location is not relevant + let ptr_addr = unsafe { *ptr::addr_of!(elem).cast() }; let mut rslt = Usize::from_usize(ptr_addr).into_u64(); rslt = rslt.wrapping_add(11_400_714_819_323_198_485); rslt = rslt.wrapping_add(COUNTER.fetch_add(3, Ordering::Release)); diff --git a/wtx/src/misc/tuple_impls.rs b/wtx/src/misc/tuple_impls.rs index 468ddb1a..8744916d 100644 --- a/wtx/src/misc/tuple_impls.rs +++ b/wtx/src/misc/tuple_impls.rs @@ -1,5 +1,7 @@ +// FIXME(STABLE): macro_metavar_expr + macro_rules! impl_0_16 { - ($( ($($T:ident)*) )+) => { + ($( [$($T:ident($N:tt))*] )+) => { #[cfg(feature = "database")] mod database { use crate::database::{Database, Encode, RecordValues, encode}; @@ -20,10 +22,9 @@ macro_rules! impl_0_16 { ) -> Result { let mut _n: usize = 0; $( - ${ignore($T)} encode( _aux, - &self.${index()}, + &self.$N, _ev, &mut _n, &mut _prefix_cb, @@ -56,11 +57,11 @@ macro_rules! impl_0_16 { where $($T: ConnAux,)* { - type Init = ($(${ignore($T)} $T::Init,)*); + type Init = ($($T::Init,)*); #[inline] fn conn_aux(_init: Self::Init) -> crate::Result { - Ok(($( ${ignore($T)} $T::conn_aux(_init.${index()})?, )*)) + Ok(($( $T::conn_aux(_init.$N)?, )*)) } } @@ -68,14 +69,14 @@ macro_rules! impl_0_16 { where $($T: ReqAux,)* { - type Init = ($(${ignore($T)} $T::Init,)*); + type Init = ($($T::Init,)*); #[inline] fn req_aux(_init: Self::Init, _req: &mut Request) -> crate::Result where RRD: ReqResDataMut { - Ok(($( ${ignore($T)} $T::req_aux(_init.${index()}, _req)?, )*)) + Ok(($( $T::req_aux(_init.$N, _req)?, )*)) } } @@ -86,7 +87,7 @@ macro_rules! impl_0_16 { { #[inline] async fn apply_req_middleware(&self, _ca: &mut CA, _ra: &mut RA, _req: &mut Request) -> Result<(), ERR> { - $( ${ignore($T)} self.${index()}.apply_req_middleware(_ca, _ra, _req).await?; )* + $( self.$N.apply_req_middleware(_ca, _ra, _req).await?; )* Ok(()) } } @@ -104,7 +105,7 @@ macro_rules! impl_0_16 { status_code: _res.status_code, version: _res.version, }; - ${ignore($T)} self.${index()}.apply_res_middleware(_ca, _ra, local_res).await?; + self.$N.apply_res_middleware(_ca, _ra, local_res).await?; })* Ok(()) } @@ -128,10 +129,9 @@ macro_rules! impl_0_16 { ) -> Result { match path_defs.1.get(usize::from(path_defs.0)).map(|el| el.1) { $( - ${ignore($T)} - Some(${index()}) => { + Some($N) => { return self - .${index()} + .$N .value .manage_path(_ca, (path_defs.0.wrapping_add(1), path_defs.1), _ra, _req) .await; @@ -149,11 +149,10 @@ macro_rules! impl_0_16 { ) -> crate::Result<()> { let mut _idx: u8 = 0; $({ - ${ignore($T)} let mut local_prev = _prev.clone(); - local_prev.push((self.${index()}.full_path, _idx))?; + local_prev.push((self.$N.full_path, _idx))?; if $T::IS_ROUTER { - self.${index()}.value.paths_indices(local_prev, _vec)?; + self.$N.value.paths_indices(local_prev, _vec)?; } else { _vec.push(local_prev)?; } @@ -216,8 +215,7 @@ macro_rules! impl_0_16 { fn encode(&self, _ev: &mut EncodeValue<'_, '_>) -> Result<(), ERR> { let mut _ev = StructEncoder::::new(_ev)?; $( - ${ignore($T)} - _ev = _ev.encode(&self.${index()})?; + _ev = _ev.encode(&self.$N)?; )* Ok(()) } @@ -228,21 +226,21 @@ macro_rules! impl_0_16 { } impl_0_16! { - () - (A) - (A B) - (A B C) - (A B C D) - (A B C D E) - (A B C D E F) - (A B C D E F G) - (A B C D E F G H) - (A B C D E F G H I) - (A B C D E F G H I J) - (A B C D E F G H I J K) - (A B C D E F G H I J K L) - (A B C D E F G H I J K L M) - (A B C D E F G H I J K L M N) - (A B C D E F G H I J K L M N O) - (A B C D E F G H I J K L M N O P) + [] + [A(0)] + [A(0) B(1)] + [A(0) B(1) C(2)] + [A(0) B(1) C(2) D(3)] + [A(0) B(1) C(2) D(3) E(4)] + [A(0) B(1) C(2) D(3) E(4) F(5)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8) J(9)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8) J(9) K(10)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8) J(9) K(10) L(11)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8) J(9) K(10) L(11) M(12)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8) J(9) K(10) L(11) M(12) N(13)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8) J(9) K(10) L(11) M(12) N(13) O(14)] + [A(0) B(1) C(2) D(3) E(4) F(5) G(6) H(7) I(8) J(9) K(10) L(11) M(12) N(13) O(14) P(15)] } diff --git a/wtx/src/misc/usize.rs b/wtx/src/misc/usize.rs index 87015b12..b6b42063 100644 --- a/wtx/src/misc/usize.rs +++ b/wtx/src/misc/usize.rs @@ -7,17 +7,21 @@ #[cfg(target_pointer_width = "16")] compile_error!("WTX does not support 16bit hardware"); +macro_rules! u32_max { + () => { + 4_294_967_295 + }; +} + use core::ops::{Deref, DerefMut}; /// An `usize` that can be infallible converted from an `u32`, which effectively drops the support /// for 16bit hardware. -/// -/// Additionally, 128bit memory addresses are also unsupported. -#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Usize(usize); impl Usize { - pub(crate) const IS_32: bool = cfg!(target_pointer_width = "32"); + const IS_32: bool = cfg!(target_pointer_width = "32"); #[inline] pub(crate) const fn from_u32(from: u32) -> Self { @@ -26,7 +30,7 @@ impl Usize { #[inline] pub(crate) const fn from_u64(from: u64) -> Option { - if Self::IS_32 { + if Self::IS_32 && from > u32_max!() { return None; } Some(Self(from as usize)) @@ -42,6 +46,14 @@ impl Usize { self.0 } + #[inline] + pub(crate) const fn into_u32(self) -> Option { + if !Self::IS_32 && self.0 > u32_max!() { + return None; + } + Some(self.0 as u32) + } + #[inline] pub(crate) const fn into_u64(self) -> u64 { self.0 as u64 diff --git a/wtx/tests/embed-migrations.rs b/wtx/tests/embed-migrations.rs index 33447e24..52857384 100644 --- a/wtx/tests/embed-migrations.rs +++ b/wtx/tests/embed-migrations.rs @@ -1,3 +1,5 @@ +//! Embed migrations + #![cfg(feature = "schema-manager")] #[allow(dead_code)] @@ -5,6 +7,7 @@ mod embedded_migrations; use wtx::{database::schema_manager::Commands, misc::Vector}; +/// Compiles pub async fn compiles() { let mut commands = Commands::with_executor(()); commands