From 55857e144d5dc3de1d45da94225b05c47f2c218e Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 4 May 2024 14:40:12 -0300 Subject: [PATCH] HTTP/2 --- .cargo/config.toml | 2 - .github/workflows/bencher.yaml | 10 +- .github/workflows/tests.yaml | 10 +- .gitignore | 12 +- .scripts/autobahn-fuzzingclient.sh | 7 +- .scripts/autobahn-fuzzingserver.sh | 4 +- .scripts/common.sh | 3 +- .scripts/integration-tests.sh | 2 +- .scripts/internal-tests.sh | 37 +- .scripts/podman-rm.sh | 4 +- .scripts/wtx-bench-postgres.sh | 5 - .scripts/wtx-bench-web-socket.sh | 41 - Cargo.lock | 1451 ++--- Cargo.toml | 14 +- README.md | 39 +- rust-toolchain | 4 +- wtx-bench/Cargo.toml | 17 - wtx-bench/README.md | 7 - wtx-bench/src/main.rs | 63 - wtx-bench/src/misc.rs | 63 - wtx-bench/src/postgres.rs | 234 - wtx-bench/src/web_socket.rs | 98 - wtx-docs/src/SUMMARY.md | 1 + wtx-docs/src/database/client-connection.md | 5 +- wtx-docs/src/database/schema-management.md | 4 +- wtx-docs/src/http2/README.md | 36 + wtx-docs/src/pool_manager/README.md | 2 +- wtx-fuzz/web_socket.rs | 6 +- wtx-macros/src/api_types.rs | 2 +- wtx-macros/src/pkg.rs | 4 +- .../pkg/fir/fir_after_sending_item_values.rs | 2 +- .../pkg/fir/fir_before_sending_item_values.rs | 2 +- wtx-macros/src/pkg/sir/sir_final_values.rs | 2 +- wtx-macros/tests/ui/pass/custom_transport.rs | 2 +- wtx-ui/src/http_client.rs | 5 + wtx-ui/src/schema_manager.rs | 2 +- wtx-ui/src/web_socket.rs | 10 +- wtx/Cargo.toml | 71 +- wtx/examples/common/mod.rs | 58 +- wtx/examples/common/web_socket.rs | 47 + wtx/examples/http2-client-tokio.rs | 35 + wtx/examples/http2-server-tokio.rs | 36 + ... => web-socket-client-raw-tokio-rustls.rs} | 11 +- .../web-socket-server-echo-raw-async-std.rs | 21 - .../web-socket-server-echo-raw-glommio.rs | 29 - .../web-socket-server-echo-raw-smol.rs | 19 - .../web-socket-server-echo-raw-tokio.rs | 17 - .../web-socket-server-pool-raw-tokio.rs | 45 - ... => web-socket-server-raw-tokio-rustls.rs} | 14 +- wtx/examples/web-socket-server-raw-tokio.rs | 40 + .../misc/optimization.txt | 7 - wtx/src/bin/autobahn-client.rs | 12 +- wtx/src/bin/autobahn-server.rs | 43 +- .../data_format/borsh/borsh_request.rs | 1 + .../data_format/graph_ql/graph_ql_request.rs | 2 + .../data_format/graph_ql/graph_ql_response.rs | 2 + .../data_format/json/json_request.rs | 3 + .../data_format/json/json_response.rs | 3 + .../data_format/json_rpc/json_rpc_request.rs | 4 +- .../data_format/json_rpc/json_rpc_response.rs | 28 +- .../data_format/verbatim/verbatim_request.rs | 1 + .../data_format/xml/xml_request.rs | 1 + .../data_format/xml/xml_response.rs | 1 + .../data_format/yaml/yaml_request.rs | 1 + .../data_format/yaml/yaml_response.rs | 1 + .../client_api_framework/dnsn/miniserde.rs | 2 + wtx/src/client_api_framework/dnsn/rkyv.rs | 1 + wtx/src/client_api_framework/dnsn/tests.rs | 7 +- wtx/src/client_api_framework/macros.rs | 13 +- wtx/src/client_api_framework/misc.rs | 9 +- .../misc/request_counter.rs | 8 +- .../misc/request_limit.rs | 2 +- wtx/src/client_api_framework/network/http.rs | 9 +- .../network/http/status_code.rs | 499 -- wtx/src/client_api_framework/network/tcp.rs | 2 +- .../client_api_framework/network/transport.rs | 26 +- .../network/transport/bi_transport.rs | 2 +- .../network/transport/mock.rs | 23 +- .../network/transport/reqwest.rs | 131 - .../network/transport/std.rs | 18 +- .../network/transport/unit.rs | 6 +- .../network/transport/{wtx.rs => wtx_ws.rs} | 34 +- wtx/src/client_api_framework/network/udp.rs | 2 +- wtx/src/client_api_framework/pkg/batch_pkg.rs | 1 + .../pkg/pkg_with_helper.rs | 2 +- wtx/src/database.rs | 2 +- wtx/src/database/client/postgres/executor.rs | 31 +- .../postgres/executor/authentication.rs | 28 +- .../client/postgres/executor/fetch.rs | 6 +- .../client/postgres/executor/prepare.rs | 11 +- .../client/postgres/executor/simple_query.rs | 11 +- .../client/postgres/executor_buffer.rs | 20 +- .../client/postgres/integration_tests.rs | 1 + wtx/src/database/client/postgres/record.rs | 6 +- wtx/src/database/client/postgres/records.rs | 8 +- .../database/client/postgres/statements.rs | 6 +- .../client/postgres/transaction_manager.rs | 5 +- wtx/src/database/client/postgres/tys.rs | 26 +- wtx/src/database/database_ty.rs | 3 +- wtx/src/database/orm/crud.rs | 20 +- wtx/src/database/orm/no_table_association.rs | 2 +- wtx/src/database/orm/sql_value.rs | 22 +- .../database/orm/table_association_wrapper.rs | 4 +- wtx/src/database/orm/tests/collection.rs | 20 +- wtx/src/database/orm/tests/diamond.rs | 24 +- wtx/src/database/orm/tuple_impls.rs | 446 +- wtx/src/database/record.rs | 2 +- wtx/src/database/record_values.rs | 202 +- wtx/src/database/schema_manager.rs | 40 +- wtx/src/database/schema_manager/commands.rs | 15 +- .../schema_manager/commands/migrate.rs | 21 +- .../schema_manager/commands/rollback.rs | 17 +- .../database/schema_manager/commands/seed.rs | 11 +- .../schema_manager/commands/validate.rs | 21 +- .../schema_manager/fixed_sql_commands.rs | 16 +- .../schema_manager/integration_tests.rs | 1 + .../integration_tests/backend.rs | 1 + .../integration_tests/db/postgres.rs | 15 +- .../integration_tests/generic.rs | 1 + .../integration_tests/schema.rs | 1 + .../integration_tests/schema/with_schema.rs | 1 + .../schema/without_schema.rs | 1 + .../schema_manager/migration/db_migration.rs | 2 +- .../migration/migration_group.rs | 6 +- .../migration/user_migration.rs | 34 +- .../schema_manager/migration_parser.rs | 14 +- wtx/src/database/schema_manager/misc.rs | 50 +- .../database/schema_manager/toml_parser.rs | 33 +- wtx/src/error.rs | 104 +- wtx/src/http.rs | 21 +- wtx/src/http/abstract_headers.rs | 484 +- wtx/src/http/client.rs | 64 + wtx/src/http/generic_request.rs | 30 + wtx/src/http/generic_response.rs | 22 + wtx/src/http/header_name.rs | 265 +- wtx/src/http/headers.rs | 100 +- wtx/src/http/method.rs | 22 +- wtx/src/http/protocol.rs | 12 + wtx/src/http/request.rs | 60 +- wtx/src/http/response.rs | 48 +- wtx/src/http/response_data.rs | 79 + wtx/src/http/server.rs | 44 + wtx/src/http/server/tokio_http2.rs | 129 + wtx/src/http/server/tokio_web_socket.rs | 72 + wtx/src/http1.rs | 6 +- wtx/src/http2.rs | 233 + wtx/src/http2/client_stream.rs | 129 + wtx/src/http2/continuation_frame.rs | 75 + wtx/src/http2/data_frame.rs | 51 + wtx/src/http2/error_code.rs | 42 + wtx/src/http2/frame_init.rs | 45 + wtx/src/http2/go_away_frame.rs | 34 + wtx/src/http2/headers_frame.rs | 289 + wtx/src/http2/hpack_decoder.rs | 418 ++ wtx/src/http2/hpack_encoder.rs | 644 +++ wtx/src/http2/hpack_header.rs | 73 + wtx/src/http2/hpack_static_headers.rs | 52 + wtx/src/http2/http2_buffer.rs | 63 + wtx/src/http2/http2_data.rs | 351 ++ wtx/src/http2/http2_params.rs | 203 + wtx/src/http2/huffman.rs | 213 + wtx/src/http2/huffman_tables.rs | 4876 +++++++++++++++++ wtx/src/http2/macros.rs | 53 + wtx/src/http2/misc.rs | 381 ++ wtx/src/http2/ping_frame.rs | 38 + wtx/src/http2/read_frame_rslt.rs | 24 + wtx/src/http2/req_res_buffer.rs | 33 + wtx/src/http2/reset_stream_frame.rs | 33 + wtx/src/http2/send_params.rs | 59 + wtx/src/http2/server_stream.rs | 139 + wtx/src/http2/settings_frame.rs | 279 + wtx/src/http2/stream_state.rs | 21 + wtx/src/http2/tests.rs | 3 + wtx/src/http2/tests/connections.rs | 73 + wtx/src/http2/tests/hpack.rs | 294 + wtx/src/http2/u31.rs | 66 + wtx/src/http2/uri_buffer.rs | 26 + wtx/src/http2/window_update_frame.rs | 27 + wtx/src/lib.rs | 14 +- wtx/src/macros.rs | 58 +- wtx/src/misc.rs | 92 +- wtx/src/misc/array_chunks.rs | 60 +- wtx/src/misc/array_string.rs | 320 ++ wtx/src/misc/array_vector.rs | 310 ++ wtx/src/misc/blocks_queue.rs | 537 ++ wtx/src/misc/connection_state.rs | 33 + wtx/src/misc/either.rs | 50 +- wtx/src/misc/enum_var_strings.rs | 4 +- wtx/src/misc/fn_fut.rs | 50 + wtx/src/misc/fn_mut_fut.rs | 21 - wtx/src/misc/lease.rs | 217 + wtx/src/misc/lock.rs | 222 + wtx/src/misc/lock_guard.rs | 60 + wtx/src/misc/mem_transfer.rs | 222 +- wtx/src/misc/optimization.rs | 51 +- wtx/src/misc/partitioned_filled_buffer.rs | 18 +- wtx/src/misc/query_writer.rs | 16 +- wtx/src/misc/queue.rs | 307 ++ wtx/src/misc/queue_utils.rs | 47 + wtx/src/misc/ref_counter.rs | 32 + .../{traits.rs => single_type_storage.rs} | 27 +- wtx/src/misc/stream.rs | 304 +- wtx/src/misc/tokio_rustls.rs | 3 +- wtx/src/misc/uri.rs | 104 +- wtx/src/misc/usize.rs | 38 +- wtx/src/misc/vector.rs | 327 ++ wtx/src/pool.rs | 65 + wtx/src/pool/fixed_pool.rs | 225 + wtx/src/pool/resource_manager.rs | 216 + wtx/src/pool_manager.rs | 69 - wtx/src/pool_manager/lock.rs | 67 - wtx/src/pool_manager/lock_guard.rs | 57 - wtx/src/pool_manager/resource_manager.rs | 145 - wtx/src/pool_manager/static_pool.rs | 135 - wtx/src/rng.rs | 3 +- wtx/src/rng/fastrand.rs | 27 +- wtx/src/rng/rand.rs | 6 +- wtx/src/rng/std.rs | 2 +- wtx/src/web_socket.rs | 159 +- wtx/src/web_socket/close_code.rs | 4 +- wtx/src/web_socket/compression/flate2.rs | 6 +- wtx/src/web_socket/frame.rs | 32 +- wtx/src/web_socket/frame_buffer.rs | 42 +- wtx/src/web_socket/handshake.rs | 4 +- wtx/src/web_socket/handshake/misc.rs | 14 +- wtx/src/web_socket/handshake/raw.rs | 27 +- wtx/src/web_socket/handshake/tests.rs | 6 +- wtx/src/web_socket/misc.rs | 11 +- wtx/src/web_socket/misc/traits.rs | 2 +- wtx/src/web_socket/unmask.rs | 9 +- wtx/src/web_socket/web_socket_buffer.rs | 21 +- 231 files changed, 15398 insertions(+), 5171 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100755 .scripts/wtx-bench-postgres.sh delete mode 100755 .scripts/wtx-bench-web-socket.sh delete mode 100644 wtx-bench/Cargo.toml delete mode 100644 wtx-bench/README.md delete mode 100644 wtx-bench/src/main.rs delete mode 100644 wtx-bench/src/misc.rs delete mode 100644 wtx-bench/src/postgres.rs delete mode 100644 wtx-bench/src/web_socket.rs create mode 100644 wtx-docs/src/http2/README.md create mode 100644 wtx-ui/src/http_client.rs create mode 100644 wtx/examples/common/web_socket.rs create mode 100644 wtx/examples/http2-client-tokio.rs create mode 100644 wtx/examples/http2-server-tokio.rs rename wtx/examples/{web-socket-client-cli-raw-tokio-rustls.rs => web-socket-client-raw-tokio-rustls.rs} (84%) delete mode 100644 wtx/examples/web-socket-server-echo-raw-async-std.rs delete mode 100644 wtx/examples/web-socket-server-echo-raw-glommio.rs delete mode 100644 wtx/examples/web-socket-server-echo-raw-smol.rs delete mode 100644 wtx/examples/web-socket-server-echo-raw-tokio.rs delete mode 100644 wtx/examples/web-socket-server-pool-raw-tokio.rs rename wtx/examples/{web-socket-server-echo-raw-tokio-rustls.rs => web-socket-server-raw-tokio-rustls.rs} (69%) create mode 100644 wtx/examples/web-socket-server-raw-tokio.rs delete mode 100644 wtx/proptest-regressions/misc/optimization.txt delete mode 100644 wtx/src/client_api_framework/network/http/status_code.rs delete mode 100644 wtx/src/client_api_framework/network/transport/reqwest.rs rename wtx/src/client_api_framework/network/transport/{wtx.rs => wtx_ws.rs} (85%) create mode 100644 wtx/src/http/client.rs create mode 100644 wtx/src/http/generic_request.rs create mode 100644 wtx/src/http/generic_response.rs create mode 100644 wtx/src/http/protocol.rs create mode 100644 wtx/src/http/response_data.rs create mode 100644 wtx/src/http/server.rs create mode 100644 wtx/src/http/server/tokio_http2.rs create mode 100644 wtx/src/http/server/tokio_web_socket.rs create mode 100644 wtx/src/http2.rs create mode 100644 wtx/src/http2/client_stream.rs create mode 100644 wtx/src/http2/continuation_frame.rs create mode 100644 wtx/src/http2/data_frame.rs create mode 100644 wtx/src/http2/error_code.rs create mode 100644 wtx/src/http2/frame_init.rs create mode 100644 wtx/src/http2/go_away_frame.rs create mode 100644 wtx/src/http2/headers_frame.rs create mode 100644 wtx/src/http2/hpack_decoder.rs create mode 100644 wtx/src/http2/hpack_encoder.rs create mode 100644 wtx/src/http2/hpack_header.rs create mode 100644 wtx/src/http2/hpack_static_headers.rs create mode 100644 wtx/src/http2/http2_buffer.rs create mode 100644 wtx/src/http2/http2_data.rs create mode 100644 wtx/src/http2/http2_params.rs create mode 100644 wtx/src/http2/huffman.rs create mode 100644 wtx/src/http2/huffman_tables.rs create mode 100644 wtx/src/http2/macros.rs create mode 100644 wtx/src/http2/misc.rs create mode 100644 wtx/src/http2/ping_frame.rs create mode 100644 wtx/src/http2/read_frame_rslt.rs create mode 100644 wtx/src/http2/req_res_buffer.rs create mode 100644 wtx/src/http2/reset_stream_frame.rs create mode 100644 wtx/src/http2/send_params.rs create mode 100644 wtx/src/http2/server_stream.rs create mode 100644 wtx/src/http2/settings_frame.rs create mode 100644 wtx/src/http2/stream_state.rs create mode 100644 wtx/src/http2/tests.rs create mode 100644 wtx/src/http2/tests/connections.rs create mode 100644 wtx/src/http2/tests/hpack.rs create mode 100644 wtx/src/http2/u31.rs create mode 100644 wtx/src/http2/uri_buffer.rs create mode 100644 wtx/src/http2/window_update_frame.rs create mode 100644 wtx/src/misc/array_string.rs create mode 100644 wtx/src/misc/array_vector.rs create mode 100644 wtx/src/misc/blocks_queue.rs create mode 100644 wtx/src/misc/connection_state.rs create mode 100644 wtx/src/misc/fn_fut.rs delete mode 100644 wtx/src/misc/fn_mut_fut.rs create mode 100644 wtx/src/misc/lease.rs create mode 100644 wtx/src/misc/lock.rs create mode 100644 wtx/src/misc/lock_guard.rs create mode 100644 wtx/src/misc/queue.rs create mode 100644 wtx/src/misc/queue_utils.rs create mode 100644 wtx/src/misc/ref_counter.rs rename wtx/src/misc/{traits.rs => single_type_storage.rs} (61%) create mode 100644 wtx/src/misc/vector.rs create mode 100644 wtx/src/pool.rs create mode 100644 wtx/src/pool/fixed_pool.rs create mode 100644 wtx/src/pool/resource_manager.rs delete mode 100644 wtx/src/pool_manager.rs delete mode 100644 wtx/src/pool_manager/lock.rs delete mode 100644 wtx/src/pool_manager/lock_guard.rs delete mode 100644 wtx/src/pool_manager/resource_manager.rs delete mode 100644 wtx/src/pool_manager/static_pool.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index f599ec91..00000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.x86_64-unknown-linux-gnu] -rustflags = ["-C", "linker=clang", "-C", "link-arg=-fuse-ld=lld"] \ No newline at end of file diff --git a/.github/workflows/bencher.yaml b/.github/workflows/bencher.yaml index fe9d952e..43e2c060 100644 --- a/.github/workflows/bencher.yaml +++ b/.github/workflows/bencher.yaml @@ -6,19 +6,17 @@ on: jobs: benchmark_with_bencher: + continue-on-error: true name: Continuous Benchmarking with Bencher runs-on: ubuntu-latest - env: - BENCHER_ADAPTER: rust_bench - BENCHER_PROJECT: wtx - BENCHER_TESTBED: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: bencherdev/bencher@main - name: Track Benchmarks with Bencher run: | bencher run \ - --branch "$GITHUB_REF_NAME" \ + --branch main \ --err \ - --token "${{ secrets.BENCHER_API_TOKEN }}" \ + --project wtx \ + --testbed ubuntu-latest \ "cargo bench --all-features" \ No newline at end of file diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 3c16f39c..f681e083 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -14,7 +14,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2023-10-15 + toolchain: nightly-2024-04-27 - uses: Swatinem/rust-cache@v2 - run: .scripts/autobahn-fuzzingclient.sh ci @@ -26,7 +26,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2023-10-15 + toolchain: nightly-2024-04-27 - uses: Swatinem/rust-cache@v2 - run: .scripts/autobahn-fuzzingserver.sh ci @@ -38,7 +38,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2023-10-15 + toolchain: nightly-2024-04-27 - uses: actions-rs/install@v0.1 with: crate: cargo-fuzz @@ -53,7 +53,7 @@ jobs: with: override: true profile: minimal - toolchain: nightly-2023-10-15 + toolchain: nightly-2024-04-27 - uses: Swatinem/rust-cache@v2 - run: docker-compose -f .test-utils/docker-compose.yml up -d - run: sleep 30 @@ -69,6 +69,6 @@ jobs: components: clippy, rustfmt override: true profile: minimal - toolchain: nightly-2023-10-15 + toolchain: nightly-2024-04-27 - uses: Swatinem/rust-cache@v2 - run: .scripts/internal-tests.sh diff --git a/.gitignore b/.gitignore index 5ca8e2d5..2b396e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ .scripts/autobahn/reports .vscode **/*.rs.bk -**/artifacts -**/corpus +**/hpack-test-case +**/profile.json +**/proptest-regressions +**/rustc-ice-*.txt **/target -**/target -profile.json -wtx-docs/book \ No newline at end of file +wtx-docs/book +wtx-fuzz/artifacts +wtx-fuzz/corpus \ No newline at end of file diff --git a/.scripts/autobahn-fuzzingclient.sh b/.scripts/autobahn-fuzzingclient.sh index 91913ec8..73ab75da 100755 --- a/.scripts/autobahn-fuzzingclient.sh +++ b/.scripts/autobahn-fuzzingclient.sh @@ -7,15 +7,16 @@ fi; ## fuzzingclient -cargo build --bin autobahn-server --features atoi,flate2,simdutf8,tokio,web-socket-handshake --profile bench -cargo run --bin autobahn-server --features atoi,flate2,simdutf8,tokio,web-socket-handshake --profile bench & cargo_pid=$! +cargo build --bin autobahn-server --features async-send,flate2,optimization,pool,tokio,web-socket-handshake --profile bench +cargo run --bin autobahn-server --features async-send,flate2,optimization,pool,tokio,web-socket-handshake --profile bench & cargo_pid=$! +sleep 1 mkdir -p .scripts/autobahn/reports/fuzzingclient podman run \ -p 9070:9070 \ -v .scripts/autobahn/fuzzingclient-min.json:/fuzzingclient.json:ro \ -v .scripts/autobahn:/autobahn \ --name fuzzingclient \ - --net=host \ + --network host \ --rm \ docker.io/crossbario/autobahn-testsuite:0.8.2 wstest -m fuzzingclient -s fuzzingclient.json podman rm --force --ignore fuzzingclient diff --git a/.scripts/autobahn-fuzzingserver.sh b/.scripts/autobahn-fuzzingserver.sh index 8c376941..f29c9f7a 100755 --- a/.scripts/autobahn-fuzzingserver.sh +++ b/.scripts/autobahn-fuzzingserver.sh @@ -5,7 +5,7 @@ if [ "$ARG" != "ci" ]; then trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT fi; -cargo build --bin autobahn-client --features atoi,flate2,simdutf8,tokio,web-socket-handshake --profile bench +cargo build --bin autobahn-client --features flate2,optimization,tokio,web-socket-handshake --profile bench mkdir -p .scripts/autobahn/reports/fuzzingserver podman run \ -d \ @@ -16,7 +16,7 @@ podman run \ --net=host \ docker.io/crossbario/autobahn-testsuite:0.8.2 wstest -m fuzzingserver -s fuzzingserver.json sleep 5 -cargo run --bin autobahn-client --features atoi,flate2,simdutf8,tokio,web-socket-handshake --profile bench +cargo run --bin autobahn-client --features flate2,optimization,tokio,web-socket-handshake --profile bench podman rm --force --ignore fuzzingserver if [ $(grep -ci "failed" .scripts/autobahn/reports/fuzzingserver/index.json) -gt 0 ] diff --git a/.scripts/common.sh b/.scripts/common.sh index 796aaf0d..160e6e85 100644 --- a/.scripts/common.sh +++ b/.scripts/common.sh @@ -8,4 +8,5 @@ export rt='rust-tools --template you-rust' export CARGO_TARGET_DIR="$($rt target-dir)" export RUST_BACKTRACE=1 -export RUSTFLAGS="$($rt rust-flags)" +export RUST_LOG=debug +export RUSTFLAGS="$($rt rust-flags -Asingle-use-lifetimes,-Aunsafe-code)" \ No newline at end of file diff --git a/.scripts/integration-tests.sh b/.scripts/integration-tests.sh index dcde4cd2..a8f86a7b 100755 --- a/.scripts/integration-tests.sh +++ b/.scripts/integration-tests.sh @@ -3,7 +3,7 @@ . "$(dirname "$0")/common.sh" --source-only export DATABASE_URI='postgres://wtx_md5:wtx@localhost:5432/wtx' -export RUSTFLAGS="$($rt rust-flags -Asingle-use-lifetimes,-Alet-underscore-drop)" +export RUST_LOG=info cargo test --all-features --release -- --test-threads=1 diff --git a/.scripts/internal-tests.sh b/.scripts/internal-tests.sh index 61e2b491..d49f3ca9 100755 --- a/.scripts/internal-tests.sh +++ b/.scripts/internal-tests.sh @@ -1,19 +1,12 @@ #!/usr/bin/env bash -set -euxo pipefail - -cargo install rust-tools --git https://github.com/c410-f3r/regular-crates - -rt='rust-tools --template you-rust' - -export CARGO_TARGET_DIR="$($rt target-dir)" -export RUST_BACKTRACE=1 -export RUST_LOG=debug -export RUSTFLAGS="$($rt rust-flags -Asingle-use-lifetimes,-Alet-underscore-drop)" +. "$(dirname "$0")/common.sh" --source-only $rt rustfmt $rt clippy +cargo miri test -p wtx + # WTX $rt check-generic wtx @@ -32,25 +25,28 @@ $rt test-with-features wtx crypto-common $rt test-with-features wtx database $rt test-with-features wtx digest $rt test-with-features wtx embassy-net,_hack +$rt test-with-features wtx embassy-sync $rt test-with-features wtx embedded-tls $rt test-with-features wtx fastrand $rt test-with-features wtx flate2 -$rt test-with-features wtx futures $rt test-with-features wtx futures-lite $rt test-with-features wtx glommio $rt test-with-features wtx hashbrown $rt test-with-features wtx hmac $rt test-with-features wtx http1 +$rt test-with-features wtx http2 $rt test-with-features wtx httparse $rt test-with-features wtx md-5 +$rt test-with-features wtx memchr $rt test-with-features wtx miniserde +$rt test-with-features wtx nightly $rt test-with-features wtx orm -$rt test-with-features wtx pool-manager +$rt test-with-features wtx parking_lot +$rt test-with-features wtx pool $rt test-with-features wtx postgres $rt test-with-features wtx proptest $rt test-with-features wtx protobuf $rt test-with-features wtx rand -$rt test-with-features wtx reqwest $rt test-with-features wtx ring $rt test-with-features wtx rkyv,_hack $rt test-with-features wtx rust_decimal @@ -67,6 +63,7 @@ $rt test-with-features wtx sha2 $rt test-with-features wtx simd-json $rt test-with-features wtx simdutf8 $rt test-with-features wtx smol +$rt test-with-features wtx smoltcp,_hack $rt test-with-features wtx std $rt test-with-features wtx test-strategy $rt test-with-features wtx tokio @@ -97,14 +94,10 @@ $rt test-with-features wtx-ui schema-manager $rt test-with-features wtx-ui schema-manager-dev $rt test-with-features wtx-ui web-socket -cargo check --bin autobahn-client --features "flate2,web-socket-handshake" -cargo check --bin autobahn-server --features "flate2,web-socket-handshake" +cargo check --bin autobahn-client --features "flate2,optimization,tokio/rt-multi-thread,web-socket-handshake" +cargo check --bin autobahn-server --features "async-send,flate2,optimization,pool,tokio/rt-multi-thread,web-socket-handshake" cargo check --example database-client-postgres-tokio-rustls --features "_tokio-rustls-client,postgres" -cargo check --example web-socket-client-cli-raw-tokio-rustls --features "_tokio-rustls-client,web-socket-handshake" -cargo check --example web-socket-server-echo-raw-async-std --features "async-std,web-socket-handshake" -cargo check --example web-socket-server-echo-raw-glommio --features "glommio,web-socket-handshake" -cargo check --example web-socket-server-echo-raw-smol --features "smol,web-socket-handshake" -cargo check --example web-socket-server-echo-raw-tokio --features "tokio,web-socket-handshake" -cargo check --example web-socket-server-echo-raw-tokio-rustls --features "_tokio-rustls-server,web-socket-handshake" -cargo check --example web-socket-server-pool-raw-tokio --features "pool-manager,web-socket-handshake" \ No newline at end of file +cargo check --example web-socket-client-raw-tokio-rustls --features "_tokio-rustls-client,web-socket-handshake" +cargo check --example web-socket-server-raw-tokio --features "async-send,pool,tokio,web-socket-handshake" +cargo check --example web-socket-server-raw-tokio-rustls --features "_tokio-rustls-server,web-socket-handshake" \ No newline at end of file diff --git a/.scripts/podman-rm.sh b/.scripts/podman-rm.sh index 526a80e5..a50b7eae 100755 --- a/.scripts/podman-rm.sh +++ b/.scripts/podman-rm.sh @@ -1,2 +1,2 @@ -podman container rm wtx_postgres_md5 -podman container rm wtx_postgres_scram +podman container rm -fi wtx_postgres_md5 +podman container rm -fi wtx_postgres_scram diff --git a/.scripts/wtx-bench-postgres.sh b/.scripts/wtx-bench-postgres.sh deleted file mode 100755 index e5ddaf09..00000000 --- a/.scripts/wtx-bench-postgres.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -RUSTFLAGS="-Ctarget-cpu=native" cargo run --bin wtx-bench --profile bench -- postgres postgres://wtx_md5:wtx@localhost:5432/wtx \ No newline at end of file diff --git a/.scripts/wtx-bench-web-socket.sh b/.scripts/wtx-bench-web-socket.sh deleted file mode 100755 index 7dbc3c9b..00000000 --- a/.scripts/wtx-bench-web-socket.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT - -export CARGO_PROFILE_RELEASE_LTO=true - -FLAGS="-Ccodegen-units=1 -Copt-level=3 -Cpanic=abort -Cstrip=symbols -Ctarget-cpu=native" - -pushd /tmp -git clone https://github.com/c410-f3r/tokio-tungstenite || true -cd tokio-tungstenite -git checkout -t origin/bench || true -RUSTFLAGS="$FLAGS" cargo build --example echo-server --profile bench -RUSTFLAGS="$FLAGS" cargo run --example echo-server --profile bench 127.0.0.1:8081 & -popd - -FEATURES="atoi,memchr,simdutf8,web-socket-handshake" - -RUSTFLAGS="$FLAGS" cargo build --example web-socket-server-echo-raw-async-std --features "async-std,$FEATURES" --profile bench -RUSTFLAGS="$FLAGS" cargo run --example web-socket-server-echo-raw-async-std --features "async-std,$FEATURES" --profile bench 127.0.0.1:8082 & - -RUSTFLAGS="$FLAGS" cargo build --example web-socket-server-echo-raw-glommio --features "glommio,$FEATURES" --profile bench -RUSTFLAGS="$FLAGS" cargo run --example web-socket-server-echo-raw-glommio --features "glommio,$FEATURES" --profile bench 127.0.0.1:8083 & - -RUSTFLAGS="$FLAGS" cargo build --example web-socket-server-echo-raw-smol --features "smol,$FEATURES" --profile bench -RUSTFLAGS="$FLAGS" cargo run --example web-socket-server-echo-raw-smol --features "smol,$FEATURES" --profile bench 127.0.0.1:8084 & - -RUSTFLAGS="$FLAGS" cargo build --example web-socket-server-echo-raw-tokio --features "tokio,$FEATURES" --profile bench -RUSTFLAGS="$FLAGS" cargo run --example web-socket-server-echo-raw-tokio --features "tokio,$FEATURES" --profile bench 127.0.0.1:8085 & - -sleep 1 - -RUSTFLAGS="$FLAGS" cargo run --bin wtx-bench --profile bench -- \ - web-socket \ - http://127.0.0.1:8081/tokio-tungstenite \ - http://127.0.0.1:8082/wtx-raw-async-std \ - http://127.0.0.1:8083/wtx-raw-glommio \ - http://127.0.0.1:8084/wtx-raw-smol \ - http://127.0.0.1:8085/wtx-raw-tokio diff --git a/Cargo.lock b/Cargo.lock index db916fa8..cde6fadb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -70,7 +70,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", "once_cell", "version_check", "zerocopy", @@ -78,18 +77,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -108,9 +107,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "arbitrary" @@ -157,47 +156,46 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ - "concurrent-queue 2.4.0", + "concurrent-queue 2.5.0", "event-listener 2.5.3", "futures-core", ] [[package]] name = "async-channel" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ - "concurrent-queue 2.4.0", - "event-listener 5.0.0", - "event-listener-strategy 0.5.0", + "concurrent-queue 2.5.0", + "event-listener 5.3.0", + "event-listener-strategy 0.5.2", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "async-lock 3.3.0", "async-task", - "concurrent-queue 2.4.0", - "fastrand 2.0.2", - "futures-lite 2.2.0", + "concurrent-queue 2.5.0", + "fastrand 2.1.0", + "futures-lite 2.3.0", "slab", ] [[package]] name = "async-fs" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ "async-lock 3.3.0", "blocking", - "futures-lite 2.2.0", + "futures-lite 2.3.0", ] [[package]] @@ -206,12 +204,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.2.1", "async-executor", - "async-io 2.3.1", + "async-io 2.3.2", "async-lock 3.3.0", "blocking", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "once_cell", ] @@ -224,7 +222,7 @@ dependencies = [ "async-lock 2.8.0", "autocfg", "cfg-if", - "concurrent-queue 2.4.0", + "concurrent-queue 2.5.0", "futures-lite 1.13.0", "log", "parking", @@ -237,18 +235,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ "async-lock 3.3.0", "cfg-if", - "concurrent-queue 2.4.0", + "concurrent-queue 2.5.0", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "parking", - "polling 3.4.0", - "rustix 0.38.31", + "polling 3.7.0", + "rustix 0.38.34", "slab", "tracing", "windows-sys 0.52.0", @@ -280,45 +278,47 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ - "async-io 2.3.1", + "async-io 2.3.2", "blocking", - "futures-lite 2.2.0", + "futures-lite 2.3.0", ] [[package]] name = "async-process" -version = "2.0.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c1cd5d253ecac3d3cf15e390fd96bd92a13b1d14497d81abf077304794fb04" +checksum = "a53fc6301894e04a92cb2584fedde80cb25ba8e02d9dc39d4a87d036e22f397d" dependencies = [ - "async-channel 2.2.0", - "async-io 2.3.1", + "async-channel 2.2.1", + "async-io 2.3.2", "async-lock 3.3.0", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 4.0.3", - "futures-lite 2.2.0", - "rustix 0.38.31", + "event-listener 5.3.0", + "futures-lite 2.3.0", + "rustix 0.38.34", + "tracing", "windows-sys 0.52.0", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" dependencies = [ - "async-io 2.3.1", - "async-lock 2.8.0", + "async-io 2.3.2", + "async-lock 3.3.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.31", + "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -349,20 +349,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" - -[[package]] -name = "async-trait" -version = "0.1.77" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "atoi" @@ -400,27 +389,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atomic-write-file" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" -dependencies = [ - "nix 0.27.1", - "rand", -] - [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -437,12 +416,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -455,15 +428,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "basic-toml" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" -dependencies = [ - "serde", -] - [[package]] name = "bcder" version = "0.7.4" @@ -482,9 +446,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitmaps" @@ -515,25 +479,23 @@ dependencies = [ [[package]] name = "blocking" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.2.1", "async-lock 3.3.0", "async-task", - "fastrand 2.0.2", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "piper", - "tracing", ] [[package]] name = "borsh" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0901fc8eb0aca4c83be0106d6f2db17d86a08dfc2c25f0e84464bf381158add6" +checksum = "dbe5b10e214954177fb1dc9fbd20a1a2608fe99e6c832033bdc7cea287a20d77" dependencies = [ "borsh-derive", "cfg_aliases", @@ -541,15 +503,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51670c3aa053938b0ee3bd67c3817e471e626151131b934038e83c5bf8de48f5" +checksum = "d7a8646f94ab393e43e8b35a2558b1624bed28b97ee09c5d15456e3c9463f46d" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", "syn_derive", ] @@ -561,9 +523,9 @@ checksum = "3240a4cb09cf0da6a51641bd40ce90e96ea6065e3a1adc46434029254bcc2d09" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecheck" @@ -594,9 +556,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cache-padded" @@ -606,12 +568,13 @@ checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -635,7 +598,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -660,9 +623,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.2" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -680,14 +643,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -716,9 +679,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -744,26 +707,11 @@ dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -789,9 +737,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -863,9 +811,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -879,54 +827,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", -] - -[[package]] -name = "diesel" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" -dependencies = [ - "bitflags 2.4.2", - "byteorder", - "diesel_derives", - "itoa", -] - -[[package]] -name = "diesel-async" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be" -dependencies = [ - "async-trait", - "diesel", - "futures-util", - "scoped-futures", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "diesel_derives" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" -dependencies = [ - "diesel_table_macro_syntax", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" -dependencies = [ - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -955,21 +856,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "either" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" -dependencies = [ - "serde", -] - [[package]] name = "elliptic-curve" version = "0.13.8" @@ -1168,17 +1054,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1191,18 +1066,18 @@ version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ - "concurrent-queue 2.4.0", + "concurrent-queue 2.5.0", "parking", "pin-project-lite", ] [[package]] name = "event-listener" -version = "5.0.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72557800024fabbaa2449dd4bf24e37b93702d457a4d4f2b0dd1f0f039f20c1" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ - "concurrent-queue 2.4.0", + "concurrent-queue 2.5.0", "parking", "pin-project-lite", ] @@ -1219,20 +1094,14 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.0.0", + "event-listener 5.3.0", "pin-project-lite", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fastrand" version = "1.9.0" @@ -1244,9 +1113,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "ff" @@ -1258,17 +1127,11 @@ dependencies = [ "subtle", ] -[[package]] -name = "finl_unicode" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" - [[package]] name = "flate2" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "libz-ng-sys", @@ -1297,21 +1160,6 @@ dependencies = [ "spin", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - [[package]] name = "funty" version = "2.0.0" @@ -1348,17 +1196,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - [[package]] name = "futures-io" version = "0.3.30" @@ -1382,11 +1219,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.2", + "fastrand 2.1.0", "futures-core", "futures-io", "parking", @@ -1401,7 +1238,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -1423,14 +1260,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", - "futures-io", "futures-macro", "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", - "slab", ] [[package]] @@ -1464,9 +1298,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -1477,9 +1311,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", @@ -1499,13 +1333,13 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "glommio" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f09bf53139d5680da6325b4e79c6bc1518e94a65ab74df14b7e3693a8c78b" +checksum = "e1f8bc1fce949d18098dc0a4e861314e40351a0144ebf61e59bdb5254a2273b2" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", "backtrace", - "bitflags 1.3.2", + "bitflags 2.5.0", "bitmaps", "buddy-alloc", "cc", @@ -1519,7 +1353,7 @@ dependencies = [ "libc", "lockfree", "log", - "nix 0.23.2", + "nix", "pin-project-lite", "rlimit", "scoped-tls", @@ -1557,11 +1391,11 @@ dependencies = [ [[package]] name = "halfbrown" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5681137554ddff44396e5f149892c769d45301dd9aa19c51602a89ee214cb0ec" +checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.5", "serde", ] @@ -1598,37 +1432,19 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.11", + "ahash 0.7.8", ] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", "allocator-api2", ] -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown 0.14.3", -] - [[package]] name = "heapless" version = "0.6.1" @@ -1666,18 +1482,15 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1703,94 +1516,12 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" -[[package]] -name = "hyper" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2 0.5.5", - "tokio", - "tower", - "tower-service", - "tracing", -] - [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1814,24 +1545,14 @@ dependencies = [ "cc", ] -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -1858,7 +1579,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b694dc9f70c3bda874626d2aed13b780f137aab435f4e9814121955cf706122e" dependencies = [ - "memoffset 0.9.0", + "memoffset", ] [[package]] @@ -1872,41 +1593,26 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1992,9 +1698,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libfuzzer-sys" @@ -2043,9 +1749,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2062,9 +1768,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ "value-bag", ] @@ -2102,50 +1808,29 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] -name = "memoffset" -version = "0.9.0" +name = "mini-internal" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mini-internal" -version = "0.1.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09fa787e06d071d09c2964b065eb7cb1b842a7af5382fc4c9142089f8383f08" +checksum = "f28c501b91dabb672c4b303fb6ea259022ab21ed8d06a457ac21b70e479fb367" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniserde" -version = "0.1.38" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19a2e17a11c24d44c84d7e1b61477e20cd503d024e4b0eb3e97eb9a4d24fa87" +checksum = "bd044c3b41ba1d92e3dbeef687ecfe5d1dcbd8b4fa1d33610f5ce5546ad6aac1" dependencies = [ "itoa", "mini-internal", @@ -2163,9 +1848,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", @@ -2196,28 +1881,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" -[[package]] -name = "nix" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" -dependencies = [ - "bitflags 1.3.2", - "cc", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", "libc", + "memoffset", ] [[package]] @@ -2226,16 +1899,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nu-ansi-term" version = "0.49.0" @@ -2247,9 +1910,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -2282,9 +1945,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "owned-alloc" @@ -2310,9 +1973,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -2320,82 +1983,52 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "pem" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.21.7", + "base64", "serde", ] -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2410,38 +2043,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.0.2", + "fastrand 2.1.0", "futures-io", ] -[[package]] -name = "plotters" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" - -[[package]] -name = "plotters-svg" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" -dependencies = [ - "plotters-backend", -] - [[package]] name = "polling" version = "2.8.0" @@ -2451,7 +2056,7 @@ dependencies = [ "autocfg", "bitflags 1.3.2", "cfg-if", - "concurrent-queue 2.4.0", + "concurrent-queue 2.5.0", "libc", "log", "pin-project-lite", @@ -2460,23 +2065,24 @@ dependencies = [ [[package]] name = "polling" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if", - "concurrent-queue 2.4.0", + "concurrent-queue 2.5.0", + "hermit-abi", "pin-project-lite", - "rustix 0.38.31", + "rustix 0.38.34", "tracing", "windows-sys 0.52.0", ] [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", @@ -2484,35 +2090,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "postgres-protocol" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" -dependencies = [ - "base64 0.21.7", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md-5", - "memchr", - "rand", - "sha2", - "stringprep", -] - -[[package]] -name = "postgres-types" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" -dependencies = [ - "bytes", - "fallible-iterator", - "postgres-protocol", -] - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2534,7 +2111,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit", + "toml_edit 0.21.1", ] [[package]] @@ -2562,9 +2139,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -2575,13 +2152,13 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", "unarray", ] @@ -2681,23 +2258,23 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -2711,13 +2288,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -2728,9 +2305,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rend" @@ -2741,41 +2318,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "reqwest" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e6cc1e89e689536eb5aeede61520e874df5a4707df811cd5da4aa5fbb2aae19" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - [[package]] name = "ring" version = "0.17.8" @@ -2829,9 +2371,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.34.3" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39449a79f45e8da28c57c341891b69a183044b29518bb8f86dbac9df60bb7df" +checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" dependencies = [ "arrayvec", "num-traits", @@ -2869,11 +2411,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys 0.4.13", @@ -2882,9 +2424,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "ring", "rustls-pki-types", @@ -2895,25 +2437,25 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.7", + "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ "ring", "rustls-pki-types", @@ -2922,19 +2464,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - -[[package]] -name = "scoped-futures" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" -dependencies = [ - "cfg-if", - "pin-utils", -] +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "scoped-tls" @@ -2969,15 +2501,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] @@ -2996,13 +2528,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3017,22 +2549,19 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", ] [[package]] name = "serde_yaml" -version = "0.9.33" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", @@ -3084,9 +2613,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3121,12 +2650,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "sketches-ddsketch" version = "0.1.3" @@ -3144,9 +2667,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol" @@ -3154,15 +2677,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e635339259e51ef85ac7aa29a1cd991b957047507288697a690e80ab97d07cad" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.2.1", "async-executor", "async-fs", - "async-io 2.3.1", + "async-io 2.3.2", "async-lock 3.3.0", "async-net", "async-process", "blocking", - "futures-lite 2.2.0", + "futures-lite 2.3.0", ] [[package]] @@ -3190,12 +2713,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3217,145 +2740,6 @@ dependencies = [ "der", ] -[[package]] -name = "sqlformat" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-postgres", -] - -[[package]] -name = "sqlx-core" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" -dependencies = [ - "ahash 0.8.11", - "atoi", - "byteorder", - "bytes", - "crc", - "crossbeam-queue", - "dotenvy", - "either", - "event-listener 2.5.3", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashlink", - "hex", - "indexmap", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlformat", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "url", -] - -[[package]] -name = "sqlx-macros" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 1.0.109", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" -dependencies = [ - "atomic-write-file", - "dotenvy", - "either", - "heck", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-postgres", - "syn 1.0.109", - "tempfile", - "tokio", - "url", -] - -[[package]] -name = "sqlx-postgres" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" -dependencies = [ - "atoi", - "base64 0.21.7", - "bitflags 2.4.2", - "byteorder", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "rand", - "serde", - "serde_json", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "tracing", - "whoami", -] - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3368,17 +2752,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stringprep" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" -dependencies = [ - "finl_unicode", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "structmeta" version = "0.2.0" @@ -3388,7 +2761,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3399,7 +2772,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3421,9 +2794,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -3439,33 +2812,15 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tempfile" -version = "3.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" -dependencies = [ - "cfg-if", - "fastrand 2.0.2", - "rustix 0.38.31", - "windows-sys 0.52.0", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -3484,34 +2839,34 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3544,7 +2899,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.7", "tokio-macros", "windows-sys 0.48.0", ] @@ -3557,33 +2912,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", -] - -[[package]] -name = "tokio-postgres" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" -dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures-channel", - "futures-util", - "log", - "parking_lot", - "percent-encoding", - "phf", - "pin-project-lite", - "postgres-protocol", - "postgres-types", - "rand", - "socket2 0.5.5", - "tokio", - "tokio-util", - "whoami", + "syn 2.0.60", ] [[package]] @@ -3598,28 +2927,15 @@ dependencies = [ ] [[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.10" +name = "toml" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.12", ] [[package]] @@ -3627,6 +2943,9 @@ name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3636,44 +2955,28 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] -name = "tower" -version = "0.4.13" +name = "toml_edit" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.7", ] -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3687,7 +2990,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3726,25 +3029,19 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "trybuild" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f" +checksum = "35b69ff3ed900f74eb1e0d9bdd3df38da829dc4a26531674f6f019ca7c093c8d" dependencies = [ - "basic-toml", "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", + "toml", ] [[package]] @@ -3759,39 +3056,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - [[package]] name = "universal-hash" version = "0.5.1" @@ -3814,17 +3084,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - [[package]] name = "valuable" version = "0.1.0" @@ -3833,9 +3092,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" [[package]] name = "value-trait" @@ -3867,15 +3126,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3884,9 +3134,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3894,24 +3144,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -3921,9 +3171,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3931,28 +3181,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -3967,16 +3217,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "whoami" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - [[package]] name = "winapi" version = "0.3.9" @@ -3995,11 +3235,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4014,7 +3254,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -4032,7 +3272,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -4052,17 +3292,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -4073,9 +3314,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -4085,9 +3326,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -4097,9 +3338,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -4109,9 +3356,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4121,9 +3368,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4133,9 +3380,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -4145,27 +3392,26 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.5.39" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.52.0" +name = "winnow" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "memchr", ] [[package]] @@ -4177,7 +3423,7 @@ dependencies = [ "arrayvec", "async-std", "atoi", - "base64 0.22.1", + "base64", "borsh", "bytes", "chrono", @@ -4185,23 +3431,23 @@ dependencies = [ "crypto-common", "digest", "embassy-net", + "embassy-sync", "embedded-io-async", "embedded-tls", - "fastrand 2.0.2", + "fastrand 2.1.0", "flate2", - "futures", "futures-lite 1.13.0", "glommio", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "hmac", "httparse", "md-5", "memchr", "miniserde", + "parking_lot", "proptest", "protobuf", "rand", - "reqwest", "ring", "rkyv", "rust_decimal", @@ -4217,6 +3463,7 @@ dependencies = [ "simdutf8", "smallvec", "smol", + "smoltcp", "test-strategy", "tokio", "tokio-rustls", @@ -4227,20 +3474,6 @@ dependencies = [ "x509-certificate", ] -[[package]] -name = "wtx-bench" -version = "0.0.1" -dependencies = [ - "diesel", - "diesel-async", - "futures", - "plotters", - "sqlx", - "tokio", - "tokio-postgres", - "wtx", -] - [[package]] name = "wtx-fuzz" version = "0.0.0" @@ -4301,9 +3534,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" [[package]] name = "zerocopy" @@ -4322,7 +3555,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -4342,5 +3575,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] diff --git a/Cargo.toml b/Cargo.toml index 62ba2c16..451e4106 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,7 @@ -[profile.bench] -codegen-units = 1 -debug = false -debug-assertions = false -incremental = false -lto = true -opt-level = 3 -overflow-checks = false -panic = 'abort' -rpath = false -strip = "symbols" - [profile.profiling] inherits = "release" debug = true [workspace] -members = ["wtx", "wtx-bench", "wtx-fuzz", "wtx-macros", "wtx-ui"] +members = ["wtx", "wtx-fuzz", "wtx-macros", "wtx-ui"] resolver = "2" diff --git a/README.md b/README.md index e1874d34..40d19e0e 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,15 @@ [![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-1.75-lightgray")](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html) -A collection of different transport implementations and related tools focused primarily on web technologies. Contains the implementations of 2 IETF RFCs ([RFC6455](https://datatracker.ietf.org/doc/html/rfc6455), [RFC7692](https://datatracker.ietf.org/doc/html/rfc7692)), 1 formal specification ([PostgreSQL](https://www.postgresql.org/docs/16/protocol.html)) and several other invented ideas. +A collection of different transport implementations and related tools focused primarily on web technologies. Contains the implementations of 4 IETF RFCs ([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)), 1 formal specification ([PostgreSQL](https://www.postgresql.org/docs/16/protocol.html)) and several other invented ideas. -1. [Client API Framework](https://c410-f3r.github.io/wtx-site/client-api-framework/index.html) -2. [Database Client](https://c410-f3r.github.io/wtx-site/database/client-connection.html) -3. [Database Object–Relational Mapping](https://c410-f3r.github.io/wtx-site/database/object%E2%80%93relational-mapping.html) -4. [Database Schema Manager](https://c410-f3r.github.io/wtx-site/database/schema-management.html) -5. [WebTransport Client/Server](https://c410-f3r.github.io/wtx-site/web-socket/index.html) -6. [Pool Manager](https://c410-f3r.github.io/wtx-site/pool_manager/index.html) +1. [Client API Framework](https://c410-f3r.github.io/wtx/client-api-framework/index.html) +2. [Database Client](https://c410-f3r.github.io/wtx/database/client-connection.html) +3. [Database Object–Relational Mapping](https://c410-f3r.github.io/wtx/database/object%E2%80%93relational-mapping.html) +4. [Database Schema Manager](https://c410-f3r.github.io/wtx/database/schema-management.html) +6. [HTTP2 Client/Server](https://c410-f3r.github.io/wtx/http2/index.html) +7. [Pool Manager](https://c410-f3r.github.io/wtx/pool_manager/index.html) +8. [WebSocket Client/Server](https://c410-f3r.github.io/wtx/web-socket/index.html) Embedded devices with a working heap allocator can use this `no_std` crate. @@ -23,32 +24,34 @@ Many things that generally improve performance are used in the project, to name 1. **Manual vectorization**: When an algorithm is known for processing large amounts of data, several experiments are performed to analyze the best way to split loops in order to allow the compiler to take advantage of SIMD instructions in x86 processors. 2. **Memory allocation**: Whenever possible, all heap allocations are called only once at the start of an instance creation and additionally, stack memory usage is preferably prioritized over heap memory. -3. **Fewer dependencies**: No third-party is injected by default. In other words, additional dependencies are up to the user through the selection of Cargo features, which decreases compilation times by a large margin. For example, you can see the mere 18 dependencies required by the PostgreSQL client using `cargo tree -e normal --features postgres`. +3. **Fewer dependencies**: No third-party is injected by default. In other words, additional dependencies are up to the user through the selection of Cargo features, which decreases compilation times. For example, you can see the mere 17 dependencies required by the PostgreSQL client using `cargo tree -e normal --features postgres`. Since memory are usually held at the instance level instead of being created and dropped on the fly, it is worth noting that its usage can growth significantly depending on the use-case. If appropriated, try using a shared pool of resources or try limiting how much data can be exchanged between parties. ## High-level benchmarks -If you disagree with any of the mentioned charts, feel free to checkout [wtx-bench](https://github.com/c410-f3r/wtx/tree/main/wtx-bench) to point any misunderstandings or misconfigurations. +Checkout [wtx-bench](https://c410-f3r.github.io/wtx-bench/) to see a variety of benchmarks or feel free to point any misunderstandings or misconfigurations. There are mainly 2 things that impact performance, the chosen runtime and the number of pre-allocated bytes. Specially for servers that have to create a new instance for each handshake, pre-allocating a high number of bytes for short-lived or low-transfer connections can have a negative impact. -### PostgreSQL client - ![PostgreSQL Benchmark](https://i.imgur.com/vf2tYxY.jpg) -### WebSocket - -![WebSocket Benchmark](https://i.imgur.com/Iv2WzJV.jpg) - ## Low-level benchmarks Anything marked with `#[bench]` in the repository is considered a low-level benchmark in the sense that they measure very specific operations that generally serve as the basis for other parts. -Take a look at to see all low-level benchmarks over different periods of time. +Take a look at to see all low-level benchmarks over different periods of time. ## Limitations -Does not support systems with 16bit memory addresses and expects the infallible addition of the sizes of 4 allocated chunks of memories, otherwise the program will overflow in certain arithmetic operations potentially resulting in unexpected operations. +Does not support systems with 16bit memory addresses and expects the infallible addition of the sizes of 8 allocated chunks of memories, otherwise the program will overflow in certain arithmetic operations involving `usize` potentially resulting in unexpected operations. + +For example, in a 32bit system you can allocate a maximum of 2^29 bytes of memory for at most 8 elements. Such a scenario should be viable with little swap memory due to the likely triggering of the OOM killer or through specific limiters like `ulimit`. + +## Possible future features -For example, in a 32bit system you can allocate a maximum of 2^30 bytes of memory for at most 4 elements. Such a scenario should be viable with little swap memory due to the likely triggering of the OOM killer or specific limiters like `ulimit`. \ No newline at end of file +* gRPC over HTTP/2 (). +* Web server framework +* WebSocket over an HTTP/2 stream (). +* Static web server +* WebTransport over HTTP/2 (). diff --git a/rust-toolchain b/rust-toolchain index 4dc039be..6c49cc6e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2023-10-15" -components = ["clippy", "rustfmt"] +channel = "nightly-2024-04-27" +components = ["clippy", "miri", "rustfmt", "rust-src"] profile = "minimal" diff --git a/wtx-bench/Cargo.toml b/wtx-bench/Cargo.toml deleted file mode 100644 index b3a83e2c..00000000 --- a/wtx-bench/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[dependencies] -diesel = { default-features = false, version = "2.1" } -diesel-async = { default-features = false, features = ["postgres"], version = "0.4" } -futures = { default-features = false, version = "0.3" } -plotters = { default-features = false, features = ["histogram", "svg_backend"], version = "0.3" } -sqlx = { default-features = false, features = ["postgres", "runtime-tokio"], version = "0.7" } -tokio = { default-features = false, features = ["macros", "rt-multi-thread"], version = "1.37" } -tokio-postgres = { default-features = false, features = ["runtime"], version = "0.7" } -wtx = { default-features = false, features = ["atoi", "memchr", "postgres", "simdutf8", "tokio", "web-socket-handshake"], path = "../wtx" } - -[package] -description = "Benchmarks" -edition = "2021" -license = "Apache-2.0" -name = "wtx-bench" -publish = false -version = "0.0.1" diff --git a/wtx-bench/README.md b/wtx-bench/README.md deleted file mode 100644 index c1c0249e..00000000 --- a/wtx-bench/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Benchmarks - -Call the `wtx-bench` binary passing the necessary parameters according to the desired target. - -``` -cargo run --bin wtx-bench --profile bench -- web-socket http://127.0.0.1:8080/some_server_name http://127.0.0.1:8081/another_server_name -``` diff --git a/wtx-bench/src/main.rs b/wtx-bench/src/main.rs deleted file mode 100644 index 28f8e468..00000000 --- a/wtx-bench/src/main.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! WebSocket benchmark - -#![allow( - // Does not matter - clippy::arithmetic_side_effects, - // Does not matter - clippy::indexing_slicing, - // Does not matter - clippy::panic, - // Does not matter - clippy::print_stdout, - // Does not matter - clippy::unwrap_used -)] - -mod misc; -mod postgres; -mod web_socket; - -use wtx::misc::UriRef; - -#[tokio::main] -async fn main() { - let args: Vec<_> = std::env::args().skip(1).collect(); - match args.as_slice() { - [first, second, rest @ ..] => match first.as_str() { - "postgres" => { - let uri = UriRef::new(second.as_str()); - let mut diesel_async = misc::Agent { name: "diesel-async".to_owned(), result: 0 }; - let mut sqlx_postgres = misc::Agent { name: "sqlx-postgres-tokio".to_owned(), result: 0 }; - let mut tokio_postgres = misc::Agent { name: "tokio-postgres".to_owned(), result: 0 }; - let mut wtx = misc::Agent { name: "wtx-tokio".to_owned(), result: 0 }; - postgres::bench( - &uri, - [&mut diesel_async, &mut sqlx_postgres, &mut tokio_postgres, &mut wtx], - ) - .await; - misc::plot( - &[diesel_async, sqlx_postgres, tokio_postgres, wtx], - &postgres::caption(), - "/tmp/wtx-postgres.png", - ); - } - "web-socket" => { - let mut agents = Vec::new(); - for uri_string in [second].into_iter().chain(rest) { - let uri = UriRef::new(uri_string.as_str()); - let mut agent = misc::Agent { name: uri.href().to_owned(), result: 0 }; - web_socket::bench(&mut agent, &uri).await; - agents.push(agent); - } - misc::plot(&agents, &web_socket::caption(), "/tmp/wtx-web-socket.png"); - } - _ => { - panic!("Unknown benchmark target"); - } - }, - _ => { - panic!("Unknown benchmark target"); - } - } - println!("Finished!"); -} diff --git a/wtx-bench/src/misc.rs b/wtx-bench/src/misc.rs deleted file mode 100644 index dec34afe..00000000 --- a/wtx-bench/src/misc.rs +++ /dev/null @@ -1,63 +0,0 @@ -use plotters::{ - prelude::{ - ChartBuilder, IntoDrawingArea, IntoSegmentedCoord, LabelAreaPosition, PathElement, SVGBackend, - SeriesLabelPosition, - }, - series::Histogram, - style::{AsRelative, Color, Palette99, PaletteColor, BLACK, WHITE}, -}; - -#[derive(Debug)] -pub(crate) struct Agent { - pub(crate) result: u128, - pub(crate) name: String, -} - -pub(crate) fn plot(agents: &[Agent], caption: &str, output: &str) { - if agents.is_empty() { - return; - } - let x_spec = agents.iter().map(|el| &el.name).cloned().collect::>(); - let root = SVGBackend::new(output, (1280, 780)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let mut ctx = ChartBuilder::on(&root) - .caption(caption, ("sans-serif", (4).percent_height())) - .margin((1).percent()) - .set_label_area_size(LabelAreaPosition::Left, (10).percent()) - .set_label_area_size(LabelAreaPosition::Bottom, (5).percent()) - .build_cartesian_2d(x_spec.into_segmented(), { - let start = 0u128; - let exact_end = agents.iter().map(|el| el.result).max().unwrap_or(5000); - let surplus_end = ((exact_end / 500) + 1) * 500; - start..surplus_end - }) - .unwrap(); - ctx - .configure_mesh() - .axis_desc_style(("sans-serif", 15)) - .bold_line_style(WHITE.mix(0.3)) - .y_desc("Time (ms)") - .draw() - .unwrap(); - for (idx, agent) in agents.iter().enumerate() { - let _ = ctx - .draw_series( - Histogram::vertical(&ctx) - .style(PaletteColor::::pick(idx).mix(0.5).filled()) - .data([(&agent.name, agent.result)]), - ) - .unwrap() - .label(format!("{} ({}ms)", &agent.name, agent.result)) - .legend(move |(x, y)| { - PathElement::new([(x, y), (x + 20, y)], PaletteColor::::pick(idx)) - }); - } - ctx - .configure_series_labels() - .border_style(BLACK) - .background_style(WHITE.mix(0.8)) - .position(SeriesLabelPosition::UpperRight) - .draw() - .unwrap(); - root.present().unwrap(); -} diff --git a/wtx-bench/src/postgres.rs b/wtx-bench/src/postgres.rs deleted file mode 100644 index ddb01320..00000000 --- a/wtx-bench/src/postgres.rs +++ /dev/null @@ -1,234 +0,0 @@ -use crate::misc::Agent; -use diesel::prelude::table; -use futures::stream::StreamExt; -use sqlx::{Connection, Either, Executor as _, Row, Statement}; -use std::time::Instant; -use tokio::{net::TcpStream, task::JoinSet}; -use tokio_postgres::NoTls; -use wtx::{ - database::{ - client::postgres::{Executor, ExecutorBuffer}, - Executor as _, Record as _, Records, - }, - misc::UriRef, - rng::{Rng, StdRng}, -}; - -// Verifies the handling of concurrent calls. -const CONNECTIONS: usize = 64; -// Bytes to create and receive. -const DATA_LEN: usize = 32 * 1024; -// Number of sequential `SELECT` statements. -const QUERIES: usize = 8 * 1024; - -const SELECT_QUERY: &str = "SELECT * FROM benchmark"; - -pub(crate) async fn bench( - uri: &UriRef<'_>, - [diesel_async, sqlx_postgres, tokio_postgres, wtx]: [&mut Agent; 4], -) { - populate_db(&mut StdRng::default(), uri).await; - bench_diesel_async(diesel_async, uri).await; - bench_sqlx_postgres(sqlx_postgres, uri).await; - bench_tokio_postgres(tokio_postgres, uri).await; - bench_wtx(wtx, uri).await; -} - -pub(crate) fn caption() -> String { - format!( - "{CONNECTIONS} connection(s) retrieving {QUERIES} sequential queries of {DATA_LEN} byte(s)" - ) -} - -#[allow(clippy::single_char_lifetime_names, unused_qualifications, clippy::shadow_unrelated)] -async fn bench_diesel_async(agent: &mut Agent, uri: &UriRef<'_>) { - use diesel_async::{AsyncPgConnection, RunQueryDsl}; - - table! { - benchmark(bar, baz) { - bar -> Text, - baz -> Text, - } - } - - let mut set = JoinSet::new(); - for _ in 0..CONNECTIONS { - let _handle = set.spawn({ - let local_uri = uri.to_string(); - async move { - let (client, conn) = tokio_postgres::Config::new() - .dbname(local_uri.path().get(1..).unwrap()) - .host(local_uri.hostname()) - .password(local_uri.password()) - .port(local_uri.port().parse().unwrap()) - .user(local_uri.user()) - .connect(NoTls) - .await - .unwrap(); - let _handle = tokio::spawn(async move { - if let Err(err) = conn.await { - println!("Error: {err}"); - } - }); - let mut pg_conn = AsyncPgConnection::try_from(client).await.unwrap(); - let instant = Instant::now(); - for _ in 0..QUERIES { - let records = benchmark::table.load::<(String, String)>(&mut pg_conn).await.unwrap(); - assert!(!records[0].0.is_empty()); - assert!(!records[0].1.is_empty()); - assert!(!records[1].0.is_empty()); - assert!(!records[1].1.is_empty()); - } - instant.elapsed().as_millis() - } - }); - } - exec(agent, &mut set).await; -} - -async fn bench_sqlx_postgres(agent: &mut Agent, uri: &UriRef<'_>) { - let mut set = JoinSet::new(); - for _ in 0..CONNECTIONS { - let _handle = set.spawn({ - let local_uri = uri.uri().to_owned(); - async move { - let mut conn = sqlx::postgres::PgConnection::connect(&local_uri).await.unwrap(); - let stmt = conn.prepare(SELECT_QUERY).await.unwrap(); - let instant = Instant::now(); - for _ in 0..QUERIES { - let mut rows = Vec::new(); - let mut stream = stmt.query().fetch_many(&mut conn); - while let Some(result) = stream.next().await { - match result.unwrap() { - Either::Left(_) => {} - Either::Right(row) => rows.push(row), - } - } - assert!(!rows[0].get::<&str, _>(0).is_empty()); - assert!(!rows[0].get::<&str, _>(1).is_empty()); - assert!(!rows[1].get::<&str, _>(0).is_empty()); - assert!(!rows[1].get::<&str, _>(1).is_empty()); - } - instant.elapsed().as_millis() - } - }); - } - exec(agent, &mut set).await; -} - -async fn bench_tokio_postgres(agent: &mut Agent, uri: &UriRef<'_>) { - let mut set = JoinSet::new(); - for _ in 0..CONNECTIONS { - let _handle = set.spawn({ - let local_uri = uri.to_string(); - async move { - let (client, conn) = tokio_postgres::Config::new() - .dbname(local_uri.path().get(1..).unwrap()) - .host(local_uri.hostname()) - .password(local_uri.password()) - .port(local_uri.port().parse().unwrap()) - .user(local_uri.user()) - .connect(NoTls) - .await - .unwrap(); - let _handle = tokio::spawn(async move { - if let Err(err) = conn.await { - println!("Error: {err}"); - } - }); - let stmt = client.prepare(SELECT_QUERY).await.unwrap(); - let instant = Instant::now(); - for _ in 0..QUERIES { - let rows = client.query(&stmt, &[]).await.unwrap(); - assert!(!rows[0].get::<_, &str>(0).is_empty()); - assert!(!rows[0].get::<_, &str>(1).is_empty()); - assert!(!rows[1].get::<_, &str>(0).is_empty()); - assert!(!rows[1].get::<_, &str>(1).is_empty()); - } - instant.elapsed().as_millis() - } - }); - } - exec(agent, &mut set).await; -} - -async fn bench_wtx(agent: &mut Agent, uri: &UriRef<'_>) { - let mut set = JoinSet::new(); - for _ in 0..CONNECTIONS { - let _handle = set.spawn({ - let local_uri = uri.to_string(); - async move { - let mut executor = wtx_executor(&mut StdRng::default(), &local_uri.to_ref()).await; - let stmt = executor.prepare(SELECT_QUERY).await.unwrap(); - let instant = Instant::now(); - for _ in 0..QUERIES { - let records = executor.fetch_many_with_stmt(stmt, (), |_| Ok(())).await.unwrap(); - assert!(!records.get(0).unwrap().decode::<_, &str>(0).unwrap().is_empty()); - assert!(!records.get(0).unwrap().decode::<_, &str>(1).unwrap().is_empty()); - assert!(!records.get(1).unwrap().decode::<_, &str>(0).unwrap().is_empty()); - assert!(!records.get(1).unwrap().decode::<_, &str>(1).unwrap().is_empty()); - } - instant.elapsed().as_millis() - } - }); - } - exec(agent, &mut set).await; -} - -fn fill_and_split_data<'data>( - data: &'data mut String, - rng: &mut StdRng, -) -> (&'data str, &'data str) { - data.extend((0..DATA_LEN).map(|_| { - let byte = rng.u8(); - if byte.is_ascii_alphanumeric() { - char::from(byte) - } else { - 'a' - } - })); - data.split_at(data.len() / 2) -} - -async fn exec(agent: &mut Agent, set: &mut JoinSet) { - let mut sum = 0; - while let Some(rslt) = set.join_next().await { - sum += rslt.unwrap(); - } - agent.result = sum / u128::try_from(CONNECTIONS).unwrap(); -} - -async fn populate_db(rng: &mut StdRng, uri: &UriRef<'_>) { - let mut executor = wtx_executor(rng, uri).await; - let mut data = String::new(); - let _ = executor.execute_with_stmt("DROP TABLE IF EXISTS benchmark", ()).await.unwrap(); - let _ = executor - .execute_with_stmt("CREATE TABLE benchmark(bar TEXT NOT NULL, baz TEXT NOT NULL)", ()) - .await - .unwrap(); - let (bar0, baz0) = fill_and_split_data(&mut data, rng); - let _ = executor - .execute_with_stmt(format!("INSERT INTO benchmark VALUES ('{bar0}', '{baz0}')").as_str(), ()) - .await - .unwrap(); - data.clear(); - let (bar1, baz1) = fill_and_split_data(&mut data, rng); - let _ = executor - .execute_with_stmt(format!("INSERT INTO benchmark VALUES ('{bar1}', '{baz1}')").as_str(), ()) - .await - .unwrap(); -} - -async fn wtx_executor( - rng: &mut StdRng, - uri: &UriRef<'_>, -) -> Executor { - Executor::connect( - &wtx::database::client::postgres::Config::from_uri(uri).unwrap(), - ExecutorBuffer::with_default_params(rng), - rng, - TcpStream::connect(uri.host()).await.unwrap(), - ) - .await - .unwrap() -} diff --git a/wtx-bench/src/web_socket.rs b/wtx-bench/src/web_socket.rs deleted file mode 100644 index d3b3e203..00000000 --- a/wtx-bench/src/web_socket.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! WebSocket benchmark - -use crate::misc::Agent; -use std::time::Instant; -use tokio::{net::TcpStream, task::JoinSet}; -use wtx::{ - misc::UriRef, - rng::StaticRng, - web_socket::{ - handshake::{WebSocketConnect, WebSocketConnectRaw}, - FrameBufferVec, FrameMutVec, OpCode, WebSocketBuffer, - }, -}; - -// Verifies the handling of concurrent calls. -const CONNECTIONS: usize = 2048; -// Bytes to receive and send -const FRAME_DATA: &[u8; FRAME_LEN] = &[53; FRAME_LEN]; -// Some applications use WebSocket to perform streaming so the length of a frame can be quite large -// but statistically it is generally low. -const FRAME_LEN: usize = 64 * 1024; -// For each message, the client always verifies the content sent back from a server and this -// leads to a sequential-like behavior. -// -// If this is the only high metric, all different servers end-up performing similarly effectively -// making this criteria an "augmenting factor" when combined with other parameters. -const NUM_MESSAGES: usize = 16; -/// A set of frames composes a message. -const NUM_FRAMES: usize = { - let n = NUM_MESSAGES / 4; - if n == 0 { - 1 - } else { - n - } -}; - -pub(crate) async fn bench(agent: &mut Agent, uri: &UriRef<'_>) { - let instant = Instant::now(); - let mut set = JoinSet::new(); - for _ in 0..CONNECTIONS { - let _handle = set.spawn({ - let local_uri = uri.to_string(); - async move { - let fb = &mut FrameBufferVec::default(); - let (_, mut ws) = WebSocketConnectRaw { - compression: (), - fb, - headers_buffer: &mut <_>::default(), - rng: StaticRng::default(), - stream: TcpStream::connect(local_uri.authority()).await.unwrap(), - uri: &local_uri.to_ref(), - wsb: WebSocketBuffer::default(), - } - .connect([]) - .await - .unwrap(); - for _ in 0..NUM_MESSAGES { - match NUM_FRAMES { - 0 => break, - 1 => { - ws.write_frame(&mut FrameMutVec::new_fin(fb, OpCode::Text, FRAME_DATA).unwrap()) - .await - .unwrap(); - } - _ => { - ws.write_frame(&mut FrameMutVec::new_unfin(fb, OpCode::Text, FRAME_DATA).unwrap()) - .await - .unwrap(); - for _ in (0..NUM_FRAMES).skip(2) { - ws.write_frame( - &mut FrameMutVec::new_unfin(fb, OpCode::Continuation, FRAME_DATA).unwrap(), - ) - .await - .unwrap(); - } - ws.write_frame( - &mut FrameMutVec::new_fin(fb, OpCode::Continuation, FRAME_DATA).unwrap(), - ) - .await - .unwrap(); - } - } - assert_eq!(ws.read_frame(fb).await.unwrap().fb().payload().len(), FRAME_LEN * NUM_FRAMES); - } - ws.write_frame(&mut FrameMutVec::new_fin(fb, OpCode::Close, &[]).unwrap()).await.unwrap(); - } - }); - } - while let Some(rslt) = set.join_next().await { - rslt.unwrap(); - } - agent.result = instant.elapsed().as_millis(); -} - -pub(crate) fn caption() -> String { - format!("{CONNECTIONS} connection(s) sending {NUM_MESSAGES} message(s) composed by {NUM_FRAMES} frame(s) of {FRAME_LEN} byte(s)") -} diff --git a/wtx-docs/src/SUMMARY.md b/wtx-docs/src/SUMMARY.md index 994241b6..ccb80d91 100644 --- a/wtx-docs/src/SUMMARY.md +++ b/wtx-docs/src/SUMMARY.md @@ -6,5 +6,6 @@ - [Client Connection](database/client-connection.md) - [Object–Relational Mapping](database/object–relational-mapping.md) - [Schema management](database/schema-management.md) +- [HTTP/2](http2/README.md) - [Pool Manager](pool_manager/README.md) - [WebSocket](web-socket/README.md) diff --git a/wtx-docs/src/database/client-connection.md b/wtx-docs/src/database/client-connection.md index 94203ae0..b8cf3800 100644 --- a/wtx-docs/src/database/client-connection.md +++ b/wtx-docs/src/database/client-connection.md @@ -8,14 +8,13 @@ Activation feature is called `postgres`. ![PostgreSQL Benchmark](https://i.imgur.com/vf2tYxY.jpg) ```ignore,rust,edition2021 -use core::borrow::BorrowMut; use wtx::{ database::{client::postgres::{Executor, ExecutorBuffer}, Executor as _, Record, Records}, - misc::Stream, + misc::{LeaseMut, Stream}, }; async fn query_foo( - executor: &mut Executor, impl Stream>, + executor: &mut Executor, impl Stream>, ) -> wtx::Result<(u32, String)> { let record = executor.fetch_with_stmt::( "SELECT bar,baz FROM foo WHERE bar = $1 AND baz = $2", diff --git a/wtx-docs/src/database/schema-management.md b/wtx-docs/src/database/schema-management.md index 2dcef54f..27398c27 100644 --- a/wtx-docs/src/database/schema-management.md +++ b/wtx-docs/src/database/schema-management.md @@ -9,7 +9,7 @@ Activation feature is called `sm`. ```bash # Example -cargo install --git https://github.com/c410-f3r/wtx --features sm-dev wtx-ui +cargo install --git https://github.com/c410-f3r/wtx --features schema-manager-dev wtx-ui echo DATABASE_URI="postgres://USER:PASSWORD@localhost:5432/DATABASE" > .env RUST_LOG=debug wtx-cli migrate ``` @@ -106,7 +106,7 @@ async fn main() { let mut commands = Commands::with_executor(()); commands .migrate_from_dir( - (&mut <_>::default(), &mut <_>::default()), + (&mut String::default(), &mut Vec::default()), Path::new("my_custom_migration_group_path"), ) .await diff --git a/wtx-docs/src/http2/README.md b/wtx-docs/src/http2/README.md new file mode 100644 index 00000000..95c88878 --- /dev/null +++ b/wtx-docs/src/http2/README.md @@ -0,0 +1,36 @@ +# HTTP/2 + +Provides low and high level abstractions to interact with clients and servers. + +```ignore,rust,edition2021 +use std::net::ToSocketAddrs; +use tokio::net::TcpStream; +use wtx::{ + http::{Headers, Method, Request}, + http2::{Http2Buffer, Http2Params, Http2Tokio, ReqResBuffer}, + misc::{from_utf8_basic, UriString}, + rng::StaticRng, +}; + +#[tokio::main] +async fn main() { + let uri = UriString::new("127.0.0.1:9000"); + let mut http2 = Http2Tokio::connect( + Http2Buffer::new(StaticRng::default()), + Http2Params::default(), + TcpStream::connect(uri.host().to_socket_addrs().unwrap().next().unwrap()).await.unwrap(), + ) + .await + .unwrap(); + let mut rrb = ReqResBuffer::default(); + let mut stream = http2.stream().await.unwrap(); + let res = stream + .send_req_recv_res( + Request::http2(&"Hello!", &Headers::new(0), Method::Get, uri.to_ref()), + &mut rrb, + ) + .await + .unwrap(); + println!("{}", from_utf8_basic(res.resource().unwrap().body()).unwrap()) +} +``` \ No newline at end of file diff --git a/wtx-docs/src/pool_manager/README.md b/wtx-docs/src/pool_manager/README.md index 4dd49583..98315a38 100644 --- a/wtx-docs/src/pool_manager/README.md +++ b/wtx-docs/src/pool_manager/README.md @@ -4,7 +4,7 @@ An asynchronous pool of arbitrary objects where each element is dynamically crea Can also be used for database connections, which is quite handy because it enhances the performance of executing commands and alleviates the use of hardware resources. -Activation feature is called `pool-manager`. +Activation feature is called `pool`. ```ignore,rust,edition2021 use wtx::pool_manager::{ResourceManager, StaticPool}; diff --git a/wtx-fuzz/web_socket.rs b/wtx-fuzz/web_socket.rs index f3a00c40..ba354456 100644 --- a/wtx-fuzz/web_socket.rs +++ b/wtx-fuzz/web_socket.rs @@ -1,9 +1,9 @@ //! WebSocket #![allow( - // Does not matter - clippy::unwrap_used -)] + // Does not matter + clippy::unwrap_used + )] #![no_main] use tokio::runtime::Builder; diff --git a/wtx-macros/src/api_types.rs b/wtx-macros/src/api_types.rs index 8dc1b7eb..112e192d 100644 --- a/wtx-macros/src/api_types.rs +++ b/wtx-macros/src/api_types.rs @@ -13,7 +13,7 @@ pub(crate) fn api_types( token_stream: proc_macro::TokenStream, ) -> crate::Result { let attr_args = parse_macro_input::parse::(attrs)?; - let mut item: Item = syn::parse_macro_input::parse(token_stream)?; + let mut item: Item = parse_macro_input::parse(token_stream)?; let attrs::Attrs { pkgs_aux, transports } = attrs::Attrs::try_from(&*attr_args)?; diff --git a/wtx-macros/src/pkg.rs b/wtx-macros/src/pkg.rs index 3ed66dd6..15c0583c 100644 --- a/wtx-macros/src/pkg.rs +++ b/wtx-macros/src/pkg.rs @@ -78,7 +78,7 @@ pub(crate) fn pkg( fbsiv, ))?; if let Some(content) = item_mod.content.as_mut() { - content.1.push(syn::Item::Verbatim(quote::quote!( + content.1.push(Item::Verbatim(quote::quote!( #params_item_unit_opt #(#auxs)* @@ -104,7 +104,7 @@ fn params_item_unit_fn(camel_case_id: &mut String) -> Item { ident: { let idx = camel_case_id.len(); camel_case_id.push_str("Params"); - let ident = Ident::new(camel_case_id, proc_macro2::Span::mixed_site()); + let ident = Ident::new(camel_case_id, Span::mixed_site()); camel_case_id.truncate(idx); ident }, diff --git a/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs b/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs index 184837b0..4e373eaf 100644 --- a/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs +++ b/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs @@ -5,7 +5,7 @@ create_fir_hook_item_values!( "after_sending", |arg| { Some(match arg { - "api" => quote::quote!(_api.borrow_mut()), + "api" => quote::quote!(_api.lease_mut()), "params" => quote::quote!(&mut self.params), "res_params" => quote::quote!(_ext_res_params), _ => return None, diff --git a/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs b/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs index 1f2a79e6..b36293b6 100644 --- a/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs +++ b/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs @@ -5,7 +5,7 @@ create_fir_hook_item_values!( "before_sending", |arg| { Some(match arg { - "api" => quote::quote!(_api.borrow_mut()), + "api" => quote::quote!(_api.lease_mut()), "params" => quote::quote!(&mut self.params), "req_bytes" => quote::quote!(_req_bytes), "req_params" => quote::quote!(_ext_req_params), diff --git a/wtx-macros/src/pkg/sir/sir_final_values.rs b/wtx-macros/src/pkg/sir/sir_final_values.rs index 13f6cd29..c1f42a54 100644 --- a/wtx-macros/src/pkg/sir/sir_final_values.rs +++ b/wtx-macros/src/pkg/sir/sir_final_values.rs @@ -135,7 +135,7 @@ impl<'attrs, 'module, 'others> wtx::client_api_framework::data_format::#dfe_ext_res_ctnt_wrapper< #res_ident >: wtx::client_api_framework::dnsn::Deserialize, - A: wtx::client_api_framework::Api::Error> + core::borrow::BorrowMut<#api>, + A: wtx::client_api_framework::Api::Error>, DRSR: wtx::misc::AsyncBounds, { type ExternalRequestContent = wtx::client_api_framework::data_format::#dfe_ext_req_ctnt_wrapper< diff --git a/wtx-macros/tests/ui/pass/custom_transport.rs b/wtx-macros/tests/ui/pass/custom_transport.rs index 6f90d1a7..395edd87 100644 --- a/wtx-macros/tests/ui/pass/custom_transport.rs +++ b/wtx-macros/tests/ui/pass/custom_transport.rs @@ -26,7 +26,7 @@ impl Transport for CustomTransport { Ok(()) } - async fn send_and_retrieve( + async fn send_recv( &mut self, _: &mut P, _: &mut PkgsAux, diff --git a/wtx-ui/src/http_client.rs b/wtx-ui/src/http_client.rs new file mode 100644 index 00000000..e75c5f79 --- /dev/null +++ b/wtx-ui/src/http_client.rs @@ -0,0 +1,5 @@ +use crate::clap::HttpClient; + +pub(crate) async fn http_client(http_client: &HttpClient) -> wtx::Result<()> { + Ok(()) +} diff --git a/wtx-ui/src/schema_manager.rs b/wtx-ui/src/schema_manager.rs index ed7b01e9..1257bc97 100644 --- a/wtx-ui/src/schema_manager.rs +++ b/wtx-ui/src/schema_manager.rs @@ -69,7 +69,7 @@ where } SchemaManagerCommands::Rollback { versions: _versions } => { commands - .rollback_from_toml((_buffer_cmd, _buffer_db_migrations), &toml_file_path(sm)?, &_versions) + .rollback_from_toml((_buffer_cmd, _buffer_db_migrations), &toml_file_path(sm)?, _versions) .await?; } #[cfg(feature = "schema-manager-dev")] diff --git a/wtx-ui/src/web_socket.rs b/wtx-ui/src/web_socket.rs index f66f2b0f..8fc520dc 100644 --- a/wtx-ui/src/web_socket.rs +++ b/wtx-ui/src/web_socket.rs @@ -6,7 +6,9 @@ use wtx::{ misc::UriRef, rng::StdRng, web_socket::{ - handshake::{WebSocketAccept, WebSocketAcceptRaw, WebSocketConnect, WebSocketConnectRaw}, + handshake::{ + HeadersBuffer, WebSocketAccept, WebSocketAcceptRaw, WebSocketConnect, WebSocketConnectRaw, + }, FrameBufferVec, FrameMutVec, OpCode, WebSocketBuffer, }, }; @@ -14,11 +16,11 @@ use wtx::{ pub(crate) async fn _connect(uri: &str, cb: impl Fn(&str)) -> wtx::Result<()> { let uri = UriRef::new(uri); let fb = &mut FrameBufferVec::default(); - let pb = &mut <_>::default(); + let wsb = &mut WebSocketBuffer::default(); let (_, mut ws) = WebSocketConnectRaw { fb, - headers_buffer: &mut <_>::default(), - wsb: pb, + headers_buffer: &mut HeadersBuffer::default(), + wsb, rng: StdRng::default(), stream: TcpStream::connect(uri.host()).await?, uri: &uri, diff --git a/wtx/Cargo.toml b/wtx/Cargo.toml index 7295e72f..749ecc7e 100644 --- a/wtx/Cargo.toml +++ b/wtx/Cargo.toml @@ -1,55 +1,45 @@ [[bin]] name = "autobahn-client" path = "src/bin/autobahn-client.rs" -required-features = ["flate2", "tokio/rt-multi-thread", "web-socket-handshake"] +required-features = ["flate2", "optimization", "tokio/rt-multi-thread", "web-socket-handshake"] [[bin]] name = "autobahn-server" path = "src/bin/autobahn-server.rs" -required-features = ["flate2", "tokio/rt-multi-thread", "web-socket-handshake"] +required-features = ["async-send", "flate2", "optimization", "pool", "tokio/rt-multi-thread", "web-socket-handshake"] [[example]] name = "database-client-postgres-tokio-rustls" path = "examples/database-client-postgres-tokio-rustls.rs" -required-features = ["_tokio-rustls-client", "postgres" ] +required-features = ["_tokio-rustls-client", "postgres"] [[example]] -name = "web-socket-client-cli-raw-tokio-rustls" -path = "examples/web-socket-client-cli-raw-tokio-rustls.rs" -required-features = ["_tokio-rustls-client", "tokio/io-std", "web-socket-handshake"] - -[[example]] -name = "web-socket-server-echo-raw-async-std" -path = "examples/web-socket-server-echo-raw-async-std.rs" -required-features = ["async-std", "web-socket-handshake"] +name = "http2-client-tokio" +path = "examples/http2-client-tokio.rs" +required-features = ["http2", "tokio"] [[example]] -name = "web-socket-server-echo-raw-glommio" -path = "examples/web-socket-server-echo-raw-glommio.rs" -required-features = ["glommio", "web-socket-handshake"] +name = "http2-server-tokio" +path = "examples/http2-server-tokio.rs" +required-features = ["async-send", "http2", "pool", "tokio"] [[example]] -name = "web-socket-server-echo-raw-smol" -path = "examples/web-socket-server-echo-raw-smol.rs" -required-features = ["smol", "web-socket-handshake"] +name = "web-socket-client-raw-tokio-rustls" +path = "examples/web-socket-client-raw-tokio-rustls.rs" +required-features = ["_tokio-rustls-client", "tokio/io-std", "web-socket-handshake"] [[example]] -name = "web-socket-server-echo-raw-tokio" -path = "examples/web-socket-server-echo-raw-tokio.rs" -required-features = ["tokio", "web-socket-handshake"] +name = "web-socket-server-raw-tokio" +path = "examples/web-socket-server-raw-tokio.rs" +required-features = ["async-send", "pool", "tokio", "web-socket-handshake"] [[example]] -name = "web-socket-server-echo-raw-tokio-rustls" -path = "examples/web-socket-server-echo-raw-tokio-rustls.rs" +name = "web-socket-server-raw-tokio-rustls" +path = "examples/web-socket-server-raw-tokio-rustls.rs" required-features = ["_tokio-rustls-server", "web-socket-handshake"] -[[example]] -name = "web-socket-server-pool-raw-tokio" -path = "examples/web-socket-server-pool-raw-tokio.rs" -required-features = ["pool-manager", "web-socket-handshake"] - [dependencies] -ahash = { default-features = false, optional = true, version = "0.8" } +ahash = { default-features = false, features = ["no-rng"], optional = true, version = "0.8" } arbitrary = { default-features = false, features = ["derive_arbitrary"], optional = true, version = "1.0" } arrayvec = { default-features = false, optional = true, version = "0.7" } async-std = { default-features = false, features = ["default"], optional = true, version = "1.0" } @@ -62,23 +52,23 @@ cl-aux = { default-features = false, optional = true, features = ["alloc"], vers crypto-common = { default-features = false, optional = true, version = "0.1" } digest = { default-features = false, features = ["mac"], optional = true, version = "0.10" } embassy-net = { default-features = false, features = ["tcp"], optional = true, version = "0.4" } +embassy-sync = { default-features = false, optional = true, version = "0.5" } embedded-io-async = { default-features = false, optional = true, version = "0.6" } embedded-tls = { default-features = false, features = ["async"], optional = true, version = "0.16" } fastrand = { default-features = false, optional = true, version = "2.0" } flate2 = { default-features = false, features = ["zlib-ng"], optional = true, version = "1.0" } -futures = { default-features = false, optional = true, version = "0.3" } futures-lite = { default-features = false, optional = true, version = "1.0" } -glommio = { default-features = false, optional = true, version = "0.8" } +glommio = { default-features = false, optional = true, version = "0.9" } hashbrown = { default-features = false, features = ["ahash", "allocator-api2", "inline-more"], optional = true, version = "0.14" } hmac = { default-features = false, optional = true, version = "0.12" } httparse = { default-features = false, optional = true, version = "1.0" } md-5 = { default-features = false, optional = true, version = "0.10" } memchr = { default-features = false, optional = true, version = "2.7" } miniserde = { default-features = false, optional = true, version = "0.1" } +parking_lot = { default-features = false, optional = true, version = "0.12" } proptest = { default-features = false, features = ["alloc"], optional = true, version = "1.0" } protobuf = { default-features = false, optional = true, version = "3.4" } rand = { default-features = false, features = ["small_rng"], optional = true, version = "0.8" } -reqwest = { default-features = false, optional = true, version = "0.12" } ring = { default-features = false, optional = true, version = "0.17" } rkyv = { default-features = false, features = ["validation"], optional = true, version = "0.7" } rust_decimal = { default-features = false, features = ["maths"], optional = true, version = "1.34" } @@ -94,6 +84,7 @@ simd-json = { default-features = false, features = ["serde_impl"], optional = tr simdutf8 = { default-features = false, features = ["aarch64_neon"], optional = true, version = "0.1" } smallvec = { default-features = false, features = ["const_generics", "union"], optional = true, version = "1.13" } smol = { default-features = false, optional = true, version = "2.0" } +smoltcp = { default-features = false, optional = true, version = "0.11" } test-strategy = { default-features = false, optional = true, version = "0.3" } tokio = { default-features = false, features = ["io-util", "net", "sync", "time"], optional = true, version = "1.37" } tokio-rustls = { default-features = false, features = ["ring"], optional = true, version = "0.25" } @@ -114,17 +105,20 @@ async-send = [] async-std = ["dep:async-std", "std"] borsh = ["dep:borsh", "std"] client-api-framework = ["cl-aux"] -database = ["arrayvec"] +database = [] default = [] embedded-tls = ["dep:embedded-io-async", "dep:embedded-tls"] glommio = ["futures-lite", "dep:glommio", "std"] http1 = ["httparse"] +http2 = ["ahash", "hashbrown", "tokio"] miniserde = ["dep:miniserde", "std"] +nightly = [] +optimization = ["atoi", "memchr", "simdutf8"] orm = ["database", "dep:smallvec"] -pool-manager = [] -postgres = ["ahash", "base64", "crypto-common", "database", "digest", "hashbrown", "md-5", "hmac", "sha2"] +pool = [] +postgres = ["ahash", "base64", "crypto-common", "database", "digest", "hashbrown", "hmac", "md-5", "sha2"] protobuf = ["dep:protobuf", "std"] -schema-manager = ["database", "chrono"] +schema-manager = ["arrayvec", "database", "chrono"] schema-manager-dev = ["schema-manager"] serde = ["arrayvec?/serde", "cl-aux?/serde", "dep:serde"] serde_json = ["serde", "dep:serde_json", "std"] @@ -132,7 +126,8 @@ serde_yaml = ["serde", "dep:serde_yaml", "std"] serde-xml-rs = ["serde", "dep:serde-xml-rs", "std"] simd-json = ["serde", "dep:simd-json", "std"] smol = ["dep:smol", "std"] -std = ["ahash?/std", "arrayvec?/std", "atoi?/std", "cl-aux?/std", "memchr?/std", "miniserde?/std", "serde?/std", "serde_json?/std", "simdutf8?/std"] +std = ["ahash?/std", "arrayvec?/std", "atoi?/std", "cl-aux?/std", "embassy-sync?/std", "memchr?/std", "miniserde?/std", "serde?/std", "serde_json?/std", "simdutf8?/std"] +test-strategy = ["dep:test-strategy", "proptest"] tokio = ["std", "dep:tokio"] tokio-rustls = ["ring", "rustls-pki-types", "tokio", "dep:tokio-rustls"] web-socket = [] @@ -140,8 +135,8 @@ web-socket-handshake = ["base64", "http1", "sha1", "web-socket"] _bench = [] # It is not up to this crate to decide what downstream should use -_hack = ["embassy-net?/medium-ip", "embassy-net?/proto-ipv4", "rkyv?/size_32", "simd-json?/allow-non-simd"] -_integration-tests = [] +_hack = ["embassy-net?/medium-ip", "embassy-net?/proto-ipv4", "rkyv?/size_32", "simd-json?/allow-non-simd", "smoltcp?/medium-ip", "smoltcp?/proto-ipv4", "smoltcp?/socket-tcp"] +_integration-tests = ["serde_json?/raw_value"] _proptest = ["proptest/std", "rust_decimal?/proptest", "std", "test-strategy"] _tokio-rustls-client = ["rustls-pemfile", "tokio-rustls/tls12", "webpki-roots"] _tokio-rustls-server = ["rustls-pemfile", "tokio-rustls"] diff --git a/wtx/examples/common/mod.rs b/wtx/examples/common/mod.rs index da000b8b..966d1627 100644 --- a/wtx/examples/common/mod.rs +++ b/wtx/examples/common/mod.rs @@ -1,59 +1,15 @@ -use wtx::{ - misc::Stream, - rng::StaticRng, - web_socket::{ - compression::NegotiatedCompression, - handshake::{WebSocketAccept, WebSocketAcceptRaw}, - Compression, FrameBufferVec, OpCode, WebSocketBuffer, WebSocketServerOwned, - }, -}; +#![allow(unused_imports)] -pub(crate) async fn _accept_conn_and_echo_frames( - compression: C, - fb: &mut FrameBufferVec, - stream: S, -) -> wtx::Result<()> -where - C: Compression, - S: Stream, -{ - let mut ws = WebSocketAcceptRaw { - compression, - rng: <_>::default(), - stream, - wsb: WebSocketBuffer::default(), - } - .accept(|_| true) - .await?; - _handle_frames(fb, &mut ws).await?; - Ok(()) -} +#[cfg(feature = "web-socket-handshake")] +mod web_socket; -pub(crate) async fn _handle_frames( - fb: &mut FrameBufferVec, - ws: &mut WebSocketServerOwned, -) -> wtx::Result<()> -where - NC: NegotiatedCompression, - S: Stream, -{ - loop { - let mut frame = ws.read_frame(fb).await?; - match frame.op_code() { - OpCode::Binary | OpCode::Text => { - ws.write_frame(&mut frame).await?; - } - OpCode::Close => break, - _ => {} - } - } - Ok(()) -} +#[cfg(feature = "web-socket-handshake")] +pub(crate) use web_socket::{_accept_conn_and_echo_frames, _handle_frames}; pub(crate) fn _host_from_args() -> String { - std::env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_owned()) + std::env::args().nth(1).unwrap_or_else(|| "127.0.0.1:9000".to_owned()) } pub(crate) fn _uri_from_args() -> String { - std::env::args().nth(1).unwrap_or_else(|| "http://127.0.0.1:8080".to_owned()) + std::env::args().nth(1).unwrap_or_else(|| "http://127.0.0.1:9000".to_owned()) } diff --git a/wtx/examples/common/web_socket.rs b/wtx/examples/common/web_socket.rs new file mode 100644 index 00000000..2fc4cc70 --- /dev/null +++ b/wtx/examples/common/web_socket.rs @@ -0,0 +1,47 @@ +use wtx::{ + misc::Stream, + rng::StaticRng, + web_socket::{ + compression::NegotiatedCompression, + handshake::{WebSocketAccept, WebSocketAcceptRaw}, + Compression, FrameBufferVec, OpCode, WebSocketBuffer, WebSocketServerMut, + }, +}; + +pub(crate) async fn _accept_conn_and_echo_frames( + compression: C, + fb: &mut FrameBufferVec, + stream: S, + wsb: &mut WebSocketBuffer, +) -> wtx::Result<()> +where + C: Compression, + S: Stream, +{ + let mut ws = WebSocketAcceptRaw { compression, rng: StaticRng::default(), stream, wsb } + .accept(|_| true) + .await?; + _handle_frames(fb, &mut ws).await?; + Ok(()) +} + +pub(crate) async fn _handle_frames( + fb: &mut FrameBufferVec, + ws: &mut WebSocketServerMut<'_, NC, StaticRng, S>, +) -> wtx::Result<()> +where + NC: NegotiatedCompression, + S: Stream, +{ + loop { + let mut frame = ws.read_frame(fb).await?; + match frame.op_code() { + OpCode::Binary | OpCode::Text => { + ws.write_frame(&mut frame).await?; + } + OpCode::Close => break, + _ => {} + } + } + Ok(()) +} diff --git a/wtx/examples/http2-client-tokio.rs b/wtx/examples/http2-client-tokio.rs new file mode 100644 index 00000000..895d510f --- /dev/null +++ b/wtx/examples/http2-client-tokio.rs @@ -0,0 +1,35 @@ +//! Http2 CLI client + +#[path = "./common/mod.rs"] +mod common; + +use std::net::ToSocketAddrs; +use tokio::net::TcpStream; +use wtx::{ + http::{Headers, Method, Request}, + http2::{Http2Buffer, Http2Params, Http2Tokio, ReqResBuffer}, + misc::{from_utf8_basic, UriString}, + rng::StaticRng, +}; + +#[tokio::main] +async fn main() { + let uri = UriString::new(common::_uri_from_args()); + let mut http2 = Http2Tokio::connect( + Http2Buffer::new(StaticRng::default()), + Http2Params::default(), + TcpStream::connect(uri.host().to_socket_addrs().unwrap().next().unwrap()).await.unwrap(), + ) + .await + .unwrap(); + let mut rrb = ReqResBuffer::default(); + let mut stream = http2.stream().await.unwrap(); + let res = stream + .send_req_recv_res( + Request::http2(&"Hello!", &Headers::new(0), Method::Get, uri.to_ref()), + &mut rrb, + ) + .await + .unwrap(); + println!("{}", from_utf8_basic(res.resource().unwrap().body()).unwrap()) +} diff --git a/wtx/examples/http2-server-tokio.rs b/wtx/examples/http2-server-tokio.rs new file mode 100644 index 00000000..25c96ea4 --- /dev/null +++ b/wtx/examples/http2-server-tokio.rs @@ -0,0 +1,36 @@ +//! Http2 echo server. + +#[path = "./common/mod.rs"] +mod common; + +use wtx::{ + http::{server::TokioHttp2, Headers, Method, RequestMut, Response, StatusCode}, + misc::ByteVector, +}; + +#[tokio::main] +async fn main() { + TokioHttp2::tokio_http2( + common::_host_from_args().parse().unwrap(), + None, + |err| println!("Connection error: {err:?}"), + |err| println!("Request error: {err:?}"), + handle, + ) + .await + .unwrap() +} + +async fn handle<'buffer>( + req: RequestMut<'buffer, 'buffer, 'buffer, ByteVector>, +) -> Result, ()> { + req.headers.clear(); + println!("{:?}", core::str::from_utf8(req.data)); + Ok(match (req.uri.path(), req.method) { + ("/", Method::Get) => Response::http2((req.data, req.headers), StatusCode::Ok), + _ => { + req.data.clear(); + Response::http2((req.data, req.headers), StatusCode::NotFound) + } + }) +} diff --git a/wtx/examples/web-socket-client-cli-raw-tokio-rustls.rs b/wtx/examples/web-socket-client-raw-tokio-rustls.rs similarity index 84% rename from wtx/examples/web-socket-client-cli-raw-tokio-rustls.rs rename to wtx/examples/web-socket-client-raw-tokio-rustls.rs index ab99c85b..18e9d4ec 100644 --- a/wtx/examples/web-socket-client-cli-raw-tokio-rustls.rs +++ b/wtx/examples/web-socket-client-raw-tokio-rustls.rs @@ -5,10 +5,10 @@ mod common; use tokio::io::{AsyncBufReadExt, BufReader}; use wtx::{ - misc::{TokioRustlsConnector, UriRef}, + misc::{TokioRustlsConnector, UriString}, rng::StdRng, web_socket::{ - handshake::{WebSocketConnect, WebSocketConnectRaw}, + handshake::{HeadersBuffer, WebSocketConnect, WebSocketConnectRaw}, FrameBufferVec, FrameMutVec, OpCode, WebSocketBuffer, }, }; @@ -16,12 +16,11 @@ use wtx::{ #[tokio::main] async fn main() { let fb = &mut FrameBufferVec::default(); - let uri = common::_uri_from_args(); - let uri = UriRef::new(uri.as_str()); + let uri = UriString::new(common::_uri_from_args()); let (_, mut ws) = WebSocketConnectRaw { compression: (), fb, - headers_buffer: &mut <_>::default(), + headers_buffer: &mut HeadersBuffer::default(), rng: StdRng::default(), stream: TokioRustlsConnector::from_webpki_roots() .push_certs(include_bytes!("../../.certs/root-ca.crt")) @@ -29,7 +28,7 @@ async fn main() { .with_tcp_stream(uri.host().parse().unwrap(), uri.hostname()) .await .unwrap(), - uri: &uri, + uri: &uri.to_ref(), wsb: WebSocketBuffer::default(), } .connect([]) diff --git a/wtx/examples/web-socket-server-echo-raw-async-std.rs b/wtx/examples/web-socket-server-echo-raw-async-std.rs deleted file mode 100644 index f11d3ceb..00000000 --- a/wtx/examples/web-socket-server-echo-raw-async-std.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! WebSocket echo server. - -#[path = "./common/mod.rs"] -mod common; - -use async_std::net::TcpListener; -use wtx::web_socket::FrameBufferVec; - -fn main() { - async_std::task::block_on(async { - let listener = TcpListener::bind(common::_host_from_args()).await.unwrap(); - loop { - let (stream, _) = listener.accept().await.unwrap(); - let _jh = async_std::task::spawn(async move { - common::_accept_conn_and_echo_frames((), &mut FrameBufferVec::default(), stream) - .await - .unwrap(); - }); - } - }); -} diff --git a/wtx/examples/web-socket-server-echo-raw-glommio.rs b/wtx/examples/web-socket-server-echo-raw-glommio.rs deleted file mode 100644 index d44b678d..00000000 --- a/wtx/examples/web-socket-server-echo-raw-glommio.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! WebSocket echo server. - -#[path = "./common/mod.rs"] -mod common; - -#[cfg(feature = "async-send")] -fn main() {} - -#[cfg(not(feature = "async-send"))] -fn main() { - use glommio::{net::TcpListener, LocalExecutorBuilder}; - - LocalExecutorBuilder::default() - .spawn::<_, _, ()>(|| async { - let listener = TcpListener::bind(crate::common::_host_from_args()).unwrap(); - loop { - let stream = listener.accept().await.unwrap(); - let _jh = glommio::spawn_local(async move { - crate::common::_accept_conn_and_echo_frames((), &mut <_>::default(), stream) - .await - .unwrap(); - }) - .detach(); - } - }) - .unwrap() - .join() - .unwrap(); -} diff --git a/wtx/examples/web-socket-server-echo-raw-smol.rs b/wtx/examples/web-socket-server-echo-raw-smol.rs deleted file mode 100644 index 7aca6799..00000000 --- a/wtx/examples/web-socket-server-echo-raw-smol.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! WebSocket echo server. - -#[path = "./common/mod.rs"] -mod common; - -use smol::net::TcpListener; - -fn main() { - smol::block_on(async { - let listener = TcpListener::bind(common::_host_from_args()).await.unwrap(); - loop { - let (stream, _) = listener.accept().await.unwrap(); - smol::spawn(async move { - common::_accept_conn_and_echo_frames((), &mut <_>::default(), stream).await.unwrap(); - }) - .detach(); - } - }); -} diff --git a/wtx/examples/web-socket-server-echo-raw-tokio.rs b/wtx/examples/web-socket-server-echo-raw-tokio.rs deleted file mode 100644 index ab912251..00000000 --- a/wtx/examples/web-socket-server-echo-raw-tokio.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! WebSocket echo server. - -#[path = "./common/mod.rs"] -mod common; - -use tokio::net::TcpListener; - -#[tokio::main(flavor = "current_thread")] -async fn main() { - let listener = TcpListener::bind(common::_host_from_args()).await.unwrap(); - loop { - let (stream, _) = listener.accept().await.unwrap(); - let _jh = tokio::spawn(async move { - common::_accept_conn_and_echo_frames((), &mut <_>::default(), stream).await.unwrap(); - }); - } -} diff --git a/wtx/examples/web-socket-server-pool-raw-tokio.rs b/wtx/examples/web-socket-server-pool-raw-tokio.rs deleted file mode 100644 index cc8b7191..00000000 --- a/wtx/examples/web-socket-server-pool-raw-tokio.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Uses a pool of resources to avoid having to heap-allocate for every new connection. -//! -//! Semantically speaking, this WebSocket code only accepts a connection and then immediately -//! closes it. - -#[path = "./common/mod.rs"] -mod common; - -use std::sync::OnceLock; -use tokio::{net::TcpListener, sync::MappedMutexGuard}; -use wtx::{ - pool_manager::{Pool as _, ResourceManager, StaticPoolTokioMutex, WebSocketRM}, - rng::StaticRng, - web_socket::{ - handshake::{WebSocketAccept, WebSocketAcceptRaw}, - FrameMutVec, OpCode, - }, -}; - -#[tokio::main] -async fn main() { - let listener = TcpListener::bind(common::_host_from_args()).await.unwrap(); - loop { - let (stream, _) = listener.accept().await.unwrap(); - let _jh = tokio::spawn(async move { - let (fb, wsb) = &mut *pool_resource().await; - let mut ws = WebSocketAcceptRaw { compression: (), rng: StaticRng::default(), stream, wsb } - .accept(|_| true) - .await - .unwrap(); - ws.write_frame(&mut FrameMutVec::new_fin(fb, OpCode::Close, &[]).unwrap()).await.unwrap(); - }); - } -} - -async fn pool_resource() -> MappedMutexGuard<'static, ::Resource> { - static POOL: OnceLock< - StaticPoolTokioMutex<::Resource, WebSocketRM, 8>, - > = OnceLock::new(); - POOL - .get_or_init(|| StaticPoolTokioMutex::new(WebSocketRM::web_socket_rm()).unwrap()) - .get() - .await - .unwrap() -} diff --git a/wtx/examples/web-socket-server-echo-raw-tokio-rustls.rs b/wtx/examples/web-socket-server-raw-tokio-rustls.rs similarity index 69% rename from wtx/examples/web-socket-server-echo-raw-tokio-rustls.rs rename to wtx/examples/web-socket-server-raw-tokio-rustls.rs index 2259f248..59484a74 100644 --- a/wtx/examples/web-socket-server-echo-raw-tokio-rustls.rs +++ b/wtx/examples/web-socket-server-raw-tokio-rustls.rs @@ -4,7 +4,10 @@ mod common; use tokio::net::TcpListener; -use wtx::misc::TokioRustlsAcceptor; +use wtx::{ + misc::TokioRustlsAcceptor, + web_socket::{FrameBufferVec, WebSocketBuffer}, +}; static CERT: &[u8] = include_bytes!("../../.certs/cert.pem"); static KEY: &[u8] = include_bytes!("../../.certs/key.pem"); @@ -18,7 +21,14 @@ async fn main() { let local_acceptor = acceptor.clone(); let _jh = tokio::spawn(async move { let tls_stream = local_acceptor.accept(stream).await.unwrap(); - common::_accept_conn_and_echo_frames((), &mut <_>::default(), tls_stream).await.unwrap(); + common::_accept_conn_and_echo_frames( + (), + &mut FrameBufferVec::default(), + tls_stream, + &mut WebSocketBuffer::default(), + ) + .await + .unwrap(); }); } } diff --git a/wtx/examples/web-socket-server-raw-tokio.rs b/wtx/examples/web-socket-server-raw-tokio.rs new file mode 100644 index 00000000..f52a97f4 --- /dev/null +++ b/wtx/examples/web-socket-server-raw-tokio.rs @@ -0,0 +1,40 @@ +//! WebSocket echo server. + +#[path = "./common/mod.rs"] +mod common; + +use tokio::net::TcpStream; +use wtx::{ + http::server::TokioWebSocket, + rng::StdRng, + web_socket::{FrameBufferVec, OpCode, WebSocketBuffer, WebSocketServer}, +}; + +#[tokio::main] +async fn main() { + TokioWebSocket::tokio_web_socket( + common::_host_from_args().parse().unwrap(), + None, + || (), + |err| println!("Connection error: {err:?}"), + handle, + ) + .await + .unwrap() +} + +async fn handle( + (fb, mut ws): (&mut FrameBufferVec, WebSocketServer<(), StdRng, TcpStream, &mut WebSocketBuffer>), +) -> wtx::Result<()> { + loop { + let mut frame = ws.read_frame(fb).await?; + match frame.op_code() { + OpCode::Binary | OpCode::Text => { + ws.write_frame(&mut frame).await?; + } + OpCode::Close => break, + _ => {} + } + } + Ok(()) +} diff --git a/wtx/proptest-regressions/misc/optimization.txt b/wtx/proptest-regressions/misc/optimization.txt deleted file mode 100644 index 6fc36c00..00000000 --- a/wtx/proptest-regressions/misc/optimization.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Seeds for failure cases proptest has generated in the past. It is -# automatically read and these particular cases re-run before any -# novel cases are generated. -# -# It is recommended to check this file in to source control so that -# everyone who runs the test benefits from these saved cases. -cc e1714be976b1dce28e8295d590495376dcbe8daa2e4fb3ec068edc2429706629 # shrinks to input = _StrSplitOnce1Args { data: "\u{a0}", elem: 160 } diff --git a/wtx/src/bin/autobahn-client.rs b/wtx/src/bin/autobahn-client.rs index 1bfdf700..4472642b 100644 --- a/wtx/src/bin/autobahn-client.rs +++ b/wtx/src/bin/autobahn-client.rs @@ -6,21 +6,21 @@ use wtx::{ rng::StdRng, web_socket::{ compression::Flate2, - handshake::{WebSocketConnect, WebSocketConnectRaw}, + handshake::{HeadersBuffer, WebSocketConnect, WebSocketConnectRaw}, CloseCode, FrameBufferVec, FrameMutVec, OpCode, WebSocketBuffer, }, }; #[tokio::main] async fn main() { - let fb = &mut <_>::default(); + let fb = &mut FrameBufferVec::default(); let host = "127.0.0.1:9080"; let mut wsb = WebSocketBuffer::default(); - for case in 1..=get_case_count(fb, &host, &mut wsb).await { + for case in 1..=get_case_count(fb, host, &mut wsb).await { let (_, mut ws) = WebSocketConnectRaw { compression: Flate2::default(), fb, - headers_buffer: &mut <_>::default(), + headers_buffer: &mut HeadersBuffer::default(), rng: StdRng::default(), stream: TcpStream::connect(host).await.unwrap(), uri: &UriRef::new(&format!("http://{host}/runCase?case={case}&agent=wtx")), @@ -48,7 +48,7 @@ async fn main() { WebSocketConnectRaw { compression: (), fb, - headers_buffer: &mut <_>::default(), + headers_buffer: &mut HeadersBuffer::default(), rng: StdRng::default(), stream: TcpStream::connect(host).await.unwrap(), uri: &UriRef::new(&format!("http://{host}/updateReports?agent=wtx")), @@ -67,7 +67,7 @@ async fn get_case_count(fb: &mut FrameBufferVec, host: &str, wsb: &mut WebSocket let (_, mut ws) = WebSocketConnectRaw { compression: (), fb, - headers_buffer: &mut <_>::default(), + headers_buffer: &mut HeadersBuffer::default(), rng: StdRng::default(), stream: TcpStream::connect(host).await.unwrap(), uri: &UriRef::new(&format!("http://{host}/getCaseCount")), diff --git a/wtx/src/bin/autobahn-server.rs b/wtx/src/bin/autobahn-server.rs index 78438df3..18672a23 100644 --- a/wtx/src/bin/autobahn-server.rs +++ b/wtx/src/bin/autobahn-server.rs @@ -3,17 +3,44 @@ #[path = "../../examples/common/mod.rs"] mod common; -use tokio::net::TcpListener; -use wtx::web_socket::compression::Flate2; +use tokio::net::TcpStream; +use wtx::{ + http::server::TokioWebSocket, + rng::StdRng, + web_socket::{ + compression::{Flate2, NegotiatedFlate2}, + FrameBufferVec, OpCode, WebSocketBuffer, WebSocketServer, + }, +}; #[tokio::main] async fn main() { - let listener = TcpListener::bind("127.0.0.1:9070").await.unwrap(); + TokioWebSocket::tokio_web_socket( + "127.0.0.1:9070".parse().unwrap(), + None, + Flate2::default, + |err| println!("Connection error: {err:?}"), + handle, + ) + .await + .unwrap() +} + +async fn handle( + (fb, mut ws): ( + &mut FrameBufferVec, + WebSocketServer, StdRng, TcpStream, &mut WebSocketBuffer>, + ), +) -> wtx::Result<()> { loop { - let (stream, _) = listener.accept().await.unwrap(); - let _jh = tokio::spawn(async move { - let _rslt = - common::_accept_conn_and_echo_frames(Flate2::default(), &mut <_>::default(), stream).await; - }); + let mut frame = ws.read_frame(fb).await?; + match frame.op_code() { + OpCode::Binary | OpCode::Text => { + ws.write_frame(&mut frame).await?; + } + OpCode::Close => break, + _ => {} + } } + Ok(()) } diff --git a/wtx/src/client_api_framework/data_format/borsh/borsh_request.rs b/wtx/src/client_api_framework/data_format/borsh/borsh_request.rs index 116e9175..ac5cb370 100644 --- a/wtx/src/client_api_framework/data_format/borsh/borsh_request.rs +++ b/wtx/src/client_api_framework/data_format/borsh/borsh_request.rs @@ -18,6 +18,7 @@ impl Serialize<()> for BorshRequest { #[cfg(feature = "borsh")] mod borsh { use crate::client_api_framework::{data_format::BorshRequest, dnsn::Borsh}; + use alloc::vec::Vec; use borsh::BorshSerialize; impl crate::client_api_framework::dnsn::Serialize for BorshRequest diff --git a/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_request.rs b/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_request.rs index 137a3b28..7b268727 100644 --- a/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_request.rs +++ b/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_request.rs @@ -22,6 +22,7 @@ pub struct GraphQlRequest { #[cfg(feature = "serde_json")] mod serde_json { use crate::client_api_framework::{data_format::GraphQlRequest, dnsn::SerdeJson}; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for GraphQlRequest where @@ -43,6 +44,7 @@ mod serde_json { #[cfg(feature = "simd-json")] mod simd_json { use crate::client_api_framework::{data_format::GraphQlRequest, dnsn::SimdJson}; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for GraphQlRequest where diff --git a/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_response.rs b/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_response.rs index 1e135fe7..3663cdc1 100644 --- a/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_response.rs +++ b/wtx/src/client_api_framework/data_format/graph_ql/graph_ql_response.rs @@ -118,6 +118,7 @@ mod serde_json { use crate::client_api_framework::{ data_format::GraphQlResponse, dnsn::SerdeJson, misc::seq_visitor::_SeqVisitor, }; + use alloc::vec::Vec; use core::fmt::Display; use serde::de::Deserializer; @@ -165,6 +166,7 @@ mod serde_json { #[cfg(feature = "simd-json")] mod simd_json { use crate::client_api_framework::{data_format::GraphQlResponse, dnsn::SimdJson}; + use alloc::vec::Vec; use core::fmt::Display; impl crate::client_api_framework::dnsn::Deserialize for GraphQlResponse diff --git a/wtx/src/client_api_framework/data_format/json/json_request.rs b/wtx/src/client_api_framework/data_format/json/json_request.rs index 52e9b9e1..8d56d72d 100644 --- a/wtx/src/client_api_framework/data_format/json/json_request.rs +++ b/wtx/src/client_api_framework/data_format/json/json_request.rs @@ -21,6 +21,7 @@ mod miniserde { data_format::JsonRequest, dnsn::{miniserde_serialize, Miniserde}, }; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for JsonRequest where @@ -38,6 +39,7 @@ mod miniserde { #[cfg(feature = "serde_json")] mod serde_json { use crate::client_api_framework::{data_format::JsonRequest, dnsn::SerdeJson}; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for JsonRequest where @@ -57,6 +59,7 @@ mod serde_json { #[cfg(feature = "simd-json")] mod simd_json { use crate::client_api_framework::{data_format::JsonRequest, dnsn::SimdJson}; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for JsonRequest where diff --git a/wtx/src/client_api_framework/data_format/json/json_response.rs b/wtx/src/client_api_framework/data_format/json/json_response.rs index 2b84d7f3..7be36681 100644 --- a/wtx/src/client_api_framework/data_format/json/json_response.rs +++ b/wtx/src/client_api_framework/data_format/json/json_response.rs @@ -42,6 +42,7 @@ mod miniserde { }, misc::from_utf8_basic, }; + use alloc::vec::Vec; use core::fmt::Display; impl crate::client_api_framework::dnsn::Deserialize for JsonResponse @@ -85,6 +86,7 @@ mod serde_json { use crate::client_api_framework::{ data_format::JsonResponse, dnsn::SerdeJson, misc::seq_visitor::_SeqVisitor, }; + use alloc::vec::Vec; use core::fmt::Display; use serde::de::Deserializer; @@ -130,6 +132,7 @@ mod serde_json { #[cfg(feature = "simd-json")] mod simd_json { use crate::client_api_framework::{data_format::JsonResponse, dnsn::SimdJson}; + use alloc::vec::Vec; use core::fmt::Display; impl crate::client_api_framework::dnsn::Deserialize for JsonResponse diff --git a/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_request.rs b/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_request.rs index cc2f691b..21edc637 100644 --- a/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_request.rs +++ b/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_request.rs @@ -2,7 +2,7 @@ use crate::client_api_framework::{dnsn::Serialize, Id}; use alloc::vec::Vec; use core::{ borrow::Borrow, - cmp::{Ord, Ordering}, + cmp::Ordering, hash::{Hash, Hasher}, }; @@ -95,6 +95,7 @@ mod serde { #[cfg(feature = "serde_json")] mod serde_json { use crate::client_api_framework::{data_format::JsonRpcRequest, dnsn::SerdeJson}; + use alloc::vec::Vec; impl

crate::client_api_framework::dnsn::Serialize for JsonRpcRequest

where @@ -111,6 +112,7 @@ mod serde_json { #[cfg(feature = "simd-json")] mod simd_json { use crate::client_api_framework::{data_format::JsonRpcRequest, dnsn::SimdJson}; + use alloc::vec::Vec; impl

crate::client_api_framework::dnsn::Serialize for JsonRpcRequest

where diff --git a/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_response.rs b/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_response.rs index b9f1b8ad..1e20f8a3 100644 --- a/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_response.rs +++ b/wtx/src/client_api_framework/data_format/json_rpc/json_rpc_response.rs @@ -5,7 +5,7 @@ use crate::client_api_framework::{ use alloc::{string::String, vec::Vec}; use core::{ borrow::Borrow, - cmp::{Ord, Ordering}, + cmp::Ordering, hash::{Hash, Hasher}, }; @@ -91,24 +91,28 @@ impl PartialOrd for JsonRpcResponse { mod serde { use crate::client_api_framework::data_format::{JsonRpcResponse, JsonRpcResponseError}; use core::marker::PhantomData; - use serde::{de::Visitor, ser::SerializeStruct}; + use serde::{ + de::{Deserializer, MapAccess, Visitor}, + ser::{SerializeStruct, Serializer}, + Deserialize, Serialize, + }; - impl<'de, R> serde::Deserialize<'de> for JsonRpcResponse + impl<'de, R> Deserialize<'de> for JsonRpcResponse where - R: serde::Deserialize<'de>, + R: Deserialize<'de>, { #[inline] fn deserialize(deserializer: D) -> Result, D::Error> where - D: serde::de::Deserializer<'de>, + D: Deserializer<'de>, { struct CustomVisitor<'de, R>(PhantomData, PhantomData<&'de ()>) where - R: serde::Deserialize<'de>; + R: Deserialize<'de>; impl<'de, R> Visitor<'de> for CustomVisitor<'de, R> where - R: serde::Deserialize<'de>, + R: Deserialize<'de>, { type Value = JsonRpcResponse; @@ -120,7 +124,7 @@ mod serde { #[inline] fn visit_map(self, mut map: V) -> Result, V::Error> where - V: serde::de::MapAccess<'de>, + V: MapAccess<'de>, { let mut error = None; let mut id = None; @@ -196,14 +200,14 @@ mod serde { } } - impl serde::Serialize for JsonRpcResponse + impl Serialize for JsonRpcResponse where - R: serde::Serialize, + R: Serialize, { #[inline] fn serialize(&self, serializer: S) -> Result where - S: serde::ser::Serializer, + S: Serializer, { let mut state = serializer.serialize_struct("JsonRpcResponse", 3)?; state.serialize_field("jsonrpc", "2.0")?; @@ -234,6 +238,7 @@ mod serde_json { use crate::client_api_framework::{ data_format::JsonRpcResponse, dnsn::SerdeJson, misc::seq_visitor::_SeqVisitor, }; + use alloc::vec::Vec; use core::fmt::Display; impl crate::client_api_framework::dnsn::Deserialize for JsonRpcResponse @@ -276,6 +281,7 @@ mod serde_json { #[cfg(feature = "simd-json")] mod simd_json { use crate::client_api_framework::{data_format::JsonRpcResponse, dnsn::SimdJson}; + use alloc::vec::Vec; use core::fmt::Display; impl crate::client_api_framework::dnsn::Deserialize for JsonRpcResponse diff --git a/wtx/src/client_api_framework/data_format/verbatim/verbatim_request.rs b/wtx/src/client_api_framework/data_format/verbatim/verbatim_request.rs index 9abf1257..32239771 100644 --- a/wtx/src/client_api_framework/data_format/verbatim/verbatim_request.rs +++ b/wtx/src/client_api_framework/data_format/verbatim/verbatim_request.rs @@ -21,6 +21,7 @@ mod rkyv { data_format::VerbatimRequest, dnsn::{Rkyv, _InnerSerializer}, }; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for VerbatimRequest where diff --git a/wtx/src/client_api_framework/data_format/xml/xml_request.rs b/wtx/src/client_api_framework/data_format/xml/xml_request.rs index 23788be1..f8fb5786 100644 --- a/wtx/src/client_api_framework/data_format/xml/xml_request.rs +++ b/wtx/src/client_api_framework/data_format/xml/xml_request.rs @@ -20,6 +20,7 @@ impl Serialize<()> for XmlRequest { #[cfg(feature = "serde-xml-rs")] mod serde_xml_rs { use crate::client_api_framework::{data_format::XmlRequest, dnsn::SerdeXmlRs}; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for XmlRequest where diff --git a/wtx/src/client_api_framework/data_format/xml/xml_response.rs b/wtx/src/client_api_framework/data_format/xml/xml_response.rs index 6d35da04..d8306a0e 100644 --- a/wtx/src/client_api_framework/data_format/xml/xml_response.rs +++ b/wtx/src/client_api_framework/data_format/xml/xml_response.rs @@ -41,6 +41,7 @@ mod serde_xml_rs { use crate::client_api_framework::{ data_format::XmlResponse, dnsn::SerdeXmlRs, misc::seq_visitor::_SeqVisitor, }; + use alloc::vec::Vec; use core::fmt::Display; use serde::de::Deserializer; diff --git a/wtx/src/client_api_framework/data_format/yaml/yaml_request.rs b/wtx/src/client_api_framework/data_format/yaml/yaml_request.rs index 5c25b5ae..aceef9a0 100644 --- a/wtx/src/client_api_framework/data_format/yaml/yaml_request.rs +++ b/wtx/src/client_api_framework/data_format/yaml/yaml_request.rs @@ -20,6 +20,7 @@ impl Serialize<()> for YamlRequest { #[cfg(feature = "serde_yaml")] mod serde_yaml { use crate::client_api_framework::{data_format::YamlRequest, dnsn::SerdeYaml}; + use alloc::vec::Vec; impl crate::client_api_framework::dnsn::Serialize for YamlRequest where diff --git a/wtx/src/client_api_framework/data_format/yaml/yaml_response.rs b/wtx/src/client_api_framework/data_format/yaml/yaml_response.rs index 1a365e85..dbc3c152 100644 --- a/wtx/src/client_api_framework/data_format/yaml/yaml_response.rs +++ b/wtx/src/client_api_framework/data_format/yaml/yaml_response.rs @@ -41,6 +41,7 @@ mod serde_yaml { use crate::client_api_framework::{ data_format::YamlResponse, dnsn::SerdeYaml, misc::seq_visitor::_SeqVisitor, }; + use alloc::vec::Vec; use core::fmt::Display; use serde::de::Deserializer; diff --git a/wtx/src/client_api_framework/dnsn/miniserde.rs b/wtx/src/client_api_framework/dnsn/miniserde.rs index 52f4aa81..b28875e6 100644 --- a/wtx/src/client_api_framework/dnsn/miniserde.rs +++ b/wtx/src/client_api_framework/dnsn/miniserde.rs @@ -1,3 +1,5 @@ +use alloc::vec::Vec; + /// Type that indicates the usage of the `miniserde` dependency. #[derive(Debug)] pub struct Miniserde; diff --git a/wtx/src/client_api_framework/dnsn/rkyv.rs b/wtx/src/client_api_framework/dnsn/rkyv.rs index a6d1fde1..e45df75d 100644 --- a/wtx/src/client_api_framework/dnsn/rkyv.rs +++ b/wtx/src/client_api_framework/dnsn/rkyv.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use rkyv::{ ser::{ serializers::{ diff --git a/wtx/src/client_api_framework/dnsn/tests.rs b/wtx/src/client_api_framework/dnsn/tests.rs index 5ac91f54..c83008e6 100644 --- a/wtx/src/client_api_framework/dnsn/tests.rs +++ b/wtx/src/client_api_framework/dnsn/tests.rs @@ -42,6 +42,7 @@ where } } +#[allow(dead_code)] #[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))] #[cfg_attr(feature = "miniserde", derive(miniserde::Serialize))] #[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize))] @@ -53,6 +54,7 @@ pub(crate) struct Foo { pub(crate) foo: &'static str, } +#[allow(dead_code)] #[cfg_attr(feature = "borsh", derive(borsh::BorshDeserialize, borsh::BorshSerialize))] #[cfg_attr(feature = "miniserde", derive(miniserde::Deserialize))] #[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Deserialize))] @@ -89,10 +91,7 @@ macro_rules! _create_dnsn_test { trans.push_response($raw_der); assert_eq!( trans - .send_retrieve_and_decode_contained( - &mut FooBar::<_, $res>::_new($fmt_ser), - pkgs_aux - ) + .send_recv_decode_contained(&mut FooBar::<_, $res>::_new($fmt_ser), pkgs_aux) .await .unwrap(), $fmt_der diff --git a/wtx/src/client_api_framework/macros.rs b/wtx/src/client_api_framework/macros.rs index 3c2812fa..91dc6273 100644 --- a/wtx/src/client_api_framework/macros.rs +++ b/wtx/src/client_api_framework/macros.rs @@ -142,7 +142,7 @@ macro_rules! _impl_se_collections { T: $bound, { #[inline] - fn to_bytes(&mut self, bytes: &mut Vec, drsr: &mut $drsr) -> crate::Result<()> + fn to_bytes(&mut self, bytes: &mut alloc::vec::Vec, drsr: &mut $drsr) -> crate::Result<()> { let $array_self = self; let $array_bytes = bytes; @@ -154,13 +154,12 @@ macro_rules! _impl_se_collections { )? $( - #[cfg(feature = "arrayvec")] - impl crate::client_api_framework::dnsn::Serialize<$drsr> for arrayvec::ArrayVec + impl crate::client_api_framework::dnsn::Serialize<$drsr> for crate::misc::ArrayVector where T: $bound, { #[inline] - fn to_bytes(&mut self, bytes: &mut Vec, drsr: &mut $drsr) -> crate::Result<()> { + fn to_bytes(&mut self, bytes: &mut alloc::vec::Vec, drsr: &mut $drsr) -> crate::Result<()> { let $arrayvec_self = self; let $arrayvec_bytes = bytes; let $arrayvec_drsr = drsr; @@ -175,7 +174,7 @@ macro_rules! _impl_se_collections { T: $bound, { #[inline] - fn to_bytes(&mut self, bytes: &mut Vec, drsr: &mut $drsr) -> crate::Result<()> { + fn to_bytes(&mut self, bytes: &mut alloc::vec::Vec, drsr: &mut $drsr) -> crate::Result<()> { let $slice_ref_self = self; let $slice_ref_bytes = bytes; let $slice_ref_drsr = drsr; @@ -184,12 +183,12 @@ macro_rules! _impl_se_collections { } } - impl crate::client_api_framework::dnsn::Serialize<$drsr> for Vec + impl crate::client_api_framework::dnsn::Serialize<$drsr> for alloc::vec::Vec where T: $bound, { #[inline] - fn to_bytes(&mut self, bytes: &mut Vec, drsr: &mut $drsr) -> crate::Result<()> { + fn to_bytes(&mut self, bytes: &mut alloc::vec::Vec, drsr: &mut $drsr) -> crate::Result<()> { let $vec_self = self; let $vec_bytes = bytes; let $vec_drsr = drsr; diff --git a/wtx/src/client_api_framework/misc.rs b/wtx/src/client_api_framework/misc.rs index bf569054..6c25c0a4 100644 --- a/wtx/src/client_api_framework/misc.rs +++ b/wtx/src/client_api_framework/misc.rs @@ -21,7 +21,7 @@ pub use request_limit::RequestLimit; pub use request_throttling::RequestThrottling; /// Used in all implementations of [crate::Transport::send] and/or -/// [crate::Transport::send_and_receive`]. +/// [crate::Transport::send_recv`]. #[allow( // Borrow checker woes clippy::needless_pass_by_value, @@ -35,20 +35,21 @@ pub(crate) fn log_req( P: Package, T: Transport, { + #[cfg(feature = "tracing")] _debug!(trans_ty = display(_trans.ty()), "Request: {:?}", { use crate::client_api_framework::dnsn::Serialize; let mut vec = alloc::vec::Vec::new(); _pgk .ext_req_content_mut() .to_bytes(&mut vec, &mut _pkgs_aux.drsr) - .and_then(|_| Ok(crate::misc::from_utf8_basic(&vec)?.to_string())) + .and_then(|_| Ok(alloc::string::String::from(crate::misc::from_utf8_basic(&vec)?))) }); } -/// Used in [crate::network::transport::Transport::send_retrieve_and_decode_contained] and all implementations of +/// Used in [crate::network::transport::Transport::send_recv_decode_contained] and all implementations of /// [crate::Requests::decode_responses]. /// -/// Not used in [crate::network::transport::Transport::send_retrieve_and_decode_batch] because +/// Not used in [crate::network::transport::Transport::send_recv_decode_batch] because /// [crate::Requests::decode_responses] takes precedence. pub(crate) fn log_res(_res: &[u8]) { _debug!("Response: {:?}", crate::misc::from_utf8_basic(_res)); diff --git a/wtx/src/client_api_framework/misc/request_counter.rs b/wtx/src/client_api_framework/misc/request_counter.rs index 54795744..233fae84 100644 --- a/wtx/src/client_api_framework/misc/request_counter.rs +++ b/wtx/src/client_api_framework/misc/request_counter.rs @@ -174,19 +174,19 @@ mod tests { let rl = RequestLimit::new(1, _100).unwrap(); let mut rc = RequestCounter::new().unwrap(); assert_eq!(rc.counter, 0); - test(&mut rc, &rl, <_>::default()).await; + test(&mut rc, &rl, Duration::default()).await; assert_eq!(rc.counter, 2); test(&mut rc, &rl, _100 - Duration::from_millis(1)).await; assert_eq!(rc.counter, 1); - test(&mut rc, &rl, <_>::default()).await; + test(&mut rc, &rl, Duration::default()).await; assert_eq!(rc.counter, 2); test(&mut rc, &rl, _100 - Duration::from_millis(1)).await; assert_eq!(rc.counter, 1); - test(&mut rc, &rl, <_>::default()).await; + test(&mut rc, &rl, Duration::default()).await; assert_eq!(rc.counter, 2); test(&mut rc, &rl, _100 - Duration::from_millis(1)).await; assert_eq!(rc.counter, 1); - test(&mut rc, &rl, <_>::default()).await; + test(&mut rc, &rl, Duration::default()).await; assert_eq!(rc.counter, 2); test(&mut rc, &rl, _100 - Duration::from_millis(1)).await; assert_eq!(rc.counter, 1); diff --git a/wtx/src/client_api_framework/misc/request_limit.rs b/wtx/src/client_api_framework/misc/request_limit.rs index ef7f78b2..810cb675 100644 --- a/wtx/src/client_api_framework/misc/request_limit.rs +++ b/wtx/src/client_api_framework/misc/request_limit.rs @@ -37,5 +37,5 @@ impl RequestLimit { #[test] fn limit_can_not_be_zero() { - assert!(RequestLimit::new(0, <_>::default()).is_err()) + assert!(RequestLimit::new(0, Duration::default()).is_err()) } diff --git a/wtx/src/client_api_framework/network/http.rs b/wtx/src/client_api_framework/network/http.rs index 93e93775..ade4e5c8 100644 --- a/wtx/src/client_api_framework/network/http.rs +++ b/wtx/src/client_api_framework/network/http.rs @@ -1,15 +1,12 @@ //! Convenient subset of HTTP parameters. Intended to be only used by HTTP endpoints. -mod status_code; - use crate::{ client_api_framework::network::transport::TransportParams, - http::{Method, Mime}, + http::{Method, Mime, StatusCode}, misc::UriString, }; use alloc::{string::String, vec::Vec}; use core::fmt::{Arguments, Write}; -pub use status_code::*; #[derive(Debug)] #[doc = generic_trans_params_doc!()] @@ -27,7 +24,7 @@ impl HttpParams { uri: UriString::new(url.into()), user_agent: None, }, - HttpResParams { headers: <_>::default(), status_code: StatusCode::Forbidden }, + HttpResParams { headers: HttpHeaders::default(), status_code: StatusCode::Forbidden }, ) } } @@ -61,7 +58,7 @@ impl TransportParams for HttpParams { self.0.headers.clear(); self.0.method = Method::Get; self.0.mime = None; - self.0.uri.retain_with_initial_len(); + self.0.uri.truncate_with_initial_len(); self.0.user_agent = None; self.1.headers.clear(); self.1.status_code = StatusCode::Forbidden; diff --git a/wtx/src/client_api_framework/network/http/status_code.rs b/wtx/src/client_api_framework/network/http/status_code.rs deleted file mode 100644 index 3046b02f..00000000 --- a/wtx/src/client_api_framework/network/http/status_code.rs +++ /dev/null @@ -1,499 +0,0 @@ -/// HTTP response status codes. -/// -/// As defined by [rfc7231 section 6](https://tools.ietf.org/html/rfc7231#section-6). -/// -/// Copied from . -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum StatusCode { - /// 100 Continue - /// - /// This interim response indicates that everything so far is OK and that - /// the client should continue the request, or ignore the response if - /// the request is already finished. - Continue = 100, - - /// 101 Switching Protocols - /// - /// This code is sent in response to an Upgrade request header from the - /// client, and indicates the protocol the server is switching to. - SwitchingProtocols = 101, - - /// 103 Early Hints - /// - /// This status code is primarily intended to be used with the Link header, - /// letting the user agent start preloading resources while the server - /// prepares a response. - EarlyHints = 103, - - /// 200 Ok - /// - /// The request has succeeded - Ok = 200, - - /// 201 Created - /// - /// The request has succeeded and a new resource has been created as a - /// result. This is typically the response sent after POST requests, or - /// some PUT requests. - Created = 201, - - /// 202 Accepted - /// - /// The request has been received but not yet acted upon. It is - /// noncommittal, since there is no way in HTTP to later send an - /// asynchronous response indicating the outcome of the request. It is - /// intended for cases where another process or server handles the request, - /// or for batch processing. - Accepted = 202, - - /// 203 Non Authoritative Information - /// - /// This response code means the returned meta-information is not exactly - /// the same as is available from the origin server, but is collected - /// from a local or a third-party copy. This is mostly used for mirrors - /// or backups of another resource. Except for that specific case, the - /// "200 OK" response is preferred to this status. - NonAuthoritativeInformation = 203, - - /// 204 No Content - /// - /// There is no content to send for this request, but the headers may be - /// useful. The user-agent may update its cached headers for this - /// resource with the new ones. - NoContent = 204, - - /// 205 Reset Content - /// - /// Tells the user-agent to reset the document which sent this request. - ResetContent = 205, - - /// 206 Partial Content - /// - /// This response code is used when the Range header is sent from the client - /// to request only part of a resource. - PartialContent = 206, - - /// 207 Multi-Status - /// - /// A Multi-Status response conveys information about - /// multiple resources in situations where multiple - /// status codes might be appropriate. - MultiStatus = 207, - - /// 226 Im Used - /// - /// The server has fulfilled a GET request for the resource, and the - /// response is a representation of the result of one or more - /// instance-manipulations applied to the current instance. - ImUsed = 226, - - /// 300 Multiple Choice - /// - /// The request has more than one possible response. The user-agent or user - /// should choose one of them. (There is no standardized way of choosing - /// one of the responses, but HTML links to the possibilities are - /// recommended so the user can pick.) - MultipleChoice = 300, - - /// 301 Moved Permanently - /// - /// The URL of the requested resource has been changed permanently. The new - /// URL is given in the response. - MovedPermanently = 301, - - /// 302 Found - /// - /// This response code means that the URI of requested resource has been - /// changed temporarily. Further changes in the URI might be made in the - /// future. Therefore, this same URI should be used by the client in - /// future requests. - Found = 302, - - /// 303 See Other - /// - /// The server sent this response to direct the client to get the requested - /// resource at another URI with a GET request. - SeeOther = 303, - - /// 304 Not Modified - /// - /// This is used for caching purposes. It tells the client that the response - /// has not been modified, so the client can continue to use the same - /// cached version of the response. - NotModified = 304, - - /// 307 Temporary Redirect - /// - /// The server sends this response to direct the client to get the requested - /// resource at another URI with same method that was used in the prior - /// request. This has the same semantics as the 302 Found HTTP response - /// code, with the exception that the user agent must not change the - /// HTTP method used: If a POST was used in the first request, a POST must - /// be used in the second request. - TemporaryRedirect = 307, - - /// 308 Permanent Redirect - /// - /// This means that the resource is now permanently located at another URI, - /// specified by the Location: HTTP Response header. This has the same - /// semantics as the 301 Moved Permanently HTTP response code, with the - /// exception that the user agent must not change the HTTP method - /// used: If a POST was used in the first request, a POST must be used in - /// the second request. - PermanentRedirect = 308, - - /// 400 Bad Request - /// - /// The server could not understand the request due to invalid syntax. - BadRequest = 400, - - /// 401 Unauthorized - /// - /// Although the HTTP standard specifies "unauthorized", semantically this - /// response means "unauthenticated". That is, the client must - /// authenticate itself to get the requested response. - Unauthorized = 401, - - /// 402 Payment Required - /// - /// This response code is reserved for future use. The initial aim for - /// creating this code was using it for digital payment systems, however - /// this status code is used very rarely and no standard convention - /// exists. - PaymentRequired = 402, - - /// 403 Forbidden - /// - /// The client does not have access rights to the content; that is, it is - /// unauthorized, so the server is refusing to give the requested - /// resource. Unlike 401, the client's identity is known to the server. - Forbidden = 403, - - /// 404 Not Found - /// - /// The server can not find requested resource. In the browser, this means - /// the URL is not recognized. In an API, this can also mean that the - /// endpoint is valid but the resource itself does not exist. Servers - /// may also send this response instead of 403 to hide the existence of - /// a resource from an unauthorized client. This response code is probably - /// the most famous one due to its frequent occurrence on the web. - NotFound = 404, - - /// 405 Method Not Allowed - /// - /// The request method is known by the server but has been disabled and - /// cannot be used. For example, an API may forbid DELETE-ing a - /// resource. The two mandatory methods, GET and HEAD, must never be - /// disabled and should not return this error code. - MethodNotAllowed = 405, - - /// 406 Not Acceptable - /// - /// This response is sent when the web server, after performing - /// server-driven content negotiation, doesn't find any content that - /// conforms to the criteria given by the user agent. - NotAcceptable = 406, - - /// 407 Proxy Authentication Required - /// - /// This is similar to 401 but authentication is needed to be done by a - /// proxy. - ProxyAuthenticationRequired = 407, - - /// 408 Request Timeout - /// - /// This response is sent on an idle connection by some servers, even - /// without any previous request by the client. It means that the server - /// would like to shut down this unused connection. This response is - /// used much more since some browsers, like Chrome, Firefox 27+, - /// or IE9, use HTTP pre-connection mechanisms to speed up surfing. Also - /// note that some servers merely shut down the connection without - /// sending this message. - RequestTimeout = 408, - - /// 409 Conflict - /// - /// This response is sent when a request conflicts with the current state of - /// the server. - Conflict = 409, - - /// 410 Gone - /// - /// This response is sent when the requested content has been permanently - /// deleted from server, with no forwarding address. Clients are - /// expected to remove their caches and links to the resource. The HTTP - /// specification intends this status code to be used for "limited-time, - /// promotional services". APIs should not feel compelled to indicate - /// resources that have been deleted with this status code. - Gone = 410, - - /// 411 Length Required - /// - /// Server rejected the request because the Content-Length header field is - /// not defined and the server requires it. - LengthRequired = 411, - - /// 412 Precondition Failed - /// - /// The client has indicated preconditions in its headers which the server - /// does not meet. - PreconditionFailed = 412, - - /// 413 Payload Too Large - /// - /// Request entity is larger than limits defined by server; the server might - /// close the connection or return an Retry-After header field. - PayloadTooLarge = 413, - - /// 414 URI Too Long - /// - /// The URI requested by the client is longer than the server is willing to - /// interpret. - UriTooLong = 414, - - /// 415 Unsupported Media Type - /// - /// The media format of the requested data is not supported by the server, - /// so the server is rejecting the request. - UnsupportedMediaType = 415, - - /// 416 Requested Range Not Satisfiable - /// - /// The range specified by the Range header field in the request can't be - /// fulfilled; it's possible that the range is outside the size of the - /// target URI's data. - RequestedRangeNotSatisfiable = 416, - - /// 417 Expectation Failed - /// - /// This response code means the expectation indicated by the Expect request - /// header field can't be met by the server. - ExpectationFailed = 417, - /// - /// 418 I'm a teapot - /// - /// The server refuses the attempt to brew coffee with a teapot. - ImATeapot = 418, - - /// 421 Misdirected Request - /// - /// The request was directed at a server that is not able to produce a - /// response. This can be sent by a server that is not configured to - /// produce responses for the combination of scheme and authority that - /// are included in the request URI. - MisdirectedRequest = 421, - - /// 422 Unprocessable Entity - /// - /// The request was well-formed but was unable to be followed due to - /// semantic errors. - UnprocessableEntity = 422, - - /// 423 Locked - /// - /// The resource that is being accessed is locked. - Locked = 423, - - /// 424 Failed Dependency - /// - /// The request failed because it depended on another request and that - /// request failed (e.g., a PROPPATCH). - FailedDependency = 424, - - /// 425 Too Early - /// - /// Indicates that the server is unwilling to risk processing a request that - /// might be replayed. - TooEarly = 425, - - /// 426 Upgrade Required - /// - /// The server refuses to perform the request using the current protocol but - /// might be willing to do so after the client upgrades to a different - /// protocol. The server sends an Upgrade header in a 426 response to - /// indicate the required protocol(s). - UpgradeRequired = 426, - - /// 428 Precondition Required - /// - /// The origin server requires the request to be conditional. This response - /// is intended to prevent the 'lost update' problem, where a client - /// GETs a resource's state, modifies it, and PUTs it back to the - /// server, when meanwhile a third party has modified the state on the - /// server, leading to a conflict. - PreconditionRequired = 428, - - /// 429 Too Many Requests - /// - /// The user has sent too many requests in a given amount of time ("rate - /// limiting"). - TooManyRequests = 429, - - /// 431 Request Header Fields Too Large - /// - /// The server is unwilling to process the request because its header fields - /// are too large. The request may be resubmitted after reducing the - /// size of the request header fields. - RequestHeaderFieldsTooLarge = 431, - - /// 451 Unavailable For Legal Reasons - /// - /// The user-agent requested a resource that cannot legally be provided, - /// such as a web page censored by a government. - UnavailableForLegalReasons = 451, - - /// 500 Internal Server Error - /// - /// The server has encountered a situation it doesn't know how to handle. - InternalServerError = 500, - - /// 501 Not Implemented - /// - /// The request method is not supported by the server and cannot be handled. - /// The only methods that servers are required to support (and therefore - /// that must not return this code) are GET and HEAD. - NotImplemented = 501, - - /// 502 Bad Gateway - /// - /// This error response means that the server, while working as a gateway to - /// get a response needed to handle the request, got an invalid - /// response. - BadGateway = 502, - - /// 503 Service Unavailable - /// - /// The server is not ready to handle the request. Common causes are a - /// server that is down for maintenance or that is overloaded. Note that - /// together with this response, a user-friendly page explaining the - /// problem should be sent. This responses should be used for temporary - /// conditions and the Retry-After: HTTP header should, if possible, contain - /// the estimated time before the recovery of the service. The webmaster - /// must also take care about the caching-related headers that are sent - /// along with this response, as these temporary condition responses - /// should usually not be cached. - ServiceUnavailable = 503, - - /// 504 Gateway Timeout - /// - /// This error response is given when the server is acting as a gateway and - /// cannot get a response in time. - GatewayTimeout = 504, - - /// 505 HTTP Version Not Supported - /// - /// The HTTP version used in the request is not supported by the server. - HttpVersionNotSupported = 505, - - /// 506 Variant Also Negotiates - /// - /// The server has an internal configuration error: the chosen variant - /// resource is configured to engage in transparent content negotiation - /// itself, and is therefore not a proper end point in the negotiation - /// process. - VariantAlsoNegotiates = 506, - - /// 507 Insufficient Storage - /// - /// The server is unable to store the representation needed to complete the - /// request. - InsufficientStorage = 507, - - /// 508 Loop Detected - /// - /// The server detected an infinite loop while processing the request. - LoopDetected = 508, - - /// 510 Not Extended - /// - /// Further extensions to the request are required for the server to fulfil - /// it. - NotExtended = 510, - - /// 511 Network Authentication Required - /// - /// The 511 status code indicates that the client needs to authenticate to - /// gain network access. - NetworkAuthenticationRequired = 511, -} - -impl TryFrom for StatusCode { - type Error = crate::Error; - - #[inline] - fn try_from(num: u16) -> Result { - Ok(match num { - 100 => StatusCode::Continue, - 101 => StatusCode::SwitchingProtocols, - 103 => StatusCode::EarlyHints, - 200 => StatusCode::Ok, - 201 => StatusCode::Created, - 202 => StatusCode::Accepted, - 203 => StatusCode::NonAuthoritativeInformation, - 204 => StatusCode::NoContent, - 205 => StatusCode::ResetContent, - 206 => StatusCode::PartialContent, - 207 => StatusCode::MultiStatus, - 226 => StatusCode::ImUsed, - 300 => StatusCode::MultipleChoice, - 301 => StatusCode::MovedPermanently, - 302 => StatusCode::Found, - 303 => StatusCode::SeeOther, - 304 => StatusCode::NotModified, - 307 => StatusCode::TemporaryRedirect, - 308 => StatusCode::PermanentRedirect, - 400 => StatusCode::BadRequest, - 401 => StatusCode::Unauthorized, - 402 => StatusCode::PaymentRequired, - 403 => StatusCode::Forbidden, - 404 => StatusCode::NotFound, - 405 => StatusCode::MethodNotAllowed, - 406 => StatusCode::NotAcceptable, - 407 => StatusCode::ProxyAuthenticationRequired, - 408 => StatusCode::RequestTimeout, - 409 => StatusCode::Conflict, - 410 => StatusCode::Gone, - 411 => StatusCode::LengthRequired, - 412 => StatusCode::PreconditionFailed, - 413 => StatusCode::PayloadTooLarge, - 414 => StatusCode::UriTooLong, - 415 => StatusCode::UnsupportedMediaType, - 416 => StatusCode::RequestedRangeNotSatisfiable, - 417 => StatusCode::ExpectationFailed, - 418 => StatusCode::ImATeapot, - 421 => StatusCode::MisdirectedRequest, - 422 => StatusCode::UnprocessableEntity, - 423 => StatusCode::Locked, - 424 => StatusCode::FailedDependency, - 425 => StatusCode::TooEarly, - 426 => StatusCode::UpgradeRequired, - 428 => StatusCode::PreconditionRequired, - 429 => StatusCode::TooManyRequests, - 431 => StatusCode::RequestHeaderFieldsTooLarge, - 451 => StatusCode::UnavailableForLegalReasons, - 500 => StatusCode::InternalServerError, - 501 => StatusCode::NotImplemented, - 502 => StatusCode::BadGateway, - 503 => StatusCode::ServiceUnavailable, - 504 => StatusCode::GatewayTimeout, - 505 => StatusCode::HttpVersionNotSupported, - 506 => StatusCode::VariantAlsoNegotiates, - 507 => StatusCode::InsufficientStorage, - 508 => StatusCode::LoopDetected, - 510 => StatusCode::NotExtended, - 511 => StatusCode::NetworkAuthenticationRequired, - _ => return Err(crate::Error::UnknownHttpStatusCode(num)), - }) - } -} - -impl From for u16 { - #[allow( - // Infallible - clippy::as_conversions - )] - #[inline] - fn from(from: StatusCode) -> Self { - from as u16 - } -} diff --git a/wtx/src/client_api_framework/network/tcp.rs b/wtx/src/client_api_framework/network/tcp.rs index 1ca8ba52..b72081ac 100644 --- a/wtx/src/client_api_framework/network/tcp.rs +++ b/wtx/src/client_api_framework/network/tcp.rs @@ -38,7 +38,7 @@ impl TransportParams for TcpParams { #[inline] fn reset(&mut self) { - self.0.url.retain_with_initial_len(); + self.0.url.truncate_with_initial_len(); } } diff --git a/wtx/src/client_api_framework/network/transport.rs b/wtx/src/client_api_framework/network/transport.rs index 054e0131..277f14b0 100644 --- a/wtx/src/client_api_framework/network/transport.rs +++ b/wtx/src/client_api_framework/network/transport.rs @@ -2,14 +2,12 @@ mod bi_transport; mod mock; -#[cfg(feature = "reqwest")] -mod reqwest; #[cfg(feature = "std")] mod std; mod transport_params; mod unit; #[cfg(feature = "web-socket")] -mod wtx; +mod wtx_ws; use crate::client_api_framework::{ dnsn::{Deserialize, Serialize}, @@ -51,7 +49,7 @@ pub trait Transport { /// Sends a request and then awaits its counterpart data response. /// /// The returned bytes are stored in `pkgs_aux` and its length is returned by this method. - fn send_and_retrieve( + fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -60,12 +58,12 @@ pub trait Transport { A: Api, P: Package; - /// Convenient method similar to [Self::send_retrieve_and_decode_contained] but used for batch + /// Convenient method similar to [Self::send_recv_decode_contained] but used for batch /// requests. /// /// All the expected data must be available in a single response. #[inline] - fn send_retrieve_and_decode_batch( + fn send_recv_decode_batch( &mut self, pkgs: &mut [P], pkgs_aux: &mut PkgsAux, @@ -81,8 +79,8 @@ pub trait Transport { { async { let batch_package = &mut BatchPkg::new(pkgs); - let range = self.send_and_retrieve(batch_package, pkgs_aux).await?; - log_res(pkgs_aux.byte_buffer.as_ref()); + let range = self.send_recv(batch_package, pkgs_aux).await?; + log_res(pkgs_aux.byte_buffer.as_slice()); batch_package.decode_and_push_from_bytes( ress, pkgs_aux.byte_buffer.get(range).unwrap_or_default(), @@ -92,10 +90,10 @@ pub trait Transport { } } - /// Internally calls [Self::send_and_retrieve] and then tries to decode the defined response specified + /// Internally calls [Self::send_recv] and then tries to decode the defined response specified /// in [Package::ExternalResponseContent]. #[inline] - fn send_retrieve_and_decode_contained( + fn send_recv_decode_contained( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -105,8 +103,8 @@ pub trait Transport { P: Package, { async { - let range = self.send_and_retrieve(pkg, pkgs_aux).await?; - log_res(pkgs_aux.byte_buffer.as_ref()); + let range = self.send_recv(pkg, pkgs_aux).await?; + log_res(pkgs_aux.byte_buffer.as_slice()); Ok(P::ExternalResponseContent::from_bytes( pkgs_aux.byte_buffer.get(range).unwrap_or_default(), &mut pkgs_aux.drsr, @@ -142,7 +140,7 @@ where } #[inline] - async fn send_and_retrieve( + async fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -151,7 +149,7 @@ where A: Api, P: Package, { - (**self).send_and_retrieve(pkg, pkgs_aux).await + (**self).send_recv(pkg, pkgs_aux).await } } diff --git a/wtx/src/client_api_framework/network/transport/bi_transport.rs b/wtx/src/client_api_framework/network/transport/bi_transport.rs index 02095fc5..aeea3fda 100644 --- a/wtx/src/client_api_framework/network/transport/bi_transport.rs +++ b/wtx/src/client_api_framework/network/transport/bi_transport.rs @@ -37,7 +37,7 @@ pub trait BiTransport: Transport { { async { let range = self.retrieve(pkgs_aux).await?; - log_res(pkgs_aux.byte_buffer.as_ref()); + log_res(pkgs_aux.byte_buffer.as_slice()); let rslt = P::ExternalResponseContent::from_bytes( pkgs_aux.byte_buffer.get(range).unwrap_or_default(), &mut pkgs_aux.drsr, diff --git a/wtx/src/client_api_framework/network/transport/mock.rs b/wtx/src/client_api_framework/network/transport/mock.rs index b52019ac..7e55ec4b 100644 --- a/wtx/src/client_api_framework/network/transport/mock.rs +++ b/wtx/src/client_api_framework/network/transport/mock.rs @@ -3,11 +3,14 @@ clippy::indexing_slicing )] -use crate::client_api_framework::{ - misc::{manage_after_sending_related, manage_before_sending_related, FromBytes}, - network::{transport::Transport, TransportGroup}, - pkg::{Package, PkgsAux}, - Api, +use crate::{ + client_api_framework::{ + misc::{manage_after_sending_related, manage_before_sending_related, FromBytes}, + network::{transport::Transport, TransportGroup}, + pkg::{Package, PkgsAux}, + Api, + }, + misc::Lease, }; use alloc::{ borrow::{Cow, ToOwned}, @@ -32,7 +35,7 @@ pub type MockStr = Mock; /// pkg::PkgsAux, /// }; /// let _ = MockStr::default() -/// .send_retrieve_and_decode_contained(&mut (), &mut PkgsAux::from_minimum((), (), ())) +/// .send_recv_decode_contained(&mut (), &mut PkgsAux::from_minimum((), (), ())) /// .await?; /// # Ok(()) } /// ``` @@ -49,7 +52,7 @@ where impl Mock where - T: AsRef<[u8]> + Debug + PartialEq + ToOwned + 'static + ?Sized, + T: Debug + Lease<[u8]> + PartialEq + ToOwned + 'static + ?Sized, ::Owned: Debug + FromBytes, { /// Ensures that no included request hasn't processed. @@ -88,7 +91,7 @@ where impl Transport for Mock where - T: AsRef<[u8]> + Debug + PartialEq + ToOwned + 'static + ?Sized, + T: Debug + Lease<[u8]> + PartialEq + ToOwned + 'static + ?Sized, ::Owned: Debug + FromBytes, { const GROUP: TransportGroup = TransportGroup::Stub; @@ -112,7 +115,7 @@ where } #[inline] - async fn send_and_retrieve( + async fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -124,7 +127,7 @@ where >::send(self, pkg, pkgs_aux).await?; let response = self.pop_response()?; pkgs_aux.byte_buffer.clear(); - pkgs_aux.byte_buffer.extend(response.as_ref().as_ref().iter().copied()); + pkgs_aux.byte_buffer.extend(response.lease().iter().copied()); Ok(0..pkgs_aux.byte_buffer.len()) } } diff --git a/wtx/src/client_api_framework/network/transport/reqwest.rs b/wtx/src/client_api_framework/network/transport/reqwest.rs deleted file mode 100644 index 3aead253..00000000 --- a/wtx/src/client_api_framework/network/transport/reqwest.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::{ - client_api_framework::{ - misc::{manage_after_sending_related, manage_before_sending_related}, - network::{ - transport::{Transport, TransportParams}, - HttpParams, TransportGroup, - }, - pkg::{Package, PkgsAux}, - Api, - }, - misc::from_utf8_basic, -}; -use core::ops::Range; -use reqwest::{ - header::{HeaderValue, CONTENT_TYPE, USER_AGENT}, - Client, Method, RequestBuilder, -}; - -/// ```rust,no_run -/// # async fn fun() -> wtx::Result<()> { -/// use wtx::client_api_framework::{ -/// network::{transport::Transport, HttpParams}, -/// pkg::PkgsAux, -/// }; -/// let _ = reqwest::Client::new() -/// .send_retrieve_and_decode_contained( -/// &mut (), -/// &mut PkgsAux::from_minimum((), (), HttpParams::from_uri("URI").into()), -/// ) -/// .await?; -/// # Ok(()) } -/// ``` -impl Transport for Client { - const GROUP: TransportGroup = TransportGroup::HTTP; - type Params = HttpParams; - - #[inline] - async fn send( - &mut self, - pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result<(), A::Error> - where - A: Api, - P: Package, - { - let _res = response(self, pkg, pkgs_aux).await?; - Ok(()) - } - - #[inline] - async fn send_and_retrieve( - &mut self, - pkg: &mut P, - pkgs_aux: &mut PkgsAux, - ) -> Result, A::Error> - where - A: Api, - P: Package, - { - let res = response(self, pkg, pkgs_aux).await?; - let received_bytes = res.bytes().await.map_err(Into::into)?; - pkgs_aux.byte_buffer.extend(received_bytes.into_iter()); - Ok(0..pkgs_aux.byte_buffer.len()) - } -} - -async fn response( - client: &mut Client, - pkg: &mut P, - pkgs_aux: &mut PkgsAux, -) -> Result -where - A: Api, - P: Package, -{ - fn manage_data( - mut rb: RequestBuilder, - pkgs_aux: &mut PkgsAux, - ) -> RequestBuilder { - if let Some(data_format) = &pkgs_aux.tp.ext_req_params().mime { - rb = rb.header(CONTENT_TYPE, HeaderValue::from_static(data_format._as_str())); - } - rb = rb.body(pkgs_aux.byte_buffer.clone()); - rb - } - pkgs_aux.byte_buffer.clear(); - manage_before_sending_related(pkg, pkgs_aux, &mut *client).await?; - let mut rb = match pkgs_aux.tp.ext_req_params().method { - crate::http::Method::Connect => { - client.request(Method::CONNECT, pkgs_aux.tp.ext_req_params().uri.uri()) - } - crate::http::Method::Delete => client.delete(pkgs_aux.tp.ext_req_params().uri.uri()), - crate::http::Method::Get => client.get(pkgs_aux.tp.ext_req_params().uri.uri()), - crate::http::Method::Head => client.head(pkgs_aux.tp.ext_req_params().uri.uri()), - crate::http::Method::Options => { - client.request(Method::OPTIONS, pkgs_aux.tp.ext_req_params().uri.uri()) - } - crate::http::Method::Patch => { - manage_data(client.patch(pkgs_aux.tp.ext_req_params().uri.uri()), pkgs_aux) - } - crate::http::Method::Post => { - manage_data(client.post(pkgs_aux.tp.ext_req_params().uri.uri()), pkgs_aux) - } - crate::http::Method::Put => { - manage_data(client.put(pkgs_aux.tp.ext_req_params().uri.uri()), pkgs_aux) - } - crate::http::Method::Trace => { - client.request(Method::TRACE, pkgs_aux.tp.ext_req_params().uri.uri()) - } - }; - pkgs_aux.byte_buffer.clear(); - for (key, value) in pkgs_aux.tp.ext_req_params().headers.iter() { - rb = rb.header(key, value); - } - if let Some(elem) = pkgs_aux.tp.ext_req_params().user_agent { - rb = rb.header(USER_AGENT, HeaderValue::from_static(elem._as_str())); - } - let res = rb.send().await.map_err(Into::into)?; - for (key, value) in res.headers() { - pkgs_aux - .tp - .ext_res_params_mut() - .headers - .push_str(key.as_str(), from_utf8_basic(value.as_bytes()).map_err(Into::into)?)?; - } - pkgs_aux.tp.ext_res_params_mut().status_code = <_>::try_from(Into::::into(res.status()))?; - manage_after_sending_related(pkg, pkgs_aux).await?; - pkgs_aux.tp.reset(); - Ok(res) -} diff --git a/wtx/src/client_api_framework/network/transport/std.rs b/wtx/src/client_api_framework/network/transport/std.rs index ad05482c..6716db7d 100644 --- a/wtx/src/client_api_framework/network/transport/std.rs +++ b/wtx/src/client_api_framework/network/transport/std.rs @@ -31,7 +31,7 @@ impl Transport for TcpStream { } #[inline] - async fn send_and_retrieve( + async fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -40,7 +40,7 @@ impl Transport for TcpStream { A: Api, P: Package, { - send_and_retrieve(pkg, pkgs_aux, self, |bytes, _, trans| Ok(trans.read(bytes)?)).await + send_recv(pkg, pkgs_aux, self, |bytes, _, trans| Ok(trans.read(bytes)?)).await } } @@ -65,7 +65,7 @@ impl Transport for UdpSocket { } #[inline] - async fn send_and_retrieve( + async fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -74,7 +74,7 @@ impl Transport for UdpSocket { A: Api, P: Package, { - send_and_retrieve(pkg, pkgs_aux, self, |bytes, _, trans| Ok(trans.recv(bytes)?)).await + send_recv(pkg, pkgs_aux, self, |bytes, _, trans| Ok(trans.recv(bytes)?)).await } } @@ -95,7 +95,7 @@ where { pkgs_aux.byte_buffer.clear(); manage_before_sending_related(pkg, pkgs_aux, &mut *trans).await?; - let mut slice = pkgs_aux.byte_buffer.as_ref(); + let mut slice = pkgs_aux.byte_buffer.as_slice(); let mut everything_was_sent = false; for _ in 0..16 { let sent = cb(slice, pkgs_aux.tp.ext_req_params(), trans)?; @@ -115,7 +115,7 @@ where } } -async fn send_and_retrieve( +async fn send_recv( pkg: &mut P, pkgs_aux: &mut PkgsAux, trans: &mut T, @@ -171,8 +171,7 @@ mod tests { sleep(Duration::from_millis(100)).await.unwrap(); let mut pa = PkgsAux::from_minimum((), (), TcpParams::from_uri(uri_client.uri())); let mut trans = TcpStream::connect(uri_client.host()).unwrap(); - let res = - trans.send_retrieve_and_decode_contained(&mut PingPong(Ping, ()), &mut pa).await.unwrap(); + let res = trans.send_recv_decode_contained(&mut PingPong(Ping, ()), &mut pa).await.unwrap(); assert_eq!(res, Pong("pong")); } @@ -181,8 +180,7 @@ mod tests { let addr = "127.0.0.1:12346"; let mut pa = PkgsAux::from_minimum((), (), UdpParams::from_uri(addr)); let mut trans = UdpSocket::bind(addr).unwrap(); - let res = - trans.send_retrieve_and_decode_contained(&mut PingPong(Ping, ()), &mut pa).await.unwrap(); + let res = trans.send_recv_decode_contained(&mut PingPong(Ping, ()), &mut pa).await.unwrap(); assert_eq!(res, Pong("pong")); } } diff --git a/wtx/src/client_api_framework/network/transport/unit.rs b/wtx/src/client_api_framework/network/transport/unit.rs index eb3ab756..69856b75 100644 --- a/wtx/src/client_api_framework/network/transport/unit.rs +++ b/wtx/src/client_api_framework/network/transport/unit.rs @@ -12,7 +12,7 @@ use core::ops::Range; /// # async fn fun() -> wtx::Result<()> { /// use wtx::client_api_framework::{network::transport::Transport, pkg::PkgsAux}; /// let _ = -/// ().send_retrieve_and_decode_contained(&mut (), &mut PkgsAux::from_minimum((), (), ())).await?; +/// ().send_recv_decode_contained(&mut (), &mut PkgsAux::from_minimum((), (), ())).await?; /// # Ok(()) } /// ``` impl Transport for () { @@ -35,7 +35,7 @@ impl Transport for () { } #[inline] - async fn send_and_retrieve( + async fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -57,6 +57,6 @@ mod tests { async fn unit() { let mut pa = PkgsAux::from_minimum((), (), ()); let mut trans = (); - assert_eq!(trans.send_retrieve_and_decode_contained(&mut (), &mut pa).await.unwrap(), ()); + assert_eq!(trans.send_recv_decode_contained(&mut (), &mut pa).await.unwrap(), ()); } } diff --git a/wtx/src/client_api_framework/network/transport/wtx.rs b/wtx/src/client_api_framework/network/transport/wtx_ws.rs similarity index 85% rename from wtx/src/client_api_framework/network/transport/wtx.rs rename to wtx/src/client_api_framework/network/transport/wtx_ws.rs index bb7978ec..7fcad51a 100644 --- a/wtx/src/client_api_framework/network/transport/wtx.rs +++ b/wtx/src/client_api_framework/network/transport/wtx_ws.rs @@ -8,21 +8,21 @@ use crate::{ pkg::{Package, PkgsAux}, Api, }, - misc::Stream, + misc::{LeaseMut, Stream}, rng::Rng, web_socket::{ compression::NegotiatedCompression, FrameBufferVec, FrameBufferVecMut, FrameMutVec, OpCode, WebSocketBuffer, WebSocketClient, }, }; -use core::{borrow::BorrowMut, ops::Range}; +use core::ops::Range; impl Transport for (FrameBufferVec, WebSocketClient) where NC: NegotiatedCompression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { const GROUP: TransportGroup = TransportGroup::WebSocket; type Params = WsParams; @@ -41,7 +41,7 @@ where } #[inline] - async fn send_and_retrieve( + async fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -50,7 +50,7 @@ where A: Api, P: Package, { - send_and_retrieve(&mut self.0, pkg, pkgs_aux, &mut self.1).await + send_recv(&mut self.0, pkg, pkgs_aux, &mut self.1).await } } @@ -59,7 +59,7 @@ where NC: NegotiatedCompression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { #[inline] async fn retrieve( @@ -79,7 +79,7 @@ where NC: NegotiatedCompression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { const GROUP: TransportGroup = TransportGroup::WebSocket; type Params = WsParams; @@ -98,7 +98,7 @@ where } #[inline] - async fn send_and_retrieve( + async fn send_recv( &mut self, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -107,7 +107,7 @@ where A: Api, P: Package, { - send_and_retrieve(self.0, pkg, pkgs_aux, self.1).await + send_recv(self.0, pkg, pkgs_aux, self.1).await } } @@ -117,7 +117,7 @@ where NC: NegotiatedCompression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { #[inline] async fn retrieve( @@ -139,13 +139,13 @@ where NC: NegotiatedCompression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { pkgs_aux.byte_buffer.clear(); let fb = &mut FrameBufferVecMut::from(&mut pkgs_aux.byte_buffer); - let frame = ws.borrow_mut().read_frame(fb).await?; + let frame = ws.read_frame(fb).await?; if let OpCode::Close = frame.op_code() { - return Err(crate::Error::ClosedWsConnection.into()); + return Err(crate::Error::ClosedWsConnection); } let indcs = frame.fb().indcs(); Ok(indcs.1.into()..indcs.2) @@ -163,7 +163,7 @@ where P: Package, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { let mut trans = (fb, ws); pkgs_aux.byte_buffer.clear(); @@ -180,7 +180,7 @@ where Ok(()) } -async fn send_and_retrieve( +async fn send_recv( fb: &mut FrameBufferVec, pkg: &mut P, pkgs_aux: &mut PkgsAux, @@ -192,11 +192,11 @@ where P: Package, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { send(fb, pkg, pkgs_aux, ws).await?; let fb = &mut FrameBufferVecMut::from(&mut pkgs_aux.byte_buffer); - let frame = ws.borrow_mut().read_frame(fb).await.map_err(Into::into)?; + let frame = ws.read_frame(fb).await.map_err(Into::into)?; if let OpCode::Close = frame.op_code() { return Err(crate::Error::ClosedWsConnection.into()); } diff --git a/wtx/src/client_api_framework/network/udp.rs b/wtx/src/client_api_framework/network/udp.rs index 0a7d369a..79882387 100644 --- a/wtx/src/client_api_framework/network/udp.rs +++ b/wtx/src/client_api_framework/network/udp.rs @@ -38,7 +38,7 @@ impl TransportParams for UdpParams { #[inline] fn reset(&mut self) { - self.0.url.retain_with_initial_len(); + self.0.url.truncate_with_initial_len(); } } diff --git a/wtx/src/client_api_framework/pkg/batch_pkg.rs b/wtx/src/client_api_framework/pkg/batch_pkg.rs index 6b4b8afe..f64e1bba 100644 --- a/wtx/src/client_api_framework/pkg/batch_pkg.rs +++ b/wtx/src/client_api_framework/pkg/batch_pkg.rs @@ -167,6 +167,7 @@ mod serde_json { pkg::{BatchElems, Package}, Api, }; + use alloc::vec::Vec; use serde::Serializer; impl crate::client_api_framework::dnsn::Serialize diff --git a/wtx/src/client_api_framework/pkg/pkg_with_helper.rs b/wtx/src/client_api_framework/pkg/pkg_with_helper.rs index 3ce4afd9..45703618 100644 --- a/wtx/src/client_api_framework/pkg/pkg_with_helper.rs +++ b/wtx/src/client_api_framework/pkg/pkg_with_helper.rs @@ -3,7 +3,7 @@ use crate::client_api_framework::{ }; use core::{ borrow::Borrow, - cmp::{Ord, Ordering}, + cmp::Ordering, hash::{Hash, Hasher}, }; diff --git a/wtx/src/database.rs b/wtx/src/database.rs index 33c9950c..c0c3771c 100644 --- a/wtx/src/database.rs +++ b/wtx/src/database.rs @@ -36,7 +36,7 @@ pub const DEFAULT_URI_VAR: &str = "DATABASE_URI"; /// The maximum number of characters that a database identifier can have. For example, tables, /// procedures, triggers, etc. -pub type Identifier = arrayvec::ArrayString<64>; +pub type Identifier = crate::misc::ArrayString<64>; /// Used by some operations to identify different tables pub type TableSuffix = u32; diff --git a/wtx/src/database/client/postgres/executor.rs b/wtx/src/database/client/postgres/executor.rs index 5eb7a0bb..e25276e0 100644 --- a/wtx/src/database/client/postgres/executor.rs +++ b/wtx/src/database/client/postgres/executor.rs @@ -14,10 +14,10 @@ use crate::{ }, Database, RecordValues, StmtCmd, TransactionManager as _, }, - misc::{AsyncBounds, FilledBufferWriter, Stream, TlsStream}, + misc::{AsyncBounds, FilledBufferWriter, Lease, LeaseMut, Stream, TlsStream}, rng::Rng, }; -use core::{borrow::BorrowMut, future::Future, marker::PhantomData}; +use core::{future::Future, marker::PhantomData}; /// Executor #[derive(Debug)] @@ -30,7 +30,7 @@ pub struct Executor { impl Executor where - EB: BorrowMut, + EB: LeaseMut, S: Stream, { /// Connects with an unencrypted stream. @@ -44,7 +44,7 @@ where where RNG: Rng, { - eb.borrow_mut().clear(); + eb.lease_mut().clear(); Self::do_connect(config, eb, rng, stream, None).await } @@ -64,8 +64,8 @@ where RNG: Rng, S: TlsStream, { - eb.borrow_mut().clear(); - let mut fbw = FilledBufferWriter::from(&mut eb.borrow_mut().nb); + eb.lease_mut().clear(); + let mut fbw = FilledBufferWriter::from(&mut eb.lease_mut().nb); encrypted_conn(&mut fbw)?; initial_stream.write_all(fbw._curr_bytes()).await?; let mut buf = [0]; @@ -75,14 +75,13 @@ where } let stream = cb(initial_stream).await?; let tls_server_end_point = stream.tls_server_end_point()?; - Self::do_connect(config, eb, rng, stream, tls_server_end_point.as_ref().map(AsRef::as_ref)) - .await + Self::do_connect(config, eb, rng, stream, tls_server_end_point.as_ref().map(Lease::lease)).await } /// Mutable buffer reference #[inline] pub fn eb_mut(&mut self) -> &mut ExecutorBuffer { - self.eb.borrow_mut() + self.eb.lease_mut() } #[inline] @@ -104,7 +103,7 @@ where } async fn send_initial_conn_msg(&mut self, config: &Config<'_>) -> crate::Result<()> { - let mut fbw = FilledBufferWriter::from(&mut self.eb.borrow_mut().nb); + let mut fbw = FilledBufferWriter::from(&mut self.eb.lease_mut().nb); initial_conn_msg(config, &mut fbw)?; self.stream.write_all(fbw._curr_bytes()).await?; Ok(()) @@ -114,7 +113,7 @@ where impl crate::database::Executor for Executor where E: AsyncBounds + From, - EB: AsyncBounds + BorrowMut, + EB: AsyncBounds + LeaseMut, S: AsyncBounds + Stream, { type Database = Postgres; @@ -137,7 +136,7 @@ where RV: RecordValues, SC: StmtCmd, { - let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.lease_mut().parts_mut(); ExecutorBuffer::clear_cmd_buffers(nb, rb, vb); let mut rows = 0; let mut fwsc = FetchWithStmtCommons { @@ -173,7 +172,7 @@ where RV: RecordValues, SC: StmtCmd, { - let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.lease_mut().parts_mut(); Self::write_send_await_fetch_with_stmt( &mut FetchWithStmtCommons { is_closed: &mut self.is_closed, @@ -201,7 +200,7 @@ where RV: AsyncBounds + RecordValues, SC: AsyncBounds + StmtCmd, { - let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.lease_mut().parts_mut(); ExecutorBuffer::clear_cmd_buffers(nb, rb, vb); let mut fwsc = FetchWithStmtCommons { is_closed: &mut self.is_closed, @@ -252,7 +251,7 @@ where #[inline] async fn prepare(&mut self, cmd: &str) -> Result { - let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, rb, stmts, vb, .. } = self.eb.lease_mut().parts_mut(); ExecutorBuffer::clear_cmd_buffers(nb, rb, vb); Ok( Self::write_send_await_stmt_prot( @@ -274,7 +273,7 @@ where #[inline] async fn transaction(&mut self) -> crate::Result> { - let ExecutorBufferPartsMut { nb, rb, vb, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, rb, vb, .. } = self.eb.lease_mut().parts_mut(); ExecutorBuffer::clear_cmd_buffers(nb, rb, vb); let mut tm = TransactionManager::new(self); tm.begin().await?; diff --git a/wtx/src/database/client/postgres/executor/authentication.rs b/wtx/src/database/client/postgres/executor/authentication.rs index b260753c..dfcf7cc0 100644 --- a/wtx/src/database/client/postgres/executor/authentication.rs +++ b/wtx/src/database/client/postgres/executor/authentication.rs @@ -6,26 +6,27 @@ use crate::{ }, Identifier, }, - misc::{bytes_split1, from_utf8_basic, FilledBufferWriter, PartitionedFilledBuffer, Stream}, + misc::{ + bytes_split1, from_utf8_basic, ArrayString, ArrayVector, FilledBufferWriter, LeaseMut, + PartitionedFilledBuffer, Stream, + }, rng::Rng, }; use alloc::vec::Vec; -use arrayvec::{ArrayString, ArrayVec}; use base64::prelude::{Engine as _, BASE64_STANDARD}; -use core::borrow::BorrowMut; use hmac::{Hmac, Mac}; use md5::{Digest, Md5}; use sha2::Sha256; impl Executor where - EB: BorrowMut, + EB: LeaseMut, S: Stream, { /// Ascending sequence of extra parameters received from the database. #[inline] pub fn params(&self) -> &[(Identifier, Identifier)] { - &self.eb.borrow().params + &self.eb.lease().params } pub(crate) async fn manage_authentication( @@ -37,7 +38,7 @@ where where RNG: Rng, { - let ExecutorBufferPartsMut { nb, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, .. } = self.eb.lease_mut().parts_mut(); let msg0 = Self::fetch_msg_from_stream(&mut self.is_closed, nb, &mut self.stream).await?; match msg0.ty { MessageTy::Authentication(Authentication::Md5Password(salt)) => { @@ -46,14 +47,9 @@ where md5.update(config.password); md5.update(config.user); let output = md5.finalize_reset(); - md5.update( - ArrayString::<{ 16 * 2 }>::try_from(format_args!("{output:x}")) - .map_err(|err| crate::Error::from(err.simplify()))? - .as_str(), - ); + md5.update(ArrayString::<{ 16 * 2 }>::try_from(format_args!("{output:x}"))?.as_str()); md5.update(salt); - ArrayString::<{ 16 * 2 + 3 }>::try_from(format_args!("md5{:x}", md5.finalize())) - .map_err(|err| crate::Error::from(err.simplify()))? + ArrayString::<{ 16 * 2 + 3 }>::try_from(format_args!("md5{:x}", md5.finalize()))? }; let mut fbw = FilledBufferWriter::from(&mut *nb); password(&mut fbw, &hashed)?; @@ -96,7 +92,7 @@ where pub(crate) async fn read_after_authentication_data(&mut self) -> crate::Result<()> { loop { - let ExecutorBufferPartsMut { nb, params, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, params, .. } = self.eb.lease_mut().parts_mut(); let msg = Self::fetch_msg_from_stream(&mut self.is_closed, nb, &mut self.stream).await?; match msg.ty { MessageTy::BackendKeyData => {} @@ -127,7 +123,7 @@ where { let tsep_data = tls_server_end_point.ok_or(crate::Error::StreamDoesNotSupportTlsChannels)?; let local_nonce = nonce(rng); - + nb._expand_buffer(1024); { let mut fbw = FilledBufferWriter::from(&mut *nb); sasl_first(&mut fbw, &local_nonce)?; @@ -156,7 +152,7 @@ where vec.extend(payload); vec }, - ArrayVec::::try_from(nonce)?, + ArrayVector::::try_from(nonce)?, salted_password(iterations, decoded_salt.get(..n).unwrap_or_default(), config.password)?, ) }; diff --git a/wtx/src/database/client/postgres/executor/fetch.rs b/wtx/src/database/client/postgres/executor/fetch.rs index cdf5ff97..827258b3 100644 --- a/wtx/src/database/client/postgres/executor/fetch.rs +++ b/wtx/src/database/client/postgres/executor/fetch.rs @@ -6,14 +6,14 @@ use crate::{ }, RecordValues, StmtCmd, }, - misc::{PartitionedFilledBuffer, Stream, _read_until}, + misc::{LeaseMut, PartitionedFilledBuffer, Stream, _read_until}, }; use alloc::vec::Vec; -use core::{borrow::BorrowMut, ops::Range}; +use core::ops::Range; impl Executor where - EB: BorrowMut, + EB: LeaseMut, S: Stream, { pub(crate) async fn write_send_await_fetch_with_stmt<'rec, SC, RV>( diff --git a/wtx/src/database/client/postgres/executor/prepare.rs b/wtx/src/database/client/postgres/executor/prepare.rs index bbb4fcb2..436214de 100644 --- a/wtx/src/database/client/postgres/executor/prepare.rs +++ b/wtx/src/database/client/postgres/executor/prepare.rs @@ -12,15 +12,16 @@ use crate::{ }, RecordValues, StmtCmd, }, - misc::{FilledBufferWriter, PartitionedFilledBuffer, Stream, _unreachable}, + misc::{ + ArrayString, FilledBufferWriter, LeaseMut, PartitionedFilledBuffer, Stream, _unreachable, + }, }; -use arrayvec::ArrayString; -use core::{borrow::BorrowMut, ops::Range}; +use core::ops::Range; impl Executor where E: From, - EB: BorrowMut, + EB: LeaseMut, S: Stream, { pub(crate) async fn write_send_await_stmt_initial( @@ -111,6 +112,6 @@ where } fn stmt_id_str(stmt_hash: u64) -> crate::Result> { - Ok(ArrayString::try_from(format_args!("s{stmt_hash}"))?) + ArrayString::try_from(format_args!("s{stmt_hash}")) } } diff --git a/wtx/src/database/client/postgres/executor/simple_query.rs b/wtx/src/database/client/postgres/executor/simple_query.rs index 21f57667..a18a3bba 100644 --- a/wtx/src/database/client/postgres/executor/simple_query.rs +++ b/wtx/src/database/client/postgres/executor/simple_query.rs @@ -2,13 +2,12 @@ use crate::{ database::client::postgres::{ executor_buffer::ExecutorBufferPartsMut, query, Executor, ExecutorBuffer, MessageTy, }, - misc::{FilledBufferWriter, Stream}, + misc::{FilledBufferWriter, LeaseMut, Stream}, }; -use core::borrow::BorrowMut; impl Executor where - EB: BorrowMut, + EB: LeaseMut, S: Stream, { pub(crate) async fn simple_query_execute( @@ -16,15 +15,15 @@ where cmd: &str, mut cb: impl FnMut(u64), ) -> crate::Result<()> { - let ExecutorBufferPartsMut { nb, rb, vb, .. } = self.eb.borrow_mut().parts_mut(); + let ExecutorBufferPartsMut { nb, rb, vb, .. } = self.eb.lease_mut().parts_mut(); ExecutorBuffer::clear_cmd_buffers(nb, rb, vb); - let mut fbw = FilledBufferWriter::from(&mut self.eb.borrow_mut().nb); + let mut fbw = FilledBufferWriter::from(&mut self.eb.lease_mut().nb); query(cmd.as_bytes(), &mut fbw)?; self.stream.write_all(fbw._curr_bytes()).await?; loop { let msg = Self::fetch_msg_from_stream( &mut self.is_closed, - &mut self.eb.borrow_mut().nb, + &mut self.eb.lease_mut().nb, &mut self.stream, ) .await?; diff --git a/wtx/src/database/client/postgres/executor_buffer.rs b/wtx/src/database/client/postgres/executor_buffer.rs index be4be43f..5d1369d3 100644 --- a/wtx/src/database/client/postgres/executor_buffer.rs +++ b/wtx/src/database/client/postgres/executor_buffer.rs @@ -3,7 +3,7 @@ use crate::{ client::postgres::{ty::Ty, Statements}, Identifier, }, - misc::PartitionedFilledBuffer, + misc::{Lease, LeaseMut, PartitionedFilledBuffer}, rng::Rng, }; use alloc::vec::Vec; @@ -46,7 +46,7 @@ impl ExecutorBuffer { { Self { ftb: Vec::new(), - nb: PartitionedFilledBuffer::with_capacity(network_buffer_cap), + nb: PartitionedFilledBuffer::_with_capacity(network_buffer_cap), params: Vec::with_capacity(DFLT_PARAMS_LEN), rb: Vec::with_capacity(records_buffer_cap), stmts: Statements::new(max_queries, rng), @@ -75,7 +75,7 @@ impl ExecutorBuffer { pub(crate) fn _empty() -> Self { Self { ftb: Vec::new(), - nb: PartitionedFilledBuffer::_empty(), + nb: PartitionedFilledBuffer::new(), params: Vec::new(), rb: Vec::new(), stmts: Statements::_empty(), @@ -118,6 +118,20 @@ impl ExecutorBuffer { } } +impl Lease for ExecutorBuffer { + #[inline] + fn lease(&self) -> &ExecutorBuffer { + self + } +} + +impl LeaseMut for ExecutorBuffer { + #[inline] + fn lease_mut(&mut self) -> &mut ExecutorBuffer { + self + } +} + pub(crate) struct ExecutorBufferPartsMut<'eb> { pub(crate) nb: &'eb mut PartitionedFilledBuffer, pub(crate) params: &'eb mut Vec<(Identifier, Identifier)>, diff --git a/wtx/src/database/client/postgres/integration_tests.rs b/wtx/src/database/client/postgres/integration_tests.rs index a006f808..cd300a1e 100644 --- a/wtx/src/database/client/postgres/integration_tests.rs +++ b/wtx/src/database/client/postgres/integration_tests.rs @@ -6,6 +6,7 @@ use crate::{ misc::{FilledBufferWriter, UriRef}, rng::StaticRng, }; +use alloc::string::String; use tokio::net::TcpStream; type Err = crate::Error; diff --git a/wtx/src/database/client/postgres/record.rs b/wtx/src/database/client/postgres/record.rs index 3fc7957b..41cdf2e9 100644 --- a/wtx/src/database/client/postgres/record.rs +++ b/wtx/src/database/client/postgres/record.rs @@ -140,13 +140,11 @@ impl<'exec, E> PartialEq for Record<'exec, E> { } } -#[cfg(feature = "arrayvec")] -mod arrayvec { +mod array { use crate::{ database::{client::postgres::Postgres, FromRecord, Record}, - misc::from_utf8_basic, + misc::{from_utf8_basic, ArrayString}, }; - use arrayvec::ArrayString; impl FromRecord> for ArrayString where diff --git a/wtx/src/database/client/postgres/records.rs b/wtx/src/database/client/postgres/records.rs index 2e22b9c4..9c4c2dc3 100644 --- a/wtx/src/database/client/postgres/records.rs +++ b/wtx/src/database/client/postgres/records.rs @@ -63,11 +63,11 @@ impl<'exec, E> Default for Records<'exec, E> { #[inline] fn default() -> Self { Self { - bytes: <_>::default(), + bytes: &[], phantom: PhantomData, - records_values_offsets: <_>::default(), - stmt: <_>::default(), - values_bytes_offsets: <_>::default(), + records_values_offsets: &[], + stmt: Statement::default(), + values_bytes_offsets: &[], } } } diff --git a/wtx/src/database/client/postgres/statements.rs b/wtx/src/database/client/postgres/statements.rs index 6a5ec7d9..726f12ef 100644 --- a/wtx/src/database/client/postgres/statements.rs +++ b/wtx/src/database/client/postgres/statements.rs @@ -16,15 +16,15 @@ const NUM_OF_ELEMENTS_TO_REMOVE_WHEN_FULL: u8 = 8; /// Statements #[derive(Debug)] pub struct Statements { - columns_start: usize, columns: VecDeque, - info_by_cmd_hash_start: usize, + columns_start: usize, info_by_cmd_hash: HashMap, + info_by_cmd_hash_start: usize, info: VecDeque, max_stmts: usize, num_of_elements_to_remove_when_full: u8, - params_start: usize, params: VecDeque, + params_start: usize, rs: RandomState, } diff --git a/wtx/src/database/client/postgres/transaction_manager.rs b/wtx/src/database/client/postgres/transaction_manager.rs index 0ecb1bed..cf15bb9c 100644 --- a/wtx/src/database/client/postgres/transaction_manager.rs +++ b/wtx/src/database/client/postgres/transaction_manager.rs @@ -1,8 +1,7 @@ use crate::{ database::client::postgres::{Executor, ExecutorBuffer}, - misc::Stream, + misc::{LeaseMut, Stream}, }; -use core::borrow::BorrowMut; /// Transaction Manager #[derive(Debug)] @@ -18,7 +17,7 @@ impl<'exec, E, EB, S> TransactionManager<'exec, E, EB, S> { impl<'exec, E, EB, S> crate::database::TransactionManager for TransactionManager<'exec, E, EB, S> where - EB: BorrowMut, + EB: LeaseMut, S: Stream, { type Executor = Executor; diff --git a/wtx/src/database/client/postgres/tys.rs b/wtx/src/database/client/postgres/tys.rs index 8bd0aeb5..d97dfbe8 100644 --- a/wtx/src/database/client/postgres/tys.rs +++ b/wtx/src/database/client/postgres/tys.rs @@ -40,16 +40,14 @@ macro_rules! test { }; } -#[cfg(feature = "arrayvec")] -mod arrayvec { +mod array { use crate::{ database::{ client::postgres::{DecodeValue, Postgres, Ty}, Decode, Encode, }, - misc::{from_utf8_basic, FilledBufferWriter}, + misc::{from_utf8_basic, ArrayString, FilledBufferWriter}, }; - use arrayvec::ArrayString; impl Decode<'_, Postgres> for ArrayString where @@ -214,9 +212,8 @@ mod pg_numeric { client::postgres::{DecodeValue, Postgres, Ty}, Decode, Encode, }, - misc::{FilledBufferWriter, Usize}, + misc::{ArrayVector, FilledBufferWriter, Usize}, }; - use arrayvec::ArrayVec; const DIGITS_CAP: usize = 64; const SIGN_NAN: u16 = 0xC000; @@ -225,7 +222,7 @@ mod pg_numeric { pub(crate) enum PgNumeric { NotANumber, - Number { digits: ArrayVec, scale: u16, sign: Sign, weight: i16 }, + Number { digits: ArrayVector, scale: u16, sign: Sign, weight: i16 }, } impl Decode<'_, Postgres> for PgNumeric @@ -264,7 +261,7 @@ mod pg_numeric { curr_slice = local_rest; } PgNumeric::Number { - digits: fbw.into_iter().take(digits_usize).collect(), + digits: ArrayVector::new(fbw, digits.into()), scale, sign: Sign::try_from(sign)?, weight, @@ -463,9 +460,8 @@ mod rust_decimal { }, Decode, Encode, }, - misc::FilledBufferWriter, + misc::{ArrayVector, FilledBufferWriter}, }; - use arrayvec::ArrayVec; use rust_decimal::{Decimal, MathematicalOps}; impl Decode<'_, Postgres> for Decimal @@ -485,7 +481,7 @@ mod rust_decimal { return Ok(0u64.into()); } let mut value = Decimal::ZERO; - for digit in digits { + for digit in digits.into_inner() { let mut operations = || { let mul = Decimal::from(10_000u16).checked_powi(weight.into())?; let part = Decimal::from(digit).checked_mul(mul)?; @@ -493,7 +489,7 @@ mod rust_decimal { weight = weight.checked_sub(1)?; Some(()) }; - operations().ok_or_else(|| crate::Error::OutOfBoundsArithmetic.into())?; + operations().ok_or_else(|| crate::Error::OutOfBoundsArithmetic)?; } match sign { Sign::Positive => value.set_sign_positive(true), @@ -512,7 +508,7 @@ mod rust_decimal { fn encode(&self, fbw: &mut FilledBufferWriter<'_>, value: &Ty) -> Result<(), E> { if self.is_zero() { let rslt = PgNumeric::Number { - digits: ArrayVec::default(), + digits: ArrayVector::default(), scale: 0, sign: Sign::Positive, weight: 0, @@ -531,9 +527,9 @@ mod rust_decimal { mantissa = mantissa.wrapping_mul(u128::from(10u32.pow(remainder))); } - let mut digits = ArrayVec::new(); + let mut digits = ArrayVector::default(); while mantissa != 0 { - digits.push((mantissa % 10_000) as i16); + digits.try_push((mantissa % 10_000) as i16)?; mantissa /= 10_000; } digits.reverse(); diff --git a/wtx/src/database/database_ty.rs b/wtx/src/database/database_ty.rs index ae24f4f4..b61108e8 100644 --- a/wtx/src/database/database_ty.rs +++ b/wtx/src/database/database_ty.rs @@ -1,12 +1,13 @@ create_enum! { /// Database - #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] + #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] pub enum DatabaseTy { /// MS-SQL Mssql = (0, "mssql"), /// MySql Mysql = (1, "mysql"), /// PostgreSQL + #[default] Postgres = (2, "postgres"), /// Redis Redis = (3, "redis"), diff --git a/wtx/src/database/orm/crud.rs b/wtx/src/database/orm/crud.rs index 8c227010..09c133a2 100644 --- a/wtx/src/database/orm/crud.rs +++ b/wtx/src/database/orm/crud.rs @@ -2,8 +2,8 @@ use crate::{ database::{ executor::Executor, orm::{ - seek_related_entities, write_select_field, InitialInsertValue, SelectLimit, SelectOrderBy, - SqlWriter, Table, TableParams, + seek_related_entities, write_select_field, AuxNodes, InitialInsertValue, SelectLimit, + SelectOrderBy, SqlWriter, Table, TableParams, }, Database, Decode, FromRecords, Records, ValueIdent, }, @@ -31,7 +31,7 @@ pub trait Crud: Executor { async move { table_params.update_all_table_fields(table); table_params.write_insert::( - &mut <_>::default(), + &mut AuxNodes::default(), buffer_cmd, &mut None, )?; @@ -56,7 +56,7 @@ pub trait Crud: Executor { { async move { table_params.update_all_table_fields(table); - table_params.write_delete(&mut <_>::default(), buffer_cmd)?; + table_params.write_delete(&mut AuxNodes::default(), buffer_cmd)?; let _ = self.execute_with_stmt(buffer_cmd.as_str(), ()).await?; Ok(()) } @@ -146,7 +146,15 @@ pub trait Crud: Executor { })?; let record = self.fetch_with_stmt(buffer_cmd.as_str(), ()).await?; buffer_cmd.clear(); - Ok(T::from_records(buffer_cmd, &record, &<_>::default(), tp.table_suffix())?.1) + Ok( + T::from_records( + buffer_cmd, + &record, + &::Records::default(), + tp.table_suffix(), + )? + .1, + ) } } @@ -166,7 +174,7 @@ pub trait Crud: Executor { { async move { table_params.update_all_table_fields(table); - table_params.write_update(&mut <_>::default(), buffer_cmd)?; + table_params.write_update(&mut AuxNodes::default(), buffer_cmd)?; let _ = self.execute_with_stmt(buffer_cmd.as_str(), ()).await?; Ok(()) } diff --git a/wtx/src/database/orm/no_table_association.rs b/wtx/src/database/orm/no_table_association.rs index a41cdd08..52380f8b 100644 --- a/wtx/src/database/orm/no_table_association.rs +++ b/wtx/src/database/orm/no_table_association.rs @@ -6,7 +6,7 @@ use alloc::string::String; use core::{array, marker::PhantomData}; /// For entities that don't have associations -#[derive(Debug)] +#[derive(Debug, Default)] pub struct NoTableAssociation(PhantomData); impl NoTableAssociation { diff --git a/wtx/src/database/orm/sql_value.rs b/wtx/src/database/orm/sql_value.rs index bc4f8a95..05dd1921 100644 --- a/wtx/src/database/orm/sql_value.rs +++ b/wtx/src/database/orm/sql_value.rs @@ -55,8 +55,7 @@ macro_rules! impl_display { } impl_display!(&'_ str); -#[cfg(feature = "arrayvec")] -impl_display!(arrayvec::ArrayString, const N: usize); +impl_display!(crate::misc::ArrayString, const N: usize); impl_display!(bool); impl_display!(i32); impl_display!(i64); @@ -66,3 +65,22 @@ impl_display!(String); #[cfg(feature = "rust_decimal")] impl_display!(rust_decimal::Decimal); + +#[cfg(feature = "chrono")] +mod chrono { + use crate::database::orm::SqlValue; + use alloc::string::String; + use chrono::{DateTime, Utc}; + use core::fmt::Write; + + impl SqlValue for DateTime + where + E: From, + { + #[inline] + fn write(&self, buffer_cmd: &mut String) -> Result<(), E> { + buffer_cmd.write_fmt(format_args!("'{self}'")).map_err(From::from)?; + Ok(()) + } + } +} diff --git a/wtx/src/database/orm/table_association_wrapper.rs b/wtx/src/database/orm/table_association_wrapper.rs index fe25200b..e0d9816b 100644 --- a/wtx/src/database/orm/table_association_wrapper.rs +++ b/wtx/src/database/orm/table_association_wrapper.rs @@ -1,6 +1,6 @@ use crate::{ database::orm::{Table, TableAssociation, TableParams}, - misc::SingleTypeStorage, + misc::{Lease, SingleTypeStorage}, }; /// A helper structure for people that manually implement [TableAssociations] @@ -11,7 +11,7 @@ use crate::{ pub struct TableAssociationWrapper<'entity, T, TS> where T: Table<'entity>, - TS: AsRef<[TableParams<'entity, T>]> + SingleTypeStorage>, + TS: Lease<[TableParams<'entity, T>]> + SingleTypeStorage>, { /// See [TableAssociation] pub association: TableAssociation, diff --git a/wtx/src/database/orm/tests/collection.rs b/wtx/src/database/orm/tests/collection.rs index 7fe169ab..231f5acd 100644 --- a/wtx/src/database/orm/tests/collection.rs +++ b/wtx/src/database/orm/tests/collection.rs @@ -2,8 +2,8 @@ use crate::database::{ orm::{ - FromSuffixRslt, InitialInsertValue, NoTableAssociation, SelectLimit, SelectOrderBy, SqlWriter, - Table, TableAssociation, TableAssociationWrapper, TableField, TableParams, + AuxNodes, FromSuffixRslt, InitialInsertValue, NoTableAssociation, SelectLimit, SelectOrderBy, + SqlWriter, Table, TableAssociation, TableAssociationWrapper, TableField, TableParams, }, TableSuffix, }; @@ -119,7 +119,7 @@ impl<'entity> Table<'entity> for C { } } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] #[test] fn assert_sizes() { assert_eq!(mem::size_of::>(), 64); @@ -142,7 +142,7 @@ fn update_some_values_has_correct_behavior() { *elem.id_field_mut().value_mut() = Some((&c3.r#as[0].id).into()); c_table_defs.associations_mut().0.tables.push(elem); - c_table_defs.write_update(&mut <_>::default(), &mut buffer).unwrap(); + c_table_defs.write_update(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!(&buffer, r#"UPDATE c SET id='3' WHERE id='3';UPDATE a SET id='1' WHERE id='1';"#); } @@ -155,11 +155,11 @@ fn write_collection_has_correct_params() { let mut buffer = String::new(); let mut c_table_defs = TableParams::::default(); - c_table_defs.write_delete(&mut <_>::default(), &mut buffer).unwrap(); + c_table_defs.write_delete(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!(&buffer, r#""#); c_table_defs - .write_insert::(&mut <_>::default(), &mut buffer, &mut None) + .write_insert::(&mut AuxNodes::default(), &mut buffer, &mut None) .unwrap(); assert_eq!(&buffer, r#""#); @@ -173,13 +173,13 @@ fn write_collection_has_correct_params() { ); buffer.clear(); - c_table_defs.write_update(&mut <_>::default(), &mut buffer).unwrap(); + c_table_defs.write_update(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!(&buffer, r#""#); c_table_defs.update_all_table_fields(&c3); buffer.clear(); - c_table_defs.write_delete(&mut <_>::default(), &mut buffer).unwrap(); + c_table_defs.write_delete(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!( &buffer, r#"DELETE FROM a WHERE id='1';DELETE FROM a WHERE id='2';DELETE FROM c WHERE id='3';"# @@ -187,7 +187,7 @@ fn write_collection_has_correct_params() { buffer.clear(); c_table_defs - .write_insert::(&mut <_>::default(), &mut buffer, &mut None) + .write_insert::(&mut AuxNodes::default(), &mut buffer, &mut None) .unwrap(); assert_eq!( &buffer, @@ -204,7 +204,7 @@ fn write_collection_has_correct_params() { ); buffer.clear(); - c_table_defs.write_update(&mut <_>::default(), &mut buffer).unwrap(); + c_table_defs.write_update(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!( &buffer, r#"UPDATE c SET id='3',name='foo3' WHERE id='3';UPDATE a SET id='1',name='foo1' WHERE id='1';UPDATE a SET id='2',name='foo2' WHERE id='2';"# diff --git a/wtx/src/database/orm/tests/diamond.rs b/wtx/src/database/orm/tests/diamond.rs index bbb6652c..d8af9ef2 100644 --- a/wtx/src/database/orm/tests/diamond.rs +++ b/wtx/src/database/orm/tests/diamond.rs @@ -33,8 +33,8 @@ use crate::database::{ orm::{ - FromSuffixRslt, InitialInsertValue, NoTableAssociation, SelectLimit, SelectOrderBy, SqlWriter, - Table, TableAssociation, TableAssociationWrapper, TableField, TableParams, + AuxNodes, FromSuffixRslt, InitialInsertValue, NoTableAssociation, SelectLimit, SelectOrderBy, + SqlWriter, Table, TableAssociation, TableAssociationWrapper, TableField, TableParams, }, TableSuffix, }; @@ -189,7 +189,7 @@ impl<'entity> Table<'entity> for D { } } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] #[test] fn assert_sizes() { assert_eq!(mem::size_of::>(), 64); @@ -214,7 +214,7 @@ fn multi_referred_table_has_correct_statements() { d_table_defs.update_all_table_fields(&D); buffer.clear(); - d_table_defs.write_delete(&mut <_>::default(), &mut buffer).unwrap(); + d_table_defs.write_delete(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!( &buffer, r#"DELETE FROM a WHERE id='1';DELETE FROM b WHERE id='2';DELETE FROM c WHERE id='3';DELETE FROM d WHERE id='4';"# @@ -222,7 +222,7 @@ fn multi_referred_table_has_correct_statements() { buffer.clear(); d_table_defs - .write_insert::(&mut <_>::default(), &mut buffer, &mut None) + .write_insert::(&mut AuxNodes::default(), &mut buffer, &mut None) .unwrap(); assert_eq!( &buffer, @@ -232,7 +232,7 @@ fn multi_referred_table_has_correct_statements() { ); buffer.clear(); - d_table_defs.write_update(&mut <_>::default(), &mut buffer).unwrap(); + d_table_defs.write_update(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!( &buffer, r#"UPDATE d SET id='4',name='foo4' WHERE id='4';UPDATE b SET id='2',name='foo2' WHERE id='2';UPDATE a SET id='1',name='foo1' WHERE id='1';UPDATE c SET id='3',name='foo3' WHERE id='3';"# @@ -254,12 +254,12 @@ fn referred_table_has_correct_statements() { b_table_defs.update_all_table_fields(&B); buffer.clear(); - b_table_defs.write_delete(&mut <_>::default(), &mut buffer).unwrap(); + b_table_defs.write_delete(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!(&buffer, r#"DELETE FROM a WHERE id='1';DELETE FROM b WHERE id='2';"#); buffer.clear(); b_table_defs - .write_insert::(&mut <_>::default(), &mut buffer, &mut None) + .write_insert::(&mut AuxNodes::default(), &mut buffer, &mut None) .unwrap(); assert_eq!( &buffer, @@ -267,7 +267,7 @@ fn referred_table_has_correct_statements() { ); buffer.clear(); - b_table_defs.write_update(&mut <_>::default(), &mut buffer).unwrap(); + b_table_defs.write_update(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!( &buffer, r#"UPDATE b SET id='2',name='foo2' WHERE id='2';UPDATE a SET id='1',name='foo1' WHERE id='1';"# @@ -289,16 +289,16 @@ fn standalone_table_has_correct_statements() { a_table_defs.update_all_table_fields(&A); buffer.clear(); - a_table_defs.write_delete(&mut <_>::default(), &mut buffer).unwrap(); + a_table_defs.write_delete(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!(&buffer, r#"DELETE FROM a WHERE id='1';"#); buffer.clear(); a_table_defs - .write_insert::(&mut <_>::default(), &mut buffer, &mut None) + .write_insert::(&mut AuxNodes::default(), &mut buffer, &mut None) .unwrap(); assert_eq!(&buffer, r#"INSERT INTO "a" (id,name) VALUES ('1','foo1');"#); buffer.clear(); - a_table_defs.write_update(&mut <_>::default(), &mut buffer).unwrap(); + a_table_defs.write_update(&mut AuxNodes::default(), &mut buffer).unwrap(); assert_eq!(&buffer, r#"UPDATE a SET id='1',name='foo1' WHERE id='1';"#); } diff --git a/wtx/src/database/orm/tuple_impls.rs b/wtx/src/database/orm/tuple_impls.rs index c5b896ce..a5646bc3 100644 --- a/wtx/src/database/orm/tuple_impls.rs +++ b/wtx/src/database/orm/tuple_impls.rs @@ -1,3 +1,8 @@ +#![allow( + // Meta variable expressions + non_snake_case +)] + use crate::{ database::orm::{ AuxNodes, FullTableAssociation, SelectLimit, SelectOrderBy, SqlValue, SqlWriter, Table, @@ -13,30 +18,34 @@ use core::{ }; macro_rules! double_tuple_impls { - ($( - $tuple_len:tt { - $(($idx:tt) -> $T:ident $U:ident)+ - } - )+) => { + ($( ($($T:ident $U:ident),+) )+) => { $( impl<'entity, $($T, $U,)+> TableAssociations for ($( TableAssociationWrapper<'entity, $U, $T>, )+) where $( - $T: AsRef<[TableParams<'entity, $U>]> + SingleTypeStorage>, + $T: crate::misc::Lease<[TableParams<'entity, $U>]> + SingleTypeStorage>, $U: Table<'entity>, )+ { - type FullTableAssociations = array::IntoIter; + type FullTableAssociations = array::IntoIter< + FullTableAssociation, + { + let mut len: usize = 0; + $({ const $T: usize = 1; len = len.wrapping_add($T); })+ + len + } + >; #[inline] fn full_associations(&self) -> Self::FullTableAssociations { + let ($($T,)+) = self; [ $( FullTableAssociation::new( - self.$idx.association, + $T.association, $U::TABLE_NAME, $U::TABLE_NAME_ALIAS, - self.$idx.guide.table_suffix() + $T.guide.table_suffix() ), )+ ].into_iter() @@ -47,7 +56,7 @@ macro_rules! double_tuple_impls { where ERR: From, $( - $T: AsRef<[TableParams<'entity, $U>]> + SingleTypeStorage>, + $T: crate::misc::Lease<[TableParams<'entity, $U>]> + SingleTypeStorage>, $U: Table<'entity, Error = ERR>, $U::Associations: SqlWriter, )+ @@ -60,8 +69,9 @@ macro_rules! double_tuple_impls { aux: &mut AuxNodes, buffer_cmd: &mut String, ) -> Result<(), Self::Error> { + let ($($T,)+) = self; $( - for elem in self.$idx.tables.as_ref() { + for elem in $T.tables.lease() { elem.write_delete(aux, buffer_cmd)?; } )+ @@ -78,11 +88,12 @@ macro_rules! double_tuple_impls { where VALUE: Display { + let ($($T,)+) = self; $( if let Some(ref mut elem) = table_source_association.as_mut() { - *elem.source_field_mut() = self.$idx.association.to_id(); + *elem.source_field_mut() = $T.association.to_id(); } - for elem in self.$idx.tables.as_ref() { + for elem in $T.tables.lease() { elem.write_insert(aux, buffer_cmd, table_source_association)?; } )+ @@ -97,8 +108,9 @@ macro_rules! double_tuple_impls { limit: SelectLimit, where_cb: &mut impl FnMut(&mut String) -> Result<(), Self::Error>, ) -> Result<(), Self::Error> { + let ($($T,)+) = self; $( - self.$idx.guide.write_select(buffer_cmd, order_by, limit, where_cb)?; + $T.guide.write_select(buffer_cmd, order_by, limit, where_cb)?; )+ Ok(()) } @@ -108,8 +120,9 @@ macro_rules! double_tuple_impls { &self, buffer_cmd: &mut String, ) -> Result<(), Self::Error> { + let ($($T,)+) = self; $( - self.$idx.guide.write_select_associations(buffer_cmd)?; + $T.guide.write_select_associations(buffer_cmd)?; )+ Ok(()) } @@ -119,16 +132,18 @@ macro_rules! double_tuple_impls { &self, buffer_cmd: &mut String, ) -> Result<(), Self::Error> { + let ($($T,)+) = self; $( - self.$idx.guide.write_select_fields(buffer_cmd)?; + $T.guide.write_select_fields(buffer_cmd)?; )+ Ok(()) } #[inline] fn write_select_orders_by(&self, buffer_cmd: &mut String) -> Result<(), Self::Error> { + let ($($T,)+) = self; $( - self.$idx.guide.write_select_orders_by(buffer_cmd)?; + $T.guide.write_select_orders_by(buffer_cmd)?; )+ Ok(()) } @@ -139,8 +154,9 @@ macro_rules! double_tuple_impls { aux: &mut AuxNodes, buffer_cmd: &mut String, ) -> Result<(), Self::Error> { + let ($($T,)+) = self; $( - for elem in self.$idx.tables.as_ref() { + for elem in $T.tables.lease() { elem.write_update(aux, buffer_cmd)?; } )+ @@ -152,27 +168,32 @@ macro_rules! double_tuple_impls { } macro_rules! tuple_impls { - ($( - $tuple_len:tt { - $(($idx:tt) -> $T:ident)+ - } - )+) => { + ($( ($($T:ident),+) )+) => { $( impl),+> TableFields for ($( TableField<$T>, )+) where ERR: From, { - type FieldNames = array::IntoIter<&'static str, $tuple_len>; + type FieldNames = array::IntoIter< + &'static str, + { + let mut len: usize = 0; + $({ const $T: usize = 1; len = len.wrapping_add($T); })+ + len + } + >; #[inline] fn field_names(&self) -> Self::FieldNames { - [ $( self.$idx.name(), )+ ].into_iter() + let ($($T,)+) = self; + [ $( $T.name(), )+ ].into_iter() } #[inline] fn write_insert_values(&self, buffer_cmd: &mut String) -> Result<(), ERR> { + let ($($T,)+) = self; $( - if let &Some(ref elem) = self.$idx.value() { + if let &Some(ref elem) = $T.value() { elem.write(buffer_cmd)?; buffer_cmd.push(','); } @@ -182,9 +203,10 @@ macro_rules! tuple_impls { #[inline] fn write_update_values(&self, buffer_cmd: &mut String) -> Result<(), ERR> { + let ($($T,)+) = self; $( - if let &Some(ref elem) = self.$idx.value() { - buffer_cmd.write_fmt(format_args!("{}=", self.$idx.name())).map_err(From::from)?; + if let &Some(ref elem) = $T.value() { + buffer_cmd.write_fmt(format_args!("{}=", $T.name())).map_err(From::from)?; elem.write(buffer_cmd)?; buffer_cmd.push(','); } @@ -197,343 +219,39 @@ macro_rules! tuple_impls { } double_tuple_impls! { - 1 { - (0) -> A B - } - 2 { - (0) -> A B - (1) -> C D - } - 3 { - (0) -> A B - (1) -> C D - (2) -> E F - } - 4 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - } - 5 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - } - 6 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - } - 7 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - } - 8 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - } - 9 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - } - 10 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - (9) -> S T - } - 11 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - (9) -> S T - (10) -> U V - } - 12 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - (9) -> S T - (10) -> U V - (11) -> W X - } - 13 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - (9) -> S T - (10) -> U V - (11) -> W X - (12) -> Y Z - } - 14 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - (9) -> S T - (10) -> U V - (11) -> W X - (12) -> Y Z - (13) -> AA AB - } - 15 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - (9) -> S T - (10) -> U V - (11) -> W X - (12) -> Y Z - (13) -> AA AB - (14) -> AC AD - } - 16 { - (0) -> A B - (1) -> C D - (2) -> E F - (3) -> G H - (4) -> I J - (5) -> K L - (6) -> M N - (7) -> O P - (8) -> Q R - (9) -> S T - (10) -> U V - (11) -> W X - (12) -> Y Z - (13) -> AA AB - (14) -> AC AD - (15) -> AE AF - } + (A B) + (A B, C D) + (A B, C D, E F) + (A B, C D, E F, G H) + (A B, C D, E F, G H, I J) + (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 N) + (A B, C D, E F, G H, I J, K L, M N, O P) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R, S T) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R, S T, U V) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R, S T, U V, W X) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R, S T, U V, W X, Y Z) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R, S T, U V, W X, Y Z, AA AB) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R, S T, U V, W X, Y Z, AA AB, AC AD) + (A B, C D, E F, G H, I J, K L, M N, O P, Q R, S T, U V, W X, Y Z, AA AB, AC AD, AE AF) } tuple_impls! { - 1 { - (0) -> A - } - 2 { - (0) -> A - (1) -> B - } - 3 { - (0) -> A - (1) -> B - (2) -> C - } - 4 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - } - 5 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - } - 6 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - } - 7 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - } - 8 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - } - 9 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - } - 10 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - } - 11 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - } - 12 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - } - 13 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - } - 14 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - (13) -> N - } - 15 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - (13) -> N - (14) -> O - } - 16 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - (13) -> N - (14) -> O - (15) -> P - } + (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) } diff --git a/wtx/src/database/record.rs b/wtx/src/database/record.rs index 997d80b9..6657db11 100644 --- a/wtx/src/database/record.rs +++ b/wtx/src/database/record.rs @@ -15,7 +15,7 @@ pub trait Record { CI: ValueIdent<::Record<'this>>, D: Decode<'this, Self::Database>, { - D::decode(&self.value(ci).ok_or(_unlikely_elem(crate::Error::AbsentFieldDataInDecoding))?) + D::decode(&self.value(ci).ok_or(_unlikely_elem(crate::Error::MissingFieldDataInDecoding))?) } /// Tries to retrieve and decode an optional value. diff --git a/wtx/src/database/record_values.rs b/wtx/src/database/record_values.rs index 8ce6004e..3056b724 100644 --- a/wtx/src/database/record_values.rs +++ b/wtx/src/database/record_values.rs @@ -1,3 +1,8 @@ +#![allow( + // Meta variable expressions + non_snake_case +)] + use crate::{ database::{Database, Encode}, misc::{into_rslt, FilledBufferWriter}, @@ -140,11 +145,7 @@ where } macro_rules! tuple_impls { - ($( - $tuple_len:tt { - $(($idx:tt) -> $T:ident)+ - } - )+) => { + ($( ($($T:ident),+) )+) => { $( impl RecordValues for ($( $T, )+) where @@ -164,10 +165,11 @@ macro_rules! tuple_impls { ::EncodeValue<'ev>: 'ev { let mut n: usize = 0; + let ($($T,)+) = self; $( encode( aux, - &self.$idx, + &$T, fbw, &mut n, into_rslt(values.next())?, @@ -180,7 +182,9 @@ macro_rules! tuple_impls { #[inline] fn len(&self) -> usize { - $tuple_len + let mut len: usize = 0; + $({ const $T: usize = 1; len = len.wrapping_add($T); })+ + len } } )+ @@ -188,174 +192,22 @@ macro_rules! tuple_impls { } tuple_impls! { - 1 { - (0) -> A - } - 2 { - (0) -> A - (1) -> B - } - 3 { - (0) -> A - (1) -> B - (2) -> C - } - 4 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - } - 5 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - } - 6 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - } - 7 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - } - 8 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - } - 9 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - } - 10 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - } - 11 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - } - 12 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - } - 13 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - } - 14 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - (13) -> N - } - 15 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - (13) -> N - (14) -> O - } - 16 { - (0) -> A - (1) -> B - (2) -> C - (3) -> D - (4) -> E - (5) -> F - (6) -> G - (7) -> H - (8) -> I - (9) -> J - (10) -> K - (11) -> L - (12) -> M - (13) -> N - (14) -> O - (15) -> P - } + (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) } fn encode( diff --git a/wtx/src/database/schema_manager.rs b/wtx/src/database/schema_manager.rs index c763bc1f..248b42ae 100644 --- a/wtx/src/database/schema_manager.rs +++ b/wtx/src/database/schema_manager.rs @@ -18,7 +18,10 @@ pub use commands::*; pub use repeatability::Repeatability; #[cfg(all(feature = "_integration-tests", feature = "schema-manager-dev", test))] mod integration_tests; -use crate::database::{executor::Executor, DatabaseTy, Identifier}; +use crate::{ + database::{executor::Executor, DatabaseTy, Identifier}, + misc::Lease, +}; use alloc::{string::String, vec::Vec}; use core::future::Future; pub use migration::*; @@ -59,7 +62,7 @@ pub trait SchemaManagement: Executor { version: i32, ) -> impl Future> where - S: AsRef; + S: Lease; /// Inserts a new set of migrations, fn insert_migrations<'migration, DBS, I, S>( @@ -69,9 +72,9 @@ pub trait SchemaManagement: Executor { migrations: I, ) -> impl Future> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration; + S: Lease + 'migration; /// Retrieves all migrations of the given `mg` group. fn migrations( @@ -81,7 +84,7 @@ pub trait SchemaManagement: Executor { results: &mut Vec, ) -> impl Future> where - S: AsRef; + S: Lease; /// Retrieves all tables contained in a schema. If the implementation does not supports schemas, /// the parameter is ignored. @@ -112,7 +115,7 @@ impl SchemaManagement for () { _: i32, ) -> crate::Result<()> where - S: AsRef, + S: Lease, { Ok(()) } @@ -125,9 +128,9 @@ impl SchemaManagement for () { _: I, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { Ok(()) } @@ -140,7 +143,7 @@ impl SchemaManagement for () { _: &mut Vec, ) -> crate::Result<()> where - S: AsRef, + S: Lease, { Ok(()) } @@ -158,6 +161,8 @@ impl SchemaManagement for () { #[cfg(feature = "postgres")] mod postgres { + use alloc::{string::String, vec::Vec}; + use crate::{ database::{ client::postgres::{Executor, ExecutorBuffer}, @@ -170,23 +175,22 @@ mod postgres { }, DatabaseTy, Executor as _, Identifier, }, - misc::{AsyncBounds, Stream}, + misc::{AsyncBounds, Lease, LeaseMut, Stream}, }; - use core::borrow::BorrowMut; impl SchemaManagement for Executor where - EB: AsyncBounds + BorrowMut, + EB: AsyncBounds + LeaseMut, STREAM: AsyncBounds + Stream, { #[inline] async fn clear(&mut self, buffer: (&mut String, &mut Vec)) -> crate::Result<()> { - _clear(buffer.into(), self).await + _clear(buffer, self).await } #[inline] async fn create_wtx_tables(&mut self) -> crate::Result<()> { - let _ = self.execute(_CREATE_MIGRATION_TABLES, |_| {}).await?; + self.execute(_CREATE_MIGRATION_TABLES, |_| {}).await?; Ok(()) } @@ -198,7 +202,7 @@ mod postgres { version: i32, ) -> crate::Result<()> where - S: AsRef, + S: Lease, { _delete_migrations(buffer_cmd, self, mg, _WTX_SCHEMA_PREFIX, version).await } @@ -211,9 +215,9 @@ mod postgres { migrations: I, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { _insert_migrations(buffer_cmd, self, mg, migrations, _WTX_SCHEMA_PREFIX).await } @@ -226,7 +230,7 @@ mod postgres { results: &mut Vec, ) -> crate::Result<()> where - S: AsRef, + S: Lease, { _migrations_by_mg_version_query(buffer_cmd, self, mg.version(), results, _WTX_SCHEMA_PREFIX) .await diff --git a/wtx/src/database/schema_manager/commands.rs b/wtx/src/database/schema_manager/commands.rs index 2ff1c8de..54b7673a 100644 --- a/wtx/src/database/schema_manager/commands.rs +++ b/wtx/src/database/schema_manager/commands.rs @@ -6,10 +6,13 @@ mod rollback; mod seed; mod validate; -use crate::database::{ - executor::Executor, - schema_manager::{UserMigration, DEFAULT_BATCH_SIZE}, - Database, DatabaseTy, +use crate::{ + database::{ + executor::Executor, + schema_manager::{UserMigration, DEFAULT_BATCH_SIZE}, + Database, DatabaseTy, + }, + misc::Lease, }; /// SQL commands facade @@ -48,9 +51,9 @@ where migrations: I, ) -> impl Clone + Iterator> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { migrations.filter(move |m| { if m.dbs().is_empty() { diff --git a/wtx/src/database/schema_manager/commands/migrate.rs b/wtx/src/database/schema_manager/commands/migrate.rs index e5727959..331324d2 100644 --- a/wtx/src/database/schema_manager/commands/migrate.rs +++ b/wtx/src/database/schema_manager/commands/migrate.rs @@ -1,6 +1,9 @@ -use crate::database::{ - schema_manager::{Commands, DbMigration, MigrationGroup, SchemaManagement, UserMigration}, - DatabaseTy, +use crate::{ + database::{ + schema_manager::{Commands, DbMigration, MigrationGroup, SchemaManagement, UserMigration}, + DatabaseTy, + }, + misc::Lease, }; use alloc::{string::String, vec::Vec}; #[cfg(feature = "std")] @@ -26,9 +29,9 @@ where user_migrations: I, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { buffer_db_migrations.clear(); self.executor.create_wtx_tables().await?; @@ -70,8 +73,8 @@ where groups: MigrationFromGroups<'_, '_, '_, DBS, S>, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]>, - S: AsRef, + DBS: Lease<[DatabaseTy]>, + S: Lease, { self.executor.create_wtx_tables().await?; for (mg, m) in groups { @@ -105,9 +108,9 @@ where user_migrations: I, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { let filtered_by_db = Self::filter_by_db(user_migrations); Self::do_validate(buffer_db_migrations, filtered_by_db.clone())?; diff --git a/wtx/src/database/schema_manager/commands/rollback.rs b/wtx/src/database/schema_manager/commands/rollback.rs index 51e41a5a..a9159b56 100644 --- a/wtx/src/database/schema_manager/commands/rollback.rs +++ b/wtx/src/database/schema_manager/commands/rollback.rs @@ -1,6 +1,9 @@ -use crate::database::{ - schema_manager::{Commands, DbMigration, MigrationGroup, SchemaManagement, UserMigration}, - DatabaseTy, TransactionManager, +use crate::{ + database::{ + schema_manager::{Commands, DbMigration, MigrationGroup, SchemaManagement, UserMigration}, + DatabaseTy, TransactionManager, + }, + misc::Lease, }; use alloc::{string::String, vec::Vec}; #[cfg(feature = "std")] @@ -25,18 +28,18 @@ where version: i32, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { self.executor.migrations(buffer_cmd, mg, buffer_db_migrations).await?; let filtered_by_db = Self::filter_by_db(migrations); Self::do_validate(buffer_db_migrations, filtered_by_db.clone())?; for elem in filtered_by_db.map(UserMigration::sql_down) { - buffer_cmd.push_str(elem.as_ref()); + buffer_cmd.push_str(elem); } let mut tm = self.executor.transaction().await?; - let _ = tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; + tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; tm.commit().await?; buffer_cmd.clear(); self.executor.delete_migrations(buffer_cmd, mg, version).await?; diff --git a/wtx/src/database/schema_manager/commands/seed.rs b/wtx/src/database/schema_manager/commands/seed.rs index 51536eda..29fd119e 100644 --- a/wtx/src/database/schema_manager/commands/seed.rs +++ b/wtx/src/database/schema_manager/commands/seed.rs @@ -1,4 +1,7 @@ -use crate::database::{executor::Executor, schema_manager::Commands, TransactionManager}; +use crate::{ + database::{executor::Executor, schema_manager::Commands, TransactionManager}, + misc::Lease, +}; use alloc::string::String; #[cfg(feature = "std")] use std::{fs::read_to_string, path::Path}; @@ -14,13 +17,13 @@ where pub async fn seed(&mut self, buffer_cmd: &mut String, seeds: I) -> crate::Result<()> where I: Iterator, - S: AsRef, + S: Lease, { for elem in seeds { - buffer_cmd.push_str(elem.as_ref()); + buffer_cmd.push_str(elem.lease()); } let mut tm = self.executor.transaction().await?; - let _ = tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; + tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; tm.commit().await?; buffer_cmd.clear(); Ok(()) diff --git a/wtx/src/database/schema_manager/commands/validate.rs b/wtx/src/database/schema_manager/commands/validate.rs index a7bfc678..eed54781 100644 --- a/wtx/src/database/schema_manager/commands/validate.rs +++ b/wtx/src/database/schema_manager/commands/validate.rs @@ -1,9 +1,12 @@ -use crate::database::{ - schema_manager::{ - misc::is_migration_divergent, Commands, DbMigration, MigrationGroup, Repeatability, - SchemaManagement, UserMigration, +use crate::{ + database::{ + schema_manager::{ + misc::is_migration_divergent, Commands, DbMigration, MigrationGroup, Repeatability, + SchemaManagement, UserMigration, + }, + DatabaseTy, }, - DatabaseTy, + misc::Lease, }; use alloc::{string::String, vec::Vec}; #[cfg(feature = "std")] @@ -26,9 +29,9 @@ where migrations: I, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { self.executor.migrations(buffer_cmd, mg, buffer_db_migrations).await?; Self::do_validate(buffer_db_migrations, Self::filter_by_db(migrations))?; @@ -69,9 +72,9 @@ where migrations: I, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, I: Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { let mut migrations_len: usize = 0; for migration in migrations { diff --git a/wtx/src/database/schema_manager/fixed_sql_commands.rs b/wtx/src/database/schema_manager/fixed_sql_commands.rs index 73d46189..fe15c97e 100644 --- a/wtx/src/database/schema_manager/fixed_sql_commands.rs +++ b/wtx/src/database/schema_manager/fixed_sql_commands.rs @@ -34,7 +34,7 @@ use crate::{ schema_manager::{DbMigration, MigrationGroup, UserMigration}, Database, DatabaseTy, FromRecord, TransactionManager, }, - misc::AsyncBounds, + misc::{AsyncBounds, Lease}, }; use alloc::{string::String, vec::Vec}; use core::fmt::Write; @@ -49,13 +49,13 @@ pub(crate) async fn _delete_migrations( ) -> crate::Result<()> where E: AsyncBounds + Executor, - S: AsRef, + S: Lease, { buffer_cmd.write_fmt(format_args!( "DELETE FROM {schema_prefix}_wtx_migration WHERE _wtx_migration_omg_version = {mg_version} AND version > {version}", mg_version = mg.version(), ))?; - let _ = executor.execute(buffer_cmd.as_str(), |_| {}).await?; + executor.execute(buffer_cmd.as_str(), |_| {}).await?; buffer_cmd.clear(); Ok(()) } @@ -69,10 +69,10 @@ pub(crate) async fn _insert_migrations<'migration, DBS, E, I, S>( schema_prefix: &str, ) -> crate::Result<()> where - DBS: AsRef<[DatabaseTy]> + 'migration, + DBS: Lease<[DatabaseTy]> + 'migration, E: AsyncBounds + Executor, I: Clone + Iterator>, - S: AsRef + 'migration, + S: Lease + 'migration, { buffer_cmd.write_fmt(format_args!( "INSERT INTO {schema_prefix}_wtx_migration_group (version, name) @@ -83,14 +83,14 @@ where mg_name = mg.name(), mg_version = mg.version(), ))?; - let _ = executor.execute(buffer_cmd.as_str(), |_| {}).await?; + executor.execute(buffer_cmd.as_str(), |_| {}).await?; buffer_cmd.clear(); for migration in migrations.clone() { buffer_cmd.push_str(migration.sql_up()); } let mut tm = executor.transaction().await?; - let _ = tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; + tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; tm.commit().await?; buffer_cmd.clear(); @@ -109,7 +109,7 @@ where ))?; } let mut tm = executor.transaction().await?; - let _ = tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; + tm.executor().execute(buffer_cmd.as_str(), |_| {}).await?; tm.commit().await?; buffer_cmd.clear(); diff --git a/wtx/src/database/schema_manager/integration_tests.rs b/wtx/src/database/schema_manager/integration_tests.rs index 5eb1d371..fe4d3d28 100644 --- a/wtx/src/database/schema_manager/integration_tests.rs +++ b/wtx/src/database/schema_manager/integration_tests.rs @@ -15,6 +15,7 @@ use crate::{ }, rng::StaticRng, }; +use alloc::{string::String, vec::Vec}; use core::fmt::Write; use tokio::net::TcpStream; diff --git a/wtx/src/database/schema_manager/integration_tests/backend.rs b/wtx/src/database/schema_manager/integration_tests/backend.rs index d7fa1069..0bebfefd 100644 --- a/wtx/src/database/schema_manager/integration_tests/backend.rs +++ b/wtx/src/database/schema_manager/integration_tests/backend.rs @@ -5,6 +5,7 @@ use crate::database::{ }, Identifier, }; +use alloc::{string::String, vec::Vec}; use chrono::{DateTime, Duration, FixedOffset, Utc}; pub(crate) async fn _backend_has_migration_with_utc_time( diff --git a/wtx/src/database/schema_manager/integration_tests/db/postgres.rs b/wtx/src/database/schema_manager/integration_tests/db/postgres.rs index 184c49d1..ad58d53b 100644 --- a/wtx/src/database/schema_manager/integration_tests/db/postgres.rs +++ b/wtx/src/database/schema_manager/integration_tests/db/postgres.rs @@ -1,11 +1,14 @@ #[cfg(feature = "schema-manager-dev")] -use crate::{ - database::{ - client::postgres::Postgres, schema_manager::fixed_sql_commands::postgres, - schema_manager::integration_tests, schema_manager::Commands, schema_manager::DbMigration, - schema_manager::SchemaManagement, FromRecord, Identifier, +use { + crate::{ + database::{ + client::postgres::Postgres, schema_manager::fixed_sql_commands::postgres, + schema_manager::integration_tests, schema_manager::Commands, schema_manager::DbMigration, + schema_manager::SchemaManagement, FromRecord, Identifier, + }, + misc::AsyncBounds, }, - misc::AsyncBounds, + alloc::{string::String, vec::Vec}, }; #[cfg(feature = "schema-manager-dev")] diff --git a/wtx/src/database/schema_manager/integration_tests/generic.rs b/wtx/src/database/schema_manager/integration_tests/generic.rs index c68a2337..c7ddf0ed 100644 --- a/wtx/src/database/schema_manager/integration_tests/generic.rs +++ b/wtx/src/database/schema_manager/integration_tests/generic.rs @@ -4,6 +4,7 @@ use crate::database::{ }, Identifier, }; +use alloc::{string::String, vec::Vec}; use std::path::Path; pub(crate) async fn all_tables_returns_the_number_of_tables_of_the_default_schema( diff --git a/wtx/src/database/schema_manager/integration_tests/schema.rs b/wtx/src/database/schema_manager/integration_tests/schema.rs index 0d7d6a08..a21e8573 100644 --- a/wtx/src/database/schema_manager/integration_tests/schema.rs +++ b/wtx/src/database/schema_manager/integration_tests/schema.rs @@ -4,6 +4,7 @@ pub(crate) mod without_schema; use crate::database::schema_manager::{ integration_tests::AuxTestParams, Commands, MigrationGroup, SchemaManagement, }; +use alloc::{string::String, vec::Vec}; use std::path::Path; pub(crate) async fn migrate_works( diff --git a/wtx/src/database/schema_manager/integration_tests/schema/with_schema.rs b/wtx/src/database/schema_manager/integration_tests/schema/with_schema.rs index 6a914319..2366a042 100644 --- a/wtx/src/database/schema_manager/integration_tests/schema/with_schema.rs +++ b/wtx/src/database/schema_manager/integration_tests/schema/with_schema.rs @@ -5,6 +5,7 @@ use crate::database::{ }, Identifier, }; +use alloc::{string::String, vec::Vec}; pub(crate) async fn all_tables_returns_the_number_of_tables_of_wtx_schema( (buffer_cmd, buffer_db_migrations, buffer_idents): ( diff --git a/wtx/src/database/schema_manager/integration_tests/schema/without_schema.rs b/wtx/src/database/schema_manager/integration_tests/schema/without_schema.rs index 9c2b9a60..36cc6207 100644 --- a/wtx/src/database/schema_manager/integration_tests/schema/without_schema.rs +++ b/wtx/src/database/schema_manager/integration_tests/schema/without_schema.rs @@ -2,6 +2,7 @@ use crate::database::{ schema_manager::{integration_tests::AuxTestParams, Commands, DbMigration, SchemaManagement}, Identifier, }; +use alloc::{string::String, vec::Vec}; pub(crate) async fn _migrate_works( (buffer_cmd, _, _): (&mut String, &mut Vec, &mut Vec), diff --git a/wtx/src/database/schema_manager/migration/db_migration.rs b/wtx/src/database/schema_manager/migration/db_migration.rs index 7635260c..3342683e 100644 --- a/wtx/src/database/schema_manager/migration/db_migration.rs +++ b/wtx/src/database/schema_manager/migration/db_migration.rs @@ -92,7 +92,7 @@ fn _checksum_from_str(bytes: &[u8]) -> crate::Result { } fn _fixed_from_naive_utc(naive: NaiveDateTime) -> DateTime { - chrono::DateTime::::from_naive_utc_and_offset(naive, Utc).into() + DateTime::::from_naive_utc_and_offset(naive, Utc) } fn _from_u32(n: Option) -> Option { diff --git a/wtx/src/database/schema_manager/migration/migration_group.rs b/wtx/src/database/schema_manager/migration/migration_group.rs index 44ae8878..d9414838 100644 --- a/wtx/src/database/schema_manager/migration/migration_group.rs +++ b/wtx/src/database/schema_manager/migration/migration_group.rs @@ -1,3 +1,5 @@ +use crate::misc::Lease; + /// A set of unique migrations /// /// * Types @@ -11,7 +13,7 @@ pub struct MigrationGroup { impl MigrationGroup where - S: AsRef, + S: Lease, { /// Creates a new instance from all necessary parameters. #[inline] @@ -29,7 +31,7 @@ where /// ``` #[inline] pub fn name(&self) -> &str { - self.name.as_ref() + self.name.lease() } /// Version diff --git a/wtx/src/database/schema_manager/migration/user_migration.rs b/wtx/src/database/schema_manager/migration/user_migration.rs index aadbc1d4..eb4075ae 100644 --- a/wtx/src/database/schema_manager/migration/user_migration.rs +++ b/wtx/src/database/schema_manager/migration/user_migration.rs @@ -1,16 +1,18 @@ -use crate::database::{ - schema_manager::{ - migration::MigrationCommon, - misc::{calc_checksum, is_sorted_and_unique}, - Repeatability, +use crate::{ + database::{ + schema_manager::{ + migration::MigrationCommon, + misc::{calc_checksum, is_sorted_and_unique}, + Repeatability, + }, + DatabaseTy, }, - DatabaseTy, + misc::{ArrayVector, Lease}, }; use alloc::string::String; -use arrayvec::ArrayVec; /// UserMigration - Owned -pub type UserMigrationOwned = UserMigration, String>; +pub type UserMigrationOwned = UserMigration, String>; /// UserMigration - Reference pub type UserMigrationRef<'dbs, 'str> = UserMigration<&'dbs [DatabaseTy], &'str str>; @@ -30,8 +32,8 @@ pub struct UserMigration { impl UserMigration where - DBS: AsRef<[DatabaseTy]>, - S: AsRef, + DBS: Lease<[DatabaseTy]>, + S: Lease, { /// Creates a new instance from all necessary parameters, including internal ones. #[inline] @@ -61,8 +63,8 @@ where [sql_up, sql_down]: [S; 2], version: i32, ) -> crate::Result { - is_sorted_and_unique(dbs.as_ref())?; - let checksum = calc_checksum(name.as_ref(), sql_up.as_ref(), sql_down.as_ref(), version); + is_sorted_and_unique(dbs.lease())?; + let checksum = calc_checksum(name.lease(), sql_up.lease(), sql_down.lease(), version); Ok(Self { dbs, common: MigrationCommon { checksum, name, repeatability, version }, @@ -96,7 +98,7 @@ where /// ``` #[inline] pub fn dbs(&self) -> &[DatabaseTy] { - self.dbs.as_ref() + self.dbs.lease() } /// Name @@ -109,7 +111,7 @@ where /// ``` #[inline] pub fn name(&self) -> &str { - self.common.name.as_ref() + self.common.name.lease() } /// If this is a repeatable migration, returns its type. @@ -135,7 +137,7 @@ where /// ``` #[inline] pub fn sql_down(&self) -> &str { - self.sql_down.as_ref() + self.sql_down.lease() } /// Raw SQL for migrations @@ -150,7 +152,7 @@ where /// ); #[inline] pub fn sql_up(&self) -> &str { - self.sql_up.as_ref() + self.sql_up.lease() } /// UserMigration version diff --git a/wtx/src/database/schema_manager/migration_parser.rs b/wtx/src/database/schema_manager/migration_parser.rs index 783db66f..908115df 100644 --- a/wtx/src/database/schema_manager/migration_parser.rs +++ b/wtx/src/database/schema_manager/migration_parser.rs @@ -8,16 +8,16 @@ use crate::{ }, DatabaseTy, }, - misc::str_split1, + misc::{str_split1, ArrayVector}, }; -use arrayvec::ArrayVec; +use alloc::string::String; use std::io::{BufRead, BufReader, Read}; /// Auxiliary parameters of a migration file #[derive(Debug, Default)] pub struct MigrationCfg { /// All unique declared databases - pub dbs: ArrayVec, + pub dbs: ArrayVector, /// Declared repeatability pub repeatability: Option, } @@ -94,13 +94,13 @@ pub(crate) fn parse_migration_toml(read: R) -> crate::Result where R: Read, { - let mut migration_toml = MigrationCfg { dbs: ArrayVec::new(), repeatability: None }; + let mut migration_toml = MigrationCfg { dbs: ArrayVector::default(), repeatability: None }; for (ident, toml_expr) in toml(read)? { - match (ident.as_ref(), toml_expr) { + match (ident.as_str(), toml_expr) { ("dbs", Expr::Array(array)) => { - for s in array { - let Ok(elem) = s.as_str().try_into() else { + for str in array.into_iter() { + let Ok(elem) = str.as_str().try_into() else { continue; }; migration_toml.dbs.try_push(elem)?; diff --git a/wtx/src/database/schema_manager/misc.rs b/wtx/src/database/schema_manager/misc.rs index 5b5eabd6..148d0574 100644 --- a/wtx/src/database/schema_manager/misc.rs +++ b/wtx/src/database/schema_manager/misc.rs @@ -7,9 +7,12 @@ macro_rules! opt_to_inv_mig { }; } -use crate::database::{ - schema_manager::migration::{DbMigration, UserMigration}, - DatabaseTy, +use crate::{ + database::{ + schema_manager::migration::{DbMigration, UserMigration}, + DatabaseTy, + }, + misc::Lease, }; use core::hash::{Hash, Hasher}; #[cfg(feature = "std")] @@ -18,7 +21,7 @@ use { toml_parser::{toml, Expr, EXPR_ARRAY_MAX_LEN}, MigrationGroup, Repeatability, UserMigrationOwned, }, - arrayvec::ArrayString, + alloc::string::String, arrayvec::ArrayVec, core::cmp::Ordering, std::path::{Path, PathBuf}, @@ -32,8 +35,14 @@ use { #[cfg(feature = "std")] type MigrationGroupParts = (String, i32); #[cfg(feature = "std")] -type MigrationParts = - (ArrayVec, String, Option, String, String, i32); +type MigrationParts = ( + crate::misc::ArrayVector, + String, + Option, + String, + String, + i32, +); /// All files of a given `path`. #[cfg(feature = "std")] @@ -56,8 +65,9 @@ pub fn group_and_migrations_from_path( where F: FnMut(&PathBuf, &PathBuf) -> Ordering, { - use crate::database::schema_manager::migration_parser::{ - parse_migration_toml, parse_unified_migration, + use crate::{ + database::schema_manager::migration_parser::{parse_migration_toml, parse_unified_migration}, + misc::ArrayString, }; fn group_and_migrations_from_path( @@ -70,7 +80,7 @@ where let (mg, mut migrations_vec) = migrations_from_dir(path)?; migrations_vec.sort_by(cb); let migrations = migrations_vec.into_iter().map(move |local_path| { - let mut dbs = ArrayVec::default(); + let mut dbs = crate::misc::ArrayVector::default(); let name; let mut repeatability = None; let mut sql_down = String::default(); @@ -96,13 +106,13 @@ where let file = file_rslt?; let file_path = file.path(); let file_name = opt_to_inv_mig!(|| file_path.file_name()?.to_str())?; - if file_name == &cfg_file_name { + if file_name == cfg_file_name.as_str() { let mc = parse_migration_toml(File::open(file_path)?)?; dbs = mc.dbs; repeatability = mc.repeatability; - } else if file_name == &down_file_name { + } else if file_name == down_file_name.as_str() { sql_down = read_to_string(file_path)?; - } else if file_name == &up_file_name { + } else if file_name == up_file_name.as_str() { sql_up = read_to_string(file_path)?; } else { continue; @@ -155,13 +165,13 @@ pub fn parse_root_toml_raw( where R: Read, { - let mut migration_groups = ArrayVec::new(); + let mut migration_groups = ArrayVec::default(); let mut seeds = None; for (ident, toml_expr) in toml(read)? { - match (ident.as_ref(), toml_expr) { + match (ident.as_str(), toml_expr) { ("migration_groups", Expr::Array(array)) => { - for elem in array { + for elem in array.into_iter() { let path = root.join(elem.as_str()); let name_opt = || path.file_name()?.to_str(); let Some(name) = name_opt() else { @@ -204,8 +214,8 @@ pub(crate) fn is_migration_divergent( migration: &UserMigration, ) -> bool where - DBS: AsRef<[DatabaseTy]>, - S: AsRef, + DBS: Lease<[DatabaseTy]>, + S: Lease, { let version = migration.version(); let opt = binary_search_migration_by_version(version, db_migrations); @@ -273,12 +283,14 @@ fn migration_file_name_parts(s: &str) -> crate::Result<(String, i32)> { #[cfg(feature = "std")] #[inline] -fn migrations_from_dir(path: &Path) -> crate::Result<(MigrationGroupParts, Vec)> { +fn migrations_from_dir( + path: &Path, +) -> crate::Result<(MigrationGroupParts, alloc::vec::Vec)> { let path_str = opt_to_inv_mig!(|| path.file_name()?.to_str())?; let (mg_name, mg_version) = dir_name_parts(path_str)?; let migration_paths = read_dir(path)? .map(|entry_rslt| Ok(entry_rslt?.path())) - .collect::>>()?; + .collect::>>()?; Ok(((mg_name, mg_version), migration_paths)) } diff --git a/wtx/src/database/schema_manager/toml_parser.rs b/wtx/src/database/schema_manager/toml_parser.rs index c579ffd0..cd4fcdd7 100644 --- a/wtx/src/database/schema_manager/toml_parser.rs +++ b/wtx/src/database/schema_manager/toml_parser.rs @@ -1,12 +1,13 @@ //! Migration TOML parser -use crate::misc::str_split1; -use arrayvec::{ArrayString, ArrayVec}; +use crate::misc::{str_split1, ArrayString, ArrayVector}; +use alloc::string::String; +use arrayvec::ArrayVec; use std::io::{BufRead, BufReader, Read}; pub(crate) const EXPR_ARRAY_MAX_LEN: usize = 8; -pub(crate) type ExprArrayTy = ArrayVec; +pub(crate) type ExprArrayTy = ArrayVector; pub(crate) type ExprStringTy = ArrayString<128>; pub(crate) type IdentTy = ArrayString<64>; pub(crate) type RootParamsTy = ArrayVec<(IdentTy, Expr), 2>; @@ -26,7 +27,7 @@ where let mut br = BufReader::new(read); let mut is_in_array_context = None; let mut buffer = String::new(); - let mut root_params = ArrayVec::new(); + let mut root_params = ArrayVec::default(); macro_rules! clear_and_continue { () => { @@ -89,7 +90,7 @@ where #[inline] fn try_parse_expr_array(s: &str) -> crate::Result { - let mut array = ArrayVec::new(); + let mut array = ArrayVector::default(); if s.is_empty() { return Ok(array); } @@ -140,8 +141,10 @@ fn try_parse_and_push_toml_expr_string( #[cfg(test)] mod tests { - use crate::database::schema_manager::toml_parser::{toml, Expr}; - use arrayvec::ArrayVec; + use crate::{ + database::schema_manager::toml_parser::{toml, Expr, ExprArrayTy}, + misc::ArrayVector, + }; #[test] fn toml_parses_root_parameter_array_in_a_single_line() { @@ -158,14 +161,14 @@ mod tests { ( "foo".try_into().unwrap(), Expr::Array({ - let mut elems = ArrayVec::new(); - elems.push("1".try_into().unwrap()); - elems.push("2".try_into().unwrap()); + let mut elems = ArrayVector::default(); + elems.try_push("1".try_into().unwrap()).unwrap(); + elems.try_push("2".try_into().unwrap()).unwrap(); elems }) ) ); - assert_eq!(array[1], ("bar".try_into().unwrap(), Expr::Array(Default::default()))); + assert_eq!(array[1], ("bar".try_into().unwrap(), Expr::Array(ExprArrayTy::default()))); } #[test] @@ -185,10 +188,10 @@ mod tests { ( "foo".try_into().unwrap(), Expr::Array({ - let mut elems = ArrayVec::new(); - elems.push("1".try_into().unwrap()); - elems.push("2".try_into().unwrap()); - elems.push("3".try_into().unwrap()); + let mut elems = ArrayVector::default(); + elems.try_push("1".try_into().unwrap()).unwrap(); + elems.try_push("2".try_into().unwrap()).unwrap(); + elems.try_push("3".try_into().unwrap()).unwrap(); elems }) ) diff --git a/wtx/src/error.rs b/wtx/src/error.rs index ace26b5b..abf57aca 100644 --- a/wtx/src/error.rs +++ b/wtx/src/error.rs @@ -31,6 +31,7 @@ pub enum Error { #[cfg(feature = "arrayvec")] ArrayVec(arrayvec::CapacityError<()>), AtoiInvalidBytes, + CapacityOverflow, #[cfg(feature = "chrono")] ChronoParseError(chrono::ParseError), #[cfg(feature = "cl-aux")] @@ -53,6 +54,8 @@ pub enum Error { Glommio(Box>), #[cfg(feature = "httparse")] HttpParse(httparse::Error), + #[cfg(feature = "http2")] + Http2ErrorCode(crate::http2::ErrorCode), #[cfg(feature = "digest")] MacError(digest::MacError), #[cfg(feature = "miniserde")] @@ -61,8 +64,7 @@ pub enum Error { PostgresDbError(Box), #[cfg(feature = "protobuf")] Protobuf(protobuf::Error), - #[cfg(feature = "reqwest")] - Reqwest(reqwest::Error), + #[cfg(feature = "rkyv")] RkyvDer(&'static str), #[cfg(feature = "rkyv")] RkyvSer(Box), @@ -74,12 +76,18 @@ pub enum Error { SerdeYaml(serde_yaml::Error), #[cfg(feature = "simd-json")] SimdJson(Box), + #[cfg(feature = "smoltcp")] + SmoltcpTcpRecvError(smoltcp::socket::tcp::RecvError), + #[cfg(feature = "smoltcp")] + SmoltcpTcpSendError(smoltcp::socket::tcp::SendError), #[cfg(feature = "embedded-tls")] TlsError(embedded_tls::TlsError), #[cfg(feature = "tokio-rustls")] TokioRustLsError(Box), #[cfg(feature = "_tracing-subscriber")] TryInitError(tracing_subscriber::util::TryInitError), + #[cfg(feature = "std")] + TryLockError(std::sync::TryLockError<()>), #[cfg(feature = "x509-certificate")] X509CertificateError(Box), @@ -133,7 +141,7 @@ pub enum Error { // ***** Internal - Database client ***** // /// A "null" field received from the database was decoded as a non-nullable type or value. - AbsentFieldDataInDecoding, + MissingFieldDataInDecoding, /// Not-A-Number is not supported DecimalCanNotBeConvertedFromNaN, /// Postgres does not support large unsigned integers. For example, `u8` can only be stored @@ -261,6 +269,12 @@ pub enum Error { // ***** Internal - HTTP ***** // + /// The length of a header field must be within a threshold. + HeaderFieldIsTooLarge, + /// Received Request does not contain a method field + MissingRequestMethod, + /// Received Response does not contain a status code field + MissingResponseStatusCode, /// Unknown header name. UnknownHeaderName, @@ -276,11 +290,40 @@ pub enum Error { VeryLargeHeaderInteger, /// Size updates of dynamic table can't be placed after the first header InvalidDynTableSizeUpdate, - - // ***** Internal - PM ***** - // - /// Zero fixed pools are unsupported - StaticPoolMustHaveCapacityForAtLeastOneElement, + /// Length of a header name or value is limited to 127 bytes. + UnsupportedHeaderNameOrValueLen, + /// Received an Hpack index that does not adhere to the standard + UnexpectedHpackIdx, + /// Type is out of range or unsupported. + UnknownSettingFrameTy, + /// Settings frame identifier is not zero + UnexpectedSettingsIdentifier, + /// Counter-part did not return the correct bytes of a HTTP2 connection preface + NoPreface, + #[doc = concat!( + "The system does not support more than", + _max_continuation_frames!(), + " continuation frames." + )] + VeryLargeAmountOfContinuationFrames, + #[doc = concat!( + "The system does not support more than", + _max_frames_mismatches!(), + " fetches of frames with mismatches IDs or mismatches types" + )] + VeryLargeAmountOfFrameMismatches, + /// Frames can not be greater than + VeryLargeFrame, + /// Endpoint didn't send an ACK response + NoAckSettings, + /// Received a continuation or data frame instead of a header frame. + NotAInitialHeaderFrame, + /// Received a stream ID that doesn't exist locally + UnknownStreamId, + VeryLargeAmountOfBufferedFrames, + ExceedAmountOfRapidResets, + ExceedAmountOfActiveConcurrentStreams, + VeryLargeHeadersLen, // ***** Internal - WebSocket ***** // @@ -451,6 +494,14 @@ impl From for Error { } } +#[cfg(feature = "http2")] +impl From for Error { + #[inline] + fn from(from: crate::http2::ErrorCode) -> Self { + Self::Http2ErrorCode(from) + } +} + #[cfg(feature = "std")] impl From for Error { #[inline] @@ -509,14 +560,6 @@ impl From for Error { } } -#[cfg(feature = "reqwest")] -impl From for Error { - #[inline] - fn from(from: reqwest::Error) -> Self { - Self::Reqwest(from) - } -} - #[cfg(feature = "rkyv")] impl From<&'static str> for Error { #[inline] @@ -565,6 +608,22 @@ impl From for Error { } } +#[cfg(feature = "smoltcp")] +impl From for Error { + #[inline] + fn from(from: smoltcp::socket::tcp::RecvError) -> Self { + Self::SmoltcpTcpRecvError(from) + } +} + +#[cfg(feature = "smoltcp")] +impl From for Error { + #[inline] + fn from(from: smoltcp::socket::tcp::SendError) -> Self { + Self::SmoltcpTcpSendError(from) + } +} + #[cfg(feature = "embedded-tls")] impl From for Error { #[inline] @@ -603,6 +662,19 @@ impl From for Error { } } +#[cfg(feature = "std")] +impl From> for Error { + #[inline] + fn from(from: std::sync::TryLockError) -> Self { + Self::TryLockError(match from { + std::sync::TryLockError::Poisoned(_) => { + std::sync::TryLockError::Poisoned(std::sync::PoisonError::new(())) + } + std::sync::TryLockError::WouldBlock => std::sync::TryLockError::WouldBlock, + }) + } +} + #[cfg(feature = "x509-certificate")] impl From for Error { #[inline] diff --git a/wtx/src/http.rs b/wtx/src/http.rs index 6cbf6c79..ec13e75e 100644 --- a/wtx/src/http.rs +++ b/wtx/src/http.rs @@ -3,23 +3,40 @@ mod abstract_headers; mod expected_header; mod generic_header; +mod generic_request; +mod generic_response; mod header_name; mod headers; mod method; mod mime; +mod protocol; mod request; mod response; +mod response_data; +pub mod server; mod status_code; mod version; pub(crate) use abstract_headers::AbstractHeaders; pub use expected_header::ExpectedHeader; pub use generic_header::GenericHeader; +pub use generic_request::GenericRequest; +pub use generic_response::GenericResponse; pub use header_name::*; pub use headers::Headers; pub use method::Method; pub use mime::Mime; -pub use request::Request; -pub use response::Response; +pub use protocol::Protocol; +pub use request::{Request, RequestMut, RequestRef}; +pub use response::{Response, ResponseMut, ResponseRef}; +pub use response_data::ResponseData; pub use status_code::StatusCode; pub use version::Version; + +/// Maximum number of bytes for the name of a header. +pub const MAX_HEADER_NAME_LEN: usize = 128; +/// Maximum number of bytes for the value of a header. +pub const MAX_HEADER_VALUE_LEN: usize = 1024 + 256; + +pub(crate) type _HeaderNameBuffer = crate::misc::ArrayVector; +pub(crate) type _HeaderValueBuffer = crate::misc::ArrayVector; diff --git a/wtx/src/http/abstract_headers.rs b/wtx/src/http/abstract_headers.rs index ea9a5893..6be90980 100644 --- a/wtx/src/http/abstract_headers.rs +++ b/wtx/src/http/abstract_headers.rs @@ -1,419 +1,167 @@ -use crate::misc::{Usize, _unlikely_cb, _unlikely_elem}; -use alloc::collections::VecDeque; -use core::ops::Range; +use crate::misc::{Block, BlocksQueue, _unreachable}; +use core::fmt::{Debug, Formatter}; -const DFLT_MAX_BYTES: u32 = 4 * 1024; - -#[derive(Debug, Eq, PartialEq)] -pub(crate) struct AbstractHeader<'ah, M> { - pub(crate) misc: &'ah M, - pub(crate) name_bytes: &'ah [u8], - pub(crate) name_range: Range, - pub(crate) value_bytes: &'ah [u8], -} - -#[derive(Debug, Eq, PartialEq)] pub(crate) struct AbstractHeaders { - buffer: VecDeque, - elements_len: u32, - first_idx: u32, - max_bytes: u32, - metadata: VecDeque>, + bq: BlocksQueue>, + max_bytes: usize, } -impl AbstractHeaders { - pub(crate) fn with_capacity(len: u32) -> Self { - Self { - buffer: VecDeque::with_capacity(*Usize::from(len)), - elements_len: 0, - first_idx: 0, - max_bytes: DFLT_MAX_BYTES, - metadata: VecDeque::with_capacity(*Usize::from(len)), - } +impl AbstractHeaders +where + M: Copy, +{ + #[inline] + pub(crate) const fn new(max_bytes: usize) -> Self { + Self { bq: BlocksQueue::new(), max_bytes } + } + + #[inline] + pub(crate) fn with_capacity(bytes: usize, headers: usize, max_bytes: usize) -> Self { + Self { bq: BlocksQueue::with_capacity(bytes.min(max_bytes), headers), max_bytes } } - // Insertions are limited to u32 - #[allow(clippy::cast_possible_truncation, clippy::as_conversions)] - pub(crate) fn bytes_len(&self) -> u32 { - self.buffer.len() as u32 + #[inline] + pub(crate) fn bytes_len(&self) -> usize { + self.bq.elements_len() } + #[inline] pub(crate) fn clear(&mut self) { - let Self { buffer, elements_len, first_idx, max_bytes: _, metadata } = self; - buffer.clear(); - *elements_len = 0; - *first_idx = 0; - metadata.clear(); + self.bq.clear(); } - pub(crate) fn elements_len(&self) -> u32 { - self.elements_len + #[inline] + pub(crate) fn headers_len(&self) -> usize { + self.bq.blocks_len() } - pub(crate) fn get_by_idx(&self, idx: usize) -> Option> { - let Some(Metadata { is_activated, name_begin_idx, misc, sep_idx, value_end_idx }) = - self.metadata.get(idx) - else { - return _unlikely_elem(None); - }; - if !is_activated { - return _unlikely_elem(None); - } - let Some(name_bytes) = self.buffer.as_slices().0.get( - *Usize::from(name_begin_idx.wrapping_sub(self.first_idx)) - ..*Usize::from(sep_idx.wrapping_sub(self.first_idx)), - ) else { - return _unlikely_elem(None); - }; - let Some(value_bytes) = self.buffer.as_slices().0.get( - *Usize::from(sep_idx.wrapping_sub(self.first_idx)) - ..*Usize::from(value_end_idx.wrapping_sub(self.first_idx)), - ) else { - return _unlikely_elem(None); - }; - Some(AbstractHeader { misc, name_bytes, name_range: *name_begin_idx..*sep_idx, value_bytes }) + #[inline] + pub(crate) fn first(&self) -> Option> { + self.bq.first().as_ref().map(Self::map) } - pub(crate) fn get_by_name(&self, name: &[u8]) -> Option> { - self.iter().find(|elem| (name == elem.name_bytes)) + #[inline] + pub(crate) fn get_by_idx(&self, idx: usize) -> Option> { + self.bq.get(idx).as_ref().map(Self::map) } + #[inline] pub(crate) fn iter(&self) -> impl Iterator> { - self.metadata.iter().filter_map( - |Metadata { is_activated, name_begin_idx, misc, sep_idx, value_end_idx }| { - if !is_activated { - return None; - } - let Some(name_bytes) = self.buffer.as_slices().0.get( - *Usize::from(name_begin_idx.wrapping_sub(self.first_idx)) - ..*Usize::from(sep_idx.wrapping_sub(self.first_idx)), - ) else { - return _unlikely_elem(None); - }; - let Some(value_bytes) = self.buffer.as_slices().0.get( - *Usize::from(sep_idx.wrapping_sub(self.first_idx)) - ..*Usize::from(value_end_idx.wrapping_sub(self.first_idx)), - ) else { - return _unlikely_elem(None); - }; - Some(AbstractHeader { - misc, - name_bytes, - name_range: *name_begin_idx..*sep_idx, - value_bytes, - }) - }, - ) + self.bq.iter().map(|el| Self::map(&el)) } - pub(crate) fn max_bytes(&self) -> u32 { - self.max_bytes + #[inline] + pub(crate) fn last(&self) -> Option> { + self.bq.last().as_ref().map(Self::map) } - pub(crate) fn normalize_indcs(&mut self) { - let mut iter = self.metadata.as_mut_slices().0.iter_mut(); - let first = if let Some(elem) = iter.next() { - let first = elem.name_begin_idx; - elem.name_begin_idx = elem.name_begin_idx.wrapping_sub(first); - elem.sep_idx = elem.sep_idx.wrapping_sub(first); - elem.value_end_idx = elem.value_end_idx.wrapping_sub(first); - first - } else { - return; - }; - for elem in iter { - elem.name_begin_idx = elem.name_begin_idx.wrapping_sub(first); - elem.sep_idx = elem.sep_idx.wrapping_sub(first); - elem.value_end_idx = elem.value_end_idx.wrapping_sub(first); - } + #[inline] + pub(crate) fn max_bytes(&self) -> usize { + self.max_bytes } - pub(crate) fn pop_back(&mut self) { - let Some(Metadata { name_begin_idx, .. }) = self.metadata.pop_back() else { - return; - }; - self.buffer.truncate(*Usize::from(name_begin_idx)); - self.elements_len = self.elements_len.wrapping_sub(1); + #[inline] + pub(crate) fn pop_back(&mut self) -> Option<(Metadata, &mut [u8])> { + self.bq.pop_back() } - pub(crate) fn pop_front(&mut self) { - let Some(Metadata { value_end_idx, .. }) = self.metadata.pop_front() else { - return; - }; - for _ in 0..value_end_idx.wrapping_sub(self.first_idx) { - let _ = self.buffer.pop_front(); - } - self.elements_len = self.elements_len.wrapping_sub(1); - self.first_idx = value_end_idx; + #[inline] + pub(crate) fn pop_front(&mut self) -> Option<(Metadata, &mut [u8])> { + self.bq.pop_front() } - pub(crate) fn push(&mut self, misc: M, name: &[u8], value: &[u8]) { + #[inline] + pub(crate) fn push_front( + &mut self, + misc: M, + name: &[u8], + value: &[u8], + is_sensitive: bool, + cb: impl FnMut(M, &mut [u8]), + ) { let local_len = name.len().wrapping_add(value.len()); - if local_len > *Usize::from(self.max_bytes) { + if local_len > self.max_bytes { self.clear(); return; } - while Usize::from(self.bytes_len()).wrapping_add(local_len) > *Usize::from(self.max_bytes) { - self.pop_front(); - } - if Usize::from(self.first_idx).overflowing_add(local_len).1 { - _unlikely_cb(|| self.normalize_indcs()); - } - let name_begin_idx = self.bytes_len(); - self.buffer.extend(name); - let sep_idx = self.bytes_len(); - self.buffer.extend(value); - let value_begin_idx = self.bytes_len(); - self.push_metadata(misc, name_begin_idx, sep_idx, value_begin_idx); + self.remove_until_max_bytes(local_len, cb); + self.bq.push_front_within_cap( + [name, value], + Metadata { is_active: true, is_sensitive, misc, name_len: name.len() }, + ); } - pub(crate) fn push_metadata( - &mut self, - misc: M, - name_begin_idx: u32, - sep_idx: u32, - value_end_idx: u32, - ) { - self.elements_len = self.elements_len.wrapping_add(1); - self.metadata.push_back(Metadata { - is_activated: true, - misc, - name_begin_idx, - sep_idx, - value_end_idx, - }); + #[inline] + pub(crate) fn remove_by_idx(&mut self, idx: usize) -> Option<()> { + let elem = self.bq.get_mut(idx)?; + elem.misc.is_active = false; + Some(()) } - pub(crate) fn remove(&mut self, names: &[&[u8]]) { - if names.is_empty() { - return; - } - let mut names_start = 0; - for metadata in &mut self.metadata { - let Metadata { is_activated, name_begin_idx, misc: _, sep_idx, value_end_idx: _ } = metadata; - if !*is_activated { - continue; - } - let tuple = ( - self.buffer.as_slices().0.get(*Usize::from(*name_begin_idx)..*Usize::from(*sep_idx)), - names.get(names_start..), - ); - let (Some(name_bytes), Some(slice)) = tuple else { - break; - }; - if slice.contains(&name_bytes) { - *is_activated = false; - names_start = names_start.wrapping_add(1); - self.elements_len = self.elements_len.wrapping_sub(1); - } - } + #[inline] + pub(crate) fn reserve(&mut self, bytes: usize, headers: usize) { + self.bq.reserve(headers, bytes.min(self.max_bytes)); } - pub(crate) fn set_max_bytes(&mut self, max_bytes: u32) { + #[inline] + pub(crate) fn set_max_bytes(&mut self, max_bytes: usize, cb: impl FnMut(M, &mut [u8])) { self.max_bytes = max_bytes; - while self.bytes_len() > self.max_bytes { - self.pop_front(); - } + self.remove_until_max_bytes(0, cb); } -} -impl Default for AbstractHeaders { #[inline] - fn default() -> Self { - Self { - buffer: VecDeque::new(), - elements_len: 0, - first_idx: 0, - max_bytes: DFLT_MAX_BYTES, - metadata: VecDeque::new(), + fn map<'this>(block: &Block<&'this [u8], &'this Metadata>) -> AbstractHeader<'this, M> { + AbstractHeader { + is_sensitive: block.misc.is_sensitive, + misc: &block.misc.misc, + name_bytes: if let Some(elem) = block.data.get(..block.misc.name_len) { + elem + } else { + _unreachable() + }, + value_bytes: if let Some(elem) = block.data.get(block.misc.name_len..) { + elem + } else { + _unreachable() + }, } } -} -#[derive(Debug, Eq, PartialEq)] -pub(crate) struct Metadata { - is_activated: bool, - misc: M, - name_begin_idx: u32, - sep_idx: u32, - value_end_idx: u32, + #[inline] + fn remove_until_max_bytes(&mut self, additional: usize, mut cb: impl FnMut(M, &mut [u8])) { + while self.bytes_len().wrapping_add(additional) > self.max_bytes { + if let Some(elem) = self.pop_back() { + cb(elem.0.misc, elem.1); + } + } + } } -#[cfg(test)] -mod tests { - use crate::http::{abstract_headers::AbstractHeader, AbstractHeaders}; - - #[test] - fn elements_are_added_and_cleared() { - let mut header = AbstractHeaders::default(); - header.push(0, b"abc", b"def"); - assert_eq!( - header.get_by_name(b"abc"), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!( - header.iter().next(), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!(header.elements_len(), 1); - assert_eq!(header.bytes_len(), 6); - header.clear(); - assert_eq!(header.get_by_name(b"abc"), None); - assert_eq!(header.iter().next(), None); - assert_eq!(header.elements_len(), 0); - assert_eq!(header.bytes_len(), 0); +impl Debug for AbstractHeaders +where + M: Copy + Debug, +{ + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { + f.debug_struct("AbstractHeaders") + .field("max_bytes", &self.max_bytes) + .field("bq", &self.bq) + .finish() } +} - #[test] - fn elements_are_added_and_removed() { - let mut header = AbstractHeaders::default(); - - header.push(0, b"abc", b"def"); - assert_eq!( - header.get_by_name(b"abc"), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!(header.get_by_name(b"ghi"), None); - assert_eq!( - header.iter().nth(0), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!(header.iter().nth(1), None); - assert_eq!(header.iter().nth(2), None); - assert_eq!(header.elements_len(), 1); - assert_eq!(header.bytes_len(), 6); - header.push(1, b"ghi", b"jkl"); - assert_eq!( - header.get_by_name(b"abc"), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!( - header.get_by_name(b"ghi"), - Some(AbstractHeader { - misc: &1, - name_range: 6..9, - name_bytes: "ghi".as_bytes(), - value_bytes: "jkl".as_bytes() - }) - ); - assert_eq!( - header.iter().nth(0), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!( - header.iter().nth(1), - Some(AbstractHeader { - misc: &1, - name_range: 6..9, - name_bytes: "ghi".as_bytes(), - value_bytes: "jkl".as_bytes() - }) - ); - assert_eq!(header.iter().nth(2), None); - assert_eq!(header.elements_len(), 2); - assert_eq!(header.bytes_len(), 12); +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct AbstractHeader<'ah, M> { + pub(crate) is_sensitive: bool, + pub(crate) misc: &'ah M, + pub(crate) name_bytes: &'ah [u8], + pub(crate) value_bytes: &'ah [u8], +} - header.remove(&[b"123"]); - assert_eq!( - header.get_by_name(b"abc"), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!( - header.get_by_name(b"ghi"), - Some(AbstractHeader { - misc: &1, - name_range: 6..9, - name_bytes: "ghi".as_bytes(), - value_bytes: "jkl".as_bytes() - }) - ); - assert_eq!( - header.iter().nth(0), - Some(AbstractHeader { - misc: &0, - name_range: 0..3, - name_bytes: "abc".as_bytes(), - value_bytes: "def".as_bytes() - }) - ); - assert_eq!( - header.iter().nth(1), - Some(AbstractHeader { - misc: &1, - name_range: 6..9, - name_bytes: "ghi".as_bytes(), - value_bytes: "jkl".as_bytes() - }) - ); - assert_eq!(header.iter().nth(2), None); - assert_eq!(header.elements_len(), 2); - assert_eq!(header.bytes_len(), 12); - header.remove(&[b"abc"]); - assert_eq!(header.get_by_name(b"abc"), None); - assert_eq!( - header.get_by_name(b"ghi"), - Some(AbstractHeader { - misc: &1, - name_range: 6..9, - name_bytes: "ghi".as_bytes(), - value_bytes: "jkl".as_bytes() - }) - ); - assert_eq!( - header.iter().nth(0), - Some(AbstractHeader { - misc: &1, - name_range: 6..9, - name_bytes: "ghi".as_bytes(), - value_bytes: "jkl".as_bytes() - }) - ); - assert_eq!(header.iter().nth(1), None); - assert_eq!(header.iter().nth(2), None); - assert_eq!(header.elements_len(), 1); - assert_eq!(header.bytes_len(), 12); - header.remove(&[b"ghi"]); - assert_eq!(header.get_by_name(b"abc"), None); - assert_eq!(header.get_by_name(b"ghi"), None); - assert_eq!(header.iter().nth(0), None); - assert_eq!(header.iter().nth(1), None); - assert_eq!(header.iter().nth(2), None); - assert_eq!(header.elements_len(), 0); - assert_eq!(header.bytes_len(), 12); - } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct Metadata { + is_active: bool, + is_sensitive: bool, + misc: M, + name_len: usize, } diff --git a/wtx/src/http/client.rs b/wtx/src/http/client.rs new file mode 100644 index 00000000..b44041e9 --- /dev/null +++ b/wtx/src/http/client.rs @@ -0,0 +1,64 @@ +use crate::{ + http::{Headers, Method, Request, Response}, + http2::{Http2, Http2Buffer, Http2Data, ReqResBuffer}, + misc::{Lock, LockGuard, RefCounter, SingleTypeStorage, Stream, Uri}, + pool::{Pool, SimpleRM}, +}; +use core::marker::PhantomData; + +/// A pool of different HTTP connections lazily constructed from different URIs. +#[derive(Debug)] +pub struct Client { + phantom: PhantomData, + pool: P, +} + +impl Client +where + P: Pool< + ResourceManager = SimpleRM< + crate::Error, + (), + (ReqResBuffer, Http2, S, SDC, true>), + >, + >, + S: Stream, + SDC: RefCounter, + SDC::Item: Lock, S, true>>, +{ + #[cfg(feature = "tokio-rustls")] + #[inline] + pub fn tokio_rustls() -> Self { + todo!() + } + + /// Sends a GET request to the specified `url`. + #[inline] + pub async fn get<'this>( + &'this self, + uri: &str, + ) -> crate::Result< + Response< + as LockGuard< + 'this, + (ReqResBuffer, Http2, S, SDC, true>), + >>::Mapped, + >, + > { + let mut guard = self.pool.get(&(), &()).await?; + let (buffer, http2) = &mut *guard; + let stream = http2.stream().await.unwrap(); + stream + .send_req(Request::http2(&[], &Headers::new(0), Method::Get, Uri::new(uri))) + .await + .unwrap(); + let res = stream.recv_res(buffer).await?; + let status = res.status_code; + let version = res.version; + Ok(Response { + data:

::Guard::map(guard, |el| &mut el.0), + status_code: status, + version, + }) + } +} diff --git a/wtx/src/http/generic_request.rs b/wtx/src/http/generic_request.rs new file mode 100644 index 00000000..b0d4465b --- /dev/null +++ b/wtx/src/http/generic_request.rs @@ -0,0 +1,30 @@ +use crate::http::Version; + +/// HTTP request. +pub trait GenericRequest { + /// Method + fn method(&self) -> &[u8]; + + /// Path + fn path(&self) -> &[u8]; + + /// Version + fn version(&self) -> Version; +} + +impl GenericRequest for () { + #[inline] + fn method(&self) -> &[u8] { + &[] + } + + #[inline] + fn path(&self) -> &[u8] { + &[] + } + + #[inline] + fn version(&self) -> Version { + Version::default() + } +} diff --git a/wtx/src/http/generic_response.rs b/wtx/src/http/generic_response.rs new file mode 100644 index 00000000..107e2169 --- /dev/null +++ b/wtx/src/http/generic_response.rs @@ -0,0 +1,22 @@ +use crate::http::Version; + +/// HTTP response +pub trait GenericResponse { + /// Code + fn code(&self) -> u16; + + /// Version + fn version(&self) -> Version; +} + +impl GenericResponse for () { + #[inline] + fn code(&self) -> u16 { + 0 + } + + #[inline] + fn version(&self) -> Version { + Version::default() + } +} diff --git a/wtx/src/http/header_name.rs b/wtx/src/http/header_name.rs index 88d339f4..5f4ce94c 100644 --- a/wtx/src/http/header_name.rs +++ b/wtx/src/http/header_name.rs @@ -1,3 +1,5 @@ +use crate::misc::Lease; + macro_rules! create_statics { ( $( @@ -5,187 +7,87 @@ macro_rules! create_statics { $name:ident = $value:literal; )* ) => { - $( - $(#[$mac])* - pub const $name: HeaderNameStaticStr = HeaderNameStaticStr::new($value); - )* - - impl<'hn> TryFrom<&'hn [u8]> for HeaderName<&'hn [u8]> { - type Error = crate::Error; - - #[inline] - fn try_from(from: &'hn [u8]) -> crate::Result { - Ok(match from { - $( - elem if elem == $value.as_bytes() => $name.into(), - )* - _ => Self(from) - }) - } - } - - impl<'hn> TryFrom<&'hn str> for HeaderName<&'hn str> { - type Error = crate::Error; - - #[inline] - fn try_from(from: &'hn str) -> crate::Result { - Ok(match from { - $( - $value => $name, - )* - _ => Self(from) - }) - } - } - - impl<'hn> From> for HeaderName<&'hn [u8]> { - #[inline] - fn from(from: HeaderName<&'hn str>) -> Self { - Self::new(from.0.as_bytes()) - } + impl HeaderNameStaticBytes { + $( + $(#[$mac])* + #[doc = stringify!($name)] + pub const $name: Self = Self::new($value); + )* } }; } create_statics! { - /// accept - ACCEPT = "accept"; - /// accept-charset - ACCEPT_CHARSET = "accept-charset"; - /// accept-encoding - ACCEPT_ENCODING = "accept-encoding"; - /// accept-language - ACCEPT_LANGUAGE = "accept-language"; - /// accept-ranges - ACCEPT_RANGES = "accept-ranges"; - /// access-control-allow-credentials - ACCESS_CONTROL_ALLOW_CREDENTIALS = "access-control-allow-credentials"; - /// access-control-allow-headers - ACCESS_CONTROL_ALLOW_HEADERS = "access-control-allow-headers"; - /// access-control-allow-methods - ACCESS_CONTROL_ALLOW_METHODS = "access-control-allow-methods"; - /// access-control-allow-origin - ACCESS_CONTROL_ALLOW_ORIGIN = "access-control-allow-origin"; - /// access-control-expose-headers - ACCESS_CONTROL_EXPOSE_HEADERS = "access-control-expose-headers"; - /// access-control-max-age - ACCESS_CONTROL_MAX_AGE = "access-control-max-age"; - /// access-control-request-headers - ACCESS_CONTROL_REQUEST_HEADERS = "access-control-request-headers"; - /// access-control-request-method - ACCESS_CONTROL_REQUEST_METHOD = "access-control-request-method"; - /// age - AGE = "age"; - /// allow - ALLOW = "allow"; - /// authorization - AUTHORIZATION = "authorization"; - /// cache-control - CACHE_CONTROL = "cache-control"; - /// clear-site-data - CLEAR_SITE_DATA = "clear-site-data"; - /// Connection - CONNECTION = "connection"; - /// content-disposition - CONTENT_DISPOSITION = "content-disposition"; - /// content-encoding - CONTENT_ENCODING = "content-encoding"; - /// content-language - CONTENT_LANGUAGE = "content-language"; - /// content-length - CONTENT_LENGTH = "content-length"; - /// content-location - CONTENT_LOCATION = "content-location"; - /// content-md5 - CONTENT_MD5 = "content-md5"; - /// content-range - CONTENT_RANGE = "content-range"; - /// content-type - CONTENT_TYPE = "content-type"; - /// cookie - COOKIE = "cookie"; - /// date - DATE = "date"; - /// eTag - ETAG = "etag"; - /// expect - EXPECT = "expect"; - /// expires - EXPIRES = "expires"; - /// forwarded - FORWARDED = "forwarded"; - /// from - FROM = "from"; - /// host - HOST = "host"; - /// if-match - IF_MATCH = "if-match"; - /// if-modified-since - IF_MODIFIED_SINCE = "if-modified-since"; - /// if-none-match - IF_NONE_MATCH = "if-none-match"; - /// if-range - IF_RANGE = "if-range"; - /// if-unmodified-since - IF_UNMODIFIED_SINCE = "if-unmodified-since"; - /// last-modified - LAST_MODIFIED = "last-modified"; - /// link - LINK = "link"; - /// location - LOCATION = "location"; - /// max-forwards - MAX_FORWARDS = "max-forwards"; - /// origin - ORIGIN = "origin"; - /// pragma - PRAGMA = "pragma"; - /// proxy-authenticate - PROXY_AUTHENTICATE = "proxy-authenticate"; - /// proxy-authorization - PROXY_AUTHORIZATION = "proxy-authorization"; - /// proxy-connection - PROXY_CONNECTION = "proxy-connection"; - /// range - RANGE = "range"; - /// referer - REFERER = "referer"; - /// refresh - REFRESH = "refresh"; - /// retry-after - RETRY_AFTER = "retry-after"; - /// server - SERVER_TIMING = "server-timing"; - /// server - SERVER = "server"; - /// set-cookie - SET_COOKIE = "set-cookie"; - /// sourcemap - SOURCE_MAP = "sourcemap"; - /// strict-transport-security - STRICT_TRANSPORT_SECURITY = "strict-transport-security"; - /// te - TE = "te"; - /// timing-allow-origin - TIMING_ALLOW_ORIGIN = "timing-allow-origin"; - /// traceparent - TRACEPARENT = "traceparent"; - /// trailer - TRAILER = "trailer"; - /// transfer-encoding - TRANSFER_ENCODING = "transfer-encoding"; - /// upgrade - UPGRADE = "upgrade"; - /// user-Agent - USER_AGENT = "user-agent"; - /// vary - VARY = "vary"; - /// via - VIA = "via"; - /// warning - WARNING = "warning"; - /// www-authenticate - WWW_AUTHENTICATE = "www-authenticate"; + ACCEPT = b"accept"; + ACCEPT_CHARSET = b"accept-charset"; + ACCEPT_ENCODING = b"accept-encoding"; + ACCEPT_LANGUAGE = b"accept-language"; + ACCEPT_RANGES = b"accept-ranges"; + ACCESS_CONTROL_ALLOW_CREDENTIALS = b"access-control-allow-credentials"; + ACCESS_CONTROL_ALLOW_HEADERS = b"access-control-allow-headers"; + ACCESS_CONTROL_ALLOW_METHODS = b"access-control-allow-methods"; + ACCESS_CONTROL_ALLOW_ORIGIN = b"access-control-allow-origin"; + ACCESS_CONTROL_EXPOSE_HEADERS = b"access-control-expose-headers"; + ACCESS_CONTROL_MAX_AGE = b"access-control-max-age"; + ACCESS_CONTROL_REQUEST_HEADERS = b"access-control-request-headers"; + ACCESS_CONTROL_REQUEST_METHOD = b"access-control-request-method"; + AGE = b"age"; + ALLOW = b"allow"; + AUTHORIZATION = b"authorization"; + CACHE_CONTROL = b"cache-control"; + CLEAR_SITE_DATA = b"clear-site-data"; + CONNECTION = b"connection"; + CONTENT_DISPOSITION = b"content-disposition"; + CONTENT_ENCODING = b"content-encoding"; + CONTENT_LANGUAGE = b"content-language"; + CONTENT_LENGTH = b"content-length"; + CONTENT_LOCATION = b"content-location"; + CONTENT_MD5 = b"content-md5"; + CONTENT_RANGE = b"content-range"; + CONTENT_TYPE = b"content-type"; + COOKIE = b"cookie"; + DATE = b"date"; + ETAG = b"etag"; + EXPECT = b"expect"; + EXPIRES = b"expires"; + FORWARDED = b"forwarded"; + FROM = b"from"; + HOST = b"host"; + IF_MATCH = b"if-match"; + IF_MODIFIED_SINCE = b"if-modified-since"; + IF_NONE_MATCH = b"if-none-match"; + IF_RANGE = b"if-range"; + IF_UNMODIFIED_SINCE = b"if-unmodified-since"; + KEEP_ALIVE = b"keep-alive"; + LAST_MODIFIED = b"last-modified"; + LINK = b"link"; + LOCATION = b"location"; + MAX_FORWARDS = b"max-forwards"; + ORIGIN = b"origin"; + PRAGMA = b"pragma"; + PROXY_AUTHENTICATE = b"proxy-authenticate"; + PROXY_AUTHORIZATION = b"proxy-authorization"; + PROXY_CONNECTION = b"proxy-connection"; + RANGE = b"range"; + REFERER = b"referer"; + REFRESH = b"refresh"; + RETRY_AFTER = b"retry-after"; + SERVER_TIMING = b"server-timing"; + SERVER = b"server"; + SET_COOKIE = b"set-cookie"; + SOURCE_MAP = b"sourcemap"; + STRICT_TRANSPORT_SECURITY = b"strict-transport-security"; + TE = b"te"; + TIMING_ALLOW_ORIGIN = b"timing-allow-origin"; + TRACEPARENT = b"traceparent"; + TRAILER = b"trailer"; + TRANSFER_ENCODING = b"transfer-encoding"; + UPGRADE = b"upgrade"; + USER_AGENT = b"user-agent"; + VARY = b"vary"; + VIA = b"via"; + WARNING = b"warning"; + WWW_AUTHENTICATE = b"www-authenticate"; } /// [HeaderName] composed by static bytes. @@ -199,7 +101,7 @@ pub struct HeaderName(S); impl HeaderName where - S: AsRef<[u8]>, + S: Lease<[u8]>, { /// Instance from a generic type content. #[inline] @@ -210,7 +112,7 @@ where /// Generic type content in bytes form #[inline] pub fn bytes(&self) -> &[u8] { - self.0.as_ref() + self.0.lease() } /// Generic type content. @@ -219,3 +121,10 @@ where &self.0 } } + +impl<'hn> From> for HeaderName<&'hn [u8]> { + #[inline] + fn from(from: HeaderName<&'hn str>) -> Self { + Self::new(from.0.as_bytes()) + } +} diff --git a/wtx/src/http/headers.rs b/wtx/src/http/headers.rs index b9d30bb6..4813de51 100644 --- a/wtx/src/http/headers.rs +++ b/wtx/src/http/headers.rs @@ -1,21 +1,32 @@ -use crate::http::AbstractHeaders; +use crate::{ + http::{abstract_headers::AbstractHeader, AbstractHeaders}, + misc::{Lease, LeaseMut}, +}; /// List of pairs sent and received on every request. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Headers { ab: AbstractHeaders<()>, } impl Headers { + /// Empty instance + #[inline] + pub const fn new(max_bytes: usize) -> Self { + Self { ab: AbstractHeaders::new(max_bytes) } + } + /// Pre-allocates bytes according to the number of passed elements. + /// + /// Bytes are capped according to the specified `max_bytes`. #[inline] - pub fn with_capacity(len: u32) -> Self { - Self { ab: AbstractHeaders::with_capacity(len) } + pub fn with_capacity(bytes: usize, headers: usize, max_bytes: usize) -> Self { + Self { ab: AbstractHeaders::with_capacity(bytes, headers, max_bytes) } } /// The amount of bytes used by all of the headers #[inline] - pub fn bytes_len(&self) -> u32 { + pub fn bytes_len(&self) -> usize { self.ab.bytes_len() } @@ -27,65 +38,102 @@ impl Headers { /// The number of headers #[inline] - pub fn elements_len(&self) -> u32 { - self.ab.elements_len() + pub fn elements_len(&self) -> usize { + self.ab.headers_len() } - /// Returns the header's pair referenced by its index, if any. + /// Returns the first header, if any. #[inline] - pub fn get_by_idx(&self, idx: usize) -> Option<(&[u8], &[u8])> { - self.ab.get_by_idx(idx).map(|el| (el.name_bytes, el.value_bytes)) + pub fn first(&self) -> Option<(&[u8], &[u8], bool)> { + self.ab.first().map(Self::map) } - /// Returns the header value of the **first** corresponding header `name` key, if any. + /// Returns the header's pair referenced by its index, if any. #[inline] - pub fn get_by_name(&self, name: &[u8]) -> Option<&[u8]> { - self.ab.get_by_name(name).map(|el| el.value_bytes) + pub fn get_by_idx(&self, idx: usize) -> Option<(&[u8], &[u8], bool)> { + self.ab.get_by_idx(idx).map(Self::map) } /// Retrieves all stored pairs. #[inline] - pub fn iter(&self) -> impl Iterator { - self.ab.iter().map(|el| (el.name_bytes, el.value_bytes)) + pub fn iter(&self) -> impl Iterator { + self.ab.iter().map(Self::map) + } + + /// Pushes a new pair of `name` and `value` at the end of the internal buffer. + /// + /// If the sum of `name` and `value` is greater than the maximum number of bytes, then the first + /// inserted entries will be deleted accordantly. + #[inline] + pub fn last(&self) -> Option<(&[u8], &[u8], bool)> { + self.ab.last().map(Self::map) } /// The maximum allowed number of bytes. #[inline] - pub fn max_bytes(&self) -> u32 { + pub fn max_bytes(&self) -> usize { self.ab.max_bytes() } /// Removes the last element. #[inline] pub fn pop_back(&mut self) { - self.ab.pop_back(); + let _ = self.ab.pop_back(); } /// Removes the first element. #[inline] pub fn pop_front(&mut self) { - self.ab.pop_front(); + let _ = self.ab.pop_front(); } - /// Pushes a new pair of `name` and `value` at the end of the internal buffer. + /// Pushes a new pair of `name` and `value` at the beginning of the internal buffer. /// /// If the sum of `name` and `value` is greater than the maximum number of bytes, then the first /// inserted entries will be deleted accordantly. #[inline] - pub fn push(&mut self, name: &[u8], value: &[u8]) { - self.ab.push((), name, value); + pub fn push_front(&mut self, name: &[u8], value: &[u8], is_sensitive: bool) { + self.ab.push_front((), name, value, is_sensitive, |_, _| {}); } - /// Removes all pairs referenced by the `names` parameter. + /// Removes all a pair referenced by `idx`. #[inline] - pub fn remove(&mut self, names: &[&[u8]]) { - self.ab.remove(names); + pub fn remove_by_idx(&mut self, idx: usize) -> bool { + self.ab.remove_by_idx(idx).is_some() + } + + /// Reserves capacity for at least `bytes` more bytes to be inserted. The same thing is applied + /// to the number of headers. + /// + /// Bytes are capped according to the specified `max_bytes`. + #[inline] + pub fn reserve(&mut self, bytes: usize, headers: usize) { + self.ab.reserve(bytes, headers); } /// If `max_bytes` is lesser than the current number of bytes, then the first inserted entries /// will be deleted accordantly. #[inline] - pub fn set_max_bytes(&mut self, max_bytes: u32) { - self.ab.set_max_bytes(max_bytes); + pub fn set_max_bytes(&mut self, max_bytes: usize) { + self.ab.set_max_bytes(max_bytes, |_, _| {}); + } + + #[inline] + fn map(elem: AbstractHeader<'_, ()>) -> (&[u8], &[u8], bool) { + (elem.name_bytes, elem.value_bytes, elem.is_sensitive) + } +} + +impl Lease for Headers { + #[inline] + fn lease(&self) -> &Headers { + self + } +} + +impl LeaseMut for Headers { + #[inline] + fn lease_mut(&mut self) -> &mut Headers { + self } } diff --git a/wtx/src/http/method.rs b/wtx/src/http/method.rs index 0f190b12..549f893e 100644 --- a/wtx/src/http/method.rs +++ b/wtx/src/http/method.rs @@ -3,23 +3,23 @@ create_enum! { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Method { /// Connect - Connect = (0, "connect"), + Connect = (0, "CONNECT" | "connect"), /// Delete - Delete = (1, "delete"), + Delete = (1, "DELETE" | "delete"), /// Get - Get = (2, "get"), + Get = (2, "GET" | "get"), /// Head - Head = (3, "head"), + Head = (3, "HEAD" | "head"), /// Options - Options = (4, "options"), + Options = (4, "OPTIONS" | "options"), /// Patch - Patch = (5, "patch"), + Patch = (5, "PATCH" | "path"), /// Post - Post = (6, "post"), + Post = (6, "POST" | "post"), /// Put - Put = (7, "put"), + Put = (7, "PUT" | "put"), /// Trace - Trace = (8, "trace"), + Trace = (8, "TRACE" | "trace"), } } @@ -35,7 +35,7 @@ mod serde { D: Deserializer<'de>, { let s = <&str>::deserialize(deserializer)?; - Self::try_from(s).map_err(|err| de::Error::custom(err)) + Self::try_from(s).map_err(de::Error::custom) } } @@ -45,7 +45,7 @@ mod serde { where S: Serializer, { - serializer.serialize_str(self.strings().custom) + serializer.serialize_str(self.strings().custom[0]) } } } diff --git a/wtx/src/http/protocol.rs b/wtx/src/http/protocol.rs new file mode 100644 index 00000000..2e3b2808 --- /dev/null +++ b/wtx/src/http/protocol.rs @@ -0,0 +1,12 @@ +create_enum! { + /// How endpoints should communicate, which is similar but not equal to an URI scheme. + #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] + pub enum Protocol { + /// Http + Http = (0, "http"), + /// Https + Https = (1, "https"), + /// WebSocket + WebSocket = (2, "websocket") + } +} diff --git a/wtx/src/http/request.rs b/wtx/src/http/request.rs index e4555acb..4996d745 100644 --- a/wtx/src/http/request.rs +++ b/wtx/src/http/request.rs @@ -1,30 +1,50 @@ -use crate::http::Version; +use crate::{ + http::{Headers, Method, Version}, + misc::{Lease, Uri}, +}; -/// HTTP request. -pub trait Request { - /// Method - fn method(&self) -> &[u8]; +/// Shortcut for mutable referenced elements. +pub type RequestMut<'data, 'headers, 'uri, D> = + Request<&'data mut D, &'headers mut Headers, &'headers str>; +/// Shortcut for referenced elements. +pub type RequestRef<'data, 'headers, 'uri, D> = Request<&'data D, &'headers Headers, &'headers str>; - /// Path - fn path(&self) -> &[u8]; - - /// Version - fn version(&self) -> Version; +/// An HTTP request received by a server or to be sent by a client. +#[derive(Debug)] +pub struct Request { + /// The payload of the request, which can be nothing. + pub data: D, + /// See [Headers]. + pub headers: H, + /// See [Method]. + pub method: Method, + /// See [Uri]. + pub uri: Uri, + /// See [Version]. + pub version: Version, } -impl Request for () { - #[inline] - fn method(&self) -> &[u8] { - &[] - } - +impl Request +where + D: Lease<[u8]>, + H: Lease, + U: Lease, +{ + /// Constructor that defaults to an HTTP/2 version. #[inline] - fn path(&self) -> &[u8] { - &[] + pub fn http2(data: D, headers: H, method: Method, uri: Uri) -> Self { + Self { data, headers, method, uri, version: Version::Http2 } } + /// See [RequestRef]. #[inline] - fn version(&self) -> Version { - <_>::default() + pub fn to_ref(&self) -> RequestRef<'_, '_, '_, D> { + RequestRef { + data: &self.data, + headers: self.headers.lease(), + method: self.method, + uri: self.uri.to_ref(), + version: self.version, + } } } diff --git a/wtx/src/http/response.rs b/wtx/src/http/response.rs index 5022c2ea..0654fe22 100644 --- a/wtx/src/http/response.rs +++ b/wtx/src/http/response.rs @@ -1,22 +1,46 @@ -use crate::http::Version; +use crate::http::{Headers, ResponseData, StatusCode, Version}; -/// HTTP response -pub trait Response { - /// Code - fn code(&self) -> u16; +/// Shortcut for mutable referenced data. +pub type ResponseMut<'data, D> = Response<&'data mut D>; +/// Shortcut for referenced data. +pub type ResponseRef<'data, D> = Response<&'data D>; - /// Version - fn version(&self) -> Version; +/// Represents the response from an HTTP request. +#[derive(Debug)] +pub struct Response { + /// See [ResponseData]. + pub data: D, + /// See [StatusCode]. + pub status_code: StatusCode, + /// See [Version]. + pub version: Version, } -impl Response for () { +impl Response +where + D: ResponseData, +{ + /// Constructor that defaults to an HTTP/2 version. #[inline] - fn code(&self) -> u16 { - 0 + pub fn http2(data: D, status_code: StatusCode) -> Self { + Self { data, status_code, version: Version::Http2 } } + /// Shortcut to access the body of `data`. #[inline] - fn version(&self) -> Version { - <_>::default() + pub fn body(&self) -> &D::Body { + self.data.body() + } + + /// Shortcut to access the headers of `data`. + #[inline] + pub fn headers(&self) -> &Headers { + self.data.headers() + } + + /// See [RequestRef]. + #[inline] + pub fn to_ref(&self) -> ResponseRef<'_, D> { + ResponseRef { data: &self.data, status_code: self.status_code, version: self.version } } } diff --git a/wtx/src/http/response_data.rs b/wtx/src/http/response_data.rs new file mode 100644 index 00000000..68f86e36 --- /dev/null +++ b/wtx/src/http/response_data.rs @@ -0,0 +1,79 @@ +use crate::{http::Headers, misc::Lease}; + +/// Groups the body and the headers of a response into a single structure. +pub trait ResponseData { + /// See [Self::body] + type Body: ?Sized; + + /// Can be a sequence of bytes, a string, a deserialized element or any other desired type. + fn body(&self) -> &Self::Body; + + /// See [Headers]. + fn headers(&self) -> &Headers; +} + +impl ResponseData for &T +where + T: ResponseData, +{ + type Body = T::Body; + + #[inline] + fn body(&self) -> &Self::Body { + (*self).body() + } + + #[inline] + fn headers(&self) -> &Headers { + (*self).headers() + } +} + +impl ResponseData for &mut T +where + T: ResponseData, +{ + type Body = T::Body; + + #[inline] + fn body(&self) -> &Self::Body { + (**self).body() + } + + #[inline] + fn headers(&self) -> &Headers { + (**self).headers() + } +} + +impl ResponseData for (B, H) +where + H: Lease, +{ + type Body = B; + + #[inline] + fn body(&self) -> &Self::Body { + &self.0 + } + + #[inline] + fn headers(&self) -> &Headers { + self.1.lease() + } +} + +#[cfg(feature = "http2")] +impl ResponseData for crate::http2::ReqResBuffer { + type Body = [u8]; + + #[inline] + fn body(&self) -> &Self::Body { + &self.data + } + + #[inline] + fn headers(&self) -> &Headers { + &self.headers + } +} diff --git a/wtx/src/http/server.rs b/wtx/src/http/server.rs new file mode 100644 index 00000000..599d6e5e --- /dev/null +++ b/wtx/src/http/server.rs @@ -0,0 +1,44 @@ +//! Optioned high-level abstraction for servers. You can use one of listed suggestions or +//! create your own. + +#[cfg(all(feature = "async-send", feature = "http2", feature = "pool", feature = "tokio"))] +mod tokio_http2; +#[cfg(all( + feature = "async-send", + feature = "pool", + feature = "tokio", + feature = "web-socket-handshake" +))] +mod tokio_web_socket; + +use core::marker::PhantomData; + +/// See [TokioHttp2::tokio_http2]. +#[cfg(all(feature = "async-send", feature = "http2", feature = "pool", feature = "tokio"))] +pub type TokioHttp2 = Server>; +/// See [TokioWebSocket::tokio_web_socket]. +#[cfg(all( + feature = "async-send", + feature = "pool", + feature = "tokio", + feature = "web-socket-handshake" +))] +pub type TokioWebSocket = Server; + +/// Optioned high-level abstraction for servers. You can use one of listed suggestions or +/// create your own. +/// +/// Suggestions depend on the activation of different features. +#[derive(Debug)] +pub struct Server { + rm: PhantomData, +} + +#[cfg(feature = "std")] +fn _buffers_len(buffers_len_opt: Option) -> crate::Result { + Ok(if let Some(elem) = buffers_len_opt { + elem + } else { + usize::from(std::thread::available_parallelism()?).wrapping_mul(2) + }) +} diff --git a/wtx/src/http/server/tokio_http2.rs b/wtx/src/http/server/tokio_http2.rs new file mode 100644 index 00000000..66dfb50d --- /dev/null +++ b/wtx/src/http/server/tokio_http2.rs @@ -0,0 +1,129 @@ +use crate::{ + http::{ + server::{TokioHttp2, _buffers_len}, + Headers, RequestMut, Response, + }, + http2::{Http2Params, Http2Tokio, ReadFrameRslt}, + misc::{ByteVector, FnFut}, + pool::{ + FixedPoolGetRsltTokio, FixedPoolTokio, Http2ServerBufferRM, Pool, ReqResBufferRM, + ResourceManager, + }, + rng::StdRng, +}; +use core::{fmt::Debug, net::SocketAddr}; +use std::sync::OnceLock; +use tokio::net::{TcpListener, TcpStream}; + +type ConnPool = FixedPoolTokio< + as ResourceManager>::Resource, + Http2ServerBufferRM, +>; +type ReqPool = FixedPoolTokio<::Resource, ReqResBufferRM>; + +impl TokioHttp2 { + /// Optioned HTTP/2 server using tokio. + #[inline] + pub async fn tokio_http2( + addr: SocketAddr, + buffers_len_opt: Option, + conn_err: fn(crate::Error), + stream_err: fn(E), + handle: F, + ) -> crate::Result<()> + where + E: Debug + From + Send + 'static, + F: Copy + + for<'any> FnFut< + RequestMut<'any, 'any, 'any, ByteVector>, + Result, E>, + > + Send + + 'static, + for<'any> &'any F: Send, + { + let buffers_len = _buffers_len(buffers_len_opt)?; + let listener = TcpListener::bind(addr).await?; + loop { + let (tcp_stream, _) = listener.accept().await?; + let http2_lock = conn_buffer(buffers_len).await?; + let _conn_jh = tokio::spawn(async move { + if let Err(err) = manage_conn(handle, http2_lock, buffers_len, stream_err, tcp_stream).await + { + conn_err(err); + } + }); + } + } +} + +async fn conn_buffer(len: usize) -> crate::Result<::GetRslt<'static>> { + static POOL: OnceLock = OnceLock::new(); + POOL + .get_or_init(|| { + FixedPoolTokio::new(len, Http2ServerBufferRM::::http2_buffer(StdRng::default())) + }) + .get(&(), &()) + .await +} + +async fn manage_conn( + handle: F, + http2_lock: FixedPoolGetRsltTokio<'static, ::GuardElement>, + len: usize, + stream_err: fn(E), + tcp_stream: TcpStream, +) -> crate::Result<()> +where + E: Debug + From + Send + 'static, + F: Copy + + for<'any> FnFut< + RequestMut<'any, 'any, 'any, ByteVector>, + Result, E>, + > + Send + + 'static, + for<'any> &'any F: Send, +{ + let hp = Http2Params::default(); + let mut http2 = Http2Tokio::accept(http2_lock, hp, tcp_stream).await?; + loop { + let mut req_buffer_guard = req_buffer(len).await?; + let mut stream = match http2.stream(&mut req_buffer_guard).await { + Err(err) => { + drop(req_buffer_guard.release().await); + return Err(err); + } + Ok(ReadFrameRslt::ClosedConnection) => { + drop(req_buffer_guard.release().await); + return Ok(()); + } + Ok(ReadFrameRslt::ClosedStream | ReadFrameRslt::IdleConnection) => { + drop(req_buffer_guard.release().await); + continue; + } + Ok(ReadFrameRslt::Resource(elem)) => elem, + }; + let _stream_jh = tokio::spawn(async move { + let rrb = &mut **req_buffer_guard; + let fun = || async move { + let ReadFrameRslt::Resource(req) = stream.recv_req(rrb).await? else { + return Ok(()); + }; + stream.send_res(handle(req).await?).await?; + Ok::<_, E>(()) + }; + let rslt = fun().await; + drop(req_buffer_guard.release().await); + if let Err(err) = rslt { + stream_err(err); + } + }); + } +} + +async fn req_buffer(len: usize) -> crate::Result<::GetRslt<'static>> { + static POOL: OnceLock = OnceLock::new(); + POOL + .get_or_init(|| FixedPoolTokio::new(len, ReqResBufferRM::req_res_buffer())) + .get(&(), &()) + .await +} diff --git a/wtx/src/http/server/tokio_web_socket.rs b/wtx/src/http/server/tokio_web_socket.rs new file mode 100644 index 00000000..10dae4b1 --- /dev/null +++ b/wtx/src/http/server/tokio_web_socket.rs @@ -0,0 +1,72 @@ +use crate::{ + http::server::{TokioWebSocket, _buffers_len}, + misc::FnFut, + pool::{FixedPoolTokio, Pool, ResourceManager, WebSocketRM}, + rng::StdRng, + web_socket::{ + handshake::{WebSocketAccept, WebSocketAcceptRaw}, + Compression, FrameBufferVec, WebSocketBuffer, WebSocketServer, + }, +}; +use core::{fmt::Debug, net::SocketAddr}; +use std::sync::OnceLock; +use tokio::net::{TcpListener, TcpStream}; + +type WebSocketPool = FixedPoolTokio<::Resource, WebSocketRM>; + +impl TokioWebSocket { + /// Optioned WebSocket server using tokio. + #[inline] + pub async fn tokio_web_socket( + addr: SocketAddr, + buffers_len_opt: Option, + compression: fn() -> C, + conn_err: fn(E), + handle: F, + ) -> crate::Result<()> + where + C: Compression + Send + 'static, + C::NegotiatedCompression: Send, + E: Debug + From + Send + 'static, + F: Copy + + for<'any> FnFut< + ( + &'any mut FrameBufferVec, + WebSocketServer, + ), + Result<(), E>, + > + Send + + 'static, + for<'any> &'any F: Send, + { + let buffers_len = _buffers_len(buffers_len_opt)?; + let listener = TcpListener::bind(addr).await?; + loop { + let (stream, _) = listener.accept().await?; + let mut conn_buffer_guard = conn_buffer(buffers_len).await?; + let _jh = tokio::spawn(async move { + let (fb, wsb) = &mut **conn_buffer_guard; + let fun = || async move { + handle(( + fb, + WebSocketAcceptRaw { compression: compression(), rng: StdRng::default(), stream, wsb } + .accept(|_| true) + .await?, + )) + .await?; + Ok::<_, E>(()) + }; + let rslt = fun().await; + drop(conn_buffer_guard.release().await); + if let Err(err) = rslt { + conn_err(err); + } + }); + } + } +} + +async fn conn_buffer(len: usize) -> crate::Result<::GetRslt<'static>> { + static POOL: OnceLock = OnceLock::new(); + POOL.get_or_init(|| FixedPoolTokio::new(len, WebSocketRM::web_socket())).get(&(), &()).await +} diff --git a/wtx/src/http1.rs b/wtx/src/http1.rs index 41d237f7..78811677 100644 --- a/wtx/src/http1.rs +++ b/wtx/src/http1.rs @@ -1,5 +1,5 @@ use crate::{ - http::{GenericHeader, Request, Response, Version}, + http::{GenericHeader, GenericRequest, GenericResponse, Version}, misc::_unreachable, }; @@ -15,7 +15,7 @@ impl GenericHeader for httparse::Header<'_> { } } -impl Request for httparse::Request<'_, '_> { +impl GenericRequest for httparse::Request<'_, '_> { #[inline] fn method(&self) -> &[u8] { if let Some(el) = self.method { @@ -44,7 +44,7 @@ impl Request for httparse::Request<'_, '_> { } } -impl Response for httparse::Response<'_, '_> { +impl GenericResponse for httparse::Response<'_, '_> { #[inline] fn code(&self) -> u16 { if let Some(el) = self.code { diff --git a/wtx/src/http2.rs b/wtx/src/http2.rs new file mode 100644 index 00000000..0dbbca29 --- /dev/null +++ b/wtx/src/http2.rs @@ -0,0 +1,233 @@ +//! HTTP/2 +//! +//! 1. Does not support padded headers when writing. +//! 2. Does not support push promises (Deprecated by the RFC). +//! 3. Does not support prioritization (Deprecated by he RFC). + +// Many elements where influenced by the code of the h2 repository (https://github.com/hyperium/h2) +// so thanks to the authors. + +#[macro_use] +mod macros; + +mod client_stream; +mod continuation_frame; +mod data_frame; +mod error_code; +mod frame_init; +mod go_away_frame; +mod headers_frame; +mod hpack_decoder; +mod hpack_encoder; +mod hpack_header; +mod hpack_static_headers; +mod http2_buffer; +mod http2_data; +mod http2_params; +mod huffman; +mod huffman_tables; +mod misc; +mod ping_frame; +mod read_frame_rslt; +mod req_res_buffer; +mod reset_stream_frame; +mod send_params; +mod server_stream; +mod settings_frame; +mod stream_state; +#[cfg(test)] +mod tests; +mod u31; +mod uri_buffer; +mod window_update_frame; + +use crate::{ + http2::misc::{ + apply_initial_params, default_stream_frames, read_frame_until_cb_unknown_id, write_to_stream, + }, + misc::{ConnectionState, LeaseMut, Lock, RefCounter, Stream}, +}; +pub use client_stream::ClientStream; +pub(crate) use continuation_frame::ContinuationFrame; +use core::marker::PhantomData; +pub(crate) use data_frame::DataFrame; +pub(crate) use error_code::ErrorCode; +pub(crate) use frame_init::{FrameHeaderTy, FrameInit}; +pub(crate) use go_away_frame::GoAwayFrame; +pub(crate) use headers_frame::HeadersFrame; +pub(crate) use hpack_decoder::HpackDecoder; +pub(crate) use hpack_encoder::HpackEncoder; +pub(crate) use hpack_header::HpackHeaderBasic; +pub(crate) use hpack_static_headers::{HpackStaticRequestHeaders, HpackStaticResponseHeaders}; +pub use http2_buffer::Http2Buffer; +pub(crate) use http2_data::Http2Data; +pub use http2_params::Http2Params; +pub(crate) use huffman::{huffman_decode, huffman_encode}; +pub(crate) use ping_frame::PingFrame; +pub use read_frame_rslt::ReadFrameRslt; +pub use req_res_buffer::ReqResBuffer; +pub(crate) use reset_stream_frame::ResetStreamFrame; +pub use server_stream::ServerStream; +pub(crate) use settings_frame::SettingsFrame; +pub(crate) use stream_state::StreamState; +use tokio::sync::MutexGuard; +pub(crate) use u31::U31; +pub(crate) use uri_buffer::UriBuffer; +pub(crate) use window_update_frame::WindowUpdateFrame; + +pub(crate) const BODY_LEN_DEFAULT: u32 = 4_194_304; +pub(crate) const BUFFERED_FRAMES_NUM_DEFAULT: u8 = 16; +pub(crate) const CACHED_HEADERS_LEN_DEFAULT: u32 = 8_192; +pub(crate) const EXPANDED_HEADERS_LEN_DEFAULT: u32 = 4_096; +pub(crate) const FRAME_LEN_DEFAULT: u32 = 1_048_576; +pub(crate) const FRAME_LEN_LOWER_BOUND: u32 = 16_384; +pub(crate) const FRAME_LEN_UPPER_BOUND: u32 = 16_777_215; +pub(crate) const INITIAL_WINDOW_LEN_DEFAULT: u32 = 524_280; +pub(crate) const RAPID_RESETS_NUM_DEFAULT: u8 = 16; +pub(crate) const READ_BUFFER_LEN_DEFAULT: u32 = 4_194_304; +pub(crate) const STREAMS_NUM_DEFAULT: u32 = 1_073_741_824; + +const ACK_MASK: u8 = 0b0000_0001; +const EOH_MASK: u8 = 0b0000_0100; +const EOS_MASK: u8 = 0b0000_0001; +const PAD_MASK: u8 = 0b0000_1000; +const PREFACE: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + +/// Http2 instance using the mutex from tokio. +#[cfg(feature = "tokio")] +pub type Http2Tokio = + Http2>>, S, IS_CLIENT>; + +/// Negotiates initial "handshakes" or connections and also manages the creation of streams. +#[derive(Debug)] +pub struct Http2 { + hd: HD, + phantom: PhantomData<(HB, S)>, + stream_id: U31, +} + +impl Http2 +where + HB: LeaseMut>, + HD: Lock>, + S: Stream, +{ + /// See [ConnectionState]. + #[inline] + pub async fn connection_state(&self) -> ConnectionState { + ConnectionState::from(self.hd.lock().await.is_conn_open()) + } + + /// Sends a GOAWAY frame to the peer, which cancels the connection and consequently all ongoing + /// streams. + pub async fn send_go_away(self, error_code: ErrorCode) -> crate::Result<()> { + misc::send_go_away( + GoAwayFrame::new(error_code, self.stream_id), + self.hd.lock().await.is_conn_open_and_stream_mut(), + ) + .await + } +} + +// Remove the `Guard` definition to see a wonderful lifetime error involving `GAT` and +// `Send` bounds that makes anyone feel happy and delightful for uncountable hours. +impl Http2 +where + HB: LeaseMut>, + HD: RefCounter, + for<'guard> HD::Item: Lock< + Guard<'guard> = MutexGuard<'guard, Http2Data>, + Resource = Http2Data, + > + 'guard, + S: Stream, +{ + /// Accepts an initial connection sending the local parameters to the remote peer. + #[inline] + pub async fn accept(mut hb: HB, hp: Http2Params, mut stream: S) -> crate::Result { + hb.lease_mut().clear(); + let mut buffer = [0; 24]; + let _ = stream.read(&mut buffer).await?; + if &buffer != PREFACE { + return Err(crate::Error::NoPreface); + } + write_to_stream([hp.to_settings_frame().bytes(&mut [0; 45])], true, &mut stream).await?; + apply_initial_params(hb.lease_mut(), &hp)?; + Ok(Self { + phantom: PhantomData, + hd: HD::new(HD::Item::new(Http2Data::new(hb, hp, stream))), + stream_id: U31::ZERO, + }) + } + + /// Opens a local stream based on initially received headers. See [ServerStream]. + #[inline] + pub async fn stream( + &mut self, + rrb: &mut ReqResBuffer, + ) -> crate::Result>> { + let mut guard = self.hd.lock().await; + if *guard.streams_num_mut() >= guard.hp().max_streams_num() { + return Err(crate::Error::ExceedAmountOfActiveConcurrentStreams); + } + *guard.streams_num_mut() = guard.streams_num_mut().wrapping_add(1); + let mut stream_state = StreamState::Open; + let rfi = rfr_resource_or_return!( + guard + .read_frames_init( + rrb, + U31::ZERO, + &mut stream_state, + |hf| { hf.hsreqh().method.ok_or(crate::Error::MissingRequestMethod) }, + |data, fi, hp, streams_frames| { + read_frame_until_cb_unknown_id(data, fi, hp, streams_frames) + } + ) + .await? + ); + let stream_frame_entry = guard.hb_mut().streams_frames.entry(rfi.stream_id); + let _ = stream_frame_entry.or_insert_with(default_stream_frames); + Ok(ReadFrameRslt::Resource(ServerStream::new(self.hd.clone(), rfi, stream_state))) + } +} + +impl Http2 +where + HB: LeaseMut>, + HD: RefCounter, + HD::Item: Lock>, + S: Stream, +{ + /// Tries to connect to a server sending the local parameters. + #[inline] + pub async fn connect(mut hb: HB, hp: Http2Params, mut stream: S) -> crate::Result { + hb.lease_mut().clear(); + stream.write_all(PREFACE).await?; + write_to_stream([hp.to_settings_frame().bytes(&mut [0; 45])], true, &mut stream).await?; + apply_initial_params(hb.lease_mut(), &hp)?; + Ok(Self { + phantom: PhantomData, + hd: HD::new(HD::Item::new(Http2Data::new(hb, hp, stream))), + stream_id: U31::ONE, + }) + } + + /// Opens a local stream. See [ClientStream]. + pub async fn stream(&mut self) -> crate::Result> { + let mut guard = self.hd.lock().await; + if *guard.streams_num_mut() >= guard.send_params_mut().max_streams_num { + return Err(crate::Error::ExceedAmountOfActiveConcurrentStreams); + } + *guard.streams_num_mut() = guard.streams_num_mut().wrapping_add(1); + let stream_id = self.stream_id; + self.stream_id = self.stream_id.wrapping_add(U31::TWO); + let _ = self + .hd + .lock() + .await + .hb_mut() + .streams_frames + .entry(stream_id) + .or_insert_with(default_stream_frames); + Ok(ClientStream::idle(self.hd.clone(), stream_id)) + } +} diff --git a/wtx/src/http2/client_stream.rs b/wtx/src/http2/client_stream.rs new file mode 100644 index 00000000..373f741d --- /dev/null +++ b/wtx/src/http2/client_stream.rs @@ -0,0 +1,129 @@ +use crate::{ + http::{RequestRef, ResponseMut}, + http2::{ + misc::{send_go_away, send_reset, write_init_headers}, + ErrorCode, GoAwayFrame, HpackStaticRequestHeaders, HpackStaticResponseHeaders, Http2Buffer, + Http2Data, ReadFrameRslt, ReqResBuffer, ResetStreamFrame, StreamState, U31, + }, + misc::{Lease, LeaseMut, Lock, RefCounter, Stream, Usize}, +}; +use core::marker::PhantomData; + +/// Groups the methods used by clients that connect to servers. +#[derive(Debug)] +pub struct ClientStream { + hd: HD, + phantom: PhantomData<(HB, S)>, + stream_id: U31, + stream_state: StreamState, +} + +impl ClientStream { + pub(crate) fn idle(hd: HD, stream_id: U31) -> Self { + Self { phantom: PhantomData, hd, stream_id, stream_state: StreamState::Idle } + } +} + +impl ClientStream +where + HB: LeaseMut>, + HD: RefCounter, + HD::Item: Lock>, + S: Stream, +{ + /// Receive Response + /// + /// Composes all header frames, data frames and trailer frames of an received stream. + /// + /// Should be called onl once after sending a request. + #[inline] + pub async fn recv_res<'rrb>( + &mut self, + rrb: &'rrb mut ReqResBuffer, + ) -> crate::Result>> { + rrb.clear(); + let status_code = rfr_until_resource_with_guard!(self.hd, |guard| { + guard + .read_frames_stream(&mut *rrb, self.stream_id, &mut self.stream_state, |hf| { + hf.hsresh().status_code.ok_or(crate::Error::MissingResponseStatusCode) + }) + .await? + }); + self.stream_state = StreamState::Closed; + Ok(ReadFrameRslt::Resource(ResponseMut::http2(rrb, status_code))) + } + + /// Sends a GOAWAY frame to the peer, which cancels the connection and consequently all ongoing + /// streams. + pub async fn send_go_away(&mut self, error_code: ErrorCode) -> crate::Result<()> { + send_go_away( + GoAwayFrame::new(error_code, self.stream_id), + self.hd.lock().await.is_conn_open_and_stream_mut(), + ) + .await + } + + /// Send Request + /// + /// Composes all header frames, data frames and trailer frames that will be sent in a stream. + /// + /// Shouldn't be called more than one time. + #[inline] + pub async fn send_req(&mut self, req: RequestRef<'_, '_, '_, D>) -> crate::Result<()> + where + D: Lease<[u8]>, + { + let mut guard = self.hd.lock().await; + if req.data.lease().len() > *Usize::from(guard.send_params_mut().max_expanded_headers_len) { + return Err(crate::Error::VeryLargeHeadersLen); + } + let (hb, is_conn_open, send_params, stream) = guard.parts_mut(); + write_init_headers::<_, true>( + req.data.lease(), + req.headers, + &mut hb.hpack_enc, + &mut hb.hpack_enc_buffer, + ( + HpackStaticRequestHeaders { + authority: req.uri.authority().as_bytes(), + method: Some(req.method), + path: req.uri.href().as_bytes(), + protocol: None, + scheme: req.uri.schema().as_bytes(), + }, + HpackStaticResponseHeaders::EMPTY, + ), + *is_conn_open, + send_params, + stream, + self.stream_id, + ) + .await?; + self.stream_state = StreamState::HalfClosedLocal; + Ok(()) + } + + /// Groups [Self::send_req] and [Self::recv_res] in a single method. + #[inline] + pub async fn send_req_recv_res<'rrb, D>( + &mut self, + req: RequestRef<'_, '_, '_, D>, + rrb: &'rrb mut ReqResBuffer, + ) -> crate::Result>> + where + D: Lease<[u8]>, + { + self.send_req(req).await?; + self.recv_res(rrb).await + } + + /// Sends a RST_STREAM frame to the peer, which cancels this stream. + pub async fn send_reset(&mut self, error_code: ErrorCode) -> crate::Result<()> { + send_reset( + ResetStreamFrame::new(error_code, self.stream_id)?, + &mut self.stream_state, + self.hd.lock().await.is_conn_open_and_stream_mut(), + ) + .await + } +} diff --git a/wtx/src/http2/continuation_frame.rs b/wtx/src/http2/continuation_frame.rs new file mode 100644 index 00000000..19ca5471 --- /dev/null +++ b/wtx/src/http2/continuation_frame.rs @@ -0,0 +1,75 @@ +use crate::{ + http::{HeaderName, Headers}, + http2::{FrameHeaderTy, FrameInit, HpackDecoder, HpackHeaderBasic, EOH_MASK, U31}, + misc::Usize, +}; + +#[derive(Debug)] +pub(crate) struct ContinuationFrame { + flag: u8, + stream_id: U31, +} + +impl ContinuationFrame { + pub(crate) fn new(stream_id: U31) -> Self { + Self { flag: 0, stream_id } + } + + pub(crate) fn bytes(&self) -> [u8; 9] { + FrameInit::new(0, self.flag, self.stream_id, FrameHeaderTy::Continuation).bytes() + } + + pub(crate) fn is_eoh(&self) -> bool { + self.flag & EOH_MASK == EOH_MASK + } + + pub(crate) fn read( + data: &[u8], + fi: FrameInit, + headers: &mut Headers, + headers_size: &mut usize, + hpack_dec: &mut HpackDecoder, + ) -> crate::Result { + let mut is_malformed = false; + if fi.stream_id.is_zero() { + return Err(crate::http2::ErrorCode::FrameSizeError.into()); + } + let max_header_list_size = *Usize::from(hpack_dec.max_bytes()); + hpack_dec.decode(data, |(elem, name, value)| match elem { + HpackHeaderBasic::Field => match HeaderName::new(name) { + HeaderName::CONNECTION + | HeaderName::KEEP_ALIVE + | HeaderName::PROXY_CONNECTION + | HeaderName::TRANSFER_ENCODING + | HeaderName::UPGRADE => { + is_malformed = true; + } + HeaderName::TE if value != b"trailers" => { + is_malformed = true; + } + _ => { + let len = decoded_header_size(name.len(), value.len()); + *headers_size = headers_size.wrapping_add(len); + let is_over_size = *headers_size >= max_header_list_size; + if !is_over_size { + headers.push_front(name, value, false); + } + } + }, + _ => { + is_malformed = true; + } + })?; + + Ok(Self { flag: fi.flags, stream_id: fi.stream_id }) + } + + pub(crate) fn set_eoh(&mut self) { + self.flag |= EOH_MASK; + } +} + +#[inline] +fn decoded_header_size(name: usize, value: usize) -> usize { + name.wrapping_add(value).wrapping_add(32) +} diff --git a/wtx/src/http2/data_frame.rs b/wtx/src/http2/data_frame.rs new file mode 100644 index 00000000..5a9b7061 --- /dev/null +++ b/wtx/src/http2/data_frame.rs @@ -0,0 +1,51 @@ +use crate::{ + http2::{misc::trim_frame_pad, FrameHeaderTy, FrameInit, EOS_MASK, PAD_MASK, U31}, + misc::_unlikely_elem, +}; + +const FLAG_MASK: u8 = EOS_MASK | PAD_MASK; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct DataFrame<'data> { + data: &'data [u8], + data_len: u32, + flag: u8, + pad_len: Option, + stream_id: U31, +} + +impl<'data> DataFrame<'data> { + pub(crate) fn eos(data: &'data [u8], data_len: u32, stream_id: U31) -> Self { + let mut this = Self { data, data_len, flag: 0, pad_len: None, stream_id }; + this.set_eos(); + this + } + + pub(crate) fn bytes(&self) -> [u8; 9] { + FrameInit::new(self.data_len, self.flag, self.stream_id, FrameHeaderTy::Data).bytes() + } + + pub(crate) fn data(&self) -> &[u8] { + self.data + } + + pub(crate) fn is_eos(&self) -> bool { + self.flag & EOS_MASK == EOS_MASK + } + + pub(crate) fn read(mut data: &'data [u8], fi: FrameInit) -> crate::Result { + if fi.stream_id.is_zero() { + return _unlikely_elem(Err(crate::http2::ErrorCode::ProtocolError.into())); + } + let flag = fi.flags & FLAG_MASK; + let pad_len = trim_frame_pad(&mut data, flag)?; + let Ok(data_len) = u32::try_from(data.len()) else { + return _unlikely_elem(Err(crate::http2::ErrorCode::ProtocolError.into())); + }; + Ok(Self { data, data_len, flag, pad_len, stream_id: fi.stream_id }) + } + + pub(crate) fn set_eos(&mut self) { + self.flag |= EOS_MASK + } +} diff --git a/wtx/src/http2/error_code.rs b/wtx/src/http2/error_code.rs new file mode 100644 index 00000000..4f2b27c0 --- /dev/null +++ b/wtx/src/http2/error_code.rs @@ -0,0 +1,42 @@ +create_enum! { + /// HTTP/2 error codes. + /// + /// http://httpwg.org/specs/rfc7540.html#ErrorCodes + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub enum ErrorCode { + /// The associated condition is not a result of an error. + NoError = (0), + /// The endpoint detected an unspecific protocol error. + ProtocolError = (1), + /// The endpoint encountered an unexpected internal error. + InternalError = (2), + /// The endpoint detected that its peer violated the flow-control protocol. + FlowControlError = (3), + /// The endpoint sent a SETTINGS frame but did not receive a response in + /// a timely manner. + SettingsTimeout = (4), + /// The endpoint received a frame after a stream was half-closed. + StreamClosed = (5), + /// The endpoint received a frame with an invalid size. + FrameSizeError = (6), + /// The endpoint refused the stream prior to performing any application + /// processing. + RefusedStream = (7), + /// Used by the endpoint to indicate that the stream is no longer needed. + Cancel = (8), + /// The endpoint is unable to maintain the header compression context for + /// the connection. + CompressionError = (9), + /// The connection established in response to a CONNECT request was reset + /// or abnormally closed. + ConnectError = (10), + /// The endpoint detected that its peer is exhibiting a behavior that might + /// be generating excessive load. + EnhanceYourCalm = (11), + /// The underlying transport has properties that do not meet minimum + /// security requirements. + InadequateSecurity = (12), + /// The endpoint requires that HTTP/1.1 be used instead of HTTP/2. + Http11Requires = (13), + } +} diff --git a/wtx/src/http2/frame_init.rs b/wtx/src/http2/frame_init.rs new file mode 100644 index 00000000..4e29dab3 --- /dev/null +++ b/wtx/src/http2/frame_init.rs @@ -0,0 +1,45 @@ +use crate::http2::U31; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct FrameInit { + pub(crate) data_len: u32, + pub(crate) flags: u8, + pub(crate) stream_id: U31, + pub(crate) ty: FrameHeaderTy, +} + +create_enum! { + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub enum FrameHeaderTy { + Data = (0), + Headers = (1), + Reset = (3), + Settings = (4), + Ping = (6), + GoAway = (7), + WindowUpdate = (8), + Continuation = (9), + } +} + +impl FrameInit { + pub(crate) fn new(data_len: u32, flags: u8, stream_id: U31, ty: FrameHeaderTy) -> Self { + Self { data_len, flags, stream_id, ty } + } + + pub(crate) fn from_array(bytes: [u8; 9]) -> crate::Result { + let [a, b, c, d, e, f, g, h, i] = bytes; + Ok(Self { + data_len: u32::from_be_bytes([0, a, b, c]), + flags: e, + stream_id: U31::new(u32::from_be_bytes([f, g, h, i])), + ty: d.try_into()?, + }) + } + + pub(crate) fn bytes(&self) -> [u8; 9] { + let [_, a, b, c] = self.data_len.to_be_bytes(); + let [e, f, g, h] = self.stream_id.to_be_bytes(); + [a, b, c, self.ty.into(), self.flags, e, f, g, h] + } +} diff --git a/wtx/src/http2/go_away_frame.rs b/wtx/src/http2/go_away_frame.rs new file mode 100644 index 00000000..f0c6c52a --- /dev/null +++ b/wtx/src/http2/go_away_frame.rs @@ -0,0 +1,34 @@ +use crate::http2::{ErrorCode, FrameHeaderTy, FrameInit, U31}; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct GoAwayFrame { + error_code: ErrorCode, + last_stream_id: U31, +} + +impl GoAwayFrame { + pub(crate) fn new(error_code: ErrorCode, last_stream_id: U31) -> Self { + Self { error_code, last_stream_id } + } + + pub(crate) fn bytes(&self) -> [u8; 17] { + let [a, b, c, d, e, f, g, h, i] = + FrameInit::new(8, 0, U31::ZERO, FrameHeaderTy::GoAway).bytes(); + let [j, k, l, m] = self.last_stream_id.to_be_bytes(); + let [n, o, p, q] = u32::from(self.error_code).to_be_bytes(); + [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q] + } + + pub(crate) fn read(data: &[u8], fi: FrameInit) -> crate::Result { + if fi.stream_id.is_not_zero() { + return Err(ErrorCode::ProtocolError.into()); + } + let [a, b, c, d, e, f, g, h, ..] = data else { + return Err(ErrorCode::FrameSizeError.into()); + }; + Ok(Self { + error_code: u32::from_be_bytes([*e, *f, *g, *h]).try_into()?, + last_stream_id: U31::new(u32::from_be_bytes([*a, *b, *c, *d])), + }) + } +} diff --git a/wtx/src/http2/headers_frame.rs b/wtx/src/http2/headers_frame.rs new file mode 100644 index 00000000..bd3febab --- /dev/null +++ b/wtx/src/http2/headers_frame.rs @@ -0,0 +1,289 @@ +use crate::{ + http::{HeaderName, Headers}, + http2::{ + misc::trim_frame_pad, uri_buffer::MAX_URI_LEN, FrameHeaderTy, FrameInit, HpackDecoder, + HpackEncoder, HpackHeaderBasic, HpackStaticRequestHeaders, HpackStaticResponseHeaders, + Http2Params, UriBuffer, EOH_MASK, EOS_MASK, U31, + }, + misc::{from_utf8_basic, ArrayString, ByteVector, Usize}, +}; + +#[derive(Debug)] +pub(crate) struct HeadersFrame<'data, 'headers> { + flag: u8, + headers: &'headers Headers, + hsreqh: HpackStaticRequestHeaders<'data>, + hsresh: HpackStaticResponseHeaders, + is_over_size: bool, + stream_id: U31, +} + +impl<'data, 'headers> HeadersFrame<'data, 'headers> { + pub(crate) fn new( + headers: &'headers Headers, + (hsreqh, hsresh): (HpackStaticRequestHeaders<'data>, HpackStaticResponseHeaders), + stream_id: U31, + ) -> Self { + Self { flag: 0, headers, hsreqh, hsresh, is_over_size: false, stream_id } + } + + pub(crate) fn bytes(&self) -> [u8; 9] { + FrameInit::new(0, self.flag, self.stream_id, FrameHeaderTy::Headers).bytes() + } + + pub(crate) fn hsreqh(&self) -> &HpackStaticRequestHeaders<'data> { + &self.hsreqh + } + + pub(crate) fn hsresh(&self) -> HpackStaticResponseHeaders { + self.hsresh + } + + pub(crate) fn is_eoh(&self) -> bool { + self.flag & EOH_MASK == EOH_MASK + } + + pub(crate) fn is_eos(&self) -> bool { + self.flag & EOS_MASK == EOS_MASK + } + + pub(crate) fn is_over_size(&self) -> bool { + self.is_over_size + } + + pub(crate) fn read( + mut data: &[u8], + fi: FrameInit, + headers: &'headers mut Headers, + hp: &Http2Params, + hpack_dec: &mut HpackDecoder, + uri: &mut ArrayString, + uri_buffer: &mut UriBuffer, + ) -> crate::Result<(Self, usize)> { + if fi.stream_id.is_zero() { + return Err(crate::http2::ErrorCode::FrameSizeError.into()); + } + + uri_buffer.clear(); + + let _ = trim_frame_pad(&mut data, fi.flags)?; + let max_expanded_headers_len = *Usize::from(hp.max_expanded_headers_len()); + let mut expanded_headers_len = 0; + let mut has_fields = false; + let mut is_malformed = false; + let mut is_over_size = false; + let mut method = None; + let mut protocol = None; + let mut status = None; + + hpack_dec.decode(data, |(elem, name, value)| match elem { + HpackHeaderBasic::Authority => { + push_uri( + &mut uri_buffer.authority, + &mut expanded_headers_len, + &mut has_fields, + &mut is_malformed, + &mut is_over_size, + max_expanded_headers_len, + name, + value, + ); + } + HpackHeaderBasic::Field => match HeaderName::new(name) { + HeaderName::CONNECTION + | HeaderName::KEEP_ALIVE + | HeaderName::PROXY_CONNECTION + | HeaderName::TRANSFER_ENCODING + | HeaderName::UPGRADE => { + is_malformed = true; + } + HeaderName::TE if value != b"trailers" => { + is_malformed = true; + } + _ => { + has_fields = true; + let len = decoded_header_size(name.len(), value.len()); + expanded_headers_len = expanded_headers_len.wrapping_add(len); + is_over_size = expanded_headers_len >= max_expanded_headers_len; + if !is_over_size { + headers.push_front(name, value, false); + } + } + }, + HpackHeaderBasic::Method(local_method) => { + if push_enum( + &mut expanded_headers_len, + &mut has_fields, + &mut is_malformed, + &mut is_over_size, + method.is_some(), + max_expanded_headers_len, + name, + value, + ) { + method = Some(local_method); + } + } + HpackHeaderBasic::Path => { + push_uri( + &mut uri_buffer.path, + &mut expanded_headers_len, + &mut has_fields, + &mut is_malformed, + &mut is_over_size, + max_expanded_headers_len, + name, + value, + ); + } + HpackHeaderBasic::Protocol(local_protocol) => { + if push_enum( + &mut expanded_headers_len, + &mut has_fields, + &mut is_malformed, + &mut is_over_size, + protocol.is_some(), + max_expanded_headers_len, + name, + value, + ) { + protocol = Some(local_protocol); + } + } + HpackHeaderBasic::Scheme => { + push_uri( + &mut uri_buffer.scheme, + &mut expanded_headers_len, + &mut has_fields, + &mut is_malformed, + &mut is_over_size, + max_expanded_headers_len, + name, + value, + ); + } + HpackHeaderBasic::StatusCode(local_status) => { + if push_enum( + &mut expanded_headers_len, + &mut has_fields, + &mut is_malformed, + &mut is_over_size, + status.is_some(), + max_expanded_headers_len, + name, + value, + ) { + status = Some(local_status); + } + } + })?; + + if DO_NOT_PUSH_URI { + uri.clear(); + uri.try_push_str(uri_buffer.scheme.as_str())?; + uri.try_push_str(uri_buffer.authority.as_str())?; + uri.try_push_str(uri_buffer.path.as_str())?; + } + + Ok(( + Self { + flag: fi.flags, + headers, + hsreqh: HpackStaticRequestHeaders { + authority: &[], + method, + path: &[], + protocol, + scheme: &[], + }, + hsresh: HpackStaticResponseHeaders { status_code: status }, + is_over_size, + stream_id: fi.stream_id, + }, + expanded_headers_len, + )) + } + + pub(crate) fn set_eoh(&mut self) { + self.flag |= EOH_MASK; + } + + pub(crate) fn set_eos(&mut self) { + self.flag |= EOS_MASK; + } + + /// Does not write frame headers, instead, a set of opaque bytes are initially written for + /// posterior overwritten. + pub(crate) fn write( + &self, + hpack_enc: &mut HpackEncoder, + wb: &mut ByteVector, + ) -> crate::Result<()> { + let before_init: usize = wb.len(); + wb.extend_from_slice(&[0; 9]); + let after_init = wb.len(); + if IS_CLIENT { + hpack_enc.encode(wb, self.hsreqh.iter(), self.headers.iter())?; + } else { + hpack_enc.encode(wb, self.hsresh.iter(), self.headers.iter())?; + } + let headers_len = wb.len().wrapping_sub(after_init); + if let Some([a, b, c, ..]) = wb.get_mut(before_init..) { + let [_, d, e, f] = u32::try_from(headers_len).unwrap_or_default().to_be_bytes(); + *a = d; + *b = e; + *c = f; + } + Ok(()) + } +} + +#[inline] +fn decoded_header_size(name: usize, value: usize) -> usize { + name.wrapping_add(value).wrapping_add(32) +} + +#[inline] +fn push_enum( + expanded_headers_len: &mut usize, + has_fields: &mut bool, + is_malformed: &mut bool, + is_over_size: &mut bool, + is_some: bool, + max_expanded_headers_len: usize, + name: &[u8], + value: &[u8], +) -> bool { + if *has_fields || is_some { + *is_malformed = true; + false + } else { + let len = decoded_header_size(name.len().wrapping_add(1), value.len()); + *expanded_headers_len = expanded_headers_len.wrapping_add(len); + *is_over_size = *expanded_headers_len >= max_expanded_headers_len; + !*is_over_size + } +} + +#[inline] +fn push_uri( + buffer: &mut ArrayString, + expanded_headers_len: &mut usize, + has_fields: &mut bool, + is_malformed: &mut bool, + is_over_size: &mut bool, + max_expanded_headers_len: usize, + name: &[u8], + value: &[u8], +) { + if *has_fields || !buffer.is_empty() { + *is_malformed = true; + } else { + let len = decoded_header_size(name.len().wrapping_add(1), value.len()); + *expanded_headers_len = expanded_headers_len.wrapping_add(len); + *is_over_size = *expanded_headers_len >= max_expanded_headers_len; + if !*is_over_size { + let _ = from_utf8_basic(value).ok().and_then(|el| buffer.try_push_str(el).ok()); + } + } +} diff --git a/wtx/src/http2/hpack_decoder.rs b/wtx/src/http2/hpack_decoder.rs new file mode 100644 index 00000000..ff3a7711 --- /dev/null +++ b/wtx/src/http2/hpack_decoder.rs @@ -0,0 +1,418 @@ +use crate::{ + http::{AbstractHeaders, HeaderName, Method, StatusCode, _HeaderNameBuffer, _HeaderValueBuffer}, + http2::{ + hpack_header::{HpackHeaderBasic, HpackHeaderName}, + huffman_decode, + }, + misc::{ArrayVector, Usize, _unlikely_elem}, +}; +use alloc::boxed::Box; + +const DYN_IDX_OFFSET: usize = 62; + +#[derive(Debug)] +pub(crate) struct HpackDecoder { + dyn_headers: AbstractHeaders, + header_buffers: Box<(_HeaderNameBuffer, _HeaderValueBuffer)>, + max_bytes: (u32, Option), +} + +impl HpackDecoder { + #[inline] + pub(crate) fn new() -> Self { + Self { + dyn_headers: AbstractHeaders::new(0), + header_buffers: Box::new((ArrayVector::default(), ArrayVector::default())), + max_bytes: (0, None), + } + } + + #[inline] + pub(crate) fn clear(&mut self) { + let Self { dyn_headers, header_buffers, max_bytes } = self; + dyn_headers.clear(); + header_buffers.0.clear(); + header_buffers.1.clear(); + *max_bytes = (0, None); + } + + #[inline] + pub(crate) fn decode( + &mut self, + mut data: &[u8], + mut cb: impl FnMut((HpackHeaderBasic, &[u8], &[u8])), + ) -> crate::Result<()> { + if let Some(elem) = self.max_bytes.1.take() { + self.max_bytes.0 = elem; + } + let mut did_update = false; + if let [first, ..] = data { + self.manage_decode(*first, &mut data, &mut cb, || { + did_update = true; + Ok(()) + })?; + } + if did_update { + if let [first, ..] = data { + self.manage_decode(*first, &mut data, &mut cb, || Ok(()))?; + } + } + while let [first, ..] = data { + self.manage_decode(*first, &mut data, &mut cb, || { + Err(crate::Error::InvalidDynTableSizeUpdate) + })?; + } + Ok(()) + } + + pub(crate) fn max_bytes(&self) -> usize { + self.dyn_headers.bytes_len() + } + + // It is not possible to lower the initial set value + #[inline] + pub(crate) fn set_max_bytes(&mut self, max_bytes: u32) { + self.max_bytes.1 = Some(match self.max_bytes.1 { + Some(elem) => elem.max(max_bytes), + None => max_bytes, + }); + } + + #[inline] + fn decode_integer(data: &mut &[u8], mask: u8) -> crate::Result<(u8, u32)> { + let mut rslt: (u8, u32) = if let [first, rest @ ..] = data { + *data = rest; + let n = *first & mask; + let rslt = (*first, n.into()); + if n < mask { + return Ok(rslt); + } + rslt + } else { + return Err(crate::Error::UnexpectedEOF); + }; + let mut shift: u32 = 0; + for _ in 0..3 { + let [first, rest @ ..] = data else { + return Err(crate::Error::UnexpectedEOF); + }; + *data = rest; + rslt.1 = rslt.1.wrapping_add(u32::from(first & 0b0111_1111) << shift); + shift = shift.wrapping_add(7); + let is_last_byte = first & 0b1000_0000 == 0; + if is_last_byte { + return Ok(rslt); + } + } + + Err(crate::Error::VeryLargeHeaderInteger) + } + + /// The common index is static-unaware so static names are inserted into `header_buffers`, + /// otherwise [DecodeIdx::Indexed] will return an empty slice. + #[inline] + fn decode_literal( + &mut self, + data: &mut &[u8], + mask: u8, + elem_cb: &mut impl FnMut((HpackHeaderBasic, &[u8], &[u8])), + ) -> crate::Result<()> { + let idx = Self::decode_integer(data, mask)?.1; + let has_indexed_name = idx != 0; + let (hhb, name, value) = if has_indexed_name { + let value = Self::decode_string_value(&mut self.header_buffers.1, data)?; + let (hhb, (static_name, dyn_name), _) = Self::get(&self.dyn_headers, *Usize::from(idx))?; + let new_hhb = match hhb { + HpackHeaderBasic::Authority => HpackHeaderBasic::Authority, + HpackHeaderBasic::Field => HpackHeaderBasic::Field, + HpackHeaderBasic::Method(_) => HpackHeaderBasic::Method(value.try_into()?), + HpackHeaderBasic::Path => HpackHeaderBasic::Path, + HpackHeaderBasic::Protocol(_) => HpackHeaderBasic::Protocol(value.try_into()?), + HpackHeaderBasic::Scheme => HpackHeaderBasic::Scheme, + HpackHeaderBasic::StatusCode(_) => HpackHeaderBasic::StatusCode(value.try_into()?), + }; + let name = if static_name.is_empty() { + elem_cb((new_hhb, dyn_name, value)); + self.header_buffers.0.clear(); + self.header_buffers.0.try_extend_from_slice(dyn_name)?; + self.header_buffers.0.get_mut(..dyn_name.len()).unwrap_or_default() + } else { + elem_cb((new_hhb, static_name, value)); + static_name + }; + (new_hhb, name, value) + } else { + let (hhn, name) = Self::decode_string_name(&mut self.header_buffers.0, data)?; + let value = Self::decode_string_value(&mut self.header_buffers.1, data)?; + let hhb = HpackHeaderBasic::try_from((hhn, value))?; + elem_cb((hhb, name, value)); + (hhb, name, value) + }; + if STORE { + self.dyn_headers.reserve(name.len().wrapping_add(value.len()), 1); + self.dyn_headers.push_front(hhb, name, value, false, |_, _| {}); + } + Ok(()) + } + + #[inline] + fn decode_string_init<'data>( + data: &mut &'data [u8], + ) -> crate::Result<(&'data [u8], &'data [u8], bool)> { + let (first, len) = Self::decode_integer(data, 0b0111_1111)?; + let (bytes_begin, bytes_end) = if data.len() >= *Usize::from(len) { + data.split_at(*Usize::from(len)) + } else { + return Err(crate::Error::UnexpectedEOF); + }; + let is_encoded = first & 0b1000_0000 == 0b1000_0000; + Ok((bytes_begin, bytes_end, is_encoded)) + } + + #[inline] + fn decode_string_name<'buffer, 'data, 'rslt>( + buffer: &'buffer mut _HeaderNameBuffer, + data: &mut &'data [u8], + ) -> crate::Result<(HpackHeaderName, &'rslt [u8])> + where + 'buffer: 'rslt, + 'data: 'rslt, + { + let (before, after, is_encoded) = Self::decode_string_init(data)?; + let rslt = if is_encoded { + huffman_decode(before, buffer)?; + (HpackHeaderName::new(buffer)?, &**buffer) + } else { + let hhn = HpackHeaderName::new(before)?; + if hhn.is_field() { + (hhn, before) + } else { + (hhn, &[][..]) + } + }; + *data = after; + Ok(rslt) + } + + #[inline] + fn decode_string_value<'buffer, 'data, 'rslt>( + buffer: &'buffer mut _HeaderValueBuffer, + bytes: &mut &'data [u8], + ) -> crate::Result<&'rslt [u8]> + where + 'buffer: 'rslt, + 'data: 'rslt, + { + let (before, after, is_encoded) = Self::decode_string_init(bytes)?; + let rslt = if is_encoded { + huffman_decode(before, buffer)?; + buffer + } else { + before + }; + *bytes = after; + Ok(rslt) + } + + #[inline] + fn get( + dyn_headers: &AbstractHeaders, + idx: usize, + ) -> crate::Result<(HpackHeaderBasic, (&'static [u8], &[u8]), (&'static [u8], &[u8]))> { + Ok(match idx { + 0 => return _unlikely_elem(Err(crate::Error::InvalidHpackIdx(0))), + 1 => (HpackHeaderBasic::Authority, (b":authority", &[]), (&[], &[])), + 2 => (HpackHeaderBasic::Method(Method::Get), (b":method", &[]), (b"GET", &[])), + 3 => (HpackHeaderBasic::Method(Method::Post), (b":method", &[]), (b"POST", &[])), + 4 => (HpackHeaderBasic::Path, (b":path", &[]), (b"/", &[])), + 5 => (HpackHeaderBasic::Path, (b":path", &[]), (b"/index.html", &[])), + 6 => (HpackHeaderBasic::Scheme, (b":scheme", &[]), (b"http", &[])), + 7 => (HpackHeaderBasic::Scheme, (b":scheme", &[]), (b"https", &[])), + 8 => (HpackHeaderBasic::StatusCode(StatusCode::Ok), (b":status", &[]), (b"200", &[])), + 9 => (HpackHeaderBasic::StatusCode(StatusCode::NoContent), (b":status", &[]), (b"204", &[])), + 10 => { + (HpackHeaderBasic::StatusCode(StatusCode::PartialContent), (b":status", &[]), (b"206", &[])) + } + 11 => { + (HpackHeaderBasic::StatusCode(StatusCode::NotModified), (b":status", &[]), (b"304", &[])) + } + 12 => { + (HpackHeaderBasic::StatusCode(StatusCode::BadRequest), (b":status", &[]), (b"400", &[])) + } + 13 => (HpackHeaderBasic::StatusCode(StatusCode::NotFound), (b":status", &[]), (b"404", &[])), + 14 => ( + HpackHeaderBasic::StatusCode(StatusCode::InternalServerError), + (b":status", &[]), + (b"500", &[]), + ), + 15 => (HpackHeaderBasic::Field, (HeaderName::ACCEPT_CHARSET.bytes(), &[]), (&[], &[])), + 16 => ( + HpackHeaderBasic::Field, + (HeaderName::ACCEPT_ENCODING.bytes(), &[]), + (b"gzip, deflate", &[]), + ), + 17 => (HpackHeaderBasic::Field, (HeaderName::ACCEPT_LANGUAGE.bytes(), &[]), (&[], &[])), + 18 => (HpackHeaderBasic::Field, (HeaderName::ACCEPT_RANGES.bytes(), &[]), (&[], &[])), + 19 => (HpackHeaderBasic::Field, (HeaderName::ACCEPT.bytes(), &[]), (&[], &[])), + 20 => ( + HpackHeaderBasic::Field, + (HeaderName::ACCESS_CONTROL_ALLOW_ORIGIN.bytes(), &[]), + (&[], &[]), + ), + 21 => (HpackHeaderBasic::Field, (HeaderName::AGE.bytes(), &[]), (&[], &[])), + 22 => (HpackHeaderBasic::Field, (HeaderName::ALLOW.bytes(), &[]), (&[], &[])), + 23 => (HpackHeaderBasic::Field, (HeaderName::AUTHORIZATION.bytes(), &[]), (&[], &[])), + 24 => (HpackHeaderBasic::Field, (HeaderName::CACHE_CONTROL.bytes(), &[]), (&[], &[])), + 25 => (HpackHeaderBasic::Field, (HeaderName::CONTENT_DISPOSITION.bytes(), &[]), (&[], &[])), + 26 => (HpackHeaderBasic::Field, (HeaderName::CONTENT_ENCODING.bytes(), &[]), (&[], &[])), + 27 => (HpackHeaderBasic::Field, (HeaderName::CONTENT_LANGUAGE.bytes(), &[]), (&[], &[])), + 28 => (HpackHeaderBasic::Field, (HeaderName::CONTENT_LENGTH.bytes(), &[]), (&[], &[])), + 29 => (HpackHeaderBasic::Field, (HeaderName::CONTENT_LOCATION.bytes(), &[]), (&[], &[])), + 30 => (HpackHeaderBasic::Field, (HeaderName::CONTENT_RANGE.bytes(), &[]), (&[], &[])), + 31 => (HpackHeaderBasic::Field, (HeaderName::CONTENT_TYPE.bytes(), &[]), (&[], &[])), + 32 => (HpackHeaderBasic::Field, (HeaderName::COOKIE.bytes(), &[]), (&[], &[])), + 33 => (HpackHeaderBasic::Field, (HeaderName::DATE.bytes(), &[]), (&[], &[])), + 34 => (HpackHeaderBasic::Field, (HeaderName::ETAG.bytes(), &[]), (&[], &[])), + 35 => (HpackHeaderBasic::Field, (HeaderName::EXPECT.bytes(), &[]), (&[], &[])), + 36 => (HpackHeaderBasic::Field, (HeaderName::EXPIRES.bytes(), &[]), (&[], &[])), + 37 => (HpackHeaderBasic::Field, (HeaderName::FROM.bytes(), &[]), (&[], &[])), + 38 => (HpackHeaderBasic::Field, (HeaderName::HOST.bytes(), &[]), (&[], &[])), + 39 => (HpackHeaderBasic::Field, (HeaderName::IF_MATCH.bytes(), &[]), (&[], &[])), + 40 => (HpackHeaderBasic::Field, (HeaderName::IF_MODIFIED_SINCE.bytes(), &[]), (&[], &[])), + 41 => (HpackHeaderBasic::Field, (HeaderName::IF_NONE_MATCH.bytes(), &[]), (&[], &[])), + 42 => (HpackHeaderBasic::Field, (HeaderName::IF_RANGE.bytes(), &[]), (&[], &[])), + 43 => (HpackHeaderBasic::Field, (HeaderName::IF_UNMODIFIED_SINCE.bytes(), &[]), (&[], &[])), + 44 => (HpackHeaderBasic::Field, (HeaderName::LAST_MODIFIED.bytes(), &[]), (&[], &[])), + 45 => (HpackHeaderBasic::Field, (HeaderName::LINK.bytes(), &[]), (&[], &[])), + 46 => (HpackHeaderBasic::Field, (HeaderName::LOCATION.bytes(), &[]), (&[], &[])), + 47 => (HpackHeaderBasic::Field, (HeaderName::MAX_FORWARDS.bytes(), &[]), (&[], &[])), + 48 => (HpackHeaderBasic::Field, (HeaderName::PROXY_AUTHENTICATE.bytes(), &[]), (&[], &[])), + 49 => (HpackHeaderBasic::Field, (HeaderName::PROXY_AUTHORIZATION.bytes(), &[]), (&[], &[])), + 50 => (HpackHeaderBasic::Field, (HeaderName::RANGE.bytes(), &[]), (&[], &[])), + 51 => (HpackHeaderBasic::Field, (HeaderName::REFERER.bytes(), &[]), (&[], &[])), + 52 => (HpackHeaderBasic::Field, (HeaderName::REFRESH.bytes(), &[]), (&[], &[])), + 53 => (HpackHeaderBasic::Field, (HeaderName::RETRY_AFTER.bytes(), &[]), (&[], &[])), + 54 => (HpackHeaderBasic::Field, (HeaderName::SERVER.bytes(), &[]), (&[], &[])), + 55 => (HpackHeaderBasic::Field, (HeaderName::SET_COOKIE.bytes(), &[]), (&[], &[])), + 56 => { + (HpackHeaderBasic::Field, (HeaderName::STRICT_TRANSPORT_SECURITY.bytes(), &[]), (&[], &[])) + } + 57 => (HpackHeaderBasic::Field, (HeaderName::TRANSFER_ENCODING.bytes(), &[]), (&[], &[])), + 58 => (HpackHeaderBasic::Field, (HeaderName::USER_AGENT.bytes(), &[]), (&[], &[])), + 59 => (HpackHeaderBasic::Field, (HeaderName::VARY.bytes(), &[]), (&[], &[])), + 60 => (HpackHeaderBasic::Field, (HeaderName::VIA.bytes(), &[]), (&[], &[])), + 61 => (HpackHeaderBasic::Field, (HeaderName::WWW_AUTHENTICATE.bytes(), &[]), (&[], &[])), + dyn_idx_with_offset => dyn_headers + .get_by_idx(dyn_idx_with_offset.wrapping_sub(DYN_IDX_OFFSET)) + .ok_or(crate::Error::InvalidHpackIdx(dyn_idx_with_offset)) + .map(|el| (*el.misc, (&[][..], el.name_bytes), (&[][..], el.value_bytes)))?, + }) + } + + #[inline] + fn manage_decode( + &mut self, + byte: u8, + data: &mut &[u8], + elem_cb: &mut impl FnMut((HpackHeaderBasic, &[u8], &[u8])), + mut size_update_cb: impl FnMut() -> crate::Result<()>, + ) -> crate::Result<()> { + match DecodeIdx::try_from(byte)? { + DecodeIdx::Indexed => { + let idx = Self::decode_integer(data, 0b0111_1111)?.1; + elem_cb(Self::get(&self.dyn_headers, *Usize::from(idx)).map(|(hhb, name, value)| { + ( + hhb, + if name.0.is_empty() { name.1 } else { name.0 }, + if value.0.is_empty() { value.1 } else { value.0 }, + ) + })?) + } + DecodeIdx::LiteralNeverIndexed | DecodeIdx::LiteralWithoutIndexing => { + self.decode_literal::(data, 0b0000_1111, elem_cb)?; + } + DecodeIdx::LiteralWithIndexing => { + self.decode_literal::(data, 0b0011_1111, elem_cb)?; + } + DecodeIdx::SizeUpdate => { + size_update_cb()?; + let local_max_bytes: u32 = Self::decode_integer(data, 0b0001_1111)?.1; + if local_max_bytes > self.max_bytes.0 { + return Err(crate::Error::UnboundedNumber { + expected: 0..=self.max_bytes.0, + received: local_max_bytes, + }); + } + self.dyn_headers.set_max_bytes(*Usize::from(local_max_bytes), |_, _| {}); + } + } + Ok(()) + } +} + +#[derive(Debug)] +enum DecodeIdx { + Indexed, + LiteralNeverIndexed, + LiteralWithIndexing, + LiteralWithoutIndexing, + SizeUpdate, +} + +impl TryFrom for DecodeIdx { + type Error = crate::Error; + + #[inline] + fn try_from(from: u8) -> Result { + Ok(match from & 0b1111_0000 { + 0b0000_0000 => Self::LiteralWithoutIndexing, + 0b0001_0000 => Self::LiteralNeverIndexed, + n => { + if n & 0b1000_0000 == 0b1000_0000 { + Self::Indexed + } else if n & 0b1100_0000 == 0b0100_0000 { + Self::LiteralWithIndexing + } else if n & 0b1110_0000 == 0b0010_0000 { + Self::SizeUpdate + } else { + return Err(crate::Error::UnexpectedHpackIdx); + } + } + }) + } +} + +#[cfg(feature = "_bench")] +#[cfg(test)] +mod bench { + use crate::{ + http2::{HpackDecoder, HpackEncoder}, + misc::{ByteVector, Usize}, + rng::StaticRng, + }; + + #[bench] + fn decode(b: &mut test::Bencher) { + const N: u32 = 1024 * 1024; + let data = { + let mut rslt = crate::bench::_data(*Usize::from(N)); + rslt.iter_mut().filter(|el| **el == b':').for_each(|el| { + *el = 0; + }); + rslt + }; + let mut buffer = ByteVector::with_capacity(*Usize::from(N)); + let mut he = HpackEncoder::new(StaticRng::default()); + he.set_max_dyn_super_bytes(N); + he.encode(&mut buffer, [].into_iter(), { + data.chunks_exact(128).map(|el| (&el[..64], &el[64..], false)) + }) + .unwrap(); + let mut hd = HpackDecoder::new(); + hd.set_max_bytes(N); + b.iter(|| { + hd.decode(&buffer, |_| {}).unwrap(); + hd.clear(); + }); + } +} diff --git a/wtx/src/http2/hpack_encoder.rs b/wtx/src/http2/hpack_encoder.rs new file mode 100644 index 00000000..ac67edab --- /dev/null +++ b/wtx/src/http2/hpack_encoder.rs @@ -0,0 +1,644 @@ +// In `EncodeIdx::RefNameSavedValue` the name is locally indexed along side the value to allow +// future usages of `RefNameRefValue` potentially reducing sent bytes. It is possible to only +// store non-static names but that would de-synchronize the decoder. +// +// On the other hand, `EncodeIdx::RefNameUnsavedValue` does not index anything although the name +// contents could be used to create a more "recent" entry. +// +// It is unknown if the above descriptions are optimal in regards to local stored size, total sent +// bytes or runtime performance. + +use crate::{ + http::{AbstractHeaders, HeaderName, Method, StatusCode}, + http2::{hpack_header::HpackHeaderBasic, huffman_encode}, + misc::{ByteVector, Lease, Usize, _random_state, _shift_bytes, _unreachable}, + rng::Rng, +}; +use ahash::RandomState; +use core::{ + hash::{BuildHasher, Hasher}, + iter, +}; +use hashbrown::HashMap; + +const DYN_IDX_OFFSET: u32 = 61; + +#[derive(Debug)] +pub(crate) struct HpackEncoder { + dyn_headers: AbstractHeaders, + idx: u32, + indcs: HashMap, + // Defined by external actors. + max_dyn_sub_bytes: Option<(u32, Option)>, + // Defined by the system. + max_dyn_super_bytes: u32, + rs: RandomState, +} + +impl HpackEncoder { + #[inline] + pub(crate) fn new(rng: RNG) -> Self + where + RNG: Rng, + { + Self { + dyn_headers: AbstractHeaders::new(0), + idx: 0, + indcs: HashMap::new(), + max_dyn_sub_bytes: None, + max_dyn_super_bytes: 0, + rs: _random_state(rng), + } + } + + #[inline] + pub(crate) fn clear(&mut self) { + let Self { dyn_headers, idx, indcs, max_dyn_sub_bytes, max_dyn_super_bytes: _, rs: _ } = self; + dyn_headers.clear(); + *idx = 0; + indcs.clear(); + *max_dyn_sub_bytes = None; + } + + #[inline] + pub(crate) fn encode<'value>( + &mut self, + buffer: &mut ByteVector, + pseudo_headers: impl Iterator, + user_headers: impl Iterator, + ) -> crate::Result<()> { + self.adjust_indices( + pseudo_headers + .size_hint() + .1 + .and_then(|el| el.checked_add(user_headers.size_hint().1?)) + .unwrap_or(usize::MAX), + ); + self.manage_size_update(buffer)?; + for (hhb, value) in pseudo_headers { + let idx = self.encode_idx((&[], value, false), hhb, Self::shi_pseudo((hhb, value))); + self.manage_encode(buffer, (&[], value), idx)?; + } + for (name, value, is_sensitive) in user_headers { + let idx = self.encode_idx( + (name, value, is_sensitive), + HpackHeaderBasic::Field, + Self::shi_user((name, value)), + ); + self.manage_encode(buffer, (name, value), idx)?; + } + Ok(()) + } + + // It is not possible to lower the initial set value + #[inline] + pub(crate) fn set_max_dyn_sub_bytes(&mut self, max_dyn_sub_bytes: u32) -> crate::Result<()> { + if max_dyn_sub_bytes > self.max_dyn_super_bytes { + return Err(crate::Error::UnboundedNumber { + expected: 0..=self.max_dyn_super_bytes, + received: max_dyn_sub_bytes, + }); + } + match self.max_dyn_sub_bytes { + Some((lower, None)) | Some((lower, Some(_))) => { + if max_dyn_sub_bytes > lower { + self.max_dyn_sub_bytes = Some((lower, Some(max_dyn_sub_bytes))); + } else { + self.max_dyn_sub_bytes = Some((max_dyn_sub_bytes, None)); + } + } + None => { + if max_dyn_sub_bytes != self.max_dyn_super_bytes { + self.max_dyn_sub_bytes = Some((max_dyn_sub_bytes, None)); + } + } + } + Ok(()) + } + + pub(crate) fn set_max_dyn_super_bytes(&mut self, max_dyn_super_bytes: u32) { + self.max_dyn_sub_bytes = None; + self.max_dyn_super_bytes = max_dyn_super_bytes; + } + + #[inline] + fn adjust_indices(&mut self, len: usize) { + let new_idx = u64::from(self.idx).checked_add(Usize::from(len).into()); + if new_idx < Some(u64::from(u32::MAX)) { + return; + } + for (_, idx) in &mut self.indcs { + *idx = idx.wrapping_sub(self.idx); + } + self.idx = 0; + } + + #[inline] + fn dyn_idx(&mut self, header: (&[u8], &[u8], bool), should_not_index: bool) -> EncodeIdx { + let (name, value, is_sensitive) = header; + + let mut name_hasher = self.rs.build_hasher(); + name_hasher.write(name); + + let mut pair_hasher = name_hasher.clone(); + pair_hasher.write(value); + let pair_hash = pair_hasher.finish(); + + if let (false, Some(pair_idx)) = (should_not_index, self.indcs.get(&pair_hash).copied()) { + return EncodeIdx::RefNameRefValue(self.from_indcs_to_encode_idx(pair_idx)); + } + + let name_hash = name_hasher.finish(); + + match (should_not_index, self.indcs.get(&name_hash).copied()) { + (false, None) => {} + (false, Some(name_idx)) => { + return self.store_header_with_ref_name::( + (name, value, is_sensitive), + self.from_indcs_to_encode_idx(name_idx), + pair_hash, + ); + } + (true, None) => return EncodeIdx::UnsavedNameUnsavedValue, + (true, Some(name_idx)) => { + return EncodeIdx::RefNameUnsavedValue(self.from_indcs_to_encode_idx(name_idx)) + } + } + + self.push_dyn_headers((name, value, is_sensitive), (Some(name_hash), pair_hash)); + let next_dyn_idx = self.next_dyn_idx(); + self.indcs.reserve(2); + let _ = self.indcs.insert(name_hash, next_dyn_idx); + let _ = self.indcs.insert(pair_hash, next_dyn_idx); + EncodeIdx::SavedNameSavedValue + } + + #[inline] + fn dyn_idx_with_static_name(&mut self, header: (&[u8], &[u8], bool), name_idx: u32) -> EncodeIdx { + let (name, value, is_sensitive) = header; + let pair_hash = self.rs.hash_one((name, value)); + if let Some(common_idx) = self.indcs.get(&pair_hash).copied() { + return EncodeIdx::RefNameRefValue(self.from_indcs_to_encode_idx(common_idx)); + } + self.store_header_with_ref_name::((name, value, is_sensitive), name_idx, pair_hash) + } + + #[inline] + fn encode_idx( + &mut self, + header: (&[u8], &[u8], bool), + hhb: HpackHeaderBasic, + static_header: Option, + ) -> EncodeIdx { + match static_header { + None => { + let (name, value, is_sensitive) = header; + let should_not_index = self.should_not_index((name, value, is_sensitive), hhb); + self.dyn_idx((name, value, is_sensitive), should_not_index) + } + Some(StaticHeader { has_value: true, idx, name: _ }) => EncodeIdx::RefNameRefValue(idx), + Some(StaticHeader { has_value: false, idx, name }) => { + let (_, value, is_sensitive) = header; + if self.should_not_index((name, value, is_sensitive), hhb) { + EncodeIdx::RefNameUnsavedValue(idx) + } else { + self.dyn_idx_with_static_name((name, value, is_sensitive), idx) + } + } + } + } + + #[inline] + fn encode_int(buffer: &mut ByteVector, first_byte: u8, mut n: u32) -> crate::Result { + let mask = first_byte.wrapping_sub(1); + buffer.reserve(4); + + if n < u32::from(mask) { + buffer.push_within_cap(first_byte | n as u8); + return Ok(1); + } + + n = n.wrapping_sub(mask.into()); + buffer.push_within_cap(first_byte | mask); + + for len in 2..5 { + if n <= 127 { + buffer.push_within_cap(n as u8); + return Ok(len); + } + buffer.push_within_cap(0b1000_0000 | n as u8); + n >>= 7; + } + + Err(crate::Error::VeryLargeHeaderInteger) + } + + // 1. 0 -> 0xxxx -> 4xxxx + // 2,3,4. 0 -> 0xxxxxxxxxx -> 0xxxxxxxxxx10 -> 10xxxxxxxxxx + #[inline] + fn encode_str(buffer: &mut ByteVector, bytes: &[u8]) -> crate::Result<()> { + let before_byte = buffer.len(); + buffer.push(0); + if bytes.is_empty() { + return Ok(()); + } + let after_byte = buffer.len(); + huffman_encode(bytes, buffer); + let after_huffman = buffer.len(); + let len_usize = after_huffman.wrapping_sub(after_byte); + let fits_in_1_byte = len_usize < 0b0111_1111; + if let (true, Ok(len)) = (fits_in_1_byte, u8::try_from(len_usize)) { + let Some(byte) = buffer.get_mut(before_byte) else { + _unreachable(); + }; + *byte = 0b1000_0000 | len; + } else if let Ok(len) = u32::try_from(len_usize) { + let octets = Self::encode_int(buffer, 0b1000_0000, len)?; + let mut array = [0; 4]; + match (octets, buffer.lease()) { + (2, [.., a, b]) => { + array[0] = *a; + array[1] = *b; + } + (3, [.., a, b, c]) => { + array[0] = *a; + array[1] = *b; + array[2] = *c; + } + (4, [.., a, b, c, d]) => { + array[0] = *a; + array[1] = *b; + array[2] = *c; + array[3] = *d; + } + _ => return Ok(()), + } + let _ = _shift_bytes( + before_byte.wrapping_add(octets.into()), + buffer, + iter::once(after_byte..after_huffman), + ); + buffer.truncate(buffer.len().wrapping_sub(1)); + match (octets, buffer.get_mut(before_byte..)) { + (2, Some([a, b, ..])) => { + *a = array[0]; + *b = array[1]; + } + (3, Some([a, b, c, ..])) => { + *a = array[0]; + *b = array[1]; + *c = array[2]; + } + (4, Some([a, b, c, d, ..])) => { + *a = array[0]; + *b = array[1]; + *c = array[2]; + *d = array[3]; + } + _ => {} + } + } else { + return Err(crate::Error::UnsupportedHeaderNameOrValueLen); + } + Ok(()) + } + + // Regardless of the "sensitive" flag set by users, these headers may carry sensitive content + // that shouldn't be indexed. + #[inline] + fn header_is_naturally_sensitive(hhb: HpackHeaderBasic, name: &[u8]) -> bool { + match hhb { + HpackHeaderBasic::Field => matches!( + HeaderName::new(name), + HeaderName::AGE + | HeaderName::AUTHORIZATION + | HeaderName::CONTENT_LENGTH + | HeaderName::ETAG + | HeaderName::IF_MODIFIED_SINCE + | HeaderName::IF_NONE_MATCH + | HeaderName::LOCATION + | HeaderName::COOKIE + | HeaderName::SET_COOKIE + ), + HpackHeaderBasic::Path => true, + _ => false, + } + } + + // Very large headers are not good candidates for indexing. + #[inline] + fn header_is_very_large(&self, hhb: HpackHeaderBasic, name: &[u8], value: &[u8]) -> bool { + hhb.len(name, value) >= (self.dyn_headers.max_bytes() / 4).wrapping_mul(3) + } + + #[inline] + fn from_indcs_to_encode_idx(&self, idx: u32) -> u32 { + self.idx.wrapping_sub(idx).wrapping_add(DYN_IDX_OFFSET) + } + + #[inline] + fn manage_encode( + &mut self, + buffer: &mut ByteVector, + header: (&[u8], &[u8]), + idx: EncodeIdx, + ) -> crate::Result<()> { + let (name, value) = header; + match idx { + EncodeIdx::RefNameRefValue(idx) => { + let _ = Self::encode_int(buffer, 0b1000_0000, idx)?; + } + EncodeIdx::RefNameSavedValue(name_idx) => { + let _ = Self::encode_int(buffer, 0b0100_0000, name_idx)?; + Self::encode_str(buffer, value)?; + } + EncodeIdx::RefNameUnsavedValue(name_idx) => { + let _ = Self::encode_int(buffer, 0b0001_0000, name_idx)?; + Self::encode_str(buffer, value)?; + } + EncodeIdx::SavedNameSavedValue => { + buffer.push(0b0100_0000); + Self::encode_str(buffer, name)?; + Self::encode_str(buffer, value)?; + } + EncodeIdx::UnsavedNameUnsavedValue => { + buffer.push(0b0001_0000); + Self::encode_str(buffer, name)?; + Self::encode_str(buffer, value)?; + } + } + Ok(()) + } + + #[inline] + fn manage_size_update(&mut self, buffer: &mut ByteVector) -> crate::Result<()> { + match self.max_dyn_sub_bytes.take() { + Some((lower, None)) => { + self.dyn_headers.set_max_bytes(*Usize::from(lower), |metadata, _| { + Self::remove_outdated_indices(&mut self.indcs, metadata); + }); + let _ = Self::encode_int(buffer, 0b0010_0000, lower)?; + } + Some((lower, Some(upper))) => { + self.dyn_headers.set_max_bytes(*Usize::from(lower), |metadata, _| { + Self::remove_outdated_indices(&mut self.indcs, metadata); + }); + self.dyn_headers.set_max_bytes(*Usize::from(upper), |metadata, _| { + Self::remove_outdated_indices(&mut self.indcs, metadata); + }); + let _ = Self::encode_int(buffer, 0b0010_0000, lower)?; + let _ = Self::encode_int(buffer, 0b0010_0000, upper)?; + } + None => {} + } + Ok(()) + } + + /// Must be called after insertion + #[inline] + fn next_dyn_idx(&self) -> u32 { + self.idx.wrapping_sub(1) + } + + #[inline] + fn push_dyn_headers( + &mut self, + (name, value, is_sensitive): (&[u8], &[u8], bool), + (name_hash, pair_hash): (Option, u64), + ) { + self.idx = self.idx.wrapping_add(1); + self.dyn_headers.reserve(name.len().wrapping_add(value.len()), 1); + self.dyn_headers.push_front( + Metadata { name_hash, pair_hash }, + name, + value, + is_sensitive, + |metadata, _| { + Self::remove_outdated_indices(&mut self.indcs, metadata); + }, + ); + } + + #[inline] + fn shi_pseudo((hhb, value): (HpackHeaderBasic, &[u8])) -> Option { + let (has_value, idx, name): (_, _, &[u8]) = match hhb { + HpackHeaderBasic::Authority => (false, 1, b":authority"), + HpackHeaderBasic::Field => return None, + HpackHeaderBasic::Method(method) => { + let name = b":method"; + let (has_value, idx) = match method { + Method::Get => (true, 2), + Method::Post => (true, 3), + _ => (false, 2), + }; + (has_value, idx, name) + } + HpackHeaderBasic::Path => { + let name = b":path"; + let (has_value, idx) = match value { + b"/" => (true, 4), + b"/index.html" => (true, 5), + _ => (false, 4), + }; + (has_value, idx, name) + } + HpackHeaderBasic::Protocol(_) => return None, + HpackHeaderBasic::Scheme => { + let name = b":path"; + let (has_value, idx) = match value { + b"http" => (true, 6), + b"https" => (true, 7), + _ => (false, 6), + }; + (has_value, idx, name) + } + HpackHeaderBasic::StatusCode(status) => { + let name = b":status"; + let (has_value, idx) = match status { + StatusCode::Ok => (true, 8), + StatusCode::NoContent => (true, 9), + StatusCode::PartialContent => (true, 10), + StatusCode::NotModified => (true, 11), + StatusCode::BadRequest => (true, 12), + StatusCode::NotFound => (true, 13), + StatusCode::InternalServerError => (true, 14), + _ => (false, 8), + }; + (has_value, idx, name) + } + }; + Some(StaticHeader { has_value, idx, name }) + } + + #[inline] + fn shi_user((name, value): (&[u8], &[u8])) -> Option { + let (has_value, idx, name) = match HeaderName::new(name) { + HeaderName::ACCEPT_CHARSET => (false, 15, HeaderName::ACCEPT_CHARSET.bytes()), + HeaderName::ACCEPT_ENCODING => { + if value == b"gzip, deflate" { + (true, 16, HeaderName::ACCEPT_ENCODING.bytes()) + } else { + (false, 16, HeaderName::ACCEPT_ENCODING.bytes()) + } + } + HeaderName::ACCEPT_LANGUAGE => (false, 17, HeaderName::ACCEPT_LANGUAGE.bytes()), + HeaderName::ACCEPT_RANGES => (false, 18, HeaderName::ACCEPT_RANGES.bytes()), + HeaderName::ACCEPT => (false, 19, HeaderName::ACCEPT.bytes()), + HeaderName::ACCESS_CONTROL_ALLOW_ORIGIN => { + (false, 20, HeaderName::ACCESS_CONTROL_ALLOW_ORIGIN.bytes()) + } + HeaderName::AGE => (false, 21, HeaderName::AGE.bytes()), + HeaderName::ALLOW => (false, 22, HeaderName::ALLOW.bytes()), + HeaderName::AUTHORIZATION => (false, 23, HeaderName::AUTHORIZATION.bytes()), + HeaderName::CACHE_CONTROL => (false, 24, HeaderName::CACHE_CONTROL.bytes()), + HeaderName::CONTENT_DISPOSITION => (false, 25, HeaderName::CONTENT_DISPOSITION.bytes()), + HeaderName::CONTENT_ENCODING => (false, 26, HeaderName::CONTENT_ENCODING.bytes()), + HeaderName::CONTENT_LANGUAGE => (false, 27, HeaderName::CONTENT_LANGUAGE.bytes()), + HeaderName::CONTENT_LENGTH => (false, 28, HeaderName::CONTENT_LENGTH.bytes()), + HeaderName::CONTENT_LOCATION => (false, 29, HeaderName::CONTENT_LOCATION.bytes()), + HeaderName::CONTENT_RANGE => (false, 30, HeaderName::CONTENT_RANGE.bytes()), + HeaderName::CONTENT_TYPE => (false, 31, HeaderName::CONTENT_TYPE.bytes()), + HeaderName::COOKIE => (false, 32, HeaderName::COOKIE.bytes()), + HeaderName::DATE => (false, 33, HeaderName::DATE.bytes()), + HeaderName::ETAG => (false, 34, HeaderName::ETAG.bytes()), + HeaderName::EXPECT => (false, 35, HeaderName::EXPECT.bytes()), + HeaderName::EXPIRES => (false, 36, HeaderName::EXPIRES.bytes()), + HeaderName::FROM => (false, 37, HeaderName::FROM.bytes()), + HeaderName::HOST => (false, 38, HeaderName::HOST.bytes()), + HeaderName::IF_MATCH => (false, 39, HeaderName::IF_MATCH.bytes()), + HeaderName::IF_MODIFIED_SINCE => (false, 40, HeaderName::IF_MODIFIED_SINCE.bytes()), + HeaderName::IF_NONE_MATCH => (false, 41, HeaderName::IF_NONE_MATCH.bytes()), + HeaderName::IF_RANGE => (false, 42, HeaderName::IF_RANGE.bytes()), + HeaderName::IF_UNMODIFIED_SINCE => (false, 43, HeaderName::IF_UNMODIFIED_SINCE.bytes()), + HeaderName::LAST_MODIFIED => (false, 44, HeaderName::LAST_MODIFIED.bytes()), + HeaderName::LINK => (false, 45, HeaderName::LINK.bytes()), + HeaderName::LOCATION => (false, 46, HeaderName::LOCATION.bytes()), + HeaderName::MAX_FORWARDS => (false, 47, HeaderName::MAX_FORWARDS.bytes()), + HeaderName::PROXY_AUTHENTICATE => (false, 48, HeaderName::PROXY_AUTHENTICATE.bytes()), + HeaderName::PROXY_AUTHORIZATION => (false, 49, HeaderName::PROXY_AUTHORIZATION.bytes()), + HeaderName::RANGE => (false, 50, HeaderName::RANGE.bytes()), + HeaderName::REFERER => (false, 51, HeaderName::REFERER.bytes()), + HeaderName::REFRESH => (false, 52, HeaderName::REFRESH.bytes()), + HeaderName::RETRY_AFTER => (false, 53, HeaderName::RETRY_AFTER.bytes()), + HeaderName::SERVER => (false, 54, HeaderName::SERVER.bytes()), + HeaderName::SET_COOKIE => (false, 55, HeaderName::SET_COOKIE.bytes()), + HeaderName::STRICT_TRANSPORT_SECURITY => { + (false, 56, HeaderName::STRICT_TRANSPORT_SECURITY.bytes()) + } + HeaderName::TRANSFER_ENCODING => (false, 57, HeaderName::TRANSFER_ENCODING.bytes()), + HeaderName::USER_AGENT => (false, 58, HeaderName::USER_AGENT.bytes()), + HeaderName::VARY => (false, 59, HeaderName::VARY.bytes()), + HeaderName::VIA => (false, 60, HeaderName::VIA.bytes()), + HeaderName::WWW_AUTHENTICATE => (false, 61, HeaderName::WWW_AUTHENTICATE.bytes()), + _ => return None, + }; + Some(StaticHeader { has_value, idx, name }) + } + + #[inline] + fn should_not_index( + &self, + (name, value, is_sensitive): (&[u8], &[u8], bool), + hhb: HpackHeaderBasic, + ) -> bool { + is_sensitive + || Self::header_is_naturally_sensitive(hhb, name) + || self.header_is_very_large(hhb, name, value) + } + + #[inline] + fn remove_outdated_indices(indcs: &mut HashMap, metadata: Metadata) { + if let Some(elem) = metadata.name_hash { + let _ = indcs.remove(&elem); + } + let _ = indcs.remove(&metadata.pair_hash); + } + + #[inline] + fn store_header_with_ref_name( + &mut self, + (name, value, is_sensitive): (&[u8], &[u8], bool), + name_idx: u32, + pair_hash: u64, + ) -> EncodeIdx { + let before = self.dyn_headers.headers_len(); + self.push_dyn_headers((name, value, is_sensitive), (None, pair_hash)); + let _ = self.indcs.insert(pair_hash, self.next_dyn_idx()); + if !HAS_STATIC_NAME { + let after = self.dyn_headers.headers_len(); + let diff = before.wrapping_sub(after.wrapping_sub(1)); + let name_idx_has_been_removed = diff > *Usize::from(name_idx); + if name_idx_has_been_removed { + return EncodeIdx::SavedNameSavedValue; + } + } + EncodeIdx::RefNameSavedValue(name_idx) + } +} + +/// https://datatracker.ietf.org/doc/html/rfc7541#section-6.2 +/// +/// Elements already stored (Ref) are encoded using their referenced indexes. Elements that were +/// recently stored (Saved) or must not be stored (Unsaved) are encoded using their literal +/// contents (Literal). +#[derive(Debug)] +enum EncodeIdx { + /// Both elements are already stored and the common referenced index is used for encoding. + RefNameRefValue(u32), + /// The name is already stored and the referenced index is used for encoding. The value has been + /// stored and the literal contents are used for encoding. + RefNameSavedValue(u32), + /// Both "Never Indexed" and "Without Indexing" variants. + /// + /// The name is already stored and the referenced index is used for encoding. The value is not stored + /// and the literal contents are used for encoding. + RefNameUnsavedValue(u32), + /// The name has been stored and the literal contents are used for encoding. The value has been + /// stored the literal contents are used for encoding. + SavedNameSavedValue, + /// Both "Never Indexed" and "Without Indexing" variants. + /// + /// The name is not stored and the literal contents are used for encoding. The value is not stored + /// and the literal contents are used for encoding. + UnsavedNameUnsavedValue, +} + +#[derive(Clone, Copy, Debug)] +struct Metadata { + name_hash: Option, + pair_hash: u64, +} + +#[derive(Clone, Copy, Debug)] +struct StaticHeader { + has_value: bool, + idx: u32, + name: &'static [u8], +} + +#[cfg(feature = "_bench")] +#[cfg(test)] +mod bench { + use crate::{ + http2::HpackEncoder, + misc::{ByteVector, Usize}, + rng::StaticRng, + }; + + #[bench] + fn encode(b: &mut test::Bencher) { + const N: u32 = 1024 * 1024 * 4; + let data = crate::bench::_data(*Usize::from(N)); + let mut he = HpackEncoder::new(StaticRng::default()); + he.set_max_dyn_super_bytes(N); + let mut buffer = ByteVector::new(); + b.iter(|| { + he.encode( + &mut buffer, + [].into_iter(), + data.chunks_exact(128).map(|el| (&el[..64], &el[64..], false)), + ) + .unwrap(); + }); + } +} diff --git a/wtx/src/http2/hpack_header.rs b/wtx/src/http2/hpack_header.rs new file mode 100644 index 00000000..fdcfc9c9 --- /dev/null +++ b/wtx/src/http2/hpack_header.rs @@ -0,0 +1,73 @@ +use crate::http::{Method, Protocol, StatusCode}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum HpackHeaderBasic { + Authority, + Field, + Method(Method), + Path, + Protocol(Protocol), + Scheme, + StatusCode(StatusCode), +} + +impl HpackHeaderBasic { + pub(crate) const fn len(self, name: &[u8], value: &[u8]) -> usize { + match self { + HpackHeaderBasic::Authority => 10usize.wrapping_add(value.len()).wrapping_add(32), + HpackHeaderBasic::Field => name.len().wrapping_add(value.len()).wrapping_add(32), + HpackHeaderBasic::Method(_) => 6usize.wrapping_add(value.len()).wrapping_add(32), + HpackHeaderBasic::Path => 5usize.wrapping_add(value.len()).wrapping_add(32), + HpackHeaderBasic::Protocol(_) => 9usize.wrapping_add(value.len()).wrapping_add(32), + HpackHeaderBasic::Scheme => 7usize.wrapping_add(value.len()).wrapping_add(32), + HpackHeaderBasic::StatusCode(_) => 7usize.wrapping_add(3).wrapping_add(32), + } + } +} + +impl TryFrom<(HpackHeaderName, &[u8])> for HpackHeaderBasic { + type Error = crate::Error; + + #[inline] + fn try_from(from: (HpackHeaderName, &[u8])) -> Result { + Ok(match from.0 { + HpackHeaderName::Authority => HpackHeaderBasic::Authority, + HpackHeaderName::Field => HpackHeaderBasic::Field, + HpackHeaderName::Method => HpackHeaderBasic::Method(from.1.try_into()?), + HpackHeaderName::Path => HpackHeaderBasic::Path, + HpackHeaderName::Protocol => HpackHeaderBasic::Protocol(from.1.try_into()?), + HpackHeaderName::Scheme => HpackHeaderBasic::Scheme, + HpackHeaderName::StatusCode => HpackHeaderBasic::StatusCode(from.1.try_into()?), + }) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum HpackHeaderName { + Authority, + Field, + Method, + Path, + Protocol, + Scheme, + StatusCode, +} + +impl HpackHeaderName { + pub(crate) fn new(name: &[u8]) -> crate::Result { + Ok(match name { + b":authority" => Self::Authority, + b":method" => Self::Method, + b":path" => Self::Path, + b":protocol" => Self::Protocol, + b":scheme" => Self::Scheme, + b":status" => Self::StatusCode, + [b':', ..] => return Err(crate::Error::UnexpectedPreFixedHeaderName), + _ => Self::Field, + }) + } + + pub(crate) fn is_field(&self) -> bool { + matches!(self, HpackHeaderName::Field) + } +} diff --git a/wtx/src/http2/hpack_static_headers.rs b/wtx/src/http2/hpack_static_headers.rs new file mode 100644 index 00000000..16677c0e --- /dev/null +++ b/wtx/src/http2/hpack_static_headers.rs @@ -0,0 +1,52 @@ +use crate::{ + http::{Method, Protocol, StatusCode}, + http2::HpackHeaderBasic, +}; + +/// Mandatory headers of a HTTP/2 request +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct HpackStaticRequestHeaders<'bytes> { + pub(crate) authority: &'bytes [u8], + pub(crate) method: Option, + pub(crate) path: &'bytes [u8], + pub(crate) protocol: Option, + pub(crate) scheme: &'bytes [u8], +} + +impl<'bytes> HpackStaticRequestHeaders<'bytes> { + pub(crate) const EMPTY: Self = + Self { authority: &[], method: None, path: &[], protocol: None, scheme: &[] }; + + pub(crate) fn iter(&self) -> impl Iterator { + let Self { authority, method, path, protocol, scheme } = *self; + let enums = [ + method.map(|el| (HpackHeaderBasic::Method(el), &[][..])), + protocol.map(|el| (HpackHeaderBasic::Protocol(el), &[][..])), + ] + .into_iter() + .flatten(); + let uri = [ + (HpackHeaderBasic::Authority, authority), + (HpackHeaderBasic::Path, path), + (HpackHeaderBasic::Scheme, scheme), + ] + .into_iter() + .filter(|el| !el.1.is_empty()); + enums.chain(uri) + } +} + +/// Mandatory headers of a HTTP/2 response +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct HpackStaticResponseHeaders { + pub(crate) status_code: Option, +} + +impl HpackStaticResponseHeaders { + pub(crate) const EMPTY: Self = Self { status_code: None }; + + pub(crate) fn iter(&self) -> impl Iterator { + let Self { status_code } = *self; + status_code.map(|el| (HpackHeaderBasic::StatusCode(el), &[][..])).into_iter() + } +} diff --git a/wtx/src/http2/http2_buffer.rs b/wtx/src/http2/http2_buffer.rs new file mode 100644 index 00000000..1f443339 --- /dev/null +++ b/wtx/src/http2/http2_buffer.rs @@ -0,0 +1,63 @@ +use crate::{ + http2::{FrameInit, HpackDecoder, HpackEncoder, UriBuffer, U31}, + misc::{BlocksQueue, ByteVector, Lease, LeaseMut, PartitionedFilledBuffer}, + rng::Rng, +}; +use alloc::boxed::Box; +use hashbrown::HashMap; + +/// Groups all intermediate structures necessary to perform HTTP/2 connections. +// +// Maximum sizes are dictated by `AcceptParams` or `ConnectParams`. +#[derive(Debug)] +pub struct Http2Buffer { + pub(crate) hpack_dec: HpackDecoder, + pub(crate) hpack_enc: HpackEncoder, + pub(crate) hpack_enc_buffer: ByteVector, + pub(crate) pfb: PartitionedFilledBuffer, + pub(crate) streams_frames: HashMap>, + pub(crate) uri_buffer: Box, +} + +impl Http2Buffer { + /// Creates a new instance without pre-allocated resources. + #[inline] + pub fn new(rng: RNG) -> Self + where + RNG: Rng, + { + Self { + hpack_dec: HpackDecoder::new(), + hpack_enc: HpackEncoder::new(rng), + hpack_enc_buffer: ByteVector::new(), + pfb: PartitionedFilledBuffer::new(), + streams_frames: HashMap::new(), + uri_buffer: Box::new(UriBuffer::new()), + } + } + + #[inline] + pub(crate) fn clear(&mut self) { + let Self { hpack_dec, hpack_enc, hpack_enc_buffer, pfb, streams_frames, uri_buffer } = self; + hpack_dec.clear(); + hpack_enc.clear(); + hpack_enc_buffer.clear(); + pfb._clear(); + streams_frames.clear(); + uri_buffer.clear(); + } +} + +impl Lease> for Http2Buffer { + #[inline] + fn lease(&self) -> &Http2Buffer { + self + } +} + +impl LeaseMut> for Http2Buffer { + #[inline] + fn lease_mut(&mut self) -> &mut Http2Buffer { + self + } +} diff --git a/wtx/src/http2/http2_data.rs b/wtx/src/http2/http2_data.rs new file mode 100644 index 00000000..56ca970e --- /dev/null +++ b/wtx/src/http2/http2_data.rs @@ -0,0 +1,351 @@ +use crate::{ + http2::{ + misc::{read_frame_others, read_frame_until, read_frame_until_cb_known_id, reset_stream}, + send_params::SendParams, + ContinuationFrame, DataFrame, FrameHeaderTy, FrameInit, HeadersFrame, Http2Buffer, Http2Params, + ReadFrameRslt, ReqResBuffer, StreamState, WindowUpdateFrame, U31, + }, + misc::{BlocksQueue, Lease, LeaseMut, Stream}, +}; +use hashbrown::HashMap; + +#[derive(Debug)] +pub struct Http2Data { + hb: HB, + hp: Http2Params, + is_conn_open: bool, + rapid_resets_num: u8, + send_params: SendParams, + stream: S, + streams_num: u32, +} + +impl Http2Data +where + HB: LeaseMut>, + S: Stream, +{ + #[inline] + pub(crate) fn new(hb: HB, hp: Http2Params, stream: S) -> Self { + Self { + hb, + hp, + is_conn_open: true, + rapid_resets_num: 0, + send_params: SendParams::default(), + stream, + streams_num: 0, + } + } + + #[inline] + pub(crate) fn hb_mut(&mut self) -> &mut Http2Buffer { + self.hb.lease_mut() + } + + #[inline] + pub(crate) fn hp(&self) -> &Http2Params { + &self.hp + } + + #[inline] + pub(crate) fn is_conn_open(&self) -> bool { + self.is_conn_open + } + + #[inline] + pub(crate) fn is_conn_open_and_stream_mut(&mut self) -> (&mut bool, &mut S) { + (&mut self.is_conn_open, &mut self.stream) + } + + #[inline] + pub(crate) fn parts_mut( + &mut self, + ) -> (&mut Http2Buffer, &mut bool, &mut SendParams, &mut S) { + (self.hb.lease_mut(), &mut self.is_conn_open, &mut self.send_params, &mut self.stream) + } + + /// Reads a frame that is expected to be the initial header of a message along with its + /// related continuation frames. + #[inline] + pub(crate) async fn read_frames_init( + &mut self, + rrb: &mut ReqResBuffer, + stream_id: U31, + stream_state: &mut StreamState, + mut headers_cb: impl FnMut(&HeadersFrame<'_, '_>) -> crate::Result, + mut read_frame_until_cb: impl FnMut( + &[u8], + FrameInit, + &Http2Params, + &mut HashMap>, + ) -> crate::Result, + ) -> crate::Result>> { + for _ in 0.._max_frames_mismatches!() { + let Http2Buffer { hpack_dec, hpack_enc, pfb, streams_frames, uri_buffer, .. } = + self.hb.lease_mut(); + let fi = rfr_resource_or_return!( + read_frame_until( + &mut self.hp, + hpack_enc, + &mut self.is_conn_open, + pfb, + &mut self.send_params, + &mut self.stream, + stream_id, + stream_state, + &mut self.streams_num, + |fi, hp, data| { read_frame_until_cb(data, fi, hp, streams_frames) }, + |hp| { + if self.rapid_resets_num >= hp.max_rapid_resets_num() { + return Err(crate::Error::ExceedAmountOfRapidResets); + } + self.rapid_resets_num = self.rapid_resets_num.wrapping_add(1); + Ok(()) + } + ) + .await? + ); + match fi.ty { + FrameHeaderTy::Continuation | FrameHeaderTy::Data => { + return Err(crate::Error::NotAInitialHeaderFrame); + } + FrameHeaderTy::Headers => { + let (hf, mut hpack_size) = HeadersFrame::read::( + pfb._current(), + fi, + &mut rrb.headers, + &self.hp, + hpack_dec, + &mut rrb.uri, + uri_buffer, + )?; + if hf.is_over_size() { + return Ok(reset_stream(stream_state, &mut self.streams_num)); + } + let is_eoh = hf.is_eoh(); + let is_eos = hf.is_eos(); + if is_eos { + *stream_state = StreamState::HalfClosedRemote; + } + let headers_rslt = headers_cb(&hf)?; + rfr_until_resource!( + self + .read_frames_continuation( + &mut hpack_size, + (is_eoh, is_eos), + rrb, + fi.stream_id, + stream_state, + ) + .await? + ); + return Ok(ReadFrameRslt::Resource(ReadFramesInit { + headers_rslt, + hpack_size, + is_eos, + stream_id: fi.stream_id, + })); + } + FrameHeaderTy::WindowUpdate => { + let _wuf = WindowUpdateFrame::read(pfb._current(), fi)?; + } + _ => return Err(crate::http2::ErrorCode::ProtocolError.into()), + } + } + Err(crate::Error::VeryLargeAmountOfFrameMismatches) + } + + /// Reads data and trailer frames that compose a stream. + #[inline] + pub(crate) async fn read_frames_others( + &mut self, + hpack_size: &mut usize, + is_eos: bool, + rrb: &mut ReqResBuffer, + stream_id: U31, + stream_state: &mut StreamState, + ) -> crate::Result> { + if is_eos { + return Ok(ReadFrameRslt::Resource(())); + } + let Http2Buffer { hpack_dec, hpack_enc, pfb, streams_frames, uri_buffer, .. } = + self.hb.lease_mut(); + let mut body_len: u32 = 0; + let mut counter: u32 = 0; + loop { + if counter >= _max_frames_mismatches!() { + return Err(crate::Error::VeryLargeAmountOfFrameMismatches); + } + let (fi, data) = rfr_resource_or_return!( + read_frame_others( + &mut self.hp, + hpack_enc, + &mut self.is_conn_open, + pfb, + &mut self.send_params, + &mut self.stream, + stream_id, + stream_state, + streams_frames, + &mut self.streams_num + ) + .await? + ); + let check_opt = body_len.checked_add(fi.data_len).filter(|el| *el <= self.hp.max_body_len()); + let Some(local_body_len) = check_opt else { + return Err(crate::http2::ErrorCode::ProtocolError.into()); + }; + body_len = local_body_len; + if let FrameHeaderTy::Data = fi.ty { + let df = DataFrame::read(data, fi)?; + rrb.data.extend_from_slice(df.data()); + if df.is_eos() { + *stream_state = StreamState::HalfClosedRemote; + return Ok(ReadFrameRslt::Resource(())); + } + } else { + let (hf, local_hpack_size) = HeadersFrame::read::( + data, + fi, + &mut rrb.headers, + &self.hp, + hpack_dec, + &mut rrb.uri, + uri_buffer, + )?; + if hf.is_over_size() { + return Ok(reset_stream(stream_state, &mut self.streams_num)); + } + *hpack_size = hpack_size.saturating_add(local_hpack_size); + if hf.is_eoh() { + return Ok(ReadFrameRslt::Resource(())); + } + break; + } + counter = counter.wrapping_add(1); + } + + for _ in 0.._max_continuation_frames!() { + let (fi, data) = rfr_resource_or_return!( + read_frame_others( + &mut self.hp, + hpack_enc, + &mut self.is_conn_open, + pfb, + &mut self.send_params, + &mut self.stream, + stream_id, + stream_state, + streams_frames, + &mut self.streams_num + ) + .await? + ); + if ContinuationFrame::read(data, fi, &mut rrb.headers, hpack_size, hpack_dec)?.is_eoh() { + return Ok(ReadFrameRslt::Resource(())); + } + counter = counter.wrapping_add(1); + } + Err(crate::Error::VeryLargeAmountOfContinuationFrames) + } + + /// Reads all header frames, data frames and trailer frames that compose a stream. + #[inline] + pub(crate) async fn read_frames_stream( + &mut self, + rrb: &mut ReqResBuffer, + stream_id: U31, + stream_state: &mut StreamState, + cb: fn(&HeadersFrame<'_, '_>) -> crate::Result, + ) -> crate::Result> { + let mut rfi = rfr_resource_or_return!( + self + .read_frames_init(rrb, stream_id, stream_state, cb, |data, fi, hp, streams_frames| { + read_frame_until_cb_known_id(data, fi, hp, stream_id, streams_frames) + }) + .await? + ); + rfr_resource_or_return!( + self + .read_frames_others(&mut rfi.hpack_size, rfi.is_eos, rrb, stream_id, stream_state) + .await? + ); + Ok(ReadFrameRslt::Resource(rfi.headers_rslt)) + } + + /// Reads all continuation frames. + #[inline] + async fn read_frames_continuation( + &mut self, + hpack_size: &mut usize, + (mut is_eoh, is_eos): (bool, bool), + rrb: &mut ReqResBuffer, + stream_id: U31, + stream_state: &mut StreamState, + ) -> crate::Result> { + if is_eoh || is_eos { + return Ok(ReadFrameRslt::Resource(())); + } + let Http2Buffer { hpack_dec, hpack_enc, pfb, streams_frames, .. } = self.hb.lease_mut(); + for _ in 0.._max_continuation_frames!() { + let (fi, data) = rfr_until_resource!( + read_frame_others( + &mut self.hp, + hpack_enc, + &mut self.is_conn_open, + pfb, + &mut self.send_params, + &mut self.stream, + stream_id, + stream_state, + streams_frames, + &mut self.streams_num + ) + .await? + ); + let ci = ContinuationFrame::read(data, fi, &mut rrb.headers, hpack_size, hpack_dec)?; + is_eoh = ci.is_eoh(); + if is_eoh { + return Ok(ReadFrameRslt::Resource(())); + } + } + Err(crate::Error::VeryLargeAmountOfContinuationFrames) + } + + #[inline] + pub(crate) fn send_params_mut(&mut self) -> &mut SendParams { + &mut self.send_params + } + + #[inline] + pub(crate) fn streams_num_mut(&mut self) -> &mut u32 { + &mut self.streams_num + } +} + +impl Lease> + for Http2Data +{ + #[inline] + fn lease(&self) -> &Http2Data { + self + } +} + +impl LeaseMut> + for Http2Data +{ + #[inline] + fn lease_mut(&mut self) -> &mut Http2Data { + self + } +} + +#[derive(Debug)] +pub(crate) struct ReadFramesInit { + pub(crate) headers_rslt: H, + pub(crate) hpack_size: usize, + pub(crate) is_eos: bool, + pub(crate) stream_id: U31, +} diff --git a/wtx/src/http2/http2_params.rs b/wtx/src/http2/http2_params.rs new file mode 100644 index 00000000..d83296ba --- /dev/null +++ b/wtx/src/http2/http2_params.rs @@ -0,0 +1,203 @@ +use crate::http2::{ + SettingsFrame, BODY_LEN_DEFAULT, BUFFERED_FRAMES_NUM_DEFAULT, CACHED_HEADERS_LEN_DEFAULT, + EXPANDED_HEADERS_LEN_DEFAULT, FRAME_LEN_DEFAULT, FRAME_LEN_LOWER_BOUND, FRAME_LEN_UPPER_BOUND, + INITIAL_WINDOW_LEN_DEFAULT, RAPID_RESETS_NUM_DEFAULT, READ_BUFFER_LEN_DEFAULT, + STREAMS_NUM_DEFAULT, U31, +}; + +/// Tells how connections and streams should behave. +#[derive(Debug)] +pub struct Http2Params { + enable_connect_protocol: bool, + initial_window_len: u32, + max_body_len: u32, + max_buffered_frames_num: u8, + max_cached_headers_len: (u32, u32), + max_expanded_headers_len: u32, + max_frame_len: u32, + max_rapid_resets_num: u8, + max_streams_num: u32, + read_buffer_len: u32, +} + +impl Http2Params { + /// Enable connect protocol + /// + /// Allows the execution of other protocols like WebSockets within HTTP/2 connections. At the + /// current time this parameter has no effect. + /// + /// Defaults to `false`. + pub fn enable_connect_protocol(&self) -> bool { + self.enable_connect_protocol + } + + /// Initial window length. + /// + /// The initial amount of "credit" a counterpart can have for sending data. + /// + /// Corresponds to `SETTINGS_INITIAL_WINDOW_SIZE`. Defaults to 512 KiB. Capped within + /// 0 ~ (2^31 - 1) bytes + pub fn initial_window_len(&self) -> u32 { + self.initial_window_len + } + + /// Maximum request/response body length. + /// + /// Or the maximum size allowed for the sum of the length of all data frames. Also servers as an + /// upper bound for the window size of a stream. + /// + /// Defaults to 4 MiB. + pub fn max_body_len(&self) -> u32 { + self.max_body_len + } + + /// Maximum number of buffered frames per stream. + /// + /// An implementation detail. Due to the concurrent nature of the HTTP/2 specification, it is + /// necessary to temporally store received frames into an intermediary structure. + /// + /// Defaults to 16 frames. + pub fn max_buffered_frames_num(&self) -> u8 { + self.max_buffered_frames_num + } + + /// Maximum cached headers length. + /// + /// Related to HPACK, indicates the maximum length of the structure that holds cached decoded + /// headers received from a counterpart. + /// + /// - The first parameter indicates the local HPACK ***decoder*** length that is externally + /// advertised and can become the remote HPACK ***encoder*** length. + /// - The second parameter indicates the maximum local HPACK ***encoder*** length. In other words, + /// it doesn't allow external actors to dictate very large lengths. + /// + /// Corresponds to `SETTINGS_HEADER_TABLE_SIZE`. Defaults to 8 KiB. + pub fn max_cached_headers_len(&self) -> (u32, u32) { + self.max_cached_headers_len + } + + /// Maximum expanded headers length. + /// + /// Or the maximum length of the final Request/Response header. Contents may or may not originate + /// from the HPACK structure that holds cached decoded headers. + /// + /// Corresponds to `SETTINGS_MAX_HEADER_LIST_SIZE`. Defaults to 4 KiB. + pub fn max_expanded_headers_len(&self) -> u32 { + self.max_expanded_headers_len + } + + /// Maximum frame length. + /// + /// Avoids the reading of very large frames sent by external actors. + /// + /// Corresponds to `SETTINGS_MAX_FRAME_SIZE`. Defaults to 1 MiB. Capped within 16 KiB ~ 16 MiB + pub fn max_frame_len(&self) -> u32 { + self.max_frame_len + } + + /// Maximum number of rapid resets. + /// + /// A rapid reset happens when a peer sends an initial header followed by a RST_STREAM frame. This + /// parameter is used to avoid CVE-2023-44487. + pub fn max_rapid_resets_num(&self) -> u8 { + self.max_rapid_resets_num + } + + /// Maximum number of active concurrent streams. + /// + /// Corresponds to `SETTINGS_MAX_CONCURRENT_STREAMS`. Defaults to 1073741824 streams. + pub fn max_streams_num(&self) -> u32 { + self.max_streams_num + } + + /// Read Buffer Length. + /// + /// Allocated space intended to read bytes sent by external actors. + /// + /// Defaults to 4 MiB. + pub fn read_buffer_len(&self) -> u32 { + self.read_buffer_len + } + + /// Mutable version of [Self::initial_window_len]. + pub fn set_initial_window_len(&mut self, value: u32) -> &mut Self { + self.initial_window_len = value.clamp(0, U31::MAX.u32()); + self + } + + /// Mutable version of [Self::max_body_len]. + pub fn set_max_body_len(&mut self, value: u32) -> &mut Self { + self.max_body_len = value; + self + } + + /// Mutable version of [Self::max_buffered_frames_num]. + pub fn set_max_buffered_frames_num(&mut self, value: u8) -> &mut Self { + self.max_buffered_frames_num = value; + self + } + + /// Mutable version of [Self::max_cached_headers_len]. + pub fn set_max_cached_headers_len(&mut self, value: (u32, u32)) -> &mut Self { + self.max_cached_headers_len = value; + self + } + + /// Mutable version of [Self::max_expanded_headers_len]. + pub fn set_max_expanded_headers_len(&mut self, value: u32) -> &mut Self { + self.max_expanded_headers_len = value; + self + } + + /// Mutable version of [Self::max_frame_len]. + pub fn set_max_frame_len(&mut self, value: u32) -> &mut Self { + self.max_frame_len = value.clamp(FRAME_LEN_LOWER_BOUND, FRAME_LEN_UPPER_BOUND); + self + } + + /// Mutable version of [Self::max_rapid_resets_num]. + pub fn set_max_rapid_resets_num(&mut self, value: u8) -> &mut Self { + self.max_rapid_resets_num = value; + self + } + + /// Mutable version of [Self::max_streams_num]. + pub fn set_max_streams_num(&mut self, value: u32) -> &mut Self { + self.max_streams_num = value; + self + } + + /// Mutable version of [Self::read_buffer_len]. + pub fn set_read_buffer_len(&mut self, value: u32) -> &mut Self { + self.read_buffer_len = value; + self + } + + pub(crate) fn to_settings_frame(&self) -> SettingsFrame { + let mut settings_frame = SettingsFrame::default(); + settings_frame.set_enable_connect_protocol(Some(self.enable_connect_protocol)); + settings_frame.set_header_table_size(Some(self.max_cached_headers_len.0)); + settings_frame.set_initial_window_size(Some(self.initial_window_len)); + settings_frame.set_max_concurrent_streams(Some(self.max_streams_num)); + settings_frame.set_max_frame_size(Some(self.max_frame_len)); + settings_frame.set_max_header_list_size(Some(self.max_expanded_headers_len)); + settings_frame + } +} + +impl Default for Http2Params { + fn default() -> Self { + Self { + enable_connect_protocol: false, + initial_window_len: INITIAL_WINDOW_LEN_DEFAULT, + max_body_len: BODY_LEN_DEFAULT, + max_buffered_frames_num: BUFFERED_FRAMES_NUM_DEFAULT, + max_cached_headers_len: (CACHED_HEADERS_LEN_DEFAULT, CACHED_HEADERS_LEN_DEFAULT), + max_expanded_headers_len: EXPANDED_HEADERS_LEN_DEFAULT, + max_frame_len: FRAME_LEN_DEFAULT, + max_rapid_resets_num: RAPID_RESETS_NUM_DEFAULT, + max_streams_num: STREAMS_NUM_DEFAULT, + read_buffer_len: READ_BUFFER_LEN_DEFAULT, + } + } +} diff --git a/wtx/src/http2/huffman.rs b/wtx/src/http2/huffman.rs new file mode 100644 index 00000000..6a79ce01 --- /dev/null +++ b/wtx/src/http2/huffman.rs @@ -0,0 +1,213 @@ +use crate::{ + http2::huffman_tables::{DECODED, DECODE_TABLE, ENCODE_TABLE, END_OF_STRING, ERROR}, + misc::{ArrayVector, ByteVector, _unreachable}, +}; + +pub(crate) fn huffman_decode( + from: &[u8], + wb: &mut ArrayVector, +) -> crate::Result<()> { + fn decode_4_bits( + curr_state: &mut u8, + input: u8, + end_of_string: &mut bool, + ) -> crate::Result> { + let Some((next_state, byte, flags)) = DECODE_TABLE + .get(usize::from(*curr_state)) + .and_then(|slice_4bits| slice_4bits.get(usize::from(input))) + .copied() + else { + _unreachable(); + }; + if flags & ERROR == ERROR { + return Err(crate::Error::UnexpectedEndingHuffman); + } + let rslt = (flags & DECODED == DECODED).then_some(byte); + *curr_state = next_state; + *end_of_string = flags & END_OF_STRING == END_OF_STRING; + Ok(rslt) + } + + let mut curr_state = 0; + let mut is_ok = true; + let mut end_of_string = false; + + wb.clear(); + + _iter4!( + from, + { + if !is_ok { + break; + } + }, + |elem| { + let left_nibble = elem >> 4; + if let Some(byte) = decode_4_bits(&mut curr_state, left_nibble, &mut end_of_string)? { + is_ok = wb.try_push(byte).is_ok(); + } + let right_nibble = elem & 0b0000_1111; + if let Some(byte) = decode_4_bits(&mut curr_state, right_nibble, &mut end_of_string)? { + is_ok = wb.try_push(byte).is_ok(); + } + } + ); + + if !is_ok { + return Err(crate::Error::HeaderFieldIsTooLarge); + } + + let is_final = curr_state == 0 || end_of_string; + if !is_final { + return Err(crate::Error::UnexpectedEndingHuffman); + } + + Ok(()) +} + +pub(crate) fn huffman_encode(from: &[u8], wb: &mut ByteVector) { + const MASK: u64 = 0b1111_1111; + + fn push_within_iter(bits: &mut u64, bits_left: &mut u64, wb: &mut ByteVector) { + let Ok(n) = u8::try_from((*bits >> 32) & MASK) else { + _unreachable(); + }; + wb.push_within_cap(n); + *bits <<= 8; + *bits_left = bits_left.wrapping_add(8); + } + + let mut bits: u64 = 0; + let mut bits_left: u64 = 40; + + wb.reserve((from.len() << 1).wrapping_add(5)); + + _iter4!(from, {}, |elem| { + let Some((nbits, code)) = ENCODE_TABLE.get(usize::from(*elem)).copied() else { + _unreachable(); + }; + let bits_offset = bits_left.wrapping_sub(<_>::from(nbits)); + bits |= code << bits_offset; + bits_left = bits_offset; + if bits_left <= 32 { + push_within_iter(&mut bits, &mut bits_left, wb); + } + if bits_left <= 32 { + push_within_iter(&mut bits, &mut bits_left, wb); + } + if bits_left <= 32 { + push_within_iter(&mut bits, &mut bits_left, wb); + } + if bits_left <= 32 { + push_within_iter(&mut bits, &mut bits_left, wb); + } + if bits_left <= 32 { + _unreachable() + } + wb.reserve(5); + }); + + if bits_left != 40 { + bits |= (1u64 << bits_left).wrapping_sub(1); + let Ok(n) = u8::try_from((bits >> 32) & MASK) else { + _unreachable(); + }; + wb.push_within_cap(n); + } +} + +#[cfg(feature = "_bench")] +#[cfg(test)] +mod bench { + use crate::{ + bench::_data, + http2::{huffman_decode, huffman_encode}, + misc::{ArrayVector, Vector}, + }; + use alloc::boxed::Box; + + #[bench] + fn decode(b: &mut test::Bencher) { + const N: usize = 1024 * 1024; + let data = { + let mut dest = Vector::with_capacity(N); + huffman_encode(&_data(N), &mut dest); + dest + }; + let mut dest = Box::new(ArrayVector::<_, N>::default()); + b.iter(|| { + huffman_decode(&data, &mut dest).unwrap(); + dest.clear(); + }); + } + + #[bench] + fn encode(b: &mut test::Bencher) { + const N: usize = 1024 * 1024 * 4; + let mut data = _data(N); + let mut dest = Vector::with_capacity(N); + b.iter(|| huffman_encode(&mut data, &mut dest)); + } +} + +#[cfg(feature = "_proptest")] +#[cfg(test)] +mod proptest { + use crate::{ + http::_HeaderValueBuffer, + http2::{huffman_decode, huffman_encode}, + misc::Vector, + }; + use alloc::vec::Vec; + + #[test_strategy::proptest] + fn encode_and_decode(data: Vec) { + let mut encoded = Vector::with_capacity(data.len()); + huffman_encode(&data, &mut encoded); + let mut decoded = _HeaderValueBuffer::default(); + if huffman_decode(&encoded, &mut decoded).is_ok() { + assert_eq!(&data, &*decoded); + } + } +} + +#[cfg(test)] +mod test { + use crate::{ + http::_HeaderValueBuffer, + http2::huffman::{huffman_decode, huffman_encode}, + misc::{ByteVector, Vector}, + }; + + #[test] + fn decode_and_encode() { + let mut decode = _HeaderValueBuffer::default(); + let mut encode = Vector::default(); + + decode_and_encode_cmp((&mut decode, &mut encode), b"o", &[0b00111111]); + decode_and_encode_cmp((&mut decode, &mut encode), b"0", &[7]); + decode_and_encode_cmp((&mut decode, &mut encode), b"A", &[(0x21 << 2) + 3]); + + decode_and_encode_cmp((&mut decode, &mut encode), b"#", &[255, 160 + 15]); + decode_and_encode_cmp((&mut decode, &mut encode), b"$", &[255, 200 + 7]); + decode_and_encode_cmp((&mut decode, &mut encode), b"\x0a", &[255, 255, 255, 240 + 3]); + + decode_and_encode_cmp((&mut decode, &mut encode), b"!0", &[254, 1]); + decode_and_encode_cmp((&mut decode, &mut encode), b" !", &[0b01010011, 0b11111000]); + } + + fn decode_and_encode_cmp( + (decode_buffer, encode_buffer): (&mut _HeaderValueBuffer, &mut ByteVector), + bytes: &[u8], + encoded: &[u8], + ) { + huffman_decode(encoded, decode_buffer).unwrap(); + assert_eq!(&**decode_buffer, bytes); + + huffman_encode(&*bytes, encode_buffer); + assert_eq!(&**encode_buffer, encoded); + + decode_buffer.clear(); + encode_buffer.clear(); + } +} diff --git a/wtx/src/http2/huffman_tables.rs b/wtx/src/http2/huffman_tables.rs new file mode 100644 index 00000000..c3b4aabb --- /dev/null +++ b/wtx/src/http2/huffman_tables.rs @@ -0,0 +1,4876 @@ +pub(crate) const OK: u8 = 0b0000_0000; +pub(crate) const END_OF_STRING: u8 = 0b0000_0001; +pub(crate) const DECODED: u8 = 0b0000_0010; +pub(crate) const CONTINUE: u8 = 0b0000_0011; +pub(crate) const ERROR: u8 = 0b0000_0100; + +pub(crate) const DECODE_TABLE: &[[(u8, u8, u8); 16]; 256] = &[ + [ + (4, 0, OK), + (5, 0, OK), + (7, 0, OK), + (8, 0, OK), + (11, 0, OK), + (12, 0, OK), + (16, 0, OK), + (19, 0, OK), + (25, 0, OK), + (28, 0, OK), + (32, 0, OK), + (35, 0, OK), + (42, 0, OK), + (49, 0, OK), + (57, 0, OK), + (64, 0, END_OF_STRING), + ], + [ + (0, 48, DECODED), + (0, 49, DECODED), + (0, 50, DECODED), + (0, 97, DECODED), + (0, 99, DECODED), + (0, 101, DECODED), + (0, 105, DECODED), + (0, 111, DECODED), + (0, 115, DECODED), + (0, 116, DECODED), + (13, 0, OK), + (14, 0, OK), + (17, 0, OK), + (18, 0, OK), + (20, 0, OK), + (21, 0, OK), + ], + [ + (1, 48, DECODED), + (22, 48, CONTINUE), + (1, 49, DECODED), + (22, 49, CONTINUE), + (1, 50, DECODED), + (22, 50, CONTINUE), + (1, 97, DECODED), + (22, 97, CONTINUE), + (1, 99, DECODED), + (22, 99, CONTINUE), + (1, 101, DECODED), + (22, 101, CONTINUE), + (1, 105, DECODED), + (22, 105, CONTINUE), + (1, 111, DECODED), + (22, 111, CONTINUE), + ], + [ + (2, 48, DECODED), + (9, 48, DECODED), + (23, 48, DECODED), + (40, 48, CONTINUE), + (2, 49, DECODED), + (9, 49, DECODED), + (23, 49, DECODED), + (40, 49, CONTINUE), + (2, 50, DECODED), + (9, 50, DECODED), + (23, 50, DECODED), + (40, 50, CONTINUE), + (2, 97, DECODED), + (9, 97, DECODED), + (23, 97, DECODED), + (40, 97, CONTINUE), + ], + [ + (3, 48, DECODED), + (6, 48, DECODED), + (10, 48, DECODED), + (15, 48, DECODED), + (24, 48, DECODED), + (31, 48, DECODED), + (41, 48, DECODED), + (56, 48, CONTINUE), + (3, 49, DECODED), + (6, 49, DECODED), + (10, 49, DECODED), + (15, 49, DECODED), + (24, 49, DECODED), + (31, 49, DECODED), + (41, 49, DECODED), + (56, 49, CONTINUE), + ], + [ + (3, 50, DECODED), + (6, 50, DECODED), + (10, 50, DECODED), + (15, 50, DECODED), + (24, 50, DECODED), + (31, 50, DECODED), + (41, 50, DECODED), + (56, 50, CONTINUE), + (3, 97, DECODED), + (6, 97, DECODED), + (10, 97, DECODED), + (15, 97, DECODED), + (24, 97, DECODED), + (31, 97, DECODED), + (41, 97, DECODED), + (56, 97, CONTINUE), + ], + [ + (2, 99, DECODED), + (9, 99, DECODED), + (23, 99, DECODED), + (40, 99, CONTINUE), + (2, 101, DECODED), + (9, 101, DECODED), + (23, 101, DECODED), + (40, 101, CONTINUE), + (2, 105, DECODED), + (9, 105, DECODED), + (23, 105, DECODED), + (40, 105, CONTINUE), + (2, 111, DECODED), + (9, 111, DECODED), + (23, 111, DECODED), + (40, 111, CONTINUE), + ], + [ + (3, 99, DECODED), + (6, 99, DECODED), + (10, 99, DECODED), + (15, 99, DECODED), + (24, 99, DECODED), + (31, 99, DECODED), + (41, 99, DECODED), + (56, 99, CONTINUE), + (3, 101, DECODED), + (6, 101, DECODED), + (10, 101, DECODED), + (15, 101, DECODED), + (24, 101, DECODED), + (31, 101, DECODED), + (41, 101, DECODED), + (56, 101, CONTINUE), + ], + [ + (3, 105, DECODED), + (6, 105, DECODED), + (10, 105, DECODED), + (15, 105, DECODED), + (24, 105, DECODED), + (31, 105, DECODED), + (41, 105, DECODED), + (56, 105, CONTINUE), + (3, 111, DECODED), + (6, 111, DECODED), + (10, 111, DECODED), + (15, 111, DECODED), + (24, 111, DECODED), + (31, 111, DECODED), + (41, 111, DECODED), + (56, 111, CONTINUE), + ], + [ + (1, 115, DECODED), + (22, 115, CONTINUE), + (1, 116, DECODED), + (22, 116, CONTINUE), + (0, 32, DECODED), + (0, 37, DECODED), + (0, 45, DECODED), + (0, 46, DECODED), + (0, 47, DECODED), + (0, 51, DECODED), + (0, 52, DECODED), + (0, 53, DECODED), + (0, 54, DECODED), + (0, 55, DECODED), + (0, 56, DECODED), + (0, 57, DECODED), + ], + [ + (2, 115, DECODED), + (9, 115, DECODED), + (23, 115, DECODED), + (40, 115, CONTINUE), + (2, 116, DECODED), + (9, 116, DECODED), + (23, 116, DECODED), + (40, 116, CONTINUE), + (1, 32, DECODED), + (22, 32, CONTINUE), + (1, 37, DECODED), + (22, 37, CONTINUE), + (1, 45, DECODED), + (22, 45, CONTINUE), + (1, 46, DECODED), + (22, 46, CONTINUE), + ], + [ + (3, 115, DECODED), + (6, 115, DECODED), + (10, 115, DECODED), + (15, 115, DECODED), + (24, 115, DECODED), + (31, 115, DECODED), + (41, 115, DECODED), + (56, 115, CONTINUE), + (3, 116, DECODED), + (6, 116, DECODED), + (10, 116, DECODED), + (15, 116, DECODED), + (24, 116, DECODED), + (31, 116, DECODED), + (41, 116, DECODED), + (56, 116, CONTINUE), + ], + [ + (2, 32, DECODED), + (9, 32, DECODED), + (23, 32, DECODED), + (40, 32, CONTINUE), + (2, 37, DECODED), + (9, 37, DECODED), + (23, 37, DECODED), + (40, 37, CONTINUE), + (2, 45, DECODED), + (9, 45, DECODED), + (23, 45, DECODED), + (40, 45, CONTINUE), + (2, 46, DECODED), + (9, 46, DECODED), + (23, 46, DECODED), + (40, 46, CONTINUE), + ], + [ + (3, 32, DECODED), + (6, 32, DECODED), + (10, 32, DECODED), + (15, 32, DECODED), + (24, 32, DECODED), + (31, 32, DECODED), + (41, 32, DECODED), + (56, 32, CONTINUE), + (3, 37, DECODED), + (6, 37, DECODED), + (10, 37, DECODED), + (15, 37, DECODED), + (24, 37, DECODED), + (31, 37, DECODED), + (41, 37, DECODED), + (56, 37, CONTINUE), + ], + [ + (3, 45, DECODED), + (6, 45, DECODED), + (10, 45, DECODED), + (15, 45, DECODED), + (24, 45, DECODED), + (31, 45, DECODED), + (41, 45, DECODED), + (56, 45, CONTINUE), + (3, 46, DECODED), + (6, 46, DECODED), + (10, 46, DECODED), + (15, 46, DECODED), + (24, 46, DECODED), + (31, 46, DECODED), + (41, 46, DECODED), + (56, 46, CONTINUE), + ], + [ + (1, 47, DECODED), + (22, 47, CONTINUE), + (1, 51, DECODED), + (22, 51, CONTINUE), + (1, 52, DECODED), + (22, 52, CONTINUE), + (1, 53, DECODED), + (22, 53, CONTINUE), + (1, 54, DECODED), + (22, 54, CONTINUE), + (1, 55, DECODED), + (22, 55, CONTINUE), + (1, 56, DECODED), + (22, 56, CONTINUE), + (1, 57, DECODED), + (22, 57, CONTINUE), + ], + [ + (2, 47, DECODED), + (9, 47, DECODED), + (23, 47, DECODED), + (40, 47, CONTINUE), + (2, 51, DECODED), + (9, 51, DECODED), + (23, 51, DECODED), + (40, 51, CONTINUE), + (2, 52, DECODED), + (9, 52, DECODED), + (23, 52, DECODED), + (40, 52, CONTINUE), + (2, 53, DECODED), + (9, 53, DECODED), + (23, 53, DECODED), + (40, 53, CONTINUE), + ], + [ + (3, 47, DECODED), + (6, 47, DECODED), + (10, 47, DECODED), + (15, 47, DECODED), + (24, 47, DECODED), + (31, 47, DECODED), + (41, 47, DECODED), + (56, 47, CONTINUE), + (3, 51, DECODED), + (6, 51, DECODED), + (10, 51, DECODED), + (15, 51, DECODED), + (24, 51, DECODED), + (31, 51, DECODED), + (41, 51, DECODED), + (56, 51, CONTINUE), + ], + [ + (3, 52, DECODED), + (6, 52, DECODED), + (10, 52, DECODED), + (15, 52, DECODED), + (24, 52, DECODED), + (31, 52, DECODED), + (41, 52, DECODED), + (56, 52, CONTINUE), + (3, 53, DECODED), + (6, 53, DECODED), + (10, 53, DECODED), + (15, 53, DECODED), + (24, 53, DECODED), + (31, 53, DECODED), + (41, 53, DECODED), + (56, 53, CONTINUE), + ], + [ + (2, 54, DECODED), + (9, 54, DECODED), + (23, 54, DECODED), + (40, 54, CONTINUE), + (2, 55, DECODED), + (9, 55, DECODED), + (23, 55, DECODED), + (40, 55, CONTINUE), + (2, 56, DECODED), + (9, 56, DECODED), + (23, 56, DECODED), + (40, 56, CONTINUE), + (2, 57, DECODED), + (9, 57, DECODED), + (23, 57, DECODED), + (40, 57, CONTINUE), + ], + [ + (3, 54, DECODED), + (6, 54, DECODED), + (10, 54, DECODED), + (15, 54, DECODED), + (24, 54, DECODED), + (31, 54, DECODED), + (41, 54, DECODED), + (56, 54, CONTINUE), + (3, 55, DECODED), + (6, 55, DECODED), + (10, 55, DECODED), + (15, 55, DECODED), + (24, 55, DECODED), + (31, 55, DECODED), + (41, 55, DECODED), + (56, 55, CONTINUE), + ], + [ + (3, 56, DECODED), + (6, 56, DECODED), + (10, 56, DECODED), + (15, 56, DECODED), + (24, 56, DECODED), + (31, 56, DECODED), + (41, 56, DECODED), + (56, 56, CONTINUE), + (3, 57, DECODED), + (6, 57, DECODED), + (10, 57, DECODED), + (15, 57, DECODED), + (24, 57, DECODED), + (31, 57, DECODED), + (41, 57, DECODED), + (56, 57, CONTINUE), + ], + [ + (26, 0, OK), + (27, 0, OK), + (29, 0, OK), + (30, 0, OK), + (33, 0, OK), + (34, 0, OK), + (36, 0, OK), + (37, 0, OK), + (43, 0, OK), + (46, 0, OK), + (50, 0, OK), + (53, 0, OK), + (58, 0, OK), + (61, 0, OK), + (65, 0, OK), + (68, 0, END_OF_STRING), + ], + [ + (0, 61, DECODED), + (0, 65, DECODED), + (0, 95, DECODED), + (0, 98, DECODED), + (0, 100, DECODED), + (0, 102, DECODED), + (0, 103, DECODED), + (0, 104, DECODED), + (0, 108, DECODED), + (0, 109, DECODED), + (0, 110, DECODED), + (0, 112, DECODED), + (0, 114, DECODED), + (0, 117, DECODED), + (38, 0, OK), + (39, 0, OK), + ], + [ + (1, 61, DECODED), + (22, 61, CONTINUE), + (1, 65, DECODED), + (22, 65, CONTINUE), + (1, 95, DECODED), + (22, 95, CONTINUE), + (1, 98, DECODED), + (22, 98, CONTINUE), + (1, 100, DECODED), + (22, 100, CONTINUE), + (1, 102, DECODED), + (22, 102, CONTINUE), + (1, 103, DECODED), + (22, 103, CONTINUE), + (1, 104, DECODED), + (22, 104, CONTINUE), + ], + [ + (2, 61, DECODED), + (9, 61, DECODED), + (23, 61, DECODED), + (40, 61, CONTINUE), + (2, 65, DECODED), + (9, 65, DECODED), + (23, 65, DECODED), + (40, 65, CONTINUE), + (2, 95, DECODED), + (9, 95, DECODED), + (23, 95, DECODED), + (40, 95, CONTINUE), + (2, 98, DECODED), + (9, 98, DECODED), + (23, 98, DECODED), + (40, 98, CONTINUE), + ], + [ + (3, 61, DECODED), + (6, 61, DECODED), + (10, 61, DECODED), + (15, 61, DECODED), + (24, 61, DECODED), + (31, 61, DECODED), + (41, 61, DECODED), + (56, 61, CONTINUE), + (3, 65, DECODED), + (6, 65, DECODED), + (10, 65, DECODED), + (15, 65, DECODED), + (24, 65, DECODED), + (31, 65, DECODED), + (41, 65, DECODED), + (56, 65, CONTINUE), + ], + [ + (3, 95, DECODED), + (6, 95, DECODED), + (10, 95, DECODED), + (15, 95, DECODED), + (24, 95, DECODED), + (31, 95, DECODED), + (41, 95, DECODED), + (56, 95, CONTINUE), + (3, 98, DECODED), + (6, 98, DECODED), + (10, 98, DECODED), + (15, 98, DECODED), + (24, 98, DECODED), + (31, 98, DECODED), + (41, 98, DECODED), + (56, 98, CONTINUE), + ], + [ + (2, 100, DECODED), + (9, 100, DECODED), + (23, 100, DECODED), + (40, 100, CONTINUE), + (2, 102, DECODED), + (9, 102, DECODED), + (23, 102, DECODED), + (40, 102, CONTINUE), + (2, 103, DECODED), + (9, 103, DECODED), + (23, 103, DECODED), + (40, 103, CONTINUE), + (2, 104, DECODED), + (9, 104, DECODED), + (23, 104, DECODED), + (40, 104, CONTINUE), + ], + [ + (3, 100, DECODED), + (6, 100, DECODED), + (10, 100, DECODED), + (15, 100, DECODED), + (24, 100, DECODED), + (31, 100, DECODED), + (41, 100, DECODED), + (56, 100, CONTINUE), + (3, 102, DECODED), + (6, 102, DECODED), + (10, 102, DECODED), + (15, 102, DECODED), + (24, 102, DECODED), + (31, 102, DECODED), + (41, 102, DECODED), + (56, 102, CONTINUE), + ], + [ + (3, 103, DECODED), + (6, 103, DECODED), + (10, 103, DECODED), + (15, 103, DECODED), + (24, 103, DECODED), + (31, 103, DECODED), + (41, 103, DECODED), + (56, 103, CONTINUE), + (3, 104, DECODED), + (6, 104, DECODED), + (10, 104, DECODED), + (15, 104, DECODED), + (24, 104, DECODED), + (31, 104, DECODED), + (41, 104, DECODED), + (56, 104, CONTINUE), + ], + [ + (1, 108, DECODED), + (22, 108, CONTINUE), + (1, 109, DECODED), + (22, 109, CONTINUE), + (1, 110, DECODED), + (22, 110, CONTINUE), + (1, 112, DECODED), + (22, 112, CONTINUE), + (1, 114, DECODED), + (22, 114, CONTINUE), + (1, 117, DECODED), + (22, 117, CONTINUE), + (0, 58, DECODED), + (0, 66, DECODED), + (0, 67, DECODED), + (0, 68, DECODED), + ], + [ + (2, 108, DECODED), + (9, 108, DECODED), + (23, 108, DECODED), + (40, 108, CONTINUE), + (2, 109, DECODED), + (9, 109, DECODED), + (23, 109, DECODED), + (40, 109, CONTINUE), + (2, 110, DECODED), + (9, 110, DECODED), + (23, 110, DECODED), + (40, 110, CONTINUE), + (2, 112, DECODED), + (9, 112, DECODED), + (23, 112, DECODED), + (40, 112, CONTINUE), + ], + [ + (3, 108, DECODED), + (6, 108, DECODED), + (10, 108, DECODED), + (15, 108, DECODED), + (24, 108, DECODED), + (31, 108, DECODED), + (41, 108, DECODED), + (56, 108, CONTINUE), + (3, 109, DECODED), + (6, 109, DECODED), + (10, 109, DECODED), + (15, 109, DECODED), + (24, 109, DECODED), + (31, 109, DECODED), + (41, 109, DECODED), + (56, 109, CONTINUE), + ], + [ + (3, 110, DECODED), + (6, 110, DECODED), + (10, 110, DECODED), + (15, 110, DECODED), + (24, 110, DECODED), + (31, 110, DECODED), + (41, 110, DECODED), + (56, 110, CONTINUE), + (3, 112, DECODED), + (6, 112, DECODED), + (10, 112, DECODED), + (15, 112, DECODED), + (24, 112, DECODED), + (31, 112, DECODED), + (41, 112, DECODED), + (56, 112, CONTINUE), + ], + [ + (2, 114, DECODED), + (9, 114, DECODED), + (23, 114, DECODED), + (40, 114, CONTINUE), + (2, 117, DECODED), + (9, 117, DECODED), + (23, 117, DECODED), + (40, 117, CONTINUE), + (1, 58, DECODED), + (22, 58, CONTINUE), + (1, 66, DECODED), + (22, 66, CONTINUE), + (1, 67, DECODED), + (22, 67, CONTINUE), + (1, 68, DECODED), + (22, 68, CONTINUE), + ], + [ + (3, 114, DECODED), + (6, 114, DECODED), + (10, 114, DECODED), + (15, 114, DECODED), + (24, 114, DECODED), + (31, 114, DECODED), + (41, 114, DECODED), + (56, 114, CONTINUE), + (3, 117, DECODED), + (6, 117, DECODED), + (10, 117, DECODED), + (15, 117, DECODED), + (24, 117, DECODED), + (31, 117, DECODED), + (41, 117, DECODED), + (56, 117, CONTINUE), + ], + [ + (2, 58, DECODED), + (9, 58, DECODED), + (23, 58, DECODED), + (40, 58, CONTINUE), + (2, 66, DECODED), + (9, 66, DECODED), + (23, 66, DECODED), + (40, 66, CONTINUE), + (2, 67, DECODED), + (9, 67, DECODED), + (23, 67, DECODED), + (40, 67, CONTINUE), + (2, 68, DECODED), + (9, 68, DECODED), + (23, 68, DECODED), + (40, 68, CONTINUE), + ], + [ + (3, 58, DECODED), + (6, 58, DECODED), + (10, 58, DECODED), + (15, 58, DECODED), + (24, 58, DECODED), + (31, 58, DECODED), + (41, 58, DECODED), + (56, 58, CONTINUE), + (3, 66, DECODED), + (6, 66, DECODED), + (10, 66, DECODED), + (15, 66, DECODED), + (24, 66, DECODED), + (31, 66, DECODED), + (41, 66, DECODED), + (56, 66, CONTINUE), + ], + [ + (3, 67, DECODED), + (6, 67, DECODED), + (10, 67, DECODED), + (15, 67, DECODED), + (24, 67, DECODED), + (31, 67, DECODED), + (41, 67, DECODED), + (56, 67, CONTINUE), + (3, 68, DECODED), + (6, 68, DECODED), + (10, 68, DECODED), + (15, 68, DECODED), + (24, 68, DECODED), + (31, 68, DECODED), + (41, 68, DECODED), + (56, 68, CONTINUE), + ], + [ + (44, 0, OK), + (45, 0, OK), + (47, 0, OK), + (48, 0, OK), + (51, 0, OK), + (52, 0, OK), + (54, 0, OK), + (55, 0, OK), + (59, 0, OK), + (60, 0, OK), + (62, 0, OK), + (63, 0, OK), + (66, 0, OK), + (67, 0, OK), + (69, 0, OK), + (72, 0, END_OF_STRING), + ], + [ + (0, 69, DECODED), + (0, 70, DECODED), + (0, 71, DECODED), + (0, 72, DECODED), + (0, 73, DECODED), + (0, 74, DECODED), + (0, 75, DECODED), + (0, 76, DECODED), + (0, 77, DECODED), + (0, 78, DECODED), + (0, 79, DECODED), + (0, 80, DECODED), + (0, 81, DECODED), + (0, 82, DECODED), + (0, 83, DECODED), + (0, 84, DECODED), + ], + [ + (1, 69, DECODED), + (22, 69, CONTINUE), + (1, 70, DECODED), + (22, 70, CONTINUE), + (1, 71, DECODED), + (22, 71, CONTINUE), + (1, 72, DECODED), + (22, 72, CONTINUE), + (1, 73, DECODED), + (22, 73, CONTINUE), + (1, 74, DECODED), + (22, 74, CONTINUE), + (1, 75, DECODED), + (22, 75, CONTINUE), + (1, 76, DECODED), + (22, 76, CONTINUE), + ], + [ + (2, 69, DECODED), + (9, 69, DECODED), + (23, 69, DECODED), + (40, 69, CONTINUE), + (2, 70, DECODED), + (9, 70, DECODED), + (23, 70, DECODED), + (40, 70, CONTINUE), + (2, 71, DECODED), + (9, 71, DECODED), + (23, 71, DECODED), + (40, 71, CONTINUE), + (2, 72, DECODED), + (9, 72, DECODED), + (23, 72, DECODED), + (40, 72, CONTINUE), + ], + [ + (3, 69, DECODED), + (6, 69, DECODED), + (10, 69, DECODED), + (15, 69, DECODED), + (24, 69, DECODED), + (31, 69, DECODED), + (41, 69, DECODED), + (56, 69, CONTINUE), + (3, 70, DECODED), + (6, 70, DECODED), + (10, 70, DECODED), + (15, 70, DECODED), + (24, 70, DECODED), + (31, 70, DECODED), + (41, 70, DECODED), + (56, 70, CONTINUE), + ], + [ + (3, 71, DECODED), + (6, 71, DECODED), + (10, 71, DECODED), + (15, 71, DECODED), + (24, 71, DECODED), + (31, 71, DECODED), + (41, 71, DECODED), + (56, 71, CONTINUE), + (3, 72, DECODED), + (6, 72, DECODED), + (10, 72, DECODED), + (15, 72, DECODED), + (24, 72, DECODED), + (31, 72, DECODED), + (41, 72, DECODED), + (56, 72, CONTINUE), + ], + [ + (2, 73, DECODED), + (9, 73, DECODED), + (23, 73, DECODED), + (40, 73, CONTINUE), + (2, 74, DECODED), + (9, 74, DECODED), + (23, 74, DECODED), + (40, 74, CONTINUE), + (2, 75, DECODED), + (9, 75, DECODED), + (23, 75, DECODED), + (40, 75, CONTINUE), + (2, 76, DECODED), + (9, 76, DECODED), + (23, 76, DECODED), + (40, 76, CONTINUE), + ], + [ + (3, 73, DECODED), + (6, 73, DECODED), + (10, 73, DECODED), + (15, 73, DECODED), + (24, 73, DECODED), + (31, 73, DECODED), + (41, 73, DECODED), + (56, 73, CONTINUE), + (3, 74, DECODED), + (6, 74, DECODED), + (10, 74, DECODED), + (15, 74, DECODED), + (24, 74, DECODED), + (31, 74, DECODED), + (41, 74, DECODED), + (56, 74, CONTINUE), + ], + [ + (3, 75, DECODED), + (6, 75, DECODED), + (10, 75, DECODED), + (15, 75, DECODED), + (24, 75, DECODED), + (31, 75, DECODED), + (41, 75, DECODED), + (56, 75, CONTINUE), + (3, 76, DECODED), + (6, 76, DECODED), + (10, 76, DECODED), + (15, 76, DECODED), + (24, 76, DECODED), + (31, 76, DECODED), + (41, 76, DECODED), + (56, 76, CONTINUE), + ], + [ + (1, 77, DECODED), + (22, 77, CONTINUE), + (1, 78, DECODED), + (22, 78, CONTINUE), + (1, 79, DECODED), + (22, 79, CONTINUE), + (1, 80, DECODED), + (22, 80, CONTINUE), + (1, 81, DECODED), + (22, 81, CONTINUE), + (1, 82, DECODED), + (22, 82, CONTINUE), + (1, 83, DECODED), + (22, 83, CONTINUE), + (1, 84, DECODED), + (22, 84, CONTINUE), + ], + [ + (2, 77, DECODED), + (9, 77, DECODED), + (23, 77, DECODED), + (40, 77, CONTINUE), + (2, 78, DECODED), + (9, 78, DECODED), + (23, 78, DECODED), + (40, 78, CONTINUE), + (2, 79, DECODED), + (9, 79, DECODED), + (23, 79, DECODED), + (40, 79, CONTINUE), + (2, 80, DECODED), + (9, 80, DECODED), + (23, 80, DECODED), + (40, 80, CONTINUE), + ], + [ + (3, 77, DECODED), + (6, 77, DECODED), + (10, 77, DECODED), + (15, 77, DECODED), + (24, 77, DECODED), + (31, 77, DECODED), + (41, 77, DECODED), + (56, 77, CONTINUE), + (3, 78, DECODED), + (6, 78, DECODED), + (10, 78, DECODED), + (15, 78, DECODED), + (24, 78, DECODED), + (31, 78, DECODED), + (41, 78, DECODED), + (56, 78, CONTINUE), + ], + [ + (3, 79, DECODED), + (6, 79, DECODED), + (10, 79, DECODED), + (15, 79, DECODED), + (24, 79, DECODED), + (31, 79, DECODED), + (41, 79, DECODED), + (56, 79, CONTINUE), + (3, 80, DECODED), + (6, 80, DECODED), + (10, 80, DECODED), + (15, 80, DECODED), + (24, 80, DECODED), + (31, 80, DECODED), + (41, 80, DECODED), + (56, 80, CONTINUE), + ], + [ + (2, 81, DECODED), + (9, 81, DECODED), + (23, 81, DECODED), + (40, 81, CONTINUE), + (2, 82, DECODED), + (9, 82, DECODED), + (23, 82, DECODED), + (40, 82, CONTINUE), + (2, 83, DECODED), + (9, 83, DECODED), + (23, 83, DECODED), + (40, 83, CONTINUE), + (2, 84, DECODED), + (9, 84, DECODED), + (23, 84, DECODED), + (40, 84, CONTINUE), + ], + [ + (3, 81, DECODED), + (6, 81, DECODED), + (10, 81, DECODED), + (15, 81, DECODED), + (24, 81, DECODED), + (31, 81, DECODED), + (41, 81, DECODED), + (56, 81, CONTINUE), + (3, 82, DECODED), + (6, 82, DECODED), + (10, 82, DECODED), + (15, 82, DECODED), + (24, 82, DECODED), + (31, 82, DECODED), + (41, 82, DECODED), + (56, 82, CONTINUE), + ], + [ + (3, 83, DECODED), + (6, 83, DECODED), + (10, 83, DECODED), + (15, 83, DECODED), + (24, 83, DECODED), + (31, 83, DECODED), + (41, 83, DECODED), + (56, 83, CONTINUE), + (3, 84, DECODED), + (6, 84, DECODED), + (10, 84, DECODED), + (15, 84, DECODED), + (24, 84, DECODED), + (31, 84, DECODED), + (41, 84, DECODED), + (56, 84, CONTINUE), + ], + [ + (0, 85, DECODED), + (0, 86, DECODED), + (0, 87, DECODED), + (0, 89, DECODED), + (0, 106, DECODED), + (0, 107, DECODED), + (0, 113, DECODED), + (0, 118, DECODED), + (0, 119, DECODED), + (0, 120, DECODED), + (0, 121, DECODED), + (0, 122, DECODED), + (70, 0, OK), + (71, 0, OK), + (73, 0, OK), + (74, 0, END_OF_STRING), + ], + [ + (1, 85, DECODED), + (22, 85, CONTINUE), + (1, 86, DECODED), + (22, 86, CONTINUE), + (1, 87, DECODED), + (22, 87, CONTINUE), + (1, 89, DECODED), + (22, 89, CONTINUE), + (1, 106, DECODED), + (22, 106, CONTINUE), + (1, 107, DECODED), + (22, 107, CONTINUE), + (1, 113, DECODED), + (22, 113, CONTINUE), + (1, 118, DECODED), + (22, 118, CONTINUE), + ], + [ + (2, 85, DECODED), + (9, 85, DECODED), + (23, 85, DECODED), + (40, 85, CONTINUE), + (2, 86, DECODED), + (9, 86, DECODED), + (23, 86, DECODED), + (40, 86, CONTINUE), + (2, 87, DECODED), + (9, 87, DECODED), + (23, 87, DECODED), + (40, 87, CONTINUE), + (2, 89, DECODED), + (9, 89, DECODED), + (23, 89, DECODED), + (40, 89, CONTINUE), + ], + [ + (3, 85, DECODED), + (6, 85, DECODED), + (10, 85, DECODED), + (15, 85, DECODED), + (24, 85, DECODED), + (31, 85, DECODED), + (41, 85, DECODED), + (56, 85, CONTINUE), + (3, 86, DECODED), + (6, 86, DECODED), + (10, 86, DECODED), + (15, 86, DECODED), + (24, 86, DECODED), + (31, 86, DECODED), + (41, 86, DECODED), + (56, 86, CONTINUE), + ], + [ + (3, 87, DECODED), + (6, 87, DECODED), + (10, 87, DECODED), + (15, 87, DECODED), + (24, 87, DECODED), + (31, 87, DECODED), + (41, 87, DECODED), + (56, 87, CONTINUE), + (3, 89, DECODED), + (6, 89, DECODED), + (10, 89, DECODED), + (15, 89, DECODED), + (24, 89, DECODED), + (31, 89, DECODED), + (41, 89, DECODED), + (56, 89, CONTINUE), + ], + [ + (2, 106, DECODED), + (9, 106, DECODED), + (23, 106, DECODED), + (40, 106, CONTINUE), + (2, 107, DECODED), + (9, 107, DECODED), + (23, 107, DECODED), + (40, 107, CONTINUE), + (2, 113, DECODED), + (9, 113, DECODED), + (23, 113, DECODED), + (40, 113, CONTINUE), + (2, 118, DECODED), + (9, 118, DECODED), + (23, 118, DECODED), + (40, 118, CONTINUE), + ], + [ + (3, 106, DECODED), + (6, 106, DECODED), + (10, 106, DECODED), + (15, 106, DECODED), + (24, 106, DECODED), + (31, 106, DECODED), + (41, 106, DECODED), + (56, 106, CONTINUE), + (3, 107, DECODED), + (6, 107, DECODED), + (10, 107, DECODED), + (15, 107, DECODED), + (24, 107, DECODED), + (31, 107, DECODED), + (41, 107, DECODED), + (56, 107, CONTINUE), + ], + [ + (3, 113, DECODED), + (6, 113, DECODED), + (10, 113, DECODED), + (15, 113, DECODED), + (24, 113, DECODED), + (31, 113, DECODED), + (41, 113, DECODED), + (56, 113, CONTINUE), + (3, 118, DECODED), + (6, 118, DECODED), + (10, 118, DECODED), + (15, 118, DECODED), + (24, 118, DECODED), + (31, 118, DECODED), + (41, 118, DECODED), + (56, 118, CONTINUE), + ], + [ + (1, 119, DECODED), + (22, 119, CONTINUE), + (1, 120, DECODED), + (22, 120, CONTINUE), + (1, 121, DECODED), + (22, 121, CONTINUE), + (1, 122, DECODED), + (22, 122, CONTINUE), + (0, 38, DECODED), + (0, 42, DECODED), + (0, 44, DECODED), + (0, 59, DECODED), + (0, 88, DECODED), + (0, 90, DECODED), + (75, 0, OK), + (78, 0, OK), + ], + [ + (2, 119, DECODED), + (9, 119, DECODED), + (23, 119, DECODED), + (40, 119, CONTINUE), + (2, 120, DECODED), + (9, 120, DECODED), + (23, 120, DECODED), + (40, 120, CONTINUE), + (2, 121, DECODED), + (9, 121, DECODED), + (23, 121, DECODED), + (40, 121, CONTINUE), + (2, 122, DECODED), + (9, 122, DECODED), + (23, 122, DECODED), + (40, 122, CONTINUE), + ], + [ + (3, 119, DECODED), + (6, 119, DECODED), + (10, 119, DECODED), + (15, 119, DECODED), + (24, 119, DECODED), + (31, 119, DECODED), + (41, 119, DECODED), + (56, 119, CONTINUE), + (3, 120, DECODED), + (6, 120, DECODED), + (10, 120, DECODED), + (15, 120, DECODED), + (24, 120, DECODED), + (31, 120, DECODED), + (41, 120, DECODED), + (56, 120, CONTINUE), + ], + [ + (3, 121, DECODED), + (6, 121, DECODED), + (10, 121, DECODED), + (15, 121, DECODED), + (24, 121, DECODED), + (31, 121, DECODED), + (41, 121, DECODED), + (56, 121, CONTINUE), + (3, 122, DECODED), + (6, 122, DECODED), + (10, 122, DECODED), + (15, 122, DECODED), + (24, 122, DECODED), + (31, 122, DECODED), + (41, 122, DECODED), + (56, 122, CONTINUE), + ], + [ + (1, 38, DECODED), + (22, 38, CONTINUE), + (1, 42, DECODED), + (22, 42, CONTINUE), + (1, 44, DECODED), + (22, 44, CONTINUE), + (1, 59, DECODED), + (22, 59, CONTINUE), + (1, 88, DECODED), + (22, 88, CONTINUE), + (1, 90, DECODED), + (22, 90, CONTINUE), + (76, 0, OK), + (77, 0, OK), + (79, 0, OK), + (81, 0, OK), + ], + [ + (2, 38, DECODED), + (9, 38, DECODED), + (23, 38, DECODED), + (40, 38, CONTINUE), + (2, 42, DECODED), + (9, 42, DECODED), + (23, 42, DECODED), + (40, 42, CONTINUE), + (2, 44, DECODED), + (9, 44, DECODED), + (23, 44, DECODED), + (40, 44, CONTINUE), + (2, 59, DECODED), + (9, 59, DECODED), + (23, 59, DECODED), + (40, 59, CONTINUE), + ], + [ + (3, 38, DECODED), + (6, 38, DECODED), + (10, 38, DECODED), + (15, 38, DECODED), + (24, 38, DECODED), + (31, 38, DECODED), + (41, 38, DECODED), + (56, 38, CONTINUE), + (3, 42, DECODED), + (6, 42, DECODED), + (10, 42, DECODED), + (15, 42, DECODED), + (24, 42, DECODED), + (31, 42, DECODED), + (41, 42, DECODED), + (56, 42, CONTINUE), + ], + [ + (3, 44, DECODED), + (6, 44, DECODED), + (10, 44, DECODED), + (15, 44, DECODED), + (24, 44, DECODED), + (31, 44, DECODED), + (41, 44, DECODED), + (56, 44, CONTINUE), + (3, 59, DECODED), + (6, 59, DECODED), + (10, 59, DECODED), + (15, 59, DECODED), + (24, 59, DECODED), + (31, 59, DECODED), + (41, 59, DECODED), + (56, 59, CONTINUE), + ], + [ + (2, 88, DECODED), + (9, 88, DECODED), + (23, 88, DECODED), + (40, 88, CONTINUE), + (2, 90, DECODED), + (9, 90, DECODED), + (23, 90, DECODED), + (40, 90, CONTINUE), + (0, 33, DECODED), + (0, 34, DECODED), + (0, 40, DECODED), + (0, 41, DECODED), + (0, 63, DECODED), + (80, 0, OK), + (82, 0, OK), + (84, 0, OK), + ], + [ + (3, 88, DECODED), + (6, 88, DECODED), + (10, 88, DECODED), + (15, 88, DECODED), + (24, 88, DECODED), + (31, 88, DECODED), + (41, 88, DECODED), + (56, 88, CONTINUE), + (3, 90, DECODED), + (6, 90, DECODED), + (10, 90, DECODED), + (15, 90, DECODED), + (24, 90, DECODED), + (31, 90, DECODED), + (41, 90, DECODED), + (56, 90, CONTINUE), + ], + [ + (1, 33, DECODED), + (22, 33, CONTINUE), + (1, 34, DECODED), + (22, 34, CONTINUE), + (1, 40, DECODED), + (22, 40, CONTINUE), + (1, 41, DECODED), + (22, 41, CONTINUE), + (1, 63, DECODED), + (22, 63, CONTINUE), + (0, 39, DECODED), + (0, 43, DECODED), + (0, 124, DECODED), + (83, 0, OK), + (85, 0, OK), + (88, 0, OK), + ], + [ + (2, 33, DECODED), + (9, 33, DECODED), + (23, 33, DECODED), + (40, 33, CONTINUE), + (2, 34, DECODED), + (9, 34, DECODED), + (23, 34, DECODED), + (40, 34, CONTINUE), + (2, 40, DECODED), + (9, 40, DECODED), + (23, 40, DECODED), + (40, 40, CONTINUE), + (2, 41, DECODED), + (9, 41, DECODED), + (23, 41, DECODED), + (40, 41, CONTINUE), + ], + [ + (3, 33, DECODED), + (6, 33, DECODED), + (10, 33, DECODED), + (15, 33, DECODED), + (24, 33, DECODED), + (31, 33, DECODED), + (41, 33, DECODED), + (56, 33, CONTINUE), + (3, 34, DECODED), + (6, 34, DECODED), + (10, 34, DECODED), + (15, 34, DECODED), + (24, 34, DECODED), + (31, 34, DECODED), + (41, 34, DECODED), + (56, 34, CONTINUE), + ], + [ + (3, 40, DECODED), + (6, 40, DECODED), + (10, 40, DECODED), + (15, 40, DECODED), + (24, 40, DECODED), + (31, 40, DECODED), + (41, 40, DECODED), + (56, 40, CONTINUE), + (3, 41, DECODED), + (6, 41, DECODED), + (10, 41, DECODED), + (15, 41, DECODED), + (24, 41, DECODED), + (31, 41, DECODED), + (41, 41, DECODED), + (56, 41, CONTINUE), + ], + [ + (2, 63, DECODED), + (9, 63, DECODED), + (23, 63, DECODED), + (40, 63, CONTINUE), + (1, 39, DECODED), + (22, 39, CONTINUE), + (1, 43, DECODED), + (22, 43, CONTINUE), + (1, 124, DECODED), + (22, 124, CONTINUE), + (0, 35, DECODED), + (0, 62, DECODED), + (86, 0, OK), + (87, 0, OK), + (89, 0, OK), + (90, 0, OK), + ], + [ + (3, 63, DECODED), + (6, 63, DECODED), + (10, 63, DECODED), + (15, 63, DECODED), + (24, 63, DECODED), + (31, 63, DECODED), + (41, 63, DECODED), + (56, 63, CONTINUE), + (2, 39, DECODED), + (9, 39, DECODED), + (23, 39, DECODED), + (40, 39, CONTINUE), + (2, 43, DECODED), + (9, 43, DECODED), + (23, 43, DECODED), + (40, 43, CONTINUE), + ], + [ + (3, 39, DECODED), + (6, 39, DECODED), + (10, 39, DECODED), + (15, 39, DECODED), + (24, 39, DECODED), + (31, 39, DECODED), + (41, 39, DECODED), + (56, 39, CONTINUE), + (3, 43, DECODED), + (6, 43, DECODED), + (10, 43, DECODED), + (15, 43, DECODED), + (24, 43, DECODED), + (31, 43, DECODED), + (41, 43, DECODED), + (56, 43, CONTINUE), + ], + [ + (2, 124, DECODED), + (9, 124, DECODED), + (23, 124, DECODED), + (40, 124, CONTINUE), + (1, 35, DECODED), + (22, 35, CONTINUE), + (1, 62, DECODED), + (22, 62, CONTINUE), + (0, 0, DECODED), + (0, 36, DECODED), + (0, 64, DECODED), + (0, 91, DECODED), + (0, 93, DECODED), + (0, 126, DECODED), + (91, 0, OK), + (92, 0, OK), + ], + [ + (3, 124, DECODED), + (6, 124, DECODED), + (10, 124, DECODED), + (15, 124, DECODED), + (24, 124, DECODED), + (31, 124, DECODED), + (41, 124, DECODED), + (56, 124, CONTINUE), + (2, 35, DECODED), + (9, 35, DECODED), + (23, 35, DECODED), + (40, 35, CONTINUE), + (2, 62, DECODED), + (9, 62, DECODED), + (23, 62, DECODED), + (40, 62, CONTINUE), + ], + [ + (3, 35, DECODED), + (6, 35, DECODED), + (10, 35, DECODED), + (15, 35, DECODED), + (24, 35, DECODED), + (31, 35, DECODED), + (41, 35, DECODED), + (56, 35, CONTINUE), + (3, 62, DECODED), + (6, 62, DECODED), + (10, 62, DECODED), + (15, 62, DECODED), + (24, 62, DECODED), + (31, 62, DECODED), + (41, 62, DECODED), + (56, 62, CONTINUE), + ], + [ + (1, 0, DECODED), + (22, 0, CONTINUE), + (1, 36, DECODED), + (22, 36, CONTINUE), + (1, 64, DECODED), + (22, 64, CONTINUE), + (1, 91, DECODED), + (22, 91, CONTINUE), + (1, 93, DECODED), + (22, 93, CONTINUE), + (1, 126, DECODED), + (22, 126, CONTINUE), + (0, 94, DECODED), + (0, 125, DECODED), + (93, 0, OK), + (94, 0, OK), + ], + [ + (2, 0, DECODED), + (9, 0, DECODED), + (23, 0, DECODED), + (40, 0, CONTINUE), + (2, 36, DECODED), + (9, 36, DECODED), + (23, 36, DECODED), + (40, 36, CONTINUE), + (2, 64, DECODED), + (9, 64, DECODED), + (23, 64, DECODED), + (40, 64, CONTINUE), + (2, 91, DECODED), + (9, 91, DECODED), + (23, 91, DECODED), + (40, 91, CONTINUE), + ], + [ + (3, 0, DECODED), + (6, 0, DECODED), + (10, 0, DECODED), + (15, 0, DECODED), + (24, 0, DECODED), + (31, 0, DECODED), + (41, 0, DECODED), + (56, 0, CONTINUE), + (3, 36, DECODED), + (6, 36, DECODED), + (10, 36, DECODED), + (15, 36, DECODED), + (24, 36, DECODED), + (31, 36, DECODED), + (41, 36, DECODED), + (56, 36, CONTINUE), + ], + [ + (3, 64, DECODED), + (6, 64, DECODED), + (10, 64, DECODED), + (15, 64, DECODED), + (24, 64, DECODED), + (31, 64, DECODED), + (41, 64, DECODED), + (56, 64, CONTINUE), + (3, 91, DECODED), + (6, 91, DECODED), + (10, 91, DECODED), + (15, 91, DECODED), + (24, 91, DECODED), + (31, 91, DECODED), + (41, 91, DECODED), + (56, 91, CONTINUE), + ], + [ + (2, 93, DECODED), + (9, 93, DECODED), + (23, 93, DECODED), + (40, 93, CONTINUE), + (2, 126, DECODED), + (9, 126, DECODED), + (23, 126, DECODED), + (40, 126, CONTINUE), + (1, 94, DECODED), + (22, 94, CONTINUE), + (1, 125, DECODED), + (22, 125, CONTINUE), + (0, 60, DECODED), + (0, 96, DECODED), + (0, 123, DECODED), + (95, 0, OK), + ], + [ + (3, 93, DECODED), + (6, 93, DECODED), + (10, 93, DECODED), + (15, 93, DECODED), + (24, 93, DECODED), + (31, 93, DECODED), + (41, 93, DECODED), + (56, 93, CONTINUE), + (3, 126, DECODED), + (6, 126, DECODED), + (10, 126, DECODED), + (15, 126, DECODED), + (24, 126, DECODED), + (31, 126, DECODED), + (41, 126, DECODED), + (56, 126, CONTINUE), + ], + [ + (2, 94, DECODED), + (9, 94, DECODED), + (23, 94, DECODED), + (40, 94, CONTINUE), + (2, 125, DECODED), + (9, 125, DECODED), + (23, 125, DECODED), + (40, 125, CONTINUE), + (1, 60, DECODED), + (22, 60, CONTINUE), + (1, 96, DECODED), + (22, 96, CONTINUE), + (1, 123, DECODED), + (22, 123, CONTINUE), + (96, 0, OK), + (110, 0, OK), + ], + [ + (3, 94, DECODED), + (6, 94, DECODED), + (10, 94, DECODED), + (15, 94, DECODED), + (24, 94, DECODED), + (31, 94, DECODED), + (41, 94, DECODED), + (56, 94, CONTINUE), + (3, 125, DECODED), + (6, 125, DECODED), + (10, 125, DECODED), + (15, 125, DECODED), + (24, 125, DECODED), + (31, 125, DECODED), + (41, 125, DECODED), + (56, 125, CONTINUE), + ], + [ + (2, 60, DECODED), + (9, 60, DECODED), + (23, 60, DECODED), + (40, 60, CONTINUE), + (2, 96, DECODED), + (9, 96, DECODED), + (23, 96, DECODED), + (40, 96, CONTINUE), + (2, 123, DECODED), + (9, 123, DECODED), + (23, 123, DECODED), + (40, 123, CONTINUE), + (97, 0, OK), + (101, 0, OK), + (111, 0, OK), + (133, 0, OK), + ], + [ + (3, 60, DECODED), + (6, 60, DECODED), + (10, 60, DECODED), + (15, 60, DECODED), + (24, 60, DECODED), + (31, 60, DECODED), + (41, 60, DECODED), + (56, 60, CONTINUE), + (3, 96, DECODED), + (6, 96, DECODED), + (10, 96, DECODED), + (15, 96, DECODED), + (24, 96, DECODED), + (31, 96, DECODED), + (41, 96, DECODED), + (56, 96, CONTINUE), + ], + [ + (3, 123, DECODED), + (6, 123, DECODED), + (10, 123, DECODED), + (15, 123, DECODED), + (24, 123, DECODED), + (31, 123, DECODED), + (41, 123, DECODED), + (56, 123, CONTINUE), + (98, 0, OK), + (99, 0, OK), + (102, 0, OK), + (105, 0, OK), + (112, 0, OK), + (119, 0, OK), + (134, 0, OK), + (153, 0, OK), + ], + [ + (0, 92, DECODED), + (0, 195, DECODED), + (0, 208, DECODED), + (100, 0, OK), + (103, 0, OK), + (104, 0, OK), + (106, 0, OK), + (107, 0, OK), + (113, 0, OK), + (116, 0, OK), + (120, 0, OK), + (126, 0, OK), + (135, 0, OK), + (142, 0, OK), + (154, 0, OK), + (169, 0, OK), + ], + [ + (1, 92, DECODED), + (22, 92, CONTINUE), + (1, 195, DECODED), + (22, 195, CONTINUE), + (1, 208, DECODED), + (22, 208, CONTINUE), + (0, 128, DECODED), + (0, 130, DECODED), + (0, 131, DECODED), + (0, 162, DECODED), + (0, 184, DECODED), + (0, 194, DECODED), + (0, 224, DECODED), + (0, 226, DECODED), + (108, 0, OK), + (109, 0, OK), + ], + [ + (2, 92, DECODED), + (9, 92, DECODED), + (23, 92, DECODED), + (40, 92, CONTINUE), + (2, 195, DECODED), + (9, 195, DECODED), + (23, 195, DECODED), + (40, 195, CONTINUE), + (2, 208, DECODED), + (9, 208, DECODED), + (23, 208, DECODED), + (40, 208, CONTINUE), + (1, 128, DECODED), + (22, 128, CONTINUE), + (1, 130, DECODED), + (22, 130, CONTINUE), + ], + [ + (3, 92, DECODED), + (6, 92, DECODED), + (10, 92, DECODED), + (15, 92, DECODED), + (24, 92, DECODED), + (31, 92, DECODED), + (41, 92, DECODED), + (56, 92, CONTINUE), + (3, 195, DECODED), + (6, 195, DECODED), + (10, 195, DECODED), + (15, 195, DECODED), + (24, 195, DECODED), + (31, 195, DECODED), + (41, 195, DECODED), + (56, 195, CONTINUE), + ], + [ + (3, 208, DECODED), + (6, 208, DECODED), + (10, 208, DECODED), + (15, 208, DECODED), + (24, 208, DECODED), + (31, 208, DECODED), + (41, 208, DECODED), + (56, 208, CONTINUE), + (2, 128, DECODED), + (9, 128, DECODED), + (23, 128, DECODED), + (40, 128, CONTINUE), + (2, 130, DECODED), + (9, 130, DECODED), + (23, 130, DECODED), + (40, 130, CONTINUE), + ], + [ + (3, 128, DECODED), + (6, 128, DECODED), + (10, 128, DECODED), + (15, 128, DECODED), + (24, 128, DECODED), + (31, 128, DECODED), + (41, 128, DECODED), + (56, 128, CONTINUE), + (3, 130, DECODED), + (6, 130, DECODED), + (10, 130, DECODED), + (15, 130, DECODED), + (24, 130, DECODED), + (31, 130, DECODED), + (41, 130, DECODED), + (56, 130, CONTINUE), + ], + [ + (1, 131, DECODED), + (22, 131, CONTINUE), + (1, 162, DECODED), + (22, 162, CONTINUE), + (1, 184, DECODED), + (22, 184, CONTINUE), + (1, 194, DECODED), + (22, 194, CONTINUE), + (1, 224, DECODED), + (22, 224, CONTINUE), + (1, 226, DECODED), + (22, 226, CONTINUE), + (0, 153, DECODED), + (0, 161, DECODED), + (0, 167, DECODED), + (0, 172, DECODED), + ], + [ + (2, 131, DECODED), + (9, 131, DECODED), + (23, 131, DECODED), + (40, 131, CONTINUE), + (2, 162, DECODED), + (9, 162, DECODED), + (23, 162, DECODED), + (40, 162, CONTINUE), + (2, 184, DECODED), + (9, 184, DECODED), + (23, 184, DECODED), + (40, 184, CONTINUE), + (2, 194, DECODED), + (9, 194, DECODED), + (23, 194, DECODED), + (40, 194, CONTINUE), + ], + [ + (3, 131, DECODED), + (6, 131, DECODED), + (10, 131, DECODED), + (15, 131, DECODED), + (24, 131, DECODED), + (31, 131, DECODED), + (41, 131, DECODED), + (56, 131, CONTINUE), + (3, 162, DECODED), + (6, 162, DECODED), + (10, 162, DECODED), + (15, 162, DECODED), + (24, 162, DECODED), + (31, 162, DECODED), + (41, 162, DECODED), + (56, 162, CONTINUE), + ], + [ + (3, 184, DECODED), + (6, 184, DECODED), + (10, 184, DECODED), + (15, 184, DECODED), + (24, 184, DECODED), + (31, 184, DECODED), + (41, 184, DECODED), + (56, 184, CONTINUE), + (3, 194, DECODED), + (6, 194, DECODED), + (10, 194, DECODED), + (15, 194, DECODED), + (24, 194, DECODED), + (31, 194, DECODED), + (41, 194, DECODED), + (56, 194, CONTINUE), + ], + [ + (2, 224, DECODED), + (9, 224, DECODED), + (23, 224, DECODED), + (40, 224, CONTINUE), + (2, 226, DECODED), + (9, 226, DECODED), + (23, 226, DECODED), + (40, 226, CONTINUE), + (1, 153, DECODED), + (22, 153, CONTINUE), + (1, 161, DECODED), + (22, 161, CONTINUE), + (1, 167, DECODED), + (22, 167, CONTINUE), + (1, 172, DECODED), + (22, 172, CONTINUE), + ], + [ + (3, 224, DECODED), + (6, 224, DECODED), + (10, 224, DECODED), + (15, 224, DECODED), + (24, 224, DECODED), + (31, 224, DECODED), + (41, 224, DECODED), + (56, 224, CONTINUE), + (3, 226, DECODED), + (6, 226, DECODED), + (10, 226, DECODED), + (15, 226, DECODED), + (24, 226, DECODED), + (31, 226, DECODED), + (41, 226, DECODED), + (56, 226, CONTINUE), + ], + [ + (2, 153, DECODED), + (9, 153, DECODED), + (23, 153, DECODED), + (40, 153, CONTINUE), + (2, 161, DECODED), + (9, 161, DECODED), + (23, 161, DECODED), + (40, 161, CONTINUE), + (2, 167, DECODED), + (9, 167, DECODED), + (23, 167, DECODED), + (40, 167, CONTINUE), + (2, 172, DECODED), + (9, 172, DECODED), + (23, 172, DECODED), + (40, 172, CONTINUE), + ], + [ + (3, 153, DECODED), + (6, 153, DECODED), + (10, 153, DECODED), + (15, 153, DECODED), + (24, 153, DECODED), + (31, 153, DECODED), + (41, 153, DECODED), + (56, 153, CONTINUE), + (3, 161, DECODED), + (6, 161, DECODED), + (10, 161, DECODED), + (15, 161, DECODED), + (24, 161, DECODED), + (31, 161, DECODED), + (41, 161, DECODED), + (56, 161, CONTINUE), + ], + [ + (3, 167, DECODED), + (6, 167, DECODED), + (10, 167, DECODED), + (15, 167, DECODED), + (24, 167, DECODED), + (31, 167, DECODED), + (41, 167, DECODED), + (56, 167, CONTINUE), + (3, 172, DECODED), + (6, 172, DECODED), + (10, 172, DECODED), + (15, 172, DECODED), + (24, 172, DECODED), + (31, 172, DECODED), + (41, 172, DECODED), + (56, 172, CONTINUE), + ], + [ + (114, 0, OK), + (115, 0, OK), + (117, 0, OK), + (118, 0, OK), + (121, 0, OK), + (123, 0, OK), + (127, 0, OK), + (130, 0, OK), + (136, 0, OK), + (139, 0, OK), + (143, 0, OK), + (146, 0, OK), + (155, 0, OK), + (162, 0, OK), + (170, 0, OK), + (180, 0, OK), + ], + [ + (0, 176, DECODED), + (0, 177, DECODED), + (0, 179, DECODED), + (0, 209, DECODED), + (0, 216, DECODED), + (0, 217, DECODED), + (0, 227, DECODED), + (0, 229, DECODED), + (0, 230, DECODED), + (122, 0, OK), + (124, 0, OK), + (125, 0, OK), + (128, 0, OK), + (129, 0, OK), + (131, 0, OK), + (132, 0, OK), + ], + [ + (1, 176, DECODED), + (22, 176, CONTINUE), + (1, 177, DECODED), + (22, 177, CONTINUE), + (1, 179, DECODED), + (22, 179, CONTINUE), + (1, 209, DECODED), + (22, 209, CONTINUE), + (1, 216, DECODED), + (22, 216, CONTINUE), + (1, 217, DECODED), + (22, 217, CONTINUE), + (1, 227, DECODED), + (22, 227, CONTINUE), + (1, 229, DECODED), + (22, 229, CONTINUE), + ], + [ + (2, 176, DECODED), + (9, 176, DECODED), + (23, 176, DECODED), + (40, 176, CONTINUE), + (2, 177, DECODED), + (9, 177, DECODED), + (23, 177, DECODED), + (40, 177, CONTINUE), + (2, 179, DECODED), + (9, 179, DECODED), + (23, 179, DECODED), + (40, 179, CONTINUE), + (2, 209, DECODED), + (9, 209, DECODED), + (23, 209, DECODED), + (40, 209, CONTINUE), + ], + [ + (3, 176, DECODED), + (6, 176, DECODED), + (10, 176, DECODED), + (15, 176, DECODED), + (24, 176, DECODED), + (31, 176, DECODED), + (41, 176, DECODED), + (56, 176, CONTINUE), + (3, 177, DECODED), + (6, 177, DECODED), + (10, 177, DECODED), + (15, 177, DECODED), + (24, 177, DECODED), + (31, 177, DECODED), + (41, 177, DECODED), + (56, 177, CONTINUE), + ], + [ + (3, 179, DECODED), + (6, 179, DECODED), + (10, 179, DECODED), + (15, 179, DECODED), + (24, 179, DECODED), + (31, 179, DECODED), + (41, 179, DECODED), + (56, 179, CONTINUE), + (3, 209, DECODED), + (6, 209, DECODED), + (10, 209, DECODED), + (15, 209, DECODED), + (24, 209, DECODED), + (31, 209, DECODED), + (41, 209, DECODED), + (56, 209, CONTINUE), + ], + [ + (2, 216, DECODED), + (9, 216, DECODED), + (23, 216, DECODED), + (40, 216, CONTINUE), + (2, 217, DECODED), + (9, 217, DECODED), + (23, 217, DECODED), + (40, 217, CONTINUE), + (2, 227, DECODED), + (9, 227, DECODED), + (23, 227, DECODED), + (40, 227, CONTINUE), + (2, 229, DECODED), + (9, 229, DECODED), + (23, 229, DECODED), + (40, 229, CONTINUE), + ], + [ + (3, 216, DECODED), + (6, 216, DECODED), + (10, 216, DECODED), + (15, 216, DECODED), + (24, 216, DECODED), + (31, 216, DECODED), + (41, 216, DECODED), + (56, 216, CONTINUE), + (3, 217, DECODED), + (6, 217, DECODED), + (10, 217, DECODED), + (15, 217, DECODED), + (24, 217, DECODED), + (31, 217, DECODED), + (41, 217, DECODED), + (56, 217, CONTINUE), + ], + [ + (3, 227, DECODED), + (6, 227, DECODED), + (10, 227, DECODED), + (15, 227, DECODED), + (24, 227, DECODED), + (31, 227, DECODED), + (41, 227, DECODED), + (56, 227, CONTINUE), + (3, 229, DECODED), + (6, 229, DECODED), + (10, 229, DECODED), + (15, 229, DECODED), + (24, 229, DECODED), + (31, 229, DECODED), + (41, 229, DECODED), + (56, 229, CONTINUE), + ], + [ + (1, 230, DECODED), + (22, 230, CONTINUE), + (0, 129, DECODED), + (0, 132, DECODED), + (0, 133, DECODED), + (0, 134, DECODED), + (0, 136, DECODED), + (0, 146, DECODED), + (0, 154, DECODED), + (0, 156, DECODED), + (0, 160, DECODED), + (0, 163, DECODED), + (0, 164, DECODED), + (0, 169, DECODED), + (0, 170, DECODED), + (0, 173, DECODED), + ], + [ + (2, 230, DECODED), + (9, 230, DECODED), + (23, 230, DECODED), + (40, 230, CONTINUE), + (1, 129, DECODED), + (22, 129, CONTINUE), + (1, 132, DECODED), + (22, 132, CONTINUE), + (1, 133, DECODED), + (22, 133, CONTINUE), + (1, 134, DECODED), + (22, 134, CONTINUE), + (1, 136, DECODED), + (22, 136, CONTINUE), + (1, 146, DECODED), + (22, 146, CONTINUE), + ], + [ + (3, 230, DECODED), + (6, 230, DECODED), + (10, 230, DECODED), + (15, 230, DECODED), + (24, 230, DECODED), + (31, 230, DECODED), + (41, 230, DECODED), + (56, 230, CONTINUE), + (2, 129, DECODED), + (9, 129, DECODED), + (23, 129, DECODED), + (40, 129, CONTINUE), + (2, 132, DECODED), + (9, 132, DECODED), + (23, 132, DECODED), + (40, 132, CONTINUE), + ], + [ + (3, 129, DECODED), + (6, 129, DECODED), + (10, 129, DECODED), + (15, 129, DECODED), + (24, 129, DECODED), + (31, 129, DECODED), + (41, 129, DECODED), + (56, 129, CONTINUE), + (3, 132, DECODED), + (6, 132, DECODED), + (10, 132, DECODED), + (15, 132, DECODED), + (24, 132, DECODED), + (31, 132, DECODED), + (41, 132, DECODED), + (56, 132, CONTINUE), + ], + [ + (2, 133, DECODED), + (9, 133, DECODED), + (23, 133, DECODED), + (40, 133, CONTINUE), + (2, 134, DECODED), + (9, 134, DECODED), + (23, 134, DECODED), + (40, 134, CONTINUE), + (2, 136, DECODED), + (9, 136, DECODED), + (23, 136, DECODED), + (40, 136, CONTINUE), + (2, 146, DECODED), + (9, 146, DECODED), + (23, 146, DECODED), + (40, 146, CONTINUE), + ], + [ + (3, 133, DECODED), + (6, 133, DECODED), + (10, 133, DECODED), + (15, 133, DECODED), + (24, 133, DECODED), + (31, 133, DECODED), + (41, 133, DECODED), + (56, 133, CONTINUE), + (3, 134, DECODED), + (6, 134, DECODED), + (10, 134, DECODED), + (15, 134, DECODED), + (24, 134, DECODED), + (31, 134, DECODED), + (41, 134, DECODED), + (56, 134, CONTINUE), + ], + [ + (3, 136, DECODED), + (6, 136, DECODED), + (10, 136, DECODED), + (15, 136, DECODED), + (24, 136, DECODED), + (31, 136, DECODED), + (41, 136, DECODED), + (56, 136, CONTINUE), + (3, 146, DECODED), + (6, 146, DECODED), + (10, 146, DECODED), + (15, 146, DECODED), + (24, 146, DECODED), + (31, 146, DECODED), + (41, 146, DECODED), + (56, 146, CONTINUE), + ], + [ + (1, 154, DECODED), + (22, 154, CONTINUE), + (1, 156, DECODED), + (22, 156, CONTINUE), + (1, 160, DECODED), + (22, 160, CONTINUE), + (1, 163, DECODED), + (22, 163, CONTINUE), + (1, 164, DECODED), + (22, 164, CONTINUE), + (1, 169, DECODED), + (22, 169, CONTINUE), + (1, 170, DECODED), + (22, 170, CONTINUE), + (1, 173, DECODED), + (22, 173, CONTINUE), + ], + [ + (2, 154, DECODED), + (9, 154, DECODED), + (23, 154, DECODED), + (40, 154, CONTINUE), + (2, 156, DECODED), + (9, 156, DECODED), + (23, 156, DECODED), + (40, 156, CONTINUE), + (2, 160, DECODED), + (9, 160, DECODED), + (23, 160, DECODED), + (40, 160, CONTINUE), + (2, 163, DECODED), + (9, 163, DECODED), + (23, 163, DECODED), + (40, 163, CONTINUE), + ], + [ + (3, 154, DECODED), + (6, 154, DECODED), + (10, 154, DECODED), + (15, 154, DECODED), + (24, 154, DECODED), + (31, 154, DECODED), + (41, 154, DECODED), + (56, 154, CONTINUE), + (3, 156, DECODED), + (6, 156, DECODED), + (10, 156, DECODED), + (15, 156, DECODED), + (24, 156, DECODED), + (31, 156, DECODED), + (41, 156, DECODED), + (56, 156, CONTINUE), + ], + [ + (3, 160, DECODED), + (6, 160, DECODED), + (10, 160, DECODED), + (15, 160, DECODED), + (24, 160, DECODED), + (31, 160, DECODED), + (41, 160, DECODED), + (56, 160, CONTINUE), + (3, 163, DECODED), + (6, 163, DECODED), + (10, 163, DECODED), + (15, 163, DECODED), + (24, 163, DECODED), + (31, 163, DECODED), + (41, 163, DECODED), + (56, 163, CONTINUE), + ], + [ + (2, 164, DECODED), + (9, 164, DECODED), + (23, 164, DECODED), + (40, 164, CONTINUE), + (2, 169, DECODED), + (9, 169, DECODED), + (23, 169, DECODED), + (40, 169, CONTINUE), + (2, 170, DECODED), + (9, 170, DECODED), + (23, 170, DECODED), + (40, 170, CONTINUE), + (2, 173, DECODED), + (9, 173, DECODED), + (23, 173, DECODED), + (40, 173, CONTINUE), + ], + [ + (3, 164, DECODED), + (6, 164, DECODED), + (10, 164, DECODED), + (15, 164, DECODED), + (24, 164, DECODED), + (31, 164, DECODED), + (41, 164, DECODED), + (56, 164, CONTINUE), + (3, 169, DECODED), + (6, 169, DECODED), + (10, 169, DECODED), + (15, 169, DECODED), + (24, 169, DECODED), + (31, 169, DECODED), + (41, 169, DECODED), + (56, 169, CONTINUE), + ], + [ + (3, 170, DECODED), + (6, 170, DECODED), + (10, 170, DECODED), + (15, 170, DECODED), + (24, 170, DECODED), + (31, 170, DECODED), + (41, 170, DECODED), + (56, 170, CONTINUE), + (3, 173, DECODED), + (6, 173, DECODED), + (10, 173, DECODED), + (15, 173, DECODED), + (24, 173, DECODED), + (31, 173, DECODED), + (41, 173, DECODED), + (56, 173, CONTINUE), + ], + [ + (137, 0, OK), + (138, 0, OK), + (140, 0, OK), + (141, 0, OK), + (144, 0, OK), + (145, 0, OK), + (147, 0, OK), + (150, 0, OK), + (156, 0, OK), + (159, 0, OK), + (163, 0, OK), + (166, 0, OK), + (171, 0, OK), + (174, 0, OK), + (181, 0, OK), + (190, 0, OK), + ], + [ + (0, 178, DECODED), + (0, 181, DECODED), + (0, 185, DECODED), + (0, 186, DECODED), + (0, 187, DECODED), + (0, 189, DECODED), + (0, 190, DECODED), + (0, 196, DECODED), + (0, 198, DECODED), + (0, 228, DECODED), + (0, 232, DECODED), + (0, 233, DECODED), + (148, 0, OK), + (149, 0, OK), + (151, 0, OK), + (152, 0, OK), + ], + [ + (1, 178, DECODED), + (22, 178, CONTINUE), + (1, 181, DECODED), + (22, 181, CONTINUE), + (1, 185, DECODED), + (22, 185, CONTINUE), + (1, 186, DECODED), + (22, 186, CONTINUE), + (1, 187, DECODED), + (22, 187, CONTINUE), + (1, 189, DECODED), + (22, 189, CONTINUE), + (1, 190, DECODED), + (22, 190, CONTINUE), + (1, 196, DECODED), + (22, 196, CONTINUE), + ], + [ + (2, 178, DECODED), + (9, 178, DECODED), + (23, 178, DECODED), + (40, 178, CONTINUE), + (2, 181, DECODED), + (9, 181, DECODED), + (23, 181, DECODED), + (40, 181, CONTINUE), + (2, 185, DECODED), + (9, 185, DECODED), + (23, 185, DECODED), + (40, 185, CONTINUE), + (2, 186, DECODED), + (9, 186, DECODED), + (23, 186, DECODED), + (40, 186, CONTINUE), + ], + [ + (3, 178, DECODED), + (6, 178, DECODED), + (10, 178, DECODED), + (15, 178, DECODED), + (24, 178, DECODED), + (31, 178, DECODED), + (41, 178, DECODED), + (56, 178, CONTINUE), + (3, 181, DECODED), + (6, 181, DECODED), + (10, 181, DECODED), + (15, 181, DECODED), + (24, 181, DECODED), + (31, 181, DECODED), + (41, 181, DECODED), + (56, 181, CONTINUE), + ], + [ + (3, 185, DECODED), + (6, 185, DECODED), + (10, 185, DECODED), + (15, 185, DECODED), + (24, 185, DECODED), + (31, 185, DECODED), + (41, 185, DECODED), + (56, 185, CONTINUE), + (3, 186, DECODED), + (6, 186, DECODED), + (10, 186, DECODED), + (15, 186, DECODED), + (24, 186, DECODED), + (31, 186, DECODED), + (41, 186, DECODED), + (56, 186, CONTINUE), + ], + [ + (2, 187, DECODED), + (9, 187, DECODED), + (23, 187, DECODED), + (40, 187, CONTINUE), + (2, 189, DECODED), + (9, 189, DECODED), + (23, 189, DECODED), + (40, 189, CONTINUE), + (2, 190, DECODED), + (9, 190, DECODED), + (23, 190, DECODED), + (40, 190, CONTINUE), + (2, 196, DECODED), + (9, 196, DECODED), + (23, 196, DECODED), + (40, 196, CONTINUE), + ], + [ + (3, 187, DECODED), + (6, 187, DECODED), + (10, 187, DECODED), + (15, 187, DECODED), + (24, 187, DECODED), + (31, 187, DECODED), + (41, 187, DECODED), + (56, 187, CONTINUE), + (3, 189, DECODED), + (6, 189, DECODED), + (10, 189, DECODED), + (15, 189, DECODED), + (24, 189, DECODED), + (31, 189, DECODED), + (41, 189, DECODED), + (56, 189, CONTINUE), + ], + [ + (3, 190, DECODED), + (6, 190, DECODED), + (10, 190, DECODED), + (15, 190, DECODED), + (24, 190, DECODED), + (31, 190, DECODED), + (41, 190, DECODED), + (56, 190, CONTINUE), + (3, 196, DECODED), + (6, 196, DECODED), + (10, 196, DECODED), + (15, 196, DECODED), + (24, 196, DECODED), + (31, 196, DECODED), + (41, 196, DECODED), + (56, 196, CONTINUE), + ], + [ + (1, 198, DECODED), + (22, 198, CONTINUE), + (1, 228, DECODED), + (22, 228, CONTINUE), + (1, 232, DECODED), + (22, 232, CONTINUE), + (1, 233, DECODED), + (22, 233, CONTINUE), + (0, 1, DECODED), + (0, 135, DECODED), + (0, 137, DECODED), + (0, 138, DECODED), + (0, 139, DECODED), + (0, 140, DECODED), + (0, 141, DECODED), + (0, 143, DECODED), + ], + [ + (2, 198, DECODED), + (9, 198, DECODED), + (23, 198, DECODED), + (40, 198, CONTINUE), + (2, 228, DECODED), + (9, 228, DECODED), + (23, 228, DECODED), + (40, 228, CONTINUE), + (2, 232, DECODED), + (9, 232, DECODED), + (23, 232, DECODED), + (40, 232, CONTINUE), + (2, 233, DECODED), + (9, 233, DECODED), + (23, 233, DECODED), + (40, 233, CONTINUE), + ], + [ + (3, 198, DECODED), + (6, 198, DECODED), + (10, 198, DECODED), + (15, 198, DECODED), + (24, 198, DECODED), + (31, 198, DECODED), + (41, 198, DECODED), + (56, 198, CONTINUE), + (3, 228, DECODED), + (6, 228, DECODED), + (10, 228, DECODED), + (15, 228, DECODED), + (24, 228, DECODED), + (31, 228, DECODED), + (41, 228, DECODED), + (56, 228, CONTINUE), + ], + [ + (3, 232, DECODED), + (6, 232, DECODED), + (10, 232, DECODED), + (15, 232, DECODED), + (24, 232, DECODED), + (31, 232, DECODED), + (41, 232, DECODED), + (56, 232, CONTINUE), + (3, 233, DECODED), + (6, 233, DECODED), + (10, 233, DECODED), + (15, 233, DECODED), + (24, 233, DECODED), + (31, 233, DECODED), + (41, 233, DECODED), + (56, 233, CONTINUE), + ], + [ + (1, 1, DECODED), + (22, 1, CONTINUE), + (1, 135, DECODED), + (22, 135, CONTINUE), + (1, 137, DECODED), + (22, 137, CONTINUE), + (1, 138, DECODED), + (22, 138, CONTINUE), + (1, 139, DECODED), + (22, 139, CONTINUE), + (1, 140, DECODED), + (22, 140, CONTINUE), + (1, 141, DECODED), + (22, 141, CONTINUE), + (1, 143, DECODED), + (22, 143, CONTINUE), + ], + [ + (2, 1, DECODED), + (9, 1, DECODED), + (23, 1, DECODED), + (40, 1, CONTINUE), + (2, 135, DECODED), + (9, 135, DECODED), + (23, 135, DECODED), + (40, 135, CONTINUE), + (2, 137, DECODED), + (9, 137, DECODED), + (23, 137, DECODED), + (40, 137, CONTINUE), + (2, 138, DECODED), + (9, 138, DECODED), + (23, 138, DECODED), + (40, 138, CONTINUE), + ], + [ + (3, 1, DECODED), + (6, 1, DECODED), + (10, 1, DECODED), + (15, 1, DECODED), + (24, 1, DECODED), + (31, 1, DECODED), + (41, 1, DECODED), + (56, 1, CONTINUE), + (3, 135, DECODED), + (6, 135, DECODED), + (10, 135, DECODED), + (15, 135, DECODED), + (24, 135, DECODED), + (31, 135, DECODED), + (41, 135, DECODED), + (56, 135, CONTINUE), + ], + [ + (3, 137, DECODED), + (6, 137, DECODED), + (10, 137, DECODED), + (15, 137, DECODED), + (24, 137, DECODED), + (31, 137, DECODED), + (41, 137, DECODED), + (56, 137, CONTINUE), + (3, 138, DECODED), + (6, 138, DECODED), + (10, 138, DECODED), + (15, 138, DECODED), + (24, 138, DECODED), + (31, 138, DECODED), + (41, 138, DECODED), + (56, 138, CONTINUE), + ], + [ + (2, 139, DECODED), + (9, 139, DECODED), + (23, 139, DECODED), + (40, 139, CONTINUE), + (2, 140, DECODED), + (9, 140, DECODED), + (23, 140, DECODED), + (40, 140, CONTINUE), + (2, 141, DECODED), + (9, 141, DECODED), + (23, 141, DECODED), + (40, 141, CONTINUE), + (2, 143, DECODED), + (9, 143, DECODED), + (23, 143, DECODED), + (40, 143, CONTINUE), + ], + [ + (3, 139, DECODED), + (6, 139, DECODED), + (10, 139, DECODED), + (15, 139, DECODED), + (24, 139, DECODED), + (31, 139, DECODED), + (41, 139, DECODED), + (56, 139, CONTINUE), + (3, 140, DECODED), + (6, 140, DECODED), + (10, 140, DECODED), + (15, 140, DECODED), + (24, 140, DECODED), + (31, 140, DECODED), + (41, 140, DECODED), + (56, 140, CONTINUE), + ], + [ + (3, 141, DECODED), + (6, 141, DECODED), + (10, 141, DECODED), + (15, 141, DECODED), + (24, 141, DECODED), + (31, 141, DECODED), + (41, 141, DECODED), + (56, 141, CONTINUE), + (3, 143, DECODED), + (6, 143, DECODED), + (10, 143, DECODED), + (15, 143, DECODED), + (24, 143, DECODED), + (31, 143, DECODED), + (41, 143, DECODED), + (56, 143, CONTINUE), + ], + [ + (157, 0, OK), + (158, 0, OK), + (160, 0, OK), + (161, 0, OK), + (164, 0, OK), + (165, 0, OK), + (167, 0, OK), + (168, 0, OK), + (172, 0, OK), + (173, 0, OK), + (175, 0, OK), + (177, 0, OK), + (182, 0, OK), + (185, 0, OK), + (191, 0, OK), + (207, 0, OK), + ], + [ + (0, 147, DECODED), + (0, 149, DECODED), + (0, 150, DECODED), + (0, 151, DECODED), + (0, 152, DECODED), + (0, 155, DECODED), + (0, 157, DECODED), + (0, 158, DECODED), + (0, 165, DECODED), + (0, 166, DECODED), + (0, 168, DECODED), + (0, 174, DECODED), + (0, 175, DECODED), + (0, 180, DECODED), + (0, 182, DECODED), + (0, 183, DECODED), + ], + [ + (1, 147, DECODED), + (22, 147, CONTINUE), + (1, 149, DECODED), + (22, 149, CONTINUE), + (1, 150, DECODED), + (22, 150, CONTINUE), + (1, 151, DECODED), + (22, 151, CONTINUE), + (1, 152, DECODED), + (22, 152, CONTINUE), + (1, 155, DECODED), + (22, 155, CONTINUE), + (1, 157, DECODED), + (22, 157, CONTINUE), + (1, 158, DECODED), + (22, 158, CONTINUE), + ], + [ + (2, 147, DECODED), + (9, 147, DECODED), + (23, 147, DECODED), + (40, 147, CONTINUE), + (2, 149, DECODED), + (9, 149, DECODED), + (23, 149, DECODED), + (40, 149, CONTINUE), + (2, 150, DECODED), + (9, 150, DECODED), + (23, 150, DECODED), + (40, 150, CONTINUE), + (2, 151, DECODED), + (9, 151, DECODED), + (23, 151, DECODED), + (40, 151, CONTINUE), + ], + [ + (3, 147, DECODED), + (6, 147, DECODED), + (10, 147, DECODED), + (15, 147, DECODED), + (24, 147, DECODED), + (31, 147, DECODED), + (41, 147, DECODED), + (56, 147, CONTINUE), + (3, 149, DECODED), + (6, 149, DECODED), + (10, 149, DECODED), + (15, 149, DECODED), + (24, 149, DECODED), + (31, 149, DECODED), + (41, 149, DECODED), + (56, 149, CONTINUE), + ], + [ + (3, 150, DECODED), + (6, 150, DECODED), + (10, 150, DECODED), + (15, 150, DECODED), + (24, 150, DECODED), + (31, 150, DECODED), + (41, 150, DECODED), + (56, 150, CONTINUE), + (3, 151, DECODED), + (6, 151, DECODED), + (10, 151, DECODED), + (15, 151, DECODED), + (24, 151, DECODED), + (31, 151, DECODED), + (41, 151, DECODED), + (56, 151, CONTINUE), + ], + [ + (2, 152, DECODED), + (9, 152, DECODED), + (23, 152, DECODED), + (40, 152, CONTINUE), + (2, 155, DECODED), + (9, 155, DECODED), + (23, 155, DECODED), + (40, 155, CONTINUE), + (2, 157, DECODED), + (9, 157, DECODED), + (23, 157, DECODED), + (40, 157, CONTINUE), + (2, 158, DECODED), + (9, 158, DECODED), + (23, 158, DECODED), + (40, 158, CONTINUE), + ], + [ + (3, 152, DECODED), + (6, 152, DECODED), + (10, 152, DECODED), + (15, 152, DECODED), + (24, 152, DECODED), + (31, 152, DECODED), + (41, 152, DECODED), + (56, 152, CONTINUE), + (3, 155, DECODED), + (6, 155, DECODED), + (10, 155, DECODED), + (15, 155, DECODED), + (24, 155, DECODED), + (31, 155, DECODED), + (41, 155, DECODED), + (56, 155, CONTINUE), + ], + [ + (3, 157, DECODED), + (6, 157, DECODED), + (10, 157, DECODED), + (15, 157, DECODED), + (24, 157, DECODED), + (31, 157, DECODED), + (41, 157, DECODED), + (56, 157, CONTINUE), + (3, 158, DECODED), + (6, 158, DECODED), + (10, 158, DECODED), + (15, 158, DECODED), + (24, 158, DECODED), + (31, 158, DECODED), + (41, 158, DECODED), + (56, 158, CONTINUE), + ], + [ + (1, 165, DECODED), + (22, 165, CONTINUE), + (1, 166, DECODED), + (22, 166, CONTINUE), + (1, 168, DECODED), + (22, 168, CONTINUE), + (1, 174, DECODED), + (22, 174, CONTINUE), + (1, 175, DECODED), + (22, 175, CONTINUE), + (1, 180, DECODED), + (22, 180, CONTINUE), + (1, 182, DECODED), + (22, 182, CONTINUE), + (1, 183, DECODED), + (22, 183, CONTINUE), + ], + [ + (2, 165, DECODED), + (9, 165, DECODED), + (23, 165, DECODED), + (40, 165, CONTINUE), + (2, 166, DECODED), + (9, 166, DECODED), + (23, 166, DECODED), + (40, 166, CONTINUE), + (2, 168, DECODED), + (9, 168, DECODED), + (23, 168, DECODED), + (40, 168, CONTINUE), + (2, 174, DECODED), + (9, 174, DECODED), + (23, 174, DECODED), + (40, 174, CONTINUE), + ], + [ + (3, 165, DECODED), + (6, 165, DECODED), + (10, 165, DECODED), + (15, 165, DECODED), + (24, 165, DECODED), + (31, 165, DECODED), + (41, 165, DECODED), + (56, 165, CONTINUE), + (3, 166, DECODED), + (6, 166, DECODED), + (10, 166, DECODED), + (15, 166, DECODED), + (24, 166, DECODED), + (31, 166, DECODED), + (41, 166, DECODED), + (56, 166, CONTINUE), + ], + [ + (3, 168, DECODED), + (6, 168, DECODED), + (10, 168, DECODED), + (15, 168, DECODED), + (24, 168, DECODED), + (31, 168, DECODED), + (41, 168, DECODED), + (56, 168, CONTINUE), + (3, 174, DECODED), + (6, 174, DECODED), + (10, 174, DECODED), + (15, 174, DECODED), + (24, 174, DECODED), + (31, 174, DECODED), + (41, 174, DECODED), + (56, 174, CONTINUE), + ], + [ + (2, 175, DECODED), + (9, 175, DECODED), + (23, 175, DECODED), + (40, 175, CONTINUE), + (2, 180, DECODED), + (9, 180, DECODED), + (23, 180, DECODED), + (40, 180, CONTINUE), + (2, 182, DECODED), + (9, 182, DECODED), + (23, 182, DECODED), + (40, 182, CONTINUE), + (2, 183, DECODED), + (9, 183, DECODED), + (23, 183, DECODED), + (40, 183, CONTINUE), + ], + [ + (3, 175, DECODED), + (6, 175, DECODED), + (10, 175, DECODED), + (15, 175, DECODED), + (24, 175, DECODED), + (31, 175, DECODED), + (41, 175, DECODED), + (56, 175, CONTINUE), + (3, 180, DECODED), + (6, 180, DECODED), + (10, 180, DECODED), + (15, 180, DECODED), + (24, 180, DECODED), + (31, 180, DECODED), + (41, 180, DECODED), + (56, 180, CONTINUE), + ], + [ + (3, 182, DECODED), + (6, 182, DECODED), + (10, 182, DECODED), + (15, 182, DECODED), + (24, 182, DECODED), + (31, 182, DECODED), + (41, 182, DECODED), + (56, 182, CONTINUE), + (3, 183, DECODED), + (6, 183, DECODED), + (10, 183, DECODED), + (15, 183, DECODED), + (24, 183, DECODED), + (31, 183, DECODED), + (41, 183, DECODED), + (56, 183, CONTINUE), + ], + [ + (0, 188, DECODED), + (0, 191, DECODED), + (0, 197, DECODED), + (0, 231, DECODED), + (0, 239, DECODED), + (176, 0, OK), + (178, 0, OK), + (179, 0, OK), + (183, 0, OK), + (184, 0, OK), + (186, 0, OK), + (187, 0, OK), + (192, 0, OK), + (199, 0, OK), + (208, 0, OK), + (223, 0, OK), + ], + [ + (1, 188, DECODED), + (22, 188, CONTINUE), + (1, 191, DECODED), + (22, 191, CONTINUE), + (1, 197, DECODED), + (22, 197, CONTINUE), + (1, 231, DECODED), + (22, 231, CONTINUE), + (1, 239, DECODED), + (22, 239, CONTINUE), + (0, 9, DECODED), + (0, 142, DECODED), + (0, 144, DECODED), + (0, 145, DECODED), + (0, 148, DECODED), + (0, 159, DECODED), + ], + [ + (2, 188, DECODED), + (9, 188, DECODED), + (23, 188, DECODED), + (40, 188, CONTINUE), + (2, 191, DECODED), + (9, 191, DECODED), + (23, 191, DECODED), + (40, 191, CONTINUE), + (2, 197, DECODED), + (9, 197, DECODED), + (23, 197, DECODED), + (40, 197, CONTINUE), + (2, 231, DECODED), + (9, 231, DECODED), + (23, 231, DECODED), + (40, 231, CONTINUE), + ], + [ + (3, 188, DECODED), + (6, 188, DECODED), + (10, 188, DECODED), + (15, 188, DECODED), + (24, 188, DECODED), + (31, 188, DECODED), + (41, 188, DECODED), + (56, 188, CONTINUE), + (3, 191, DECODED), + (6, 191, DECODED), + (10, 191, DECODED), + (15, 191, DECODED), + (24, 191, DECODED), + (31, 191, DECODED), + (41, 191, DECODED), + (56, 191, CONTINUE), + ], + [ + (3, 197, DECODED), + (6, 197, DECODED), + (10, 197, DECODED), + (15, 197, DECODED), + (24, 197, DECODED), + (31, 197, DECODED), + (41, 197, DECODED), + (56, 197, CONTINUE), + (3, 231, DECODED), + (6, 231, DECODED), + (10, 231, DECODED), + (15, 231, DECODED), + (24, 231, DECODED), + (31, 231, DECODED), + (41, 231, DECODED), + (56, 231, CONTINUE), + ], + [ + (2, 239, DECODED), + (9, 239, DECODED), + (23, 239, DECODED), + (40, 239, CONTINUE), + (1, 9, DECODED), + (22, 9, CONTINUE), + (1, 142, DECODED), + (22, 142, CONTINUE), + (1, 144, DECODED), + (22, 144, CONTINUE), + (1, 145, DECODED), + (22, 145, CONTINUE), + (1, 148, DECODED), + (22, 148, CONTINUE), + (1, 159, DECODED), + (22, 159, CONTINUE), + ], + [ + (3, 239, DECODED), + (6, 239, DECODED), + (10, 239, DECODED), + (15, 239, DECODED), + (24, 239, DECODED), + (31, 239, DECODED), + (41, 239, DECODED), + (56, 239, CONTINUE), + (2, 9, DECODED), + (9, 9, DECODED), + (23, 9, DECODED), + (40, 9, CONTINUE), + (2, 142, DECODED), + (9, 142, DECODED), + (23, 142, DECODED), + (40, 142, CONTINUE), + ], + [ + (3, 9, DECODED), + (6, 9, DECODED), + (10, 9, DECODED), + (15, 9, DECODED), + (24, 9, DECODED), + (31, 9, DECODED), + (41, 9, DECODED), + (56, 9, CONTINUE), + (3, 142, DECODED), + (6, 142, DECODED), + (10, 142, DECODED), + (15, 142, DECODED), + (24, 142, DECODED), + (31, 142, DECODED), + (41, 142, DECODED), + (56, 142, CONTINUE), + ], + [ + (2, 144, DECODED), + (9, 144, DECODED), + (23, 144, DECODED), + (40, 144, CONTINUE), + (2, 145, DECODED), + (9, 145, DECODED), + (23, 145, DECODED), + (40, 145, CONTINUE), + (2, 148, DECODED), + (9, 148, DECODED), + (23, 148, DECODED), + (40, 148, CONTINUE), + (2, 159, DECODED), + (9, 159, DECODED), + (23, 159, DECODED), + (40, 159, CONTINUE), + ], + [ + (3, 144, DECODED), + (6, 144, DECODED), + (10, 144, DECODED), + (15, 144, DECODED), + (24, 144, DECODED), + (31, 144, DECODED), + (41, 144, DECODED), + (56, 144, CONTINUE), + (3, 145, DECODED), + (6, 145, DECODED), + (10, 145, DECODED), + (15, 145, DECODED), + (24, 145, DECODED), + (31, 145, DECODED), + (41, 145, DECODED), + (56, 145, CONTINUE), + ], + [ + (3, 148, DECODED), + (6, 148, DECODED), + (10, 148, DECODED), + (15, 148, DECODED), + (24, 148, DECODED), + (31, 148, DECODED), + (41, 148, DECODED), + (56, 148, CONTINUE), + (3, 159, DECODED), + (6, 159, DECODED), + (10, 159, DECODED), + (15, 159, DECODED), + (24, 159, DECODED), + (31, 159, DECODED), + (41, 159, DECODED), + (56, 159, CONTINUE), + ], + [ + (0, 171, DECODED), + (0, 206, DECODED), + (0, 215, DECODED), + (0, 225, DECODED), + (0, 236, DECODED), + (0, 237, DECODED), + (188, 0, OK), + (189, 0, OK), + (193, 0, OK), + (196, 0, OK), + (200, 0, OK), + (203, 0, OK), + (209, 0, OK), + (216, 0, OK), + (224, 0, OK), + (238, 0, OK), + ], + [ + (1, 171, DECODED), + (22, 171, CONTINUE), + (1, 206, DECODED), + (22, 206, CONTINUE), + (1, 215, DECODED), + (22, 215, CONTINUE), + (1, 225, DECODED), + (22, 225, CONTINUE), + (1, 236, DECODED), + (22, 236, CONTINUE), + (1, 237, DECODED), + (22, 237, CONTINUE), + (0, 199, DECODED), + (0, 207, DECODED), + (0, 234, DECODED), + (0, 235, DECODED), + ], + [ + (2, 171, DECODED), + (9, 171, DECODED), + (23, 171, DECODED), + (40, 171, CONTINUE), + (2, 206, DECODED), + (9, 206, DECODED), + (23, 206, DECODED), + (40, 206, CONTINUE), + (2, 215, DECODED), + (9, 215, DECODED), + (23, 215, DECODED), + (40, 215, CONTINUE), + (2, 225, DECODED), + (9, 225, DECODED), + (23, 225, DECODED), + (40, 225, CONTINUE), + ], + [ + (3, 171, DECODED), + (6, 171, DECODED), + (10, 171, DECODED), + (15, 171, DECODED), + (24, 171, DECODED), + (31, 171, DECODED), + (41, 171, DECODED), + (56, 171, CONTINUE), + (3, 206, DECODED), + (6, 206, DECODED), + (10, 206, DECODED), + (15, 206, DECODED), + (24, 206, DECODED), + (31, 206, DECODED), + (41, 206, DECODED), + (56, 206, CONTINUE), + ], + [ + (3, 215, DECODED), + (6, 215, DECODED), + (10, 215, DECODED), + (15, 215, DECODED), + (24, 215, DECODED), + (31, 215, DECODED), + (41, 215, DECODED), + (56, 215, CONTINUE), + (3, 225, DECODED), + (6, 225, DECODED), + (10, 225, DECODED), + (15, 225, DECODED), + (24, 225, DECODED), + (31, 225, DECODED), + (41, 225, DECODED), + (56, 225, CONTINUE), + ], + [ + (2, 236, DECODED), + (9, 236, DECODED), + (23, 236, DECODED), + (40, 236, CONTINUE), + (2, 237, DECODED), + (9, 237, DECODED), + (23, 237, DECODED), + (40, 237, CONTINUE), + (1, 199, DECODED), + (22, 199, CONTINUE), + (1, 207, DECODED), + (22, 207, CONTINUE), + (1, 234, DECODED), + (22, 234, CONTINUE), + (1, 235, DECODED), + (22, 235, CONTINUE), + ], + [ + (3, 236, DECODED), + (6, 236, DECODED), + (10, 236, DECODED), + (15, 236, DECODED), + (24, 236, DECODED), + (31, 236, DECODED), + (41, 236, DECODED), + (56, 236, CONTINUE), + (3, 237, DECODED), + (6, 237, DECODED), + (10, 237, DECODED), + (15, 237, DECODED), + (24, 237, DECODED), + (31, 237, DECODED), + (41, 237, DECODED), + (56, 237, CONTINUE), + ], + [ + (2, 199, DECODED), + (9, 199, DECODED), + (23, 199, DECODED), + (40, 199, CONTINUE), + (2, 207, DECODED), + (9, 207, DECODED), + (23, 207, DECODED), + (40, 207, CONTINUE), + (2, 234, DECODED), + (9, 234, DECODED), + (23, 234, DECODED), + (40, 234, CONTINUE), + (2, 235, DECODED), + (9, 235, DECODED), + (23, 235, DECODED), + (40, 235, CONTINUE), + ], + [ + (3, 199, DECODED), + (6, 199, DECODED), + (10, 199, DECODED), + (15, 199, DECODED), + (24, 199, DECODED), + (31, 199, DECODED), + (41, 199, DECODED), + (56, 199, CONTINUE), + (3, 207, DECODED), + (6, 207, DECODED), + (10, 207, DECODED), + (15, 207, DECODED), + (24, 207, DECODED), + (31, 207, DECODED), + (41, 207, DECODED), + (56, 207, CONTINUE), + ], + [ + (3, 234, DECODED), + (6, 234, DECODED), + (10, 234, DECODED), + (15, 234, DECODED), + (24, 234, DECODED), + (31, 234, DECODED), + (41, 234, DECODED), + (56, 234, CONTINUE), + (3, 235, DECODED), + (6, 235, DECODED), + (10, 235, DECODED), + (15, 235, DECODED), + (24, 235, DECODED), + (31, 235, DECODED), + (41, 235, DECODED), + (56, 235, CONTINUE), + ], + [ + (194, 0, OK), + (195, 0, OK), + (197, 0, OK), + (198, 0, OK), + (201, 0, OK), + (202, 0, OK), + (204, 0, OK), + (205, 0, OK), + (210, 0, OK), + (213, 0, OK), + (217, 0, OK), + (220, 0, OK), + (225, 0, OK), + (231, 0, OK), + (239, 0, OK), + (246, 0, OK), + ], + [ + (0, 192, DECODED), + (0, 193, DECODED), + (0, 200, DECODED), + (0, 201, DECODED), + (0, 202, DECODED), + (0, 205, DECODED), + (0, 210, DECODED), + (0, 213, DECODED), + (0, 218, DECODED), + (0, 219, DECODED), + (0, 238, DECODED), + (0, 240, DECODED), + (0, 242, DECODED), + (0, 243, DECODED), + (0, 255, DECODED), + (206, 0, OK), + ], + [ + (1, 192, DECODED), + (22, 192, CONTINUE), + (1, 193, DECODED), + (22, 193, CONTINUE), + (1, 200, DECODED), + (22, 200, CONTINUE), + (1, 201, DECODED), + (22, 201, CONTINUE), + (1, 202, DECODED), + (22, 202, CONTINUE), + (1, 205, DECODED), + (22, 205, CONTINUE), + (1, 210, DECODED), + (22, 210, CONTINUE), + (1, 213, DECODED), + (22, 213, CONTINUE), + ], + [ + (2, 192, DECODED), + (9, 192, DECODED), + (23, 192, DECODED), + (40, 192, CONTINUE), + (2, 193, DECODED), + (9, 193, DECODED), + (23, 193, DECODED), + (40, 193, CONTINUE), + (2, 200, DECODED), + (9, 200, DECODED), + (23, 200, DECODED), + (40, 200, CONTINUE), + (2, 201, DECODED), + (9, 201, DECODED), + (23, 201, DECODED), + (40, 201, CONTINUE), + ], + [ + (3, 192, DECODED), + (6, 192, DECODED), + (10, 192, DECODED), + (15, 192, DECODED), + (24, 192, DECODED), + (31, 192, DECODED), + (41, 192, DECODED), + (56, 192, CONTINUE), + (3, 193, DECODED), + (6, 193, DECODED), + (10, 193, DECODED), + (15, 193, DECODED), + (24, 193, DECODED), + (31, 193, DECODED), + (41, 193, DECODED), + (56, 193, CONTINUE), + ], + [ + (3, 200, DECODED), + (6, 200, DECODED), + (10, 200, DECODED), + (15, 200, DECODED), + (24, 200, DECODED), + (31, 200, DECODED), + (41, 200, DECODED), + (56, 200, CONTINUE), + (3, 201, DECODED), + (6, 201, DECODED), + (10, 201, DECODED), + (15, 201, DECODED), + (24, 201, DECODED), + (31, 201, DECODED), + (41, 201, DECODED), + (56, 201, CONTINUE), + ], + [ + (2, 202, DECODED), + (9, 202, DECODED), + (23, 202, DECODED), + (40, 202, CONTINUE), + (2, 205, DECODED), + (9, 205, DECODED), + (23, 205, DECODED), + (40, 205, CONTINUE), + (2, 210, DECODED), + (9, 210, DECODED), + (23, 210, DECODED), + (40, 210, CONTINUE), + (2, 213, DECODED), + (9, 213, DECODED), + (23, 213, DECODED), + (40, 213, CONTINUE), + ], + [ + (3, 202, DECODED), + (6, 202, DECODED), + (10, 202, DECODED), + (15, 202, DECODED), + (24, 202, DECODED), + (31, 202, DECODED), + (41, 202, DECODED), + (56, 202, CONTINUE), + (3, 205, DECODED), + (6, 205, DECODED), + (10, 205, DECODED), + (15, 205, DECODED), + (24, 205, DECODED), + (31, 205, DECODED), + (41, 205, DECODED), + (56, 205, CONTINUE), + ], + [ + (3, 210, DECODED), + (6, 210, DECODED), + (10, 210, DECODED), + (15, 210, DECODED), + (24, 210, DECODED), + (31, 210, DECODED), + (41, 210, DECODED), + (56, 210, CONTINUE), + (3, 213, DECODED), + (6, 213, DECODED), + (10, 213, DECODED), + (15, 213, DECODED), + (24, 213, DECODED), + (31, 213, DECODED), + (41, 213, DECODED), + (56, 213, CONTINUE), + ], + [ + (1, 218, DECODED), + (22, 218, CONTINUE), + (1, 219, DECODED), + (22, 219, CONTINUE), + (1, 238, DECODED), + (22, 238, CONTINUE), + (1, 240, DECODED), + (22, 240, CONTINUE), + (1, 242, DECODED), + (22, 242, CONTINUE), + (1, 243, DECODED), + (22, 243, CONTINUE), + (1, 255, DECODED), + (22, 255, CONTINUE), + (0, 203, DECODED), + (0, 204, DECODED), + ], + [ + (2, 218, DECODED), + (9, 218, DECODED), + (23, 218, DECODED), + (40, 218, CONTINUE), + (2, 219, DECODED), + (9, 219, DECODED), + (23, 219, DECODED), + (40, 219, CONTINUE), + (2, 238, DECODED), + (9, 238, DECODED), + (23, 238, DECODED), + (40, 238, CONTINUE), + (2, 240, DECODED), + (9, 240, DECODED), + (23, 240, DECODED), + (40, 240, CONTINUE), + ], + [ + (3, 218, DECODED), + (6, 218, DECODED), + (10, 218, DECODED), + (15, 218, DECODED), + (24, 218, DECODED), + (31, 218, DECODED), + (41, 218, DECODED), + (56, 218, CONTINUE), + (3, 219, DECODED), + (6, 219, DECODED), + (10, 219, DECODED), + (15, 219, DECODED), + (24, 219, DECODED), + (31, 219, DECODED), + (41, 219, DECODED), + (56, 219, CONTINUE), + ], + [ + (3, 238, DECODED), + (6, 238, DECODED), + (10, 238, DECODED), + (15, 238, DECODED), + (24, 238, DECODED), + (31, 238, DECODED), + (41, 238, DECODED), + (56, 238, CONTINUE), + (3, 240, DECODED), + (6, 240, DECODED), + (10, 240, DECODED), + (15, 240, DECODED), + (24, 240, DECODED), + (31, 240, DECODED), + (41, 240, DECODED), + (56, 240, CONTINUE), + ], + [ + (2, 242, DECODED), + (9, 242, DECODED), + (23, 242, DECODED), + (40, 242, CONTINUE), + (2, 243, DECODED), + (9, 243, DECODED), + (23, 243, DECODED), + (40, 243, CONTINUE), + (2, 255, DECODED), + (9, 255, DECODED), + (23, 255, DECODED), + (40, 255, CONTINUE), + (1, 203, DECODED), + (22, 203, CONTINUE), + (1, 204, DECODED), + (22, 204, CONTINUE), + ], + [ + (3, 242, DECODED), + (6, 242, DECODED), + (10, 242, DECODED), + (15, 242, DECODED), + (24, 242, DECODED), + (31, 242, DECODED), + (41, 242, DECODED), + (56, 242, CONTINUE), + (3, 243, DECODED), + (6, 243, DECODED), + (10, 243, DECODED), + (15, 243, DECODED), + (24, 243, DECODED), + (31, 243, DECODED), + (41, 243, DECODED), + (56, 243, CONTINUE), + ], + [ + (3, 255, DECODED), + (6, 255, DECODED), + (10, 255, DECODED), + (15, 255, DECODED), + (24, 255, DECODED), + (31, 255, DECODED), + (41, 255, DECODED), + (56, 255, CONTINUE), + (2, 203, DECODED), + (9, 203, DECODED), + (23, 203, DECODED), + (40, 203, CONTINUE), + (2, 204, DECODED), + (9, 204, DECODED), + (23, 204, DECODED), + (40, 204, CONTINUE), + ], + [ + (3, 203, DECODED), + (6, 203, DECODED), + (10, 203, DECODED), + (15, 203, DECODED), + (24, 203, DECODED), + (31, 203, DECODED), + (41, 203, DECODED), + (56, 203, CONTINUE), + (3, 204, DECODED), + (6, 204, DECODED), + (10, 204, DECODED), + (15, 204, DECODED), + (24, 204, DECODED), + (31, 204, DECODED), + (41, 204, DECODED), + (56, 204, CONTINUE), + ], + [ + (211, 0, OK), + (212, 0, OK), + (214, 0, OK), + (215, 0, OK), + (218, 0, OK), + (219, 0, OK), + (221, 0, OK), + (222, 0, OK), + (226, 0, OK), + (228, 0, OK), + (232, 0, OK), + (235, 0, OK), + (240, 0, OK), + (243, 0, OK), + (247, 0, OK), + (250, 0, OK), + ], + [ + (0, 211, DECODED), + (0, 212, DECODED), + (0, 214, DECODED), + (0, 221, DECODED), + (0, 222, DECODED), + (0, 223, DECODED), + (0, 241, DECODED), + (0, 244, DECODED), + (0, 245, DECODED), + (0, 246, DECODED), + (0, 247, DECODED), + (0, 248, DECODED), + (0, 250, DECODED), + (0, 251, DECODED), + (0, 252, DECODED), + (0, 253, DECODED), + ], + [ + (1, 211, DECODED), + (22, 211, CONTINUE), + (1, 212, DECODED), + (22, 212, CONTINUE), + (1, 214, DECODED), + (22, 214, CONTINUE), + (1, 221, DECODED), + (22, 221, CONTINUE), + (1, 222, DECODED), + (22, 222, CONTINUE), + (1, 223, DECODED), + (22, 223, CONTINUE), + (1, 241, DECODED), + (22, 241, CONTINUE), + (1, 244, DECODED), + (22, 244, CONTINUE), + ], + [ + (2, 211, DECODED), + (9, 211, DECODED), + (23, 211, DECODED), + (40, 211, CONTINUE), + (2, 212, DECODED), + (9, 212, DECODED), + (23, 212, DECODED), + (40, 212, CONTINUE), + (2, 214, DECODED), + (9, 214, DECODED), + (23, 214, DECODED), + (40, 214, CONTINUE), + (2, 221, DECODED), + (9, 221, DECODED), + (23, 221, DECODED), + (40, 221, CONTINUE), + ], + [ + (3, 211, DECODED), + (6, 211, DECODED), + (10, 211, DECODED), + (15, 211, DECODED), + (24, 211, DECODED), + (31, 211, DECODED), + (41, 211, DECODED), + (56, 211, CONTINUE), + (3, 212, DECODED), + (6, 212, DECODED), + (10, 212, DECODED), + (15, 212, DECODED), + (24, 212, DECODED), + (31, 212, DECODED), + (41, 212, DECODED), + (56, 212, CONTINUE), + ], + [ + (3, 214, DECODED), + (6, 214, DECODED), + (10, 214, DECODED), + (15, 214, DECODED), + (24, 214, DECODED), + (31, 214, DECODED), + (41, 214, DECODED), + (56, 214, CONTINUE), + (3, 221, DECODED), + (6, 221, DECODED), + (10, 221, DECODED), + (15, 221, DECODED), + (24, 221, DECODED), + (31, 221, DECODED), + (41, 221, DECODED), + (56, 221, CONTINUE), + ], + [ + (2, 222, DECODED), + (9, 222, DECODED), + (23, 222, DECODED), + (40, 222, CONTINUE), + (2, 223, DECODED), + (9, 223, DECODED), + (23, 223, DECODED), + (40, 223, CONTINUE), + (2, 241, DECODED), + (9, 241, DECODED), + (23, 241, DECODED), + (40, 241, CONTINUE), + (2, 244, DECODED), + (9, 244, DECODED), + (23, 244, DECODED), + (40, 244, CONTINUE), + ], + [ + (3, 222, DECODED), + (6, 222, DECODED), + (10, 222, DECODED), + (15, 222, DECODED), + (24, 222, DECODED), + (31, 222, DECODED), + (41, 222, DECODED), + (56, 222, CONTINUE), + (3, 223, DECODED), + (6, 223, DECODED), + (10, 223, DECODED), + (15, 223, DECODED), + (24, 223, DECODED), + (31, 223, DECODED), + (41, 223, DECODED), + (56, 223, CONTINUE), + ], + [ + (3, 241, DECODED), + (6, 241, DECODED), + (10, 241, DECODED), + (15, 241, DECODED), + (24, 241, DECODED), + (31, 241, DECODED), + (41, 241, DECODED), + (56, 241, CONTINUE), + (3, 244, DECODED), + (6, 244, DECODED), + (10, 244, DECODED), + (15, 244, DECODED), + (24, 244, DECODED), + (31, 244, DECODED), + (41, 244, DECODED), + (56, 244, CONTINUE), + ], + [ + (1, 245, DECODED), + (22, 245, CONTINUE), + (1, 246, DECODED), + (22, 246, CONTINUE), + (1, 247, DECODED), + (22, 247, CONTINUE), + (1, 248, DECODED), + (22, 248, CONTINUE), + (1, 250, DECODED), + (22, 250, CONTINUE), + (1, 251, DECODED), + (22, 251, CONTINUE), + (1, 252, DECODED), + (22, 252, CONTINUE), + (1, 253, DECODED), + (22, 253, CONTINUE), + ], + [ + (2, 245, DECODED), + (9, 245, DECODED), + (23, 245, DECODED), + (40, 245, CONTINUE), + (2, 246, DECODED), + (9, 246, DECODED), + (23, 246, DECODED), + (40, 246, CONTINUE), + (2, 247, DECODED), + (9, 247, DECODED), + (23, 247, DECODED), + (40, 247, CONTINUE), + (2, 248, DECODED), + (9, 248, DECODED), + (23, 248, DECODED), + (40, 248, CONTINUE), + ], + [ + (3, 245, DECODED), + (6, 245, DECODED), + (10, 245, DECODED), + (15, 245, DECODED), + (24, 245, DECODED), + (31, 245, DECODED), + (41, 245, DECODED), + (56, 245, CONTINUE), + (3, 246, DECODED), + (6, 246, DECODED), + (10, 246, DECODED), + (15, 246, DECODED), + (24, 246, DECODED), + (31, 246, DECODED), + (41, 246, DECODED), + (56, 246, CONTINUE), + ], + [ + (3, 247, DECODED), + (6, 247, DECODED), + (10, 247, DECODED), + (15, 247, DECODED), + (24, 247, DECODED), + (31, 247, DECODED), + (41, 247, DECODED), + (56, 247, CONTINUE), + (3, 248, DECODED), + (6, 248, DECODED), + (10, 248, DECODED), + (15, 248, DECODED), + (24, 248, DECODED), + (31, 248, DECODED), + (41, 248, DECODED), + (56, 248, CONTINUE), + ], + [ + (2, 250, DECODED), + (9, 250, DECODED), + (23, 250, DECODED), + (40, 250, CONTINUE), + (2, 251, DECODED), + (9, 251, DECODED), + (23, 251, DECODED), + (40, 251, CONTINUE), + (2, 252, DECODED), + (9, 252, DECODED), + (23, 252, DECODED), + (40, 252, CONTINUE), + (2, 253, DECODED), + (9, 253, DECODED), + (23, 253, DECODED), + (40, 253, CONTINUE), + ], + [ + (3, 250, DECODED), + (6, 250, DECODED), + (10, 250, DECODED), + (15, 250, DECODED), + (24, 250, DECODED), + (31, 250, DECODED), + (41, 250, DECODED), + (56, 250, CONTINUE), + (3, 251, DECODED), + (6, 251, DECODED), + (10, 251, DECODED), + (15, 251, DECODED), + (24, 251, DECODED), + (31, 251, DECODED), + (41, 251, DECODED), + (56, 251, CONTINUE), + ], + [ + (3, 252, DECODED), + (6, 252, DECODED), + (10, 252, DECODED), + (15, 252, DECODED), + (24, 252, DECODED), + (31, 252, DECODED), + (41, 252, DECODED), + (56, 252, CONTINUE), + (3, 253, DECODED), + (6, 253, DECODED), + (10, 253, DECODED), + (15, 253, DECODED), + (24, 253, DECODED), + (31, 253, DECODED), + (41, 253, DECODED), + (56, 253, CONTINUE), + ], + [ + (0, 254, DECODED), + (227, 0, OK), + (229, 0, OK), + (230, 0, OK), + (233, 0, OK), + (234, 0, OK), + (236, 0, OK), + (237, 0, OK), + (241, 0, OK), + (242, 0, OK), + (244, 0, OK), + (245, 0, OK), + (248, 0, OK), + (249, 0, OK), + (251, 0, OK), + (252, 0, OK), + ], + [ + (1, 254, DECODED), + (22, 254, CONTINUE), + (0, 2, DECODED), + (0, 3, DECODED), + (0, 4, DECODED), + (0, 5, DECODED), + (0, 6, DECODED), + (0, 7, DECODED), + (0, 8, DECODED), + (0, 11, DECODED), + (0, 12, DECODED), + (0, 14, DECODED), + (0, 15, DECODED), + (0, 16, DECODED), + (0, 17, DECODED), + (0, 18, DECODED), + ], + [ + (2, 254, DECODED), + (9, 254, DECODED), + (23, 254, DECODED), + (40, 254, CONTINUE), + (1, 2, DECODED), + (22, 2, CONTINUE), + (1, 3, DECODED), + (22, 3, CONTINUE), + (1, 4, DECODED), + (22, 4, CONTINUE), + (1, 5, DECODED), + (22, 5, CONTINUE), + (1, 6, DECODED), + (22, 6, CONTINUE), + (1, 7, DECODED), + (22, 7, CONTINUE), + ], + [ + (3, 254, DECODED), + (6, 254, DECODED), + (10, 254, DECODED), + (15, 254, DECODED), + (24, 254, DECODED), + (31, 254, DECODED), + (41, 254, DECODED), + (56, 254, CONTINUE), + (2, 2, DECODED), + (9, 2, DECODED), + (23, 2, DECODED), + (40, 2, CONTINUE), + (2, 3, DECODED), + (9, 3, DECODED), + (23, 3, DECODED), + (40, 3, CONTINUE), + ], + [ + (3, 2, DECODED), + (6, 2, DECODED), + (10, 2, DECODED), + (15, 2, DECODED), + (24, 2, DECODED), + (31, 2, DECODED), + (41, 2, DECODED), + (56, 2, CONTINUE), + (3, 3, DECODED), + (6, 3, DECODED), + (10, 3, DECODED), + (15, 3, DECODED), + (24, 3, DECODED), + (31, 3, DECODED), + (41, 3, DECODED), + (56, 3, CONTINUE), + ], + [ + (2, 4, DECODED), + (9, 4, DECODED), + (23, 4, DECODED), + (40, 4, CONTINUE), + (2, 5, DECODED), + (9, 5, DECODED), + (23, 5, DECODED), + (40, 5, CONTINUE), + (2, 6, DECODED), + (9, 6, DECODED), + (23, 6, DECODED), + (40, 6, CONTINUE), + (2, 7, DECODED), + (9, 7, DECODED), + (23, 7, DECODED), + (40, 7, CONTINUE), + ], + [ + (3, 4, DECODED), + (6, 4, DECODED), + (10, 4, DECODED), + (15, 4, DECODED), + (24, 4, DECODED), + (31, 4, DECODED), + (41, 4, DECODED), + (56, 4, CONTINUE), + (3, 5, DECODED), + (6, 5, DECODED), + (10, 5, DECODED), + (15, 5, DECODED), + (24, 5, DECODED), + (31, 5, DECODED), + (41, 5, DECODED), + (56, 5, CONTINUE), + ], + [ + (3, 6, DECODED), + (6, 6, DECODED), + (10, 6, DECODED), + (15, 6, DECODED), + (24, 6, DECODED), + (31, 6, DECODED), + (41, 6, DECODED), + (56, 6, CONTINUE), + (3, 7, DECODED), + (6, 7, DECODED), + (10, 7, DECODED), + (15, 7, DECODED), + (24, 7, DECODED), + (31, 7, DECODED), + (41, 7, DECODED), + (56, 7, CONTINUE), + ], + [ + (1, 8, DECODED), + (22, 8, CONTINUE), + (1, 11, DECODED), + (22, 11, CONTINUE), + (1, 12, DECODED), + (22, 12, CONTINUE), + (1, 14, DECODED), + (22, 14, CONTINUE), + (1, 15, DECODED), + (22, 15, CONTINUE), + (1, 16, DECODED), + (22, 16, CONTINUE), + (1, 17, DECODED), + (22, 17, CONTINUE), + (1, 18, DECODED), + (22, 18, CONTINUE), + ], + [ + (2, 8, DECODED), + (9, 8, DECODED), + (23, 8, DECODED), + (40, 8, CONTINUE), + (2, 11, DECODED), + (9, 11, DECODED), + (23, 11, DECODED), + (40, 11, CONTINUE), + (2, 12, DECODED), + (9, 12, DECODED), + (23, 12, DECODED), + (40, 12, CONTINUE), + (2, 14, DECODED), + (9, 14, DECODED), + (23, 14, DECODED), + (40, 14, CONTINUE), + ], + [ + (3, 8, DECODED), + (6, 8, DECODED), + (10, 8, DECODED), + (15, 8, DECODED), + (24, 8, DECODED), + (31, 8, DECODED), + (41, 8, DECODED), + (56, 8, CONTINUE), + (3, 11, DECODED), + (6, 11, DECODED), + (10, 11, DECODED), + (15, 11, DECODED), + (24, 11, DECODED), + (31, 11, DECODED), + (41, 11, DECODED), + (56, 11, CONTINUE), + ], + [ + (3, 12, DECODED), + (6, 12, DECODED), + (10, 12, DECODED), + (15, 12, DECODED), + (24, 12, DECODED), + (31, 12, DECODED), + (41, 12, DECODED), + (56, 12, CONTINUE), + (3, 14, DECODED), + (6, 14, DECODED), + (10, 14, DECODED), + (15, 14, DECODED), + (24, 14, DECODED), + (31, 14, DECODED), + (41, 14, DECODED), + (56, 14, CONTINUE), + ], + [ + (2, 15, DECODED), + (9, 15, DECODED), + (23, 15, DECODED), + (40, 15, CONTINUE), + (2, 16, DECODED), + (9, 16, DECODED), + (23, 16, DECODED), + (40, 16, CONTINUE), + (2, 17, DECODED), + (9, 17, DECODED), + (23, 17, DECODED), + (40, 17, CONTINUE), + (2, 18, DECODED), + (9, 18, DECODED), + (23, 18, DECODED), + (40, 18, CONTINUE), + ], + [ + (3, 15, DECODED), + (6, 15, DECODED), + (10, 15, DECODED), + (15, 15, DECODED), + (24, 15, DECODED), + (31, 15, DECODED), + (41, 15, DECODED), + (56, 15, CONTINUE), + (3, 16, DECODED), + (6, 16, DECODED), + (10, 16, DECODED), + (15, 16, DECODED), + (24, 16, DECODED), + (31, 16, DECODED), + (41, 16, DECODED), + (56, 16, CONTINUE), + ], + [ + (3, 17, DECODED), + (6, 17, DECODED), + (10, 17, DECODED), + (15, 17, DECODED), + (24, 17, DECODED), + (31, 17, DECODED), + (41, 17, DECODED), + (56, 17, CONTINUE), + (3, 18, DECODED), + (6, 18, DECODED), + (10, 18, DECODED), + (15, 18, DECODED), + (24, 18, DECODED), + (31, 18, DECODED), + (41, 18, DECODED), + (56, 18, CONTINUE), + ], + [ + (0, 19, DECODED), + (0, 20, DECODED), + (0, 21, DECODED), + (0, 23, DECODED), + (0, 24, DECODED), + (0, 25, DECODED), + (0, 26, DECODED), + (0, 27, DECODED), + (0, 28, DECODED), + (0, 29, DECODED), + (0, 30, DECODED), + (0, 31, DECODED), + (0, 127, DECODED), + (0, 220, DECODED), + (0, 249, DECODED), + (253, 0, OK), + ], + [ + (1, 19, DECODED), + (22, 19, CONTINUE), + (1, 20, DECODED), + (22, 20, CONTINUE), + (1, 21, DECODED), + (22, 21, CONTINUE), + (1, 23, DECODED), + (22, 23, CONTINUE), + (1, 24, DECODED), + (22, 24, CONTINUE), + (1, 25, DECODED), + (22, 25, CONTINUE), + (1, 26, DECODED), + (22, 26, CONTINUE), + (1, 27, DECODED), + (22, 27, CONTINUE), + ], + [ + (2, 19, DECODED), + (9, 19, DECODED), + (23, 19, DECODED), + (40, 19, CONTINUE), + (2, 20, DECODED), + (9, 20, DECODED), + (23, 20, DECODED), + (40, 20, CONTINUE), + (2, 21, DECODED), + (9, 21, DECODED), + (23, 21, DECODED), + (40, 21, CONTINUE), + (2, 23, DECODED), + (9, 23, DECODED), + (23, 23, DECODED), + (40, 23, CONTINUE), + ], + [ + (3, 19, DECODED), + (6, 19, DECODED), + (10, 19, DECODED), + (15, 19, DECODED), + (24, 19, DECODED), + (31, 19, DECODED), + (41, 19, DECODED), + (56, 19, CONTINUE), + (3, 20, DECODED), + (6, 20, DECODED), + (10, 20, DECODED), + (15, 20, DECODED), + (24, 20, DECODED), + (31, 20, DECODED), + (41, 20, DECODED), + (56, 20, CONTINUE), + ], + [ + (3, 21, DECODED), + (6, 21, DECODED), + (10, 21, DECODED), + (15, 21, DECODED), + (24, 21, DECODED), + (31, 21, DECODED), + (41, 21, DECODED), + (56, 21, CONTINUE), + (3, 23, DECODED), + (6, 23, DECODED), + (10, 23, DECODED), + (15, 23, DECODED), + (24, 23, DECODED), + (31, 23, DECODED), + (41, 23, DECODED), + (56, 23, CONTINUE), + ], + [ + (2, 24, DECODED), + (9, 24, DECODED), + (23, 24, DECODED), + (40, 24, CONTINUE), + (2, 25, DECODED), + (9, 25, DECODED), + (23, 25, DECODED), + (40, 25, CONTINUE), + (2, 26, DECODED), + (9, 26, DECODED), + (23, 26, DECODED), + (40, 26, CONTINUE), + (2, 27, DECODED), + (9, 27, DECODED), + (23, 27, DECODED), + (40, 27, CONTINUE), + ], + [ + (3, 24, DECODED), + (6, 24, DECODED), + (10, 24, DECODED), + (15, 24, DECODED), + (24, 24, DECODED), + (31, 24, DECODED), + (41, 24, DECODED), + (56, 24, CONTINUE), + (3, 25, DECODED), + (6, 25, DECODED), + (10, 25, DECODED), + (15, 25, DECODED), + (24, 25, DECODED), + (31, 25, DECODED), + (41, 25, DECODED), + (56, 25, CONTINUE), + ], + [ + (3, 26, DECODED), + (6, 26, DECODED), + (10, 26, DECODED), + (15, 26, DECODED), + (24, 26, DECODED), + (31, 26, DECODED), + (41, 26, DECODED), + (56, 26, CONTINUE), + (3, 27, DECODED), + (6, 27, DECODED), + (10, 27, DECODED), + (15, 27, DECODED), + (24, 27, DECODED), + (31, 27, DECODED), + (41, 27, DECODED), + (56, 27, CONTINUE), + ], + [ + (1, 28, DECODED), + (22, 28, CONTINUE), + (1, 29, DECODED), + (22, 29, CONTINUE), + (1, 30, DECODED), + (22, 30, CONTINUE), + (1, 31, DECODED), + (22, 31, CONTINUE), + (1, 127, DECODED), + (22, 127, CONTINUE), + (1, 220, DECODED), + (22, 220, CONTINUE), + (1, 249, DECODED), + (22, 249, CONTINUE), + (254, 0, OK), + (255, 0, OK), + ], + [ + (2, 28, DECODED), + (9, 28, DECODED), + (23, 28, DECODED), + (40, 28, CONTINUE), + (2, 29, DECODED), + (9, 29, DECODED), + (23, 29, DECODED), + (40, 29, CONTINUE), + (2, 30, DECODED), + (9, 30, DECODED), + (23, 30, DECODED), + (40, 30, CONTINUE), + (2, 31, DECODED), + (9, 31, DECODED), + (23, 31, DECODED), + (40, 31, CONTINUE), + ], + [ + (3, 28, DECODED), + (6, 28, DECODED), + (10, 28, DECODED), + (15, 28, DECODED), + (24, 28, DECODED), + (31, 28, DECODED), + (41, 28, DECODED), + (56, 28, CONTINUE), + (3, 29, DECODED), + (6, 29, DECODED), + (10, 29, DECODED), + (15, 29, DECODED), + (24, 29, DECODED), + (31, 29, DECODED), + (41, 29, DECODED), + (56, 29, CONTINUE), + ], + [ + (3, 30, DECODED), + (6, 30, DECODED), + (10, 30, DECODED), + (15, 30, DECODED), + (24, 30, DECODED), + (31, 30, DECODED), + (41, 30, DECODED), + (56, 30, CONTINUE), + (3, 31, DECODED), + (6, 31, DECODED), + (10, 31, DECODED), + (15, 31, DECODED), + (24, 31, DECODED), + (31, 31, DECODED), + (41, 31, DECODED), + (56, 31, CONTINUE), + ], + [ + (2, 127, DECODED), + (9, 127, DECODED), + (23, 127, DECODED), + (40, 127, CONTINUE), + (2, 220, DECODED), + (9, 220, DECODED), + (23, 220, DECODED), + (40, 220, CONTINUE), + (2, 249, DECODED), + (9, 249, DECODED), + (23, 249, DECODED), + (40, 249, CONTINUE), + (0, 10, DECODED), + (0, 13, DECODED), + (0, 22, DECODED), + (0, 0, ERROR), + ], + [ + (3, 127, DECODED), + (6, 127, DECODED), + (10, 127, DECODED), + (15, 127, DECODED), + (24, 127, DECODED), + (31, 127, DECODED), + (41, 127, DECODED), + (56, 127, CONTINUE), + (3, 220, DECODED), + (6, 220, DECODED), + (10, 220, DECODED), + (15, 220, DECODED), + (24, 220, DECODED), + (31, 220, DECODED), + (41, 220, DECODED), + (56, 220, CONTINUE), + ], + [ + (3, 249, DECODED), + (6, 249, DECODED), + (10, 249, DECODED), + (15, 249, DECODED), + (24, 249, DECODED), + (31, 249, DECODED), + (41, 249, DECODED), + (56, 249, CONTINUE), + (1, 10, DECODED), + (22, 10, CONTINUE), + (1, 13, DECODED), + (22, 13, CONTINUE), + (1, 22, DECODED), + (22, 22, CONTINUE), + (0, 0, ERROR), + (0, 0, 0x05), + ], + [ + (2, 10, DECODED), + (9, 10, DECODED), + (23, 10, DECODED), + (40, 10, CONTINUE), + (2, 13, DECODED), + (9, 13, DECODED), + (23, 13, DECODED), + (40, 13, CONTINUE), + (2, 22, DECODED), + (9, 22, DECODED), + (23, 22, DECODED), + (40, 22, CONTINUE), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, 0x05), + ], + [ + (3, 10, DECODED), + (6, 10, DECODED), + (10, 10, DECODED), + (15, 10, DECODED), + (24, 10, DECODED), + (31, 10, DECODED), + (41, 10, DECODED), + (56, 10, CONTINUE), + (3, 13, DECODED), + (6, 13, DECODED), + (10, 13, DECODED), + (15, 13, DECODED), + (24, 13, DECODED), + (31, 13, DECODED), + (41, 13, DECODED), + (56, 13, CONTINUE), + ], + [ + (3, 22, DECODED), + (6, 22, DECODED), + (10, 22, DECODED), + (15, 22, DECODED), + (24, 22, DECODED), + (31, 22, DECODED), + (41, 22, DECODED), + (56, 22, CONTINUE), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, ERROR), + (0, 0, 0x05), + ], +]; + +pub(crate) const ENCODE_TABLE: &[(u8, u64); 257] = &[ + (13, 0x1ff8), + (23, 0x007f_ffd8), + (28, 0x0fff_ffe2), + (28, 0x0fff_ffe3), + (28, 0x0fff_ffe4), + (28, 0x0fff_ffe5), + (28, 0x0fff_ffe6), + (28, 0x0fff_ffe7), + (28, 0x0fff_ffe8), + (24, 0x00ff_ffea), + (30, 0x3fff_fffc), + (28, 0x0fff_ffe9), + (28, 0x0fff_ffea), + (30, 0x3fff_fffd), + (28, 0x0fff_ffeb), + (28, 0x0fff_ffec), + (28, 0x0fff_ffed), + (28, 0x0fff_ffee), + (28, 0x0fff_ffef), + (28, 0x0fff_fff0), + (28, 0x0fff_fff1), + (28, 0x0fff_fff2), + (30, 0x3fff_fffe), + (28, 0x0fff_fff3), + (28, 0x0fff_fff4), + (28, 0x0fff_fff5), + (28, 0x0fff_fff6), + (28, 0x0fff_fff7), + (28, 0x0fff_fff8), + (28, 0x0fff_fff9), + (28, 0x0fff_fffa), + (28, 0x0fff_fffb), + (6, 0x14), + (10, 0x3f8), + (10, 0x3f9), + (12, 0xffa), + (13, 0x1ff9), + (6, 0x15), + (8, 0xf8), + (11, 0x7fa), + (10, 0x3fa), + (10, 0x3fb), + (8, 0xf9), + (11, 0x7fb), + (8, 0xfa), + (6, 0x16), + (6, 0x17), + (6, 0x18), + (5, 0x0), + (5, 0x1), + (5, 0x2), + (6, 0x19), + (6, 0x1a), + (6, 0x1b), + (6, 0x1c), + (6, 0x1d), + (6, 0x1e), + (6, 0x1f), + (7, 0x5c), + (8, 0xfb), + (15, 0x7ffc), + (6, 0x20), + (12, 0xffb), + (10, 0x3fc), + (13, 0x1ffa), + (6, 0x21), + (7, 0x5d), + (7, 0x5e), + (7, 0x5f), + (7, 0x60), + (7, 0x61), + (7, 0x62), + (7, 0x63), + (7, 0x64), + (7, 0x65), + (7, 0x66), + (7, 0x67), + (7, 0x68), + (7, 0x69), + (7, 0x6a), + (7, 0x6b), + (7, 0x6c), + (7, 0x6d), + (7, 0x6e), + (7, 0x6f), + (7, 0x70), + (7, 0x71), + (7, 0x72), + (8, 0xfc), + (7, 0x73), + (8, 0xfd), + (13, 0x1ffb), + (19, 0x7fff0), + (13, 0x1ffc), + (14, 0x3ffc), + (6, 0x22), + (15, 0x7ffd), + (5, 0x3), + (6, 0x23), + (5, 0x4), + (6, 0x24), + (5, 0x5), + (6, 0x25), + (6, 0x26), + (6, 0x27), + (5, 0x6), + (7, 0x74), + (7, 0x75), + (6, 0x28), + (6, 0x29), + (6, 0x2a), + (5, 0x7), + (6, 0x2b), + (7, 0x76), + (6, 0x2c), + (5, 0x8), + (5, 0x9), + (6, 0x2d), + (7, 0x77), + (7, 0x78), + (7, 0x79), + (7, 0x7a), + (7, 0x7b), + (15, 0x7ffe), + (11, 0x7fc), + (14, 0x3ffd), + (13, 0x1ffd), + (28, 0x0fff_fffc), + (20, 0xfffe6), + (22, 0x003f_ffd2), + (20, 0xfffe7), + (20, 0xfffe8), + (22, 0x003f_ffd3), + (22, 0x003f_ffd4), + (22, 0x003f_ffd5), + (23, 0x007f_ffd9), + (22, 0x003f_ffd6), + (23, 0x007f_ffda), + (23, 0x007f_ffdb), + (23, 0x007f_ffdc), + (23, 0x007f_ffdd), + (23, 0x007f_ffde), + (24, 0x00ff_ffeb), + (23, 0x007f_ffdf), + (24, 0x00ff_ffec), + (24, 0x00ff_ffed), + (22, 0x003f_ffd7), + (23, 0x007f_ffe0), + (24, 0x00ff_ffee), + (23, 0x007f_ffe1), + (23, 0x007f_ffe2), + (23, 0x007f_ffe3), + (23, 0x007f_ffe4), + (21, 0x001f_ffdc), + (22, 0x003f_ffd8), + (23, 0x007f_ffe5), + (22, 0x003f_ffd9), + (23, 0x007f_ffe6), + (23, 0x007f_ffe7), + (24, 0x00ff_ffef), + (22, 0x003f_ffda), + (21, 0x001f_ffdd), + (20, 0xfffe9), + (22, 0x003f_ffdb), + (22, 0x003f_ffdc), + (23, 0x007f_ffe8), + (23, 0x007f_ffe9), + (21, 0x001f_ffde), + (23, 0x007f_ffea), + (22, 0x003f_ffdd), + (22, 0x003f_ffde), + (24, 0x00ff_fff0), + (21, 0x001f_ffdf), + (22, 0x003f_ffdf), + (23, 0x007f_ffeb), + (23, 0x007f_ffec), + (21, 0x001f_ffe0), + (21, 0x001f_ffe1), + (22, 0x003f_ffe0), + (21, 0x001f_ffe2), + (23, 0x007f_ffed), + (22, 0x003f_ffe1), + (23, 0x007f_ffee), + (23, 0x007f_ffef), + (20, 0xfffea), + (22, 0x003f_ffe2), + (22, 0x003f_ffe3), + (22, 0x003f_ffe4), + (23, 0x007f_fff0), + (22, 0x003f_ffe5), + (22, 0x003f_ffe6), + (23, 0x007f_fff1), + (26, 0x03ff_ffe0), + (26, 0x03ff_ffe1), + (20, 0xfffeb), + (19, 0x7fff1), + (22, 0x003f_ffe7), + (23, 0x007f_fff2), + (22, 0x003f_ffe8), + (25, 0x01ff_ffec), + (26, 0x03ff_ffe2), + (26, 0x03ff_ffe3), + (26, 0x03ff_ffe4), + (27, 0x07ff_ffde), + (27, 0x07ff_ffdf), + (26, 0x03ff_ffe5), + (24, 0x00ff_fff1), + (25, 0x01ff_ffed), + (19, 0x7fff2), + (21, 0x001f_ffe3), + (26, 0x03ff_ffe6), + (27, 0x07ff_ffe0), + (27, 0x07ff_ffe1), + (26, 0x03ff_ffe7), + (27, 0x07ff_ffe2), + (24, 0x00ff_fff2), + (21, 0x001f_ffe4), + (21, 0x001f_ffe5), + (26, 0x03ff_ffe8), + (26, 0x03ff_ffe9), + (28, 0x0fff_fffd), + (27, 0x07ff_ffe3), + (27, 0x07ff_ffe4), + (27, 0x07ff_ffe5), + (20, 0xfffec), + (24, 0x00ff_fff3), + (20, 0xfffed), + (21, 0x001f_ffe6), + (22, 0x003f_ffe9), + (21, 0x001f_ffe7), + (21, 0x001f_ffe8), + (23, 0x007f_fff3), + (22, 0x003f_ffea), + (22, 0x003f_ffeb), + (25, 0x01ff_ffee), + (25, 0x01ff_ffef), + (24, 0x00ff_fff4), + (24, 0x00ff_fff5), + (26, 0x03ff_ffea), + (23, 0x007f_fff4), + (26, 0x03ff_ffeb), + (27, 0x07ff_ffe6), + (26, 0x03ff_ffec), + (26, 0x03ff_ffed), + (27, 0x07ff_ffe7), + (27, 0x07ff_ffe8), + (27, 0x07ff_ffe9), + (27, 0x07ff_ffea), + (27, 0x07ff_ffeb), + (28, 0x0fff_fffe), + (27, 0x07ff_ffec), + (27, 0x07ff_ffed), + (27, 0x07ff_ffee), + (27, 0x07ff_ffef), + (27, 0x07ff_fff0), + (26, 0x03ff_ffee), + (30, 0x3fff_ffff), +]; diff --git a/wtx/src/http2/macros.rs b/wtx/src/http2/macros.rs new file mode 100644 index 00000000..56268e15 --- /dev/null +++ b/wtx/src/http2/macros.rs @@ -0,0 +1,53 @@ +macro_rules! rfr_resource_or_return { + ($rfr:expr) => { + match $rfr { + ReadFrameRslt::ClosedConnection => return Ok(ReadFrameRslt::ClosedConnection), + ReadFrameRslt::ClosedStream => return Ok(ReadFrameRslt::ClosedStream), + ReadFrameRslt::IdleConnection => return Ok(ReadFrameRslt::IdleConnection), + ReadFrameRslt::Resource(elem) => elem, + } + }; +} + +macro_rules! rfr_until_resource { + ($rfr:expr) => {{ + let rfr_resource = 'rfr_resource: { + for _ in 0.._max_frames_mismatches!() { + match $rfr { + ReadFrameRslt::ClosedConnection => return Ok(ReadFrameRslt::ClosedConnection), + ReadFrameRslt::ClosedStream => return Ok(ReadFrameRslt::ClosedStream), + ReadFrameRslt::IdleConnection => continue, + ReadFrameRslt::Resource(elem) => break 'rfr_resource elem, + } + } + return Err(crate::Error::VeryLargeAmountOfFrameMismatches); + }; + rfr_resource + }}; +} + +macro_rules! rfr_until_resource_with_guard { + ($lock:expr, |$guard:ident| $cb:expr $(, |$another_guard:ident, $rslt:ident| $rest:expr)?) => {{ + let rfr_resource = 'rfr_resource: { + for _ in 0.._max_frames_mismatches!() { + let mut $guard = $lock.lock().await; + match $cb { + ReadFrameRslt::ClosedConnection => return Ok(ReadFrameRslt::ClosedConnection), + ReadFrameRslt::ClosedStream => return Ok(ReadFrameRslt::ClosedStream), + ReadFrameRslt::IdleConnection => continue, + ReadFrameRslt::Resource(elem) => { + $( + let mut $another_guard = $guard; + let $rslt = elem; + $rest; + let elem = $rslt; + )? + break 'rfr_resource elem; + } + } + } + return Err(crate::Error::VeryLargeAmountOfFrameMismatches); + }; + rfr_resource + }}; +} diff --git a/wtx/src/http2/misc.rs b/wtx/src/http2/misc.rs new file mode 100644 index 00000000..275e2202 --- /dev/null +++ b/wtx/src/http2/misc.rs @@ -0,0 +1,381 @@ +use crate::{ + http::Headers, + http2::{ + send_params::SendParams, ContinuationFrame, ErrorCode, FrameHeaderTy, FrameInit, GoAwayFrame, + HeadersFrame, HpackEncoder, HpackStaticRequestHeaders, HpackStaticResponseHeaders, Http2Buffer, + Http2Params, PingFrame, ReadFrameRslt, ResetStreamFrame, SettingsFrame, StreamState, + WindowUpdateFrame, PAD_MASK, U31, + }, + misc::{ + BlocksQueue, ByteVector, PartitionedFilledBuffer, PollOnce, Stream, Usize, _read_until, + _unlikely_elem, + }, +}; +use core::pin::pin; +use hashbrown::HashMap; + +#[inline] +pub(crate) fn apply_initial_params( + hb: &mut Http2Buffer, + hp: &Http2Params, +) -> crate::Result<()> { + hb.hpack_dec.set_max_bytes(hp.max_cached_headers_len().0); + hb.hpack_enc.set_max_dyn_super_bytes(hp.max_cached_headers_len().1); + hb.pfb._expand_buffer(*Usize::from(hp.read_buffer_len())); + Ok(()) +} + +#[inline] +pub(crate) fn default_stream_frames() -> BlocksQueue { + BlocksQueue::with_capacity(8, 64) +} + +#[inline] +pub(crate) async fn read_frame( + hp: &mut Http2Params, + is_conn_open: bool, + pfb: &mut PartitionedFilledBuffer, + stream: &mut S, +) -> crate::Result> +where + S: Stream, +{ + if !is_conn_open { + return Ok(ReadFrameRslt::ClosedConnection); + } + let mut read = pfb._following_len(); + let buffer = pfb._following_trail_mut(); + let Some(array) = PollOnce(pin!(_read_until::<9, _>(buffer, &mut read, 0, stream))).await else { + return Ok(ReadFrameRslt::IdleConnection); + }; + let fi = FrameInit::from_array(array?)?; + if fi.data_len > hp.max_frame_len() { + return Err(crate::Error::VeryLargePayload); + } + let frame_len = fi.data_len.wrapping_add(9); + let mut is_fulfilled = false; + pfb._expand_following(*Usize::from(fi.data_len)); + for _ in 0..fi.data_len { + if read >= *Usize::from(frame_len) { + is_fulfilled = true; + break; + } + read = read.wrapping_add( + stream.read(pfb._following_trail_mut().get_mut(read..).unwrap_or_default()).await?, + ); + } + if !is_fulfilled { + return Err(crate::Error::UnexpectedBufferState); + } + pfb._set_indices( + pfb._current_end_idx().wrapping_add(9), + *Usize::from(fi.data_len), + read.wrapping_sub(*Usize::from(frame_len)), + )?; + Ok(ReadFrameRslt::Resource(fi)) +} + +/// Reads a non-initial frame that corresponds to the desired `stream_id` which is locally stored +/// or externally reachable. +#[inline] +pub(crate) async fn read_frame_others<'rslt, S>( + hp: &mut Http2Params, + hpack_enc: &mut HpackEncoder, + is_conn_open: &mut bool, + pfb: &'rslt mut PartitionedFilledBuffer, + send_params: &mut SendParams, + stream: &mut S, + stream_id: U31, + stream_state: &mut StreamState, + streams_frames: &'rslt mut HashMap>, + streams_num: &mut u32, +) -> crate::Result> +where + S: Stream, +{ + if let Some(true) = streams_frames.get(&stream_id).map(|el| el.blocks_len() > 0) { + #[allow( + // Borrow checker limitation + clippy::unwrap_used + )] + let (fi, data) = streams_frames.get_mut(&stream_id).unwrap().pop_back().unwrap(); + return Ok(ReadFrameRslt::Resource((fi, data))); + } + let fi = rfr_resource_or_return!( + read_frame_until( + hp, + hpack_enc, + is_conn_open, + pfb, + send_params, + stream, + stream_id, + stream_state, + streams_num, + |fi, local_hp, data| { + read_frame_until_cb_known_id(data, fi, local_hp, stream_id, streams_frames) + }, + |_| Ok(()), + ) + .await? + ); + let rslt = (fi, pfb._current()); + Ok(ReadFrameRslt::Resource(rslt)) +} + +/// Fetches a frame until `cb` yields a positive boolean. +#[inline] +pub(crate) async fn read_frame_until( + hp: &mut Http2Params, + hpack_enc: &mut HpackEncoder, + is_conn_open: &mut bool, + pfb: &mut PartitionedFilledBuffer, + send_params: &mut SendParams, + stream: &mut S, + stream_id: U31, + stream_state: &mut StreamState, + streams_num: &mut u32, + mut loop_cb: impl FnMut(FrameInit, &Http2Params, &[u8]) -> crate::Result, + mut reset_cb: impl FnMut(&Http2Params) -> crate::Result<()>, +) -> crate::Result> +where + S: Stream, +{ + for _ in 0.._max_frames_mismatches!() { + let fi = rfr_resource_or_return!(read_frame(hp, *is_conn_open, pfb, stream).await?); + if fi.stream_id == U31::ZERO { + match fi.ty { + FrameHeaderTy::GoAway => { + let _ = GoAwayFrame::read(pfb._current(), fi)?; + let go_away_frame = GoAwayFrame::new(ErrorCode::Cancel, stream_id); + send_go_away(go_away_frame, (is_conn_open, stream)).await?; + return _unlikely_elem(Ok(ReadFrameRslt::ClosedConnection)); + } + FrameHeaderTy::Ping => { + let mut pf = PingFrame::read(pfb._current(), fi)?; + if !pf.is_ack() { + pf.set_ack(); + write_to_stream([&pf.bytes()], *is_conn_open, stream).await?; + } + continue; + } + FrameHeaderTy::Reset => { + reset_cb(hp)?; + let _ = ResetStreamFrame::read(pfb._current(), fi)?; + return Ok(reset_stream(stream_state, streams_num)); + } + FrameHeaderTy::Settings => { + let sf = SettingsFrame::read(pfb._current(), fi)?; + if !sf.is_ack() { + send_params.update(hpack_enc, &sf)?; + write_to_stream([SettingsFrame::ack().bytes(&mut [0; 45])], *is_conn_open, stream) + .await?; + } + continue; + } + FrameHeaderTy::WindowUpdate => { + let _wuf = WindowUpdateFrame::read(pfb._current(), fi)?; + continue; + } + _ => return Err(ErrorCode::ProtocolError.into()), + } + } + if loop_cb(fi, hp, pfb._current())? { + return Ok(ReadFrameRslt::Resource(fi)); + } + pfb._clear_if_following_is_empty(); + } + Err(crate::Error::VeryLargeAmountOfFrameMismatches) +} + +#[inline] +pub(crate) fn read_frame_until_cb_known_id( + data: &[u8], + fi: FrameInit, + hp: &Http2Params, + stream_id: U31, + streams_frames: &mut HashMap>, +) -> crate::Result { + if fi.stream_id == stream_id { + return Ok(true); + } + let Some(stream_frames) = streams_frames.get_mut(&fi.stream_id) else { + return Err(crate::Error::UnknownStreamId); + }; + if stream_frames.elements_len() > hp.max_buffered_frames_num().into() { + return Err(crate::Error::VeryLargeAmountOfBufferedFrames); + } + stream_frames.push_front_within_cap([data], fi); + Ok(false) +} + +#[inline] +pub(crate) fn read_frame_until_cb_unknown_id( + data: &[u8], + fi: FrameInit, + hp: &Http2Params, + streams_frames: &mut HashMap>, +) -> crate::Result { + let Some(stream_frames) = streams_frames.get_mut(&fi.stream_id) else { + return Ok(true); + }; + if stream_frames.elements_len() > hp.max_buffered_frames_num().into() { + return Err(crate::Error::VeryLargeAmountOfBufferedFrames); + } + stream_frames.push_front_within_cap([data], fi); + Ok(false) +} + +#[inline] +pub(crate) fn reset_stream( + stream_state: &mut StreamState, + streams_num: &mut u32, +) -> ReadFrameRslt { + *stream_state = StreamState::Closed; + *streams_num = streams_num.wrapping_sub(1); + return ReadFrameRslt::ClosedStream; +} + +#[inline] +pub(crate) async fn send_go_away( + go_away_frame: GoAwayFrame, + (is_conn_open, stream): (&mut bool, &mut S), +) -> crate::Result<()> +where + S: Stream, +{ + write_to_stream([go_away_frame.bytes().as_slice()], *is_conn_open, stream).await?; + *is_conn_open = false; + Ok(()) +} + +#[inline] +pub(crate) async fn send_reset( + reset_frame: ResetStreamFrame, + stream_state: &mut StreamState, + (is_conn_open, stream): (&mut bool, &mut S), +) -> crate::Result<()> +where + S: Stream, +{ + *stream_state = StreamState::Closed; + write_to_stream([reset_frame.bytes().as_slice()], *is_conn_open, stream).await?; + Ok(()) +} + +#[inline] +pub(crate) fn trim_frame_pad(data: &mut &[u8], flags: u8) -> crate::Result> { + let mut pad_len = None; + if flags & PAD_MASK == PAD_MASK { + let [local_pad_len, rest @ ..] = data else { + return _unlikely_elem(Err(ErrorCode::ProtocolError.into())); + }; + let diff_opt = rest.len().checked_sub(usize::from(*local_pad_len)); + let Some(local_data) = diff_opt.and_then(|idx| data.get(..idx)) else { + return _unlikely_elem(Err(ErrorCode::ProtocolError.into())); + }; + *data = local_data; + pad_len = Some(*local_pad_len); + } + Ok(pad_len) +} + +#[inline] +pub(crate) async fn write_init_headers( + body: &[u8], + headers: &Headers, + hpack_enc: &mut HpackEncoder, + hpack_enc_buffer: &mut ByteVector, + (hsreqh, hsresh): (HpackStaticRequestHeaders<'_>, HpackStaticResponseHeaders), + is_conn_open: bool, + send_params: &SendParams, + stream: &mut S, + stream_id: U31, +) -> crate::Result<()> +where + S: Stream, +{ + #[inline] + fn adjust_frame_init(content: &[u8], frame_init: [u8; 9], frame_init_buffer: &mut [u8; 9]) { + let [a, b, c, d, e, f, g, h, i] = frame_init_buffer; + let [_, j, k, l] = u32::try_from(content.len()).unwrap_or_default().to_be_bytes(); + let [_, _, _, m, n, o, p, q, r] = frame_init; + *a = j; + *b = k; + *c = l; + *d = m; + *e = n; + *f = o; + *g = p; + *h = q; + *i = r; + } + + #[inline] + async fn single_header_frame( + frame_init_buffer: &mut [u8; 9], + hf: &mut HeadersFrame<'_, '_>, + hf_content: &[u8], + is_conn_open: bool, + stream: &mut S, + ) -> crate::Result<()> + where + S: Stream, + { + hf.set_eoh(); + adjust_frame_init(hf_content, hf.bytes(), frame_init_buffer); + write_to_stream([frame_init_buffer, hf_content], is_conn_open, stream).await?; + return Ok(()); + } + + if IS_CLIENT { + hpack_enc.encode(hpack_enc_buffer, hsreqh.iter(), headers.iter())?; + } else { + hpack_enc.encode(hpack_enc_buffer, hsresh.iter(), headers.iter())?; + } + + let header_frame_init = &mut [0; 9]; + let hf = &mut HeadersFrame::new(headers, (hsreqh, hsresh), stream_id); + if body.is_empty() { + hf.set_eos(); + } + + if hpack_enc_buffer.is_empty() { + return single_header_frame(header_frame_init, hf, &[], is_conn_open, stream).await; + } + + let mut iter = hpack_enc_buffer.chunks_mut(*Usize::from(send_params.max_frame_len)); + + let Some(first) = iter.next() else { + return Ok(()); + }; + let Some(second) = iter.next() else { + return single_header_frame(header_frame_init, hf, first, is_conn_open, stream).await; + }; + + if iter.len() <= 2 { + adjust_frame_init(first, hf.bytes(), header_frame_init); + let mut cf = ContinuationFrame::new(stream_id); + cf.set_eoh(); + adjust_frame_init(second, cf.bytes(), header_frame_init); + write_to_stream([header_frame_init, first, second], is_conn_open, stream).await?; + } + + Ok(()) +} + +#[inline] +pub(crate) async fn write_to_stream( + bytes: [&[u8]; N], + is_conn_open: bool, + stream: &mut S, +) -> crate::Result<()> +where + S: Stream, +{ + if is_conn_open { + return Ok(()); + } + stream.write_all_vectored(bytes).await?; + Ok(()) +} diff --git a/wtx/src/http2/ping_frame.rs b/wtx/src/http2/ping_frame.rs new file mode 100644 index 00000000..077fc5b4 --- /dev/null +++ b/wtx/src/http2/ping_frame.rs @@ -0,0 +1,38 @@ +use crate::http2::{FrameHeaderTy, FrameInit, ACK_MASK, U31}; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct PingFrame { + flags: u8, + payload: [u8; 8], +} + +impl PingFrame { + #[inline] + pub(crate) fn read(bytes: &[u8], fi: FrameInit) -> crate::Result { + if fi.stream_id.is_not_zero() { + return Err(crate::http2::ErrorCode::ProtocolError.into()); + } + let [a, b, c, d, e, f, g, h] = bytes else { + return Err(crate::http2::ErrorCode::FrameSizeError.into()); + }; + Ok(Self { flags: fi.flags & ACK_MASK, payload: [*a, *b, *c, *d, *e, *f, *g, *h] }) + } + + #[inline] + pub(crate) fn bytes(&self) -> [u8; 17] { + let fi = FrameInit::new(8, self.flags, U31::ZERO, FrameHeaderTy::Ping); + let [a, b, c, d, e, f, g, h, i] = fi.bytes(); + let [j, k, l, m, n, o, p, q] = self.payload; + [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q] + } + + #[inline] + pub(crate) fn is_ack(&self) -> bool { + self.flags == ACK_MASK + } + + #[inline] + pub(crate) fn set_ack(&mut self) { + self.flags = ACK_MASK; + } +} diff --git a/wtx/src/http2/read_frame_rslt.rs b/wtx/src/http2/read_frame_rslt.rs new file mode 100644 index 00000000..71c53528 --- /dev/null +++ b/wtx/src/http2/read_frame_rslt.rs @@ -0,0 +1,24 @@ +/// Read Frame Result +#[derive(Debug)] +pub enum ReadFrameRslt { + /// When a GOAWAY frame is sent or received. + ClosedConnection, + /// When a RST_STREAM frame is sent or received. + ClosedStream, + /// Remote part didn't send any frames + IdleConnection, + /// Resource was successfully fetched. + Resource(T), +} + +impl ReadFrameRslt { + /// Extracts a successful resource, if any. + #[inline] + pub fn resource(self) -> Option { + if let Self::Resource(elem) = self { + Some(elem) + } else { + None + } + } +} diff --git a/wtx/src/http2/req_res_buffer.rs b/wtx/src/http2/req_res_buffer.rs new file mode 100644 index 00000000..9a2ecb8a --- /dev/null +++ b/wtx/src/http2/req_res_buffer.rs @@ -0,0 +1,33 @@ +use crate::{ + http::Headers, + http2::uri_buffer::MAX_URI_LEN, + misc::{ArrayString, ByteVector}, +}; +use alloc::boxed::Box; + +/// Buffer used for requests or responses. +// +// Maximum sizes are dictated by `AcceptParams` or `ConnectParams`. +#[derive(Debug)] +pub struct ReqResBuffer { + /// See [ByteVector]. + pub data: ByteVector, + /// See [Headers]. + pub headers: Headers, + /// Scheme, authority and path. + pub uri: Box>, +} + +impl Default for ReqResBuffer { + fn default() -> Self { + Self { data: ByteVector::new(), headers: Headers::new(0), uri: Box::new(ArrayString::new()) } + } +} + +impl ReqResBuffer { + #[inline] + pub(crate) fn clear(&mut self) { + self.data.clear(); + self.headers.clear(); + } +} diff --git a/wtx/src/http2/reset_stream_frame.rs b/wtx/src/http2/reset_stream_frame.rs new file mode 100644 index 00000000..8d044291 --- /dev/null +++ b/wtx/src/http2/reset_stream_frame.rs @@ -0,0 +1,33 @@ +use crate::http2::{ErrorCode, FrameHeaderTy, FrameInit, U31}; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct ResetStreamFrame { + error_code: ErrorCode, + stream_id: U31, +} + +impl ResetStreamFrame { + pub(crate) fn new(error_code: ErrorCode, stream_id: U31) -> crate::Result { + if stream_id.is_zero() { + return Err(ErrorCode::ProtocolError.into()); + } + Ok(Self { error_code, stream_id }) + } + + pub(crate) fn bytes(&self) -> [u8; 13] { + let [a, b, c, d, e, f, g, h, i] = + FrameInit::new(4, 0, self.stream_id, FrameHeaderTy::Reset).bytes(); + let [j, k, l, m] = u32::from(self.error_code).to_be_bytes(); + [a, b, c, d, e, f, g, h, i, j, k, l, m] + } + + pub(crate) fn read(bytes: &[u8], fi: FrameInit) -> crate::Result { + let [a, b, c, d] = bytes else { + return Err(ErrorCode::FrameSizeError.into()); + }; + Ok(Self { + error_code: u32::from_be_bytes([*a, *b, *c, *d]).try_into()?, + stream_id: fi.stream_id, + }) + } +} diff --git a/wtx/src/http2/send_params.rs b/wtx/src/http2/send_params.rs new file mode 100644 index 00000000..5900a654 --- /dev/null +++ b/wtx/src/http2/send_params.rs @@ -0,0 +1,59 @@ +use crate::http2::{ + HpackEncoder, SettingsFrame, FRAME_LEN_LOWER_BOUND, FRAME_LEN_UPPER_BOUND, U31, +}; + +/// Parameters used when sending data. +#[derive(Debug)] +pub(crate) struct SendParams { + pub(crate) enable_connect_protocol: u32, + pub(crate) initial_window_len: u32, + pub(crate) max_cached_headers_len: u32, + pub(crate) max_expanded_headers_len: u32, + pub(crate) max_frame_len: u32, + pub(crate) max_streams_num: u32, +} + +impl SendParams { + pub(crate) fn update( + &mut self, + hpack_enc: &mut HpackEncoder, + sf: &SettingsFrame, + ) -> crate::Result<()> { + if let Some(elem) = sf.enable_connect_protocol() { + self.enable_connect_protocol = u32::from(elem); + } + if let Some(elem) = sf.initial_window_size() { + self.initial_window_len = elem.clamp(0, U31::MAX.u32()); + } + if let Some(elem) = sf.header_table_size() { + self.max_cached_headers_len = elem; + hpack_enc.set_max_dyn_sub_bytes(elem)?; + } + if let Some(elem) = sf.max_header_list_size() { + self.max_expanded_headers_len = elem; + } + if let Some(elem) = sf.max_frame_size() { + self.max_frame_len = elem.clamp(FRAME_LEN_LOWER_BOUND, FRAME_LEN_UPPER_BOUND); + } + if let Some(elem) = sf.max_concurrent_streams() { + self.max_streams_num = elem; + } + Ok(()) + } +} + +/// It is not possible to use the same default values of `Http2Params` because, for sending +/// purposes, the default values provided by the RFC must be used until a settings frame +/// is received. +impl Default for SendParams { + fn default() -> Self { + Self { + enable_connect_protocol: 0, + initial_window_len: 65_535, + max_cached_headers_len: 4_096, + max_expanded_headers_len: u32::MAX, + max_frame_len: 16_384, + max_streams_num: u32::MAX, + } + } +} diff --git a/wtx/src/http2/server_stream.rs b/wtx/src/http2/server_stream.rs new file mode 100644 index 00000000..814374d1 --- /dev/null +++ b/wtx/src/http2/server_stream.rs @@ -0,0 +1,139 @@ +use crate::{ + http::{Method, RequestMut, Response, ResponseData}, + http2::{ + http2_data::ReadFramesInit, + misc::{send_go_away, send_reset, write_to_stream}, + DataFrame, ErrorCode, GoAwayFrame, HeadersFrame, HpackStaticRequestHeaders, + HpackStaticResponseHeaders, Http2Buffer, Http2Data, ReadFrameRslt, ReqResBuffer, + ResetStreamFrame, StreamState, U31, + }, + misc::{ByteVector, Lease, LeaseMut, Lock, RefCounter, Stream, Uri}, +}; +use core::marker::PhantomData; +use tokio::sync::MutexGuard; + +/// Created when a server receives an initial stream. Used mainly to poll remaining data but can +/// also be used to send additional streams. +#[derive(Debug)] +pub struct ServerStream { + hd: HD, + hpack_size: usize, + is_eos: bool, + method: Method, + phantom: PhantomData<(HB, S)>, + stream_id: U31, + stream_state: StreamState, +} + +impl ServerStream { + #[inline] + pub(crate) fn new(hd: HD, rfi: ReadFramesInit, stream_state: StreamState) -> Self { + Self { + hd, + hpack_size: rfi.hpack_size, + is_eos: rfi.is_eos, + method: rfi.headers_rslt, + phantom: PhantomData, + stream_id: rfi.stream_id, + stream_state, + } + } +} + +impl ServerStream +where + HB: LeaseMut>, + HD: RefCounter, + for<'guard> HD::Item: Lock< + Guard<'guard> = MutexGuard<'guard, Http2Data>, + Resource = Http2Data, + > + 'guard, + S: Stream, +{ + /// High-level method that reads all remaining data to build a request. + #[inline] + pub async fn recv_req<'rrb>( + &mut self, + rrb: &'rrb mut ReqResBuffer, + ) -> crate::Result>> { + rrb.clear(); + rfr_until_resource_with_guard!(self.hd, |guard| { + guard + .read_frames_others( + &mut self.hpack_size, + self.is_eos, + rrb, + self.stream_id, + &mut self.stream_state, + ) + .await? + }); + self.stream_state = StreamState::HalfClosedRemote; + Ok(ReadFrameRslt::Resource(RequestMut::http2( + &mut rrb.data, + &mut rrb.headers, + self.method, + Uri::new(&*rrb.uri), + ))) + } + + /// Sends a GOAWAY frame to the peer, which cancels the connection and consequently all ongoing + /// streams. + pub async fn send_go_away(self, error_code: ErrorCode) -> crate::Result<()> { + send_go_away( + GoAwayFrame::new(error_code, self.stream_id), + self.hd.lock().await.is_conn_open_and_stream_mut(), + ) + .await + } + + /// Auxiliary high-level method that sends a response. + #[inline] + pub async fn send_res(&mut self, res: Response) -> crate::Result<()> + where + D: ResponseData, + D::Body: Lease<[u8]>, + { + if !self.stream_state.can_server_send() { + return Ok(()); + } + let mut guard = self.hd.lock().await; + let (hb, is_conn_open, _, stream) = guard.parts_mut(); + let mut hf = HeadersFrame::new( + res.data.headers(), + ( + HpackStaticRequestHeaders::EMPTY, + HpackStaticResponseHeaders { status_code: Some(res.status_code) }, + ), + self.stream_id, + ); + let body = res.data.body().lease(); + if body.is_empty() { + hf.set_eos(); + hf.write::(&mut hb.hpack_enc, &mut hb.hpack_enc_buffer)?; + write_to_stream([&*hb.hpack_enc_buffer], *is_conn_open, stream).await?; + } else { + hf.write::(&mut hb.hpack_enc, &mut hb.hpack_enc_buffer)?; + let data_len = u32::try_from(body.len())?; + let df = DataFrame::eos(body, data_len, self.stream_id); + write_to_stream( + [&*hb.hpack_enc_buffer, df.bytes().as_slice(), df.data()], + *is_conn_open, + stream, + ) + .await?; + } + self.stream_state = StreamState::Closed; + Ok(()) + } + + /// Sends a stream reset to the peer, which cancels this stream. + pub async fn send_reset(&mut self, error_code: ErrorCode) -> crate::Result<()> { + send_reset( + ResetStreamFrame::new(error_code, self.stream_id)?, + &mut self.stream_state, + self.hd.lock().await.is_conn_open_and_stream_mut(), + ) + .await + } +} diff --git a/wtx/src/http2/settings_frame.rs b/wtx/src/http2/settings_frame.rs new file mode 100644 index 00000000..af7cb428 --- /dev/null +++ b/wtx/src/http2/settings_frame.rs @@ -0,0 +1,279 @@ +use crate::{ + http2::{FrameHeaderTy, FrameInit, ACK_MASK, FRAME_LEN_LOWER_BOUND, FRAME_LEN_UPPER_BOUND, U31}, + misc::{ArrayChunks, _unlikely_elem}, +}; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct SettingsFrame { + // SETTINGS_ENABLE_CONNECT_PROTOCOL + enable_connect_protocol: Option, + flags: u8, + // SETTINGS_HEADER_TABLE_SIZE + header_table_size: Option, + // SETTINGS_INITIAL_WINDOW_SIZE + initial_window_size: Option, + len: u8, + // SETTINGS_MAX_CONCURRENT_STREAMS + max_concurrent_streams: Option, + // SETTINGS_MAX_FRAME_SIZE + max_frame_size: Option, + // SETTINGS_MAX_HEADER_LIST_SIZE + max_header_list_size: Option, +} + +impl SettingsFrame { + pub(crate) const fn default() -> Self { + Self { + enable_connect_protocol: None, + flags: 0, + header_table_size: None, + initial_window_size: None, + len: 0, + max_concurrent_streams: None, + max_frame_size: None, + max_header_list_size: None, + } + } + + pub(crate) const fn ack() -> Self { + SettingsFrame { flags: ACK_MASK, ..SettingsFrame::empty() } + } + + const fn empty() -> Self { + Self { + enable_connect_protocol: None, + flags: 0, + header_table_size: None, + initial_window_size: None, + len: 0, + max_concurrent_streams: None, + max_frame_size: None, + max_header_list_size: None, + } + } + + pub(crate) fn bytes<'buffer>(&self, buffer: &'buffer mut [u8; 45]) -> &'buffer [u8] { + macro_rules! copy_bytes { + ($buffer:expr, $bytes:expr, $idx:expr) => {{ + let next_idx = $idx.wrapping_add(6); + if let Some([a, b, c, d, e, f]) = $buffer.get_mut($idx..next_idx) { + if let Some([g, h, i, j, k, l]) = $bytes { + *a = g; + *b = h; + *c = i; + *d = j; + *e = k; + *f = l; + $idx = next_idx; + } + } + }}; + } + + #[inline] + fn bytes(ty: u16, value: u32) -> [u8; 6] { + let [a, b] = ty.to_be_bytes(); + let [c, d, e, f] = value.to_be_bytes(); + [a, b, c, d, e, f] + } + + { + let [a, b, c, d, e, f, g, h, i, ..] = buffer; + let [j, k, l, m, n, o, p, q, r] = + FrameInit::new(self.len.into(), self.flags, U31::ZERO, FrameHeaderTy::Settings).bytes(); + *a = j; + *b = k; + *c = l; + *d = m; + *e = n; + *f = o; + *g = p; + *h = q; + *i = r; + } + let Self { + enable_connect_protocol, + flags: _, + header_table_size, + initial_window_size, + len: _, + max_concurrent_streams, + max_frame_size, + max_header_list_size, + } = self; + let mut idx: usize = 9; + copy_bytes!(buffer, header_table_size.map(|el| bytes(1, el)), idx); + copy_bytes!(buffer, max_concurrent_streams.map(|el| bytes(3, el)), idx); + copy_bytes!(buffer, initial_window_size.map(|el| bytes(4, el)), idx); + copy_bytes!(buffer, max_frame_size.map(|el| bytes(5, el)), idx); + copy_bytes!(buffer, max_header_list_size.map(|el| bytes(6, el)), idx); + copy_bytes!(buffer, enable_connect_protocol.map(|el| bytes(8, u32::from(el))), idx); + buffer.get(..idx).unwrap_or_default() + } + + pub(crate) fn enable_connect_protocol(&self) -> Option { + self.enable_connect_protocol + } + + pub(crate) fn header_table_size(&self) -> Option { + self.header_table_size + } + + pub(crate) fn initial_window_size(&self) -> Option { + self.initial_window_size + } + + pub(crate) fn is_ack(&self) -> bool { + self.flags == ACK_MASK + } + + pub(crate) fn max_concurrent_streams(&self) -> Option { + self.max_concurrent_streams + } + + pub(crate) fn max_frame_size(&self) -> Option { + self.max_frame_size + } + + pub(crate) fn max_header_list_size(&self) -> Option { + self.max_header_list_size + } + + pub(crate) fn read(bytes: &[u8], fi: FrameInit) -> crate::Result { + if fi.stream_id.is_not_zero() { + return Err(crate::http2::ErrorCode::ProtocolError.into()); + } + + let mut settings_frame = SettingsFrame { flags: fi.flags & ACK_MASK, ..Self::empty() }; + + if settings_frame.is_ack() { + if !bytes.is_empty() { + return Err(crate::http2::ErrorCode::FrameSizeError.into()); + } + return Ok(settings_frame); + } + + if bytes.len() % 6 != 0 { + return _unlikely_elem(Err(crate::http2::ErrorCode::FrameSizeError.into())); + } + + let Self { + enable_connect_protocol, + flags: _, + header_table_size, + initial_window_size, + len, + max_concurrent_streams, + max_frame_size, + max_header_list_size, + } = &mut settings_frame; + + for [a, b, c, d, e, f] in ArrayChunks::new(bytes) { + let Ok(setting) = Setting::new(&[*a, *b, *c, *d, *e, *f]) else { + continue; + }; + match setting { + Setting::EnableConnectProtocol(elem) => { + *enable_connect_protocol = Some(elem); + } + Setting::HeaderTableSize(elem) => { + *header_table_size = Some(elem); + } + Setting::InitialWindowSize(elem) => { + if elem > U31::MAX { + return Err(crate::http2::ErrorCode::ProtocolError.into()); + } else { + *initial_window_size = Some(elem); + } + } + Setting::MaxConcurrentStreams(elem) => { + *max_concurrent_streams = Some(elem); + } + Setting::MaxFrameSize(elem) => { + if (FRAME_LEN_LOWER_BOUND..=FRAME_LEN_UPPER_BOUND).contains(&elem) { + *max_frame_size = Some(elem); + } else { + return Err(crate::http2::ErrorCode::ProtocolError.into()); + } + } + Setting::MaxHeaderListSize(elem) => { + *max_header_list_size = Some(elem); + } + } + } + + if enable_connect_protocol.is_some() { + *len = len.wrapping_add(6); + } + for _ in [ + header_table_size, + initial_window_size, + max_concurrent_streams, + max_frame_size, + max_header_list_size, + ] + .into_iter() + .flatten() + { + *len = len.wrapping_add(6); + } + + Ok(settings_frame) + } + + pub(crate) fn set_enable_connect_protocol(&mut self, elem: Option) { + self.enable_connect_protocol = elem; + } + + pub(crate) fn set_header_table_size(&mut self, elem: Option) { + self.header_table_size = elem; + } + + pub(crate) fn set_initial_window_size(&mut self, elem: Option) { + self.initial_window_size = elem; + } + + pub(crate) fn set_max_concurrent_streams(&mut self, elem: Option) { + self.max_concurrent_streams = elem; + } + + pub(crate) fn set_max_frame_size(&mut self, elem: Option) { + if let Some(elem) = elem { + assert!((FRAME_LEN_LOWER_BOUND..=FRAME_LEN_UPPER_BOUND).contains(&elem)); + } + self.max_frame_size = elem; + } + + pub(crate) fn set_max_header_list_size(&mut self, elem: Option) { + self.max_header_list_size = elem; + } +} + +#[derive(Debug)] +enum Setting { + EnableConnectProtocol(bool), + HeaderTableSize(u32), + InitialWindowSize(u32), + MaxConcurrentStreams(u32), + MaxFrameSize(u32), + MaxHeaderListSize(u32), +} + +impl Setting { + pub(crate) fn new(array: &[u8; 6]) -> crate::Result { + let [a, b, c, d, e, f] = array; + Setting::from_id((u16::from(*a) << 8) | u16::from(*b), u32::from_be_bytes([*c, *d, *e, *f])) + } + + pub(crate) fn from_id(id: u16, value: u32) -> crate::Result { + Ok(match id { + 1 => Self::HeaderTableSize(value), + 3 => Self::MaxConcurrentStreams(value), + 4 => Self::InitialWindowSize(value), + 5 => Self::MaxFrameSize(value), + 6 => Self::MaxHeaderListSize(value), + 8 => Self::EnableConnectProtocol(value != 0), + _ => return Err(crate::Error::UnknownSettingFrameTy), + }) + } +} diff --git a/wtx/src/http2/stream_state.rs b/wtx/src/http2/stream_state.rs new file mode 100644 index 00000000..8a3994bd --- /dev/null +++ b/wtx/src/http2/stream_state.rs @@ -0,0 +1,21 @@ +#[derive(Debug, Clone, Copy)] +pub(crate) enum StreamState { + /// Final stage. Sent/received EOS_STREAM/RST_STREAM after + /// [StreamState::HalfClosedLocal]/[StreamState::HalfClosedRemote] or sent/received + /// RST_STREAM after [StreamState::Open]. + Closed, + /// The system sent EOS_STREAM after [StreamState::Open]. + HalfClosedLocal, + /// The system received EOS_STREAM after [StreamState::Open]. + HalfClosedRemote, + /// Initial state. Awaiting initial headers. + Idle, + /// The system is receiving data after [StreamState::Open]. + Open, +} + +impl StreamState { + pub(crate) fn can_server_send(self) -> bool { + matches!(self, Self::Open | Self::HalfClosedRemote) + } +} diff --git a/wtx/src/http2/tests.rs b/wtx/src/http2/tests.rs new file mode 100644 index 00000000..432b6bfe --- /dev/null +++ b/wtx/src/http2/tests.rs @@ -0,0 +1,3 @@ +//mod connections; +#[cfg(all(feature = "_integration-tests", feature = "serde_json"))] +mod hpack; diff --git a/wtx/src/http2/tests/connections.rs b/wtx/src/http2/tests/connections.rs new file mode 100644 index 00000000..eacd5950 --- /dev/null +++ b/wtx/src/http2/tests/connections.rs @@ -0,0 +1,73 @@ +macro_rules! call_tests { + (($ty:ident, $http2:expr), $($struct:ident),+ $(,)?) => { + $( + $struct::$ty($http2).await; + tokio::time::sleep(Duration::from_millis(200)).await; + )+ + }; +} + +use crate::{ + http2::{Http2Buffer, Http2Params, Http2Tokio}, + misc::_uri, + rng::StaticRng, +}; +use core::{ + sync::atomic::{AtomicBool, Ordering}, + time::Duration, +}; +use std::net::ToSocketAddrs; +use tokio::net::{TcpListener, TcpStream}; + +static HAS_SERVER_FINISHED: AtomicBool = AtomicBool::new(false); + +#[tokio::test] +async fn connections() { + let uri = _uri(); + + let listener = TcpListener::bind(uri.host()).await.unwrap(); + let _server_jh = tokio::spawn(async move { + let (stream, _) = listener.accept().await.unwrap(); + let mut server = + Http2Tokio::accept(Http2Buffer::new(StaticRng::default()), Http2Params::default(), stream) + .await + .unwrap(); + call_tests!((server, &mut server), Stub); + HAS_SERVER_FINISHED.store(true, Ordering::Relaxed); + }); + + let mut client = Http2Tokio::connect( + Http2Buffer::new(StaticRng::default()), + Http2Params::default(), + TcpStream::connect(uri.host().to_socket_addrs().unwrap().next().unwrap()).await.unwrap(), + ) + .await + .unwrap(); + call_tests!((client, &mut client), Stub); + + let mut has_server_finished = false; + for _ in 0..15 { + let local_has_server_finished = HAS_SERVER_FINISHED.load(Ordering::Relaxed); + if local_has_server_finished { + has_server_finished = local_has_server_finished; + break; + } + tokio::time::sleep(Duration::from_millis(200)).await; + } + if !has_server_finished { + panic!("Server didn't finish"); + } +} + +trait Test { + async fn client(http2: &mut Http2Tokio, TcpStream, true>); + + async fn server(http2: &mut Http2Tokio, TcpStream, false>); +} + +struct Stub; +impl Test for Stub { + async fn client(_: &mut Http2Tokio, TcpStream, true>) {} + + async fn server(_: &mut Http2Tokio, TcpStream, false>) {} +} diff --git a/wtx/src/http2/tests/hpack.rs b/wtx/src/http2/tests/hpack.rs new file mode 100644 index 00000000..c84616f3 --- /dev/null +++ b/wtx/src/http2/tests/hpack.rs @@ -0,0 +1,294 @@ +use crate::{ + http::StatusCode, + http2::{HpackDecoder, HpackEncoder, HpackHeaderBasic, CACHED_HEADERS_LEN_DEFAULT}, + misc::{from_utf8_basic, ByteVector, Vector}, + rng::StaticRng, +}; +use alloc::{string::String, vec::Vec}; +use core::{fmt::Formatter, marker::PhantomData}; +use serde::{ + de::{Deserializer, MapAccess, Visitor}, + Deserialize, +}; +use std::{ + fs::{read_dir, File}, + io::Read, + path::Path, + process::Command, +}; + +// Looks like some `wire` contents were stored assuming 16384 bytes. +const MAX_HEADER_LEN: u32 = 16384; + +#[test] +fn hpack_test_cases() { + fetch_hpack_test_cases(); + let mut buffer = ByteVector::new(); + let mut decoder = HpackDecoder::new(); + let mut encoder = HpackEncoder::new(StaticRng::default()); + decoder.set_max_bytes(MAX_HEADER_LEN); + encoder.set_max_dyn_super_bytes(MAX_HEADER_LEN); + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("hpack-test-case"); + for impl_entry_rslt in read_dir(root).unwrap() { + let impl_entry = impl_entry_rslt.unwrap(); + if impl_entry.file_type().unwrap().is_dir() { + let impl_entry_path = impl_entry.path(); + for story_entry_rslt in read_dir(&impl_entry_path).unwrap() { + let story_entry = story_entry_rslt.unwrap(); + if story_entry.file_name().as_os_str().to_str().unwrap().starts_with("story_") { + test_story( + &mut buffer, + (&impl_entry_path, &story_entry.path()), + (&mut decoder, &mut encoder), + ); + } + } + } + } +} + +#[derive(Debug, serde::Deserialize)] +struct Case { + header_table_size: Option, + headers: Vec, + seqno: Option, + wire: Option, +} + +#[derive(Clone, Debug)] +struct CaseHeader { + name: String, + value: String, +} + +impl<'de> Deserialize<'de> for CaseHeader { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CustomVisitor<'de>(PhantomData<&'de ()>); + + impl<'de> Visitor<'de> for CustomVisitor<'de> { + type Value = CaseHeader; + + fn expecting(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result { + formatter.write_str("struct CaseHeader") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut key: Option = None; + let mut value: Option = None; + if let Some(local_key) = map.next_key()? { + key = Some(local_key); + value = Some(map.next_value()?); + } + Ok(CaseHeader { + name: key.ok_or_else(|| serde::de::Error::missing_field("key"))?, + value: value.ok_or_else(|| serde::de::Error::missing_field("value"))?, + }) + } + } + + deserializer.deserialize_struct("CaseHeader", &["key", "value"], CustomVisitor(PhantomData)) + } +} + +#[derive(Debug, serde::Deserialize)] +struct Root { + cases: Vec, +} + +fn fetch_hpack_test_cases() { + let _output = Command::new("git") + .arg("clone") + .arg("https://github.com/http2jp/hpack-test-case") + .output() + .unwrap(); +} + +pub(crate) const fn hhb_name<'name>(hhb: HpackHeaderBasic, name: &'name [u8]) -> &'name [u8] { + match hhb { + HpackHeaderBasic::Authority => b":authority", + HpackHeaderBasic::Field => name, + HpackHeaderBasic::Method(_) => b":method", + HpackHeaderBasic::Path => b":path", + HpackHeaderBasic::Protocol(_) => b":protocol", + HpackHeaderBasic::Scheme => b":scheme", + HpackHeaderBasic::StatusCode(_) => b":status", + } +} + +fn parse_hex(hex: &[u8]) -> Vec { + let mut hex_bytes = hex + .iter() + .filter_map(|b| match b { + b'0'..=b'9' => Some(b - b'0'), + b'a'..=b'f' => Some(b - b'a' + 10), + b'A'..=b'F' => Some(b - b'A' + 10), + _ => None, + }) + .fuse(); + let mut bytes = Vec::new(); + while let (Some(h), Some(l)) = (hex_bytes.next(), hex_bytes.next()) { + bytes.push(h << 4 | l) + } + bytes +} + +fn print_case(seqno: Option, impl_path: &Path, story_path: &Path) { + std::println!( + "***** Testing case {:?} of story {:?} from implementation {:?} *****", + seqno, + story_path.file_name().unwrap(), + impl_path.file_name().unwrap(), + ); +} + +fn strs<'key, 'value>( + hhb: HpackHeaderBasic, + name: &'key [u8], + value: &'value [u8], +) -> (&'key str, &'value str) { + match hhb { + HpackHeaderBasic::Authority => (":authority", from_utf8_basic(value).unwrap()), + HpackHeaderBasic::Field => (from_utf8_basic(name).unwrap(), from_utf8_basic(value).unwrap()), + HpackHeaderBasic::Method(elem) => (":method", elem.strings().custom[0]), + HpackHeaderBasic::Path => (":path", from_utf8_basic(value).unwrap()), + HpackHeaderBasic::Protocol(elem) => (":protocol", elem.strings().custom[0]), + HpackHeaderBasic::Scheme => (":scheme", from_utf8_basic(value).unwrap()), + HpackHeaderBasic::StatusCode(elem) => (":status", elem.strings().number), + } +} + +fn test_story( + buffer: &mut Vector, + (impl_path, story_path): (&Path, &Path), + (decoder, encoder): (&mut HpackDecoder, &mut HpackEncoder), +) { + let mut file = File::open(story_path).unwrap(); + let mut data = String::new(); + let _ = file.read_to_string(&mut data).unwrap(); + let root: Root = serde_json::from_str(&data).unwrap(); + + let mut cases = root.cases; + cases.sort_unstable_by_key(|case| case.seqno); + + test_story_encoding_and_decoding(buffer, &cases, (impl_path, story_path), (decoder, encoder)); + + decoder.clear(); + + test_story_wired_decoding(&mut cases, decoder, (impl_path, story_path)); + + buffer.clear(); + decoder.clear(); + encoder.clear(); +} + +fn test_story_encoding_and_decoding( + buffer: &mut Vector, + cases: &[Case], + (impl_path, story_path): (&Path, &Path), + (decoder, encoder): (&mut HpackDecoder, &mut HpackEncoder), +) { + for case in cases { + print_case(case.seqno, impl_path, story_path); + + if let Some(size) = case.header_table_size { + decoder.set_max_bytes(size); + encoder.set_max_dyn_sub_bytes(size).unwrap(); + } else { + decoder.set_max_bytes(CACHED_HEADERS_LEN_DEFAULT); + encoder.set_max_dyn_sub_bytes(CACHED_HEADERS_LEN_DEFAULT).unwrap(); + } + + let mut pseudo_headers = case + .headers + .iter() + .filter_map(|header| { + Some(match header.name.as_str() { + ":authority" => (HpackHeaderBasic::Authority, header.value.as_bytes()), + ":method" => { + let method = header.value.as_str().try_into().unwrap(); + (HpackHeaderBasic::Method(method), method.strings().custom[0].as_bytes()) + } + ":path" => (HpackHeaderBasic::Path, header.value.as_bytes()), + ":protocol" => { + let protocol = header.value.as_str().try_into().unwrap(); + (HpackHeaderBasic::Protocol(protocol), protocol.strings().custom[0].as_bytes()) + } + ":scheme" => (HpackHeaderBasic::Scheme, header.value.as_bytes()), + ":status" => { + let status: StatusCode = header.value.as_str().try_into().unwrap(); + (HpackHeaderBasic::StatusCode(status), status.strings().number.as_bytes()) + } + _ => return None, + }) + }) + .collect::>(); + + let mut user_headers = case + .headers + .iter() + .filter_map(|header| { + if header.name.starts_with(":") { + None + } else { + Some((HpackHeaderBasic::Field, header.name.as_bytes(), header.value.as_bytes(), false)) + } + }) + .collect::>(); + + encoder + .encode( + buffer, + pseudo_headers.iter().copied(), + user_headers.iter().map(|el| (el.1, el.2, el.3)), + ) + .unwrap(); + + decoder + .decode(&buffer, |(hhb, name, value)| { + if pseudo_headers.is_empty() { + assert_eq!((hhb, hhb_name(hhb, name), value, false), user_headers.remove(0)); + } else { + assert_eq!((hhb, value), pseudo_headers.remove(0)); + } + }) + .unwrap(); + + buffer.clear(); + assert_eq!(0, pseudo_headers.len()); + assert_eq!(0, user_headers.len()); + } +} + +fn test_story_wired_decoding( + cases: &mut Vec, + decoder: &mut HpackDecoder, + (impl_path, story_path): (&Path, &Path), +) { + for case in cases { + print_case(case.seqno, impl_path, story_path); + + if let Some(elem) = case.header_table_size { + decoder.set_max_bytes(elem); + } + + let Some(wire) = &case.wire else { + continue; + }; + + decoder + .decode(&parse_hex(wire.as_bytes()), |(hhb, name, value)| { + let case_header = case.headers.remove(0); + let (name, value) = strs(hhb, name, value); + assert_eq!(case_header.name, name); + assert_eq!(case_header.value, value); + }) + .unwrap(); + assert_eq!(0, case.headers.len()); + } +} diff --git a/wtx/src/http2/u31.rs b/wtx/src/http2/u31.rs new file mode 100644 index 00000000..8a35e24b --- /dev/null +++ b/wtx/src/http2/u31.rs @@ -0,0 +1,66 @@ +use core::cmp::Ordering; + +const MASK: u32 = 0b0111_1111_1111_1111_1111_1111_1111_1111; + +/// Unsigned integer that occupies 32 bits but the actual values are composed by 31 bits. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct U31(u32); + +impl U31 { + pub(crate) const ZERO: Self = Self(0); + pub(crate) const ONE: Self = Self(1); + pub(crate) const TWO: Self = Self(2); + pub(crate) const MAX: Self = Self(2_147_483_647); + + pub(crate) const fn new(value: u32) -> Self { + Self(value & MASK) + } + + pub(crate) const fn is_not_zero(self) -> bool { + self.0 != 0 + } + + pub(crate) const fn is_zero(self) -> bool { + self.0 == 0 + } + + pub(crate) const fn to_be_bytes(self) -> [u8; 4] { + self.0.to_be_bytes() + } + + pub(crate) const fn u32(self) -> u32 { + self.0 + } + + pub(crate) const fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } +} + +impl PartialEq for u32 { + #[inline] + fn eq(&self, other: &U31) -> bool { + *self == other.0 + } +} + +impl PartialEq for U31 { + #[inline] + fn eq(&self, other: &u32) -> bool { + self.0 == *other + } +} + +impl PartialOrd for u32 { + #[inline] + fn partial_cmp(&self, other: &U31) -> Option { + self.partial_cmp(&other.0) + } +} + +impl PartialOrd for U31 { + #[inline] + fn partial_cmp(&self, other: &u32) -> Option { + self.0.partial_cmp(other) + } +} diff --git a/wtx/src/http2/uri_buffer.rs b/wtx/src/http2/uri_buffer.rs new file mode 100644 index 00000000..79eef2a9 --- /dev/null +++ b/wtx/src/http2/uri_buffer.rs @@ -0,0 +1,26 @@ +use crate::misc::ArrayString; + +pub(crate) const MAX_AUTHORITY_LEN: usize = 64; +pub(crate) const MAX_PATH_LEN: usize = 128; +pub(crate) const MAX_SCHEME_LEN: usize = 16; +pub(crate) const MAX_URI_LEN: usize = MAX_SCHEME_LEN + MAX_AUTHORITY_LEN + MAX_PATH_LEN; + +#[derive(Debug)] +pub(crate) struct UriBuffer { + pub(crate) authority: ArrayString, + pub(crate) path: ArrayString, + pub(crate) scheme: ArrayString, +} + +impl UriBuffer { + pub(crate) const fn new() -> Self { + Self { authority: ArrayString::new(), path: ArrayString::new(), scheme: ArrayString::new() } + } + + pub(crate) fn clear(&mut self) { + let Self { authority, path, scheme } = self; + authority.clear(); + path.clear(); + scheme.clear(); + } +} diff --git a/wtx/src/http2/window_update_frame.rs b/wtx/src/http2/window_update_frame.rs new file mode 100644 index 00000000..3da26d21 --- /dev/null +++ b/wtx/src/http2/window_update_frame.rs @@ -0,0 +1,27 @@ +use crate::http2::{FrameHeaderTy, FrameInit, U31}; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct WindowUpdateFrame { + size_increment: U31, + stream_id: U31, +} + +impl WindowUpdateFrame { + pub(crate) fn read(bytes: &[u8], fi: FrameInit) -> crate::Result { + let [a, b, c, d] = bytes else { + return Err(crate::http2::ErrorCode::FrameSizeError.into()); + }; + let size_increment = U31::new(u32::from_be_bytes([*a, *b, *c, *d])); + if size_increment.is_zero() { + return Err(crate::http2::ErrorCode::ProtocolError.into()); + } + Ok(Self { size_increment, stream_id: fi.stream_id }) + } + + pub(crate) fn _bytes(&self) -> [u8; 13] { + let [a, b, c, d, e, f, g, h, i] = + FrameInit::new(4, 0, self.stream_id, FrameHeaderTy::WindowUpdate).bytes(); + let [j, k, l, m] = self.size_increment.to_be_bytes(); + [a, b, c, d, e, f, g, h, i, j, k, l, m] + } +} diff --git a/wtx/src/lib.rs b/wtx/src/lib.rs index 4422fd4e..936e9a63 100644 --- a/wtx/src/lib.rs +++ b/wtx/src/lib.rs @@ -1,9 +1,12 @@ +#![doc = include_str!("../README.md")] #![cfg_attr(feature = "_bench", allow(soft_unstable))] #![cfg_attr(feature = "_bench", feature(test))] -#![cfg_attr(not(feature = "std"), no_std)] -#![doc = include_str!("../README.md")] +#![cfg_attr(feature = "nightly", feature(hint_assert_unchecked))] +#![no_std] extern crate alloc; +//#[cfg(feature = "std")] +extern crate std; #[cfg(all(feature = "_bench", test))] extern crate test; @@ -20,16 +23,17 @@ mod error; pub mod http; #[cfg(feature = "http1")] mod http1; +#[cfg(feature = "http2")] +pub mod http2; pub mod misc; -#[cfg(feature = "pool-manager")] -pub mod pool_manager; +#[cfg(feature = "pool")] +pub mod pool; pub mod rng; #[cfg(feature = "web-socket")] pub mod web_socket; pub use error::Error; -pub(crate) const DFLT_PARTITIONED_BUFFER_LEN: usize = 32 * 1024; pub(crate) const _MAX_PAYLOAD_LEN: usize = 64 * 1024 * 1024; /// Shortcut of [core::result::Result]. diff --git a/wtx/src/macros.rs b/wtx/src/macros.rs index 990f3a9c..012e07ef 100644 --- a/wtx/src/macros.rs +++ b/wtx/src/macros.rs @@ -4,7 +4,7 @@ macro_rules! create_enum { $v:vis enum $enum_ident:ident<$n:ty> { $( $(#[$variant_mac_fixed:meta])* - $variant_ident_fixed:ident = ($variant_n_fixed:literal $(, $variant_str_fixed:literal)?) + $variant_ident_fixed:ident = ($variant_n_fixed:literal $(, $variant_str_fixed:literal)? $(| $variant_str_fixed_n:literal)*) ),* $(,)? } ) => { @@ -25,18 +25,25 @@ macro_rules! create_enum { len } - /// See [crate::EnumVarStrings]. + /// See [crate::misc::EnumVarStrings]. #[inline] - pub const fn strings(&self) -> crate::misc::EnumVarStrings { + pub const fn strings(&self) -> crate::misc::EnumVarStrings<{ + let mut n; + $({ + #[allow(unused_mut)] + let mut local_n = 0; + let _ = $variant_n_fixed; + $({ let _ = $variant_str_fixed; local_n += 1; })? + $({ let _ = $variant_str_fixed_n; local_n += 1; })* + #[allow(unused_assignments)] + { n = local_n; } + })* + n + }> { match self { $( $enum_ident::$variant_ident_fixed => crate::misc::EnumVarStrings { - custom: { - #[allow(unused_assignments, unused_mut)] - let mut rslt = ""; - $(rslt = $variant_str_fixed;)? - rslt - }, + custom: [$($variant_str_fixed,)? $($variant_str_fixed_n,)*], ident: stringify!($variant_ident_fixed), number: stringify!($variant_n_fixed), }, @@ -85,7 +92,11 @@ macro_rules! create_enum { fn try_from(from: &str) -> crate::Result { let rslt = match from { $( - stringify!($variant_ident_fixed) | stringify!($variant_n_fixed) $(| $variant_str_fixed)? => { + stringify!($variant_ident_fixed) + | stringify!($variant_n_fixed) + $(| $variant_str_fixed)? + $(| $variant_str_fixed_n)* => + { Self::$variant_ident_fixed }, )* @@ -104,6 +115,7 @@ macro_rules! create_enum { if from == stringify!($variant_ident_fixed).as_bytes() || from == stringify!($variant_n_fixed).as_bytes() $(|| from == $variant_str_fixed.as_bytes())? + $(|| from == $variant_str_fixed_n.as_bytes())* { return Ok(Self::$variant_ident_fixed); } @@ -134,9 +146,10 @@ macro_rules! _internal_doc { } macro_rules! _iter4 { - ($slice:expr, |$elem:ident| $block:block) => {{ - let mut iter = crate::misc::ArrayChunks::_new($slice); + ($slice:expr, $init:block, |$elem:ident| $block:block) => {{ + let mut iter = crate::misc::ArrayChunks::new($slice); for [a, b, c, d] in iter.by_ref() { + $init let $elem = a; $block; let $elem = b; @@ -146,7 +159,7 @@ macro_rules! _iter4 { let $elem = d; $block; } - for elem in iter._into_remainder() { + for elem in iter.into_remainder() { let $elem = elem; $block; } @@ -154,9 +167,10 @@ macro_rules! _iter4 { } macro_rules! _iter4_mut { - ($slice:expr, |$elem:ident| $block:block) => {{ - let mut iter = crate::misc::ArrayChunksMut::_new($slice); + ($slice:expr, $init:block, |$elem:ident| $block:block) => {{ + let mut iter = crate::misc::ArrayChunksMut::new($slice); for [a, b, c, d] in iter.by_ref() { + $init let $elem = a; $block; let $elem = b; @@ -166,9 +180,21 @@ macro_rules! _iter4_mut { let $elem = d; $block; } - for elem in iter._into_remainder() { + for elem in iter.into_remainder() { let $elem = elem; $block; } }}; } + +macro_rules! _max_continuation_frames { + () => { + 16 + }; +} + +macro_rules! _max_frames_mismatches { + () => { + 2_147_483_647 + }; +} diff --git a/wtx/src/misc.rs b/wtx/src/misc.rs index 1cac8718..ec96b61f 100644 --- a/wtx/src/misc.rs +++ b/wtx/src/misc.rs @@ -1,56 +1,84 @@ //! Miscellaneous mod array_chunks; +mod array_string; +mod array_vector; mod async_bounds; +mod blocks_queue; +mod connection_state; mod either; mod enum_var_strings; mod filled_buffer_writer; -mod fn_mut_fut; +mod fn_fut; mod fx_hasher; mod generic_time; mod incomplete_utf8_char; +mod lease; +mod lock; +mod lock_guard; mod mem_transfer; mod optimization; mod partitioned_filled_buffer; mod poll_once; mod query_writer; +mod queue; +mod queue_utils; +mod ref_counter; +mod single_type_storage; mod stream; #[cfg(feature = "tokio-rustls")] mod tokio_rustls; -mod traits; mod uri; mod usize; mod utf8_errors; +mod vector; #[cfg(feature = "tokio-rustls")] pub use self::tokio_rustls::{TokioRustlsAcceptor, TokioRustlsConnector}; +pub use array_chunks::{ArrayChunks, ArrayChunksMut}; +pub use array_string::ArrayString; +pub use array_vector::ArrayVector; pub use async_bounds::AsyncBounds; +pub use connection_state::ConnectionState; use core::{any::type_name, ops::Range, time::Duration}; pub use either::Either; pub use enum_var_strings::EnumVarStrings; pub use filled_buffer_writer::FilledBufferWriter; -pub use fn_mut_fut::FnMutFut; +pub use fn_fut::{FnFut, FnMutFut, FnOnceFut}; pub use fx_hasher::FxHasher; pub use generic_time::GenericTime; pub use incomplete_utf8_char::{CompletionErr, IncompleteUtf8Char}; +pub use lease::{Lease, LeaseMut}; +pub use lock::{Lock, SyncLock}; +pub use lock_guard::LockGuard; pub use optimization::*; pub use poll_once::PollOnce; pub use query_writer::QueryWriter; +pub use queue::Queue; +pub use ref_counter::RefCounter; +pub use single_type_storage::SingleTypeStorage; pub use stream::{BytesStream, Stream, TlsStream}; -pub use traits::SingleTypeStorage; -pub use uri::{Uri, UriRef, UriString}; +pub use uri::{Uri, UriArrayString, UriRef, UriString}; pub use usize::Usize; pub use utf8_errors::{BasicUtf8Error, ExtUtf8Error, StdUtf8Error}; +pub use vector::Vector; #[allow( // Used by other features unused_imports )] pub(crate) use { - array_chunks::{ArrayChunks, ArrayChunksMut}, + blocks_queue::{Block, BlocksQueue}, mem_transfer::_shift_bytes, partitioned_filled_buffer::PartitionedFilledBuffer, }; +/// Vector of bytes +pub type ByteVector = Vector; + +/// A collection can not insert more elements. +#[derive(Debug)] +pub struct CapacityOverflow; + /// Useful when a request returns an optional field but the actual usage is within a /// [core::result::Result] context. #[inline] @@ -114,6 +142,56 @@ pub fn tracing_subscriber_init() -> Result<(), tracing_subscriber::util::TryInit tracing_subscriber::Registry::default().with(env_filter).with(tracing_tree).try_init() } +#[allow( + // `match` correctly handles sizes + clippy::as_conversions, + // `match` correctly handles sizes + clippy::cast_possible_truncation +)] +#[inline] +pub(crate) fn char_slice(buffer: &mut [u8; 4], ch: char) -> &[u8] { + #[inline] + const fn shift(number: u32, len: u8) -> u8 { + (number >> len) as u8 + } + + const BYTES2: u8 = 0b1100_0000; + const BYTES3: u8 = 0b1110_0000; + const BYTES4: u8 = 0b1111_0000; + const CONTINUATION: u8 = 0b1000_0000; + + const MASK3: u8 = 0b0000_0111; + const MASK4: u8 = 0b0000_1111; + const MASK5: u8 = 0b0001_1111; + const MASK6: u8 = 0b0011_1111; + + let number = u32::from(ch); + match number { + 0..=127 => { + buffer[0] = shift(number, 0); + &buffer[0..1] + } + 128..=2047 => { + buffer[0] = shift(number, 6) & MASK5 | BYTES2; + buffer[1] = shift(number, 0) & MASK6 | CONTINUATION; + &buffer[0..2] + } + 2048..=65535 => { + buffer[0] = shift(number, 12) & MASK4 | BYTES3; + buffer[1] = shift(number, 6) & MASK6 | CONTINUATION; + buffer[2] = shift(number, 0) & MASK6 | CONTINUATION; + &buffer[0..3] + } + _ => { + buffer[0] = shift(number, 18) & MASK3 | BYTES4; + buffer[1] = shift(number, 12) & MASK6 | CONTINUATION; + buffer[2] = shift(number, 6) & MASK6 | CONTINUATION; + buffer[3] = shift(number, 0) & MASK6 | CONTINUATION; + buffer + } + } +} + #[cfg(feature = "ahash")] pub(crate) fn _random_state(mut rng: impl crate::rng::Rng) -> ahash::RandomState { let (seed0, seed1) = { @@ -189,7 +267,7 @@ pub(crate) fn _unlikely_elem(elem: T) -> T { #[inline(never)] #[track_caller] pub(crate) const fn _unreachable() -> ! { - panic!("Entered in a branch that should be impossible. This is a bug!"); + panic!("Entered in a branch that should be impossible, which is likely a programming error"); } pub(crate) fn _usize_range_from_u32_range(range: Range) -> Range { diff --git a/wtx/src/misc/array_chunks.rs b/wtx/src/misc/array_chunks.rs index 1845e6c4..4bb19323 100644 --- a/wtx/src/misc/array_chunks.rs +++ b/wtx/src/misc/array_chunks.rs @@ -1,56 +1,70 @@ -#![allow( - // N is not zero - clippy::arithmetic_side_effects -)] - use core::{ iter::FusedIterator, slice::{self, Iter, IterMut}, }; +#[rustfmt::skip] macro_rules! create_and_impl { - ($array:ty, $from_raw_parts:ident, $iter_method:ident, $iter_struct:ident, $name:ident, $ptr_method:ident, $split_method:ident, $slice:ty) => { + ( + $array:ty, + $from_raw_parts:ident, + $iter_method:ident, + $iter_struct:ident, + $name:ident, + $ptr_method:ident, + $split_method:ident, + $slice:ty + ) => { /// Stable in-house version of the `ArrayChunks` struct found in the standard library. #[derive(Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] - pub(crate) struct $name<'slice, T, const N: usize> { - _iter: $iter_struct<'slice, [T; N]>, - _remainder: $slice, + pub struct $name<'slice, T, const N: usize> { + iter: $iter_struct<'slice, [T; N]>, + remainder: $slice, } impl<'slice, T, const N: usize> $name<'slice, T, N> { + #[allow( + // N is not zero, therefore, no following arithmetic will panic. + clippy::arithmetic_side_effects, + )] #[inline] - pub(crate) fn _new(slice: $slice) -> Self { - assert!(N != 0, "chunk size must be non-zero"); + /// Returns an iterator over N elements of the slice at a time, starting at the beginning of + /// the slice. + pub fn new(slice: $slice) -> Self { + const { + assert!(N != 0); + } let len = slice.len() / N; let (multiple_of_n, remainder) = slice.$split_method(len * N); - #[allow(unsafe_code)] // SAFETY: `N` is not zero and `slice` is multiple of `N`. let arrays = unsafe { slice::$from_raw_parts(multiple_of_n.$ptr_method().cast(), len) }; - Self { _iter: arrays.$iter_method(), _remainder: remainder } + Self { iter: arrays.$iter_method(), remainder } } - pub(crate) fn _into_remainder(self) -> $slice { - self._remainder + /// Returns the remainder of the original slice that is not going to be returned by the iterator. + #[inline] + pub fn into_remainder(self) -> $slice { + self.remainder } } impl<'slice, T, const N: usize> DoubleEndedIterator for $name<'slice, T, N> { #[inline] fn next_back(&mut self) -> Option<$array> { - self._iter.next_back() + self.iter.next_back() } #[inline] fn nth_back(&mut self, n: usize) -> Option { - self._iter.nth_back(n) + self.iter.nth_back(n) } } impl ExactSizeIterator for $name<'_, T, N> { #[inline] fn len(&self) -> usize { - self._iter.len() + self.iter.len() } } @@ -61,27 +75,27 @@ macro_rules! create_and_impl { #[inline] fn count(self) -> usize { - self._iter.count() + self.iter.count() } #[inline] fn last(self) -> Option { - self._iter.last() + self.iter.last() } #[inline] fn next(&mut self) -> Option<$array> { - self._iter.next() + self.iter.next() } #[inline] fn nth(&mut self, n: usize) -> Option { - self._iter.nth(n) + self.iter.nth(n) } #[inline] fn size_hint(&self) -> (usize, Option) { - self._iter.size_hint() + self.iter.size_hint() } } }; diff --git a/wtx/src/misc/array_string.rs b/wtx/src/misc/array_string.rs new file mode 100644 index 00000000..312e4e52 --- /dev/null +++ b/wtx/src/misc/array_string.rs @@ -0,0 +1,320 @@ +use crate::misc::{char_slice, Lease, Usize}; +use core::{ + cmp::Ordering, + fmt::{self, Arguments, Debug, Display, Formatter, Write}, + ops::Deref, + str, +}; + +/// A wrapper around the std's vector with some additional methods to manipulate copyable data. +#[derive(Clone, Copy)] +pub struct ArrayString { + len: u32, + data: [u8; N], +} + +impl ArrayString { + /// Constructs a new, empty instance. + #[allow( + // False-positive + clippy::missing_panics_doc + )] + #[inline] + pub const fn new() -> Self { + const { + if N > Usize::from_u32(u32::MAX).into_usize() { + panic!("Capacity is too large"); + } + } + Self { len: 0, data: [0; N] } + } + + /// The filled elements that composed a string. + #[inline] + pub fn as_str(&self) -> &str { + self + } + + /// The number of elements that can be stored. + #[inline] + pub 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]) + } + } + + /// Clears the vector, removing all values. + #[inline] + pub fn clear(&mut self) { + self.len = 0; + } + + /// How many elements can be added to this collection. + #[inline] + pub fn remaining(&self) -> u32 { + self.capacity().wrapping_sub(self.len) + } + + /// How many elements can be added to this collection. + #[inline] + pub fn replace(&mut self, start: usize, str: &str) -> crate::Result<()> { + let Some(slice) = start.checked_add(str.len()).and_then(|end| self.data.get_mut(start..end)) + else { + return Err(crate::Error::OutOfBoundsArithmetic); + }; + slice.copy_from_slice(str.as_bytes()); + Ok(()) + } + + /// Appends an element to the back of the collection. + #[inline] + pub fn try_push(&mut self, cf: char) -> crate::Result<()> { + self.try_push_bytes(char_slice(&mut [0; 4], cf)) + } + + /// Iterates over the slice `other`, copies each element, and then appends + /// it to this vector. The `other` slice is traversed in-order. + /// + /// # Panics + /// + /// If there is no available capacity. + #[inline] + pub fn try_push_str(&mut self, str: &str) -> crate::Result<()> { + self.try_push_bytes(str.as_bytes()) + } + + /// Shortens the vector, keeping the first `len` elements. + #[inline] + pub fn truncate(&mut self, len: u32) { + self.len = len.min(self.capacity()); + } + + #[inline] + fn try_push_bytes(&mut self, other: &[u8]) -> crate::Result<()> { + let Some(len) = u32::try_from(other.len()).ok().filter(|el| self.remaining() >= *el) else { + return Err(crate::Error::CapacityOverflow); + }; + let begin = *Usize::from(self.len); + let end = *Usize::from(self.len.wrapping_add(len)); + self.data.get_mut(begin..end).unwrap_or_default().copy_from_slice(other); + self.len = self.len.wrapping_add(len); + Ok(()) + } +} + +impl Debug for ArrayString { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl Default for ArrayString { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Display for ArrayString { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl Deref for ArrayString { + type Target = str; + + #[inline] + fn deref(&self) -> &Self::Target { + // SAFETY: Has valid UTF-8 and length is never greater than N + unsafe { str::from_utf8_unchecked(self.data.get(..*Usize::from(self.len)).unwrap_unchecked()) } + } +} + +impl Lease for ArrayString { + #[inline] + fn lease(&self) -> &str { + self + } +} + +impl Eq for ArrayString {} + +impl PartialEq for ArrayString { + #[inline] + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl PartialEq<[u8]> for ArrayString { + #[inline] + fn eq(&self, other: &[u8]) -> bool { + self.as_bytes() == other + } +} + +impl PartialEq for ArrayString { + #[inline] + fn eq(&self, other: &str) -> bool { + self.as_str() == other + } +} + +impl PartialOrd for ArrayString { + #[inline] + fn ge(&self, other: &Self) -> bool { + (**self).ge(&**other) + } + + #[inline] + fn gt(&self, other: &Self) -> bool { + (**self).gt(&**other) + } + + #[inline] + fn le(&self, other: &Self) -> bool { + (**self).le(&**other) + } + + #[inline] + fn lt(&self, other: &Self) -> bool { + (**self).lt(&**other) + } + + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ArrayString { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(other) + } +} + +impl<'args, const N: usize> TryFrom> for ArrayString { + type Error = crate::Error; + + #[inline] + fn try_from(from: Arguments<'args>) -> Result { + let mut v = Self::new(); + v.write_fmt(from)?; + Ok(v) + } +} + +impl TryFrom<&str> for ArrayString { + type Error = crate::Error; + + #[inline] + fn try_from(from: &str) -> Result { + let mut this = Self::default(); + this.try_push_str(from)?; + Ok(this) + } +} + +impl Write for ArrayString { + #[inline] + fn write_char(&mut self, ch: char) -> fmt::Result { + self.try_push(ch).map_err(|_err| fmt::Error) + } + + #[inline] + fn write_str(&mut self, str: &str) -> fmt::Result { + self.try_push_str(str).map_err(|_err| fmt::Error) + } +} + +#[cfg(feature = "arbitrary")] +mod arbitrary { + use crate::misc::{ArrayString, Usize}; + use arbitrary::{Arbitrary, Unstructured}; + + impl<'any, const N: usize> Arbitrary<'any> for ArrayString { + #[inline] + fn arbitrary(u: &mut Unstructured<'any>) -> arbitrary::Result { + let mut len = const { + let [_, _, _, _, a, b, c, d] = Usize::from_usize(N).into_u64().to_be_bytes(); + u32::from_be_bytes([a, b, c, d]) + }; + len = u32::arbitrary(u)?.min(len); + let mut data = [0; N]; + for elem in data.iter_mut().take(*Usize::from(len)) { + loop { + let byte = u8::arbitrary(u)?; + if byte.is_ascii_alphanumeric() { + *elem = byte; + break; + } + } + } + Ok(ArrayString { len, data }) + } + } +} + +#[cfg(feature = "serde")] +mod serde { + use crate::misc::{from_utf8_basic, ArrayString}; + use core::{fmt::Formatter, marker::PhantomData}; + use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, + }; + + impl<'de, const N: usize> Deserialize<'de> for ArrayString { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ArrayStringVisitor(PhantomData<[u8; N]>); + + impl<'de, const N: usize> Visitor<'de> for ArrayStringVisitor { + type Value = ArrayString; + + #[inline] + fn expecting(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result { + write!(formatter, "a string no more than {} bytes long", N) + } + + #[inline] + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + let rslt = from_utf8_basic(v); + let str = rslt.map_err(|_| E::invalid_value(de::Unexpected::Bytes(v), &self))?; + ArrayString::try_from(str).map_err(|_| E::invalid_length(str.len(), &self)) + } + + #[inline] + fn visit_str(self, str: &str) -> Result + where + E: de::Error, + { + ArrayString::try_from(str).map_err(|_| E::invalid_length(str.len(), &self)) + } + } + + deserializer.deserialize_str(ArrayStringVisitor(PhantomData)) + } + } + + impl Serialize for ArrayString { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self) + } + } +} diff --git a/wtx/src/misc/array_vector.rs b/wtx/src/misc/array_vector.rs new file mode 100644 index 00000000..1d1b4a66 --- /dev/null +++ b/wtx/src/misc/array_vector.rs @@ -0,0 +1,310 @@ +use crate::misc::{char_slice, Lease, Usize}; +use core::{ + cmp::Ordering, + fmt::{self, Debug, Formatter}, + ops::{Deref, DerefMut}, + slice, +}; + +/// A wrapper around the std's vector with some additional methods to manipulate copyable data. +pub struct ArrayVector { + len: u32, + data: [D; N], +} + +impl ArrayVector +where + D: Copy, +{ + /// Constructs a new instance reusing any `data` elements delimited by `len`. + #[allow( + // False positive + clippy::missing_panics_doc + )] + #[inline] + pub const fn new(data: [D; N], len: u32) -> Self { + let n = const { + if N > Usize::from_u32(u32::MAX).into_usize() { + panic!("Capacity is too large"); + } + let [_, _, _, _, a, b, c, d] = Usize::from_usize(N).into_u64().to_be_bytes(); + u32::from_be_bytes([a, b, c, d]) + }; + Self { len: if len > n { n } else { len }, data } + } + + /// The number of elements that can be stored. + #[inline] + pub 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]) + } + } + + /// Clears the vector, removing all values. + #[inline] + pub fn clear(&mut self) { + self.len = 0; + } + + /// Clears the vector, removing all values. + #[inline] + pub fn into_inner(self) -> impl Iterator { + self.data.into_iter().take(*Usize::from(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 + } else { + false + } + } + + /// How many elements can be added to this collection. + #[inline] + pub fn remaining(&self) -> u32 { + self.capacity().wrapping_sub(self.len) + } + + /// 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 try_extend_from_slice(&mut self, other: &[D]) -> crate::Result<()> { + let Some(len) = u32::try_from(other.len()).ok().filter(|el| self.remaining() >= *el) else { + return Err(crate::Error::CapacityOverflow); + }; + let begin = *Usize::from(self.len); + let end = *Usize::from(self.len.wrapping_add(len)); + self.data.get_mut(begin..end).unwrap_or_default().copy_from_slice(other); + self.len = self.len.wrapping_add(len); + Ok(()) + } + + /// Appends an element to the back of the collection. + #[inline] + pub fn try_push(&mut self, value: D) -> crate::Result<()> { + if let Some(elem) = self.data.get_mut(*Usize::from(self.len)) { + *elem = value; + self.len = self.len.wrapping_add(1); + Ok(()) + } else { + Err(crate::Error::CapacityOverflow) + } + } + + /// Shortens the vector, keeping the first `len` elements. + #[inline] + pub fn truncate(&mut self, len: u32) { + self.len = len.min(self.capacity()); + } +} + +impl Clone for ArrayVector +where + D: Copy, +{ + #[inline] + fn clone(&self) -> Self { + Self { data: self.data, len: self.len } + } +} + +impl Debug for ArrayVector +where + D: Debug, +{ + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.lease().fmt(f) + } +} + +impl Default for ArrayVector +where + D: Copy + Default, +{ + #[inline] + fn default() -> Self { + Self::new([D::default(); N], 0) + } +} + +impl Deref for ArrayVector { + type Target = [D]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.data.get(..*Usize::from(self.len)).unwrap_or_default() + } +} + +impl DerefMut for ArrayVector { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.data.get_mut(..*Usize::from(self.len)).unwrap_or_default() + } +} + +impl Eq for ArrayVector where D: Eq {} + +impl<'any, D, const N: usize> IntoIterator for &'any ArrayVector +where + D: 'any, +{ + type IntoIter = slice::Iter<'any, D>; + type Item = &'any D; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'any, D, const N: usize> IntoIterator for &'any mut ArrayVector +where + D: 'any, +{ + type IntoIter = slice::IterMut<'any, D>; + type Item = &'any mut D; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl Lease<[D]> for ArrayVector { + #[inline] + fn lease(&self) -> &[D] { + self + } +} + +impl PartialEq for ArrayVector +where + D: PartialEq, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl PartialEq<[D]> for ArrayVector +where + D: PartialEq, +{ + #[inline] + fn eq(&self, other: &[D]) -> bool { + **self == *other + } +} + +impl PartialOrd for ArrayVector +where + D: PartialOrd, +{ + #[inline] + fn ge(&self, other: &Self) -> bool { + (**self).ge(&**other) + } + + #[inline] + fn gt(&self, other: &Self) -> bool { + (**self).gt(&**other) + } + + #[inline] + fn le(&self, other: &Self) -> bool { + (**self).le(&**other) + } + + #[inline] + fn lt(&self, other: &Self) -> bool { + (**self).lt(&**other) + } + + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } +} + +impl Ord for ArrayVector +where + D: Ord, +{ + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(other) + } +} + +impl TryFrom<&[D]> for ArrayVector +where + D: Copy + Default, +{ + type Error = crate::Error; + + #[inline] + fn try_from(from: &[D]) -> Result { + let mut this = Self::default(); + this.try_extend_from_slice(from)?; + Ok(this) + } +} + +impl fmt::Write for ArrayVector { + #[inline] + fn write_char(&mut self, ch: char) -> fmt::Result { + self.try_extend_from_slice(char_slice(&mut [0; 4], ch)).map_err(|_err| fmt::Error) + } + + #[inline] + fn write_str(&mut self, str: &str) -> fmt::Result { + self.try_extend_from_slice(str.as_bytes()).map_err(|_err| fmt::Error) + } +} + +#[cfg(feature = "std")] +impl std::io::Write for ArrayVector { + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + + #[inline] + fn write(&mut self, data: &[u8]) -> std::io::Result { + let len = (*Usize::from(self.remaining())).min(data.len()); + let _rslt = self.try_extend_from_slice(data.get(..len).unwrap_or_default()); + Ok(len) + } +} + +#[cfg(feature = "serde")] +mod serde { + use crate::misc::ArrayVector; + use serde::{ser::SerializeTuple, Serialize, Serializer}; + + impl Serialize for ArrayVector + where + D: Serialize, + { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_tuple(N)?; + for elem in self.iter() { + seq.serialize_element(elem)?; + } + seq.end() + } + } +} diff --git a/wtx/src/misc/blocks_queue.rs b/wtx/src/misc/blocks_queue.rs new file mode 100644 index 00000000..8f8fa16e --- /dev/null +++ b/wtx/src/misc/blocks_queue.rs @@ -0,0 +1,537 @@ +macro_rules! as_slices { + ($empty:expr, $ptr:ident, $slice:ident, $this:expr, $($ref:tt)*) => {{ + let len = $this.data.len(); + let rhs_len = $this.data.capacity().wrapping_sub($this.head); + let ptr = $this.data.$ptr(); + if rhs_len < len { + let lhs_len = len.wrapping_sub(rhs_len); + // SAFETY: indices point to valid memory locations + unsafe { + ( + $($ref)* *ptr::$slice(ptr.add($this.head), rhs_len), + $($ref)* *ptr::$slice(ptr, lhs_len), + ) + } + } else { + // SAFETY: indices point to valid memory locations + unsafe { + ($($ref)* *ptr::$slice(ptr.add($this.head), len), $empty) + } + } + }} +} + +macro_rules! do_get { + ($block:ident, $metadata:expr, $ptr:expr, $slice:ident, $($ref:tt)*) => { + $block { + // SAFETY: `metadata` is always constructed with valid indices + data: unsafe { + $($ref)* *ptr::$slice($ptr.add($metadata.begin), $metadata.len) + }, + misc: $($ref)* $metadata.misc, + range: { + let end = $metadata.begin.wrapping_add($metadata.len); + $metadata.begin..end + } + } + } +} + +use crate::misc::{ + _unreachable, + queue_utils::{reserve, wrap_sub}, + Lease, Queue, SingleTypeStorage, Vector, +}; +use core::{ + fmt::{Debug, Formatter}, + ops::Range, + ptr, +}; + +pub(crate) type BlockRef<'bq, D, M> = Block<&'bq [D], &'bq M>; +pub(crate) type BlockMut<'bq, D, M> = Block<&'bq mut [D], &'bq mut M>; + +/// A circular buffer where elements are added in one-way blocks that will never intersect +/// boundaries. +pub(crate) struct BlocksQueue { + data: Vector, + head: usize, + metadata: Queue>, + tail: usize, +} + +impl BlocksQueue +where + D: Copy, + M: Copy, +{ + #[inline] + pub(crate) const fn new() -> Self { + Self { data: Vector::new(), head: 0, metadata: Queue::new(), tail: 0 } + } + + #[inline] + pub(crate) fn with_capacity(blocks: usize, elements: usize) -> Self { + Self { + data: Vector::with_capacity(elements), + head: 0, + metadata: Queue::with_capacity(blocks), + tail: 0, + } + } + + #[inline] + pub(crate) fn as_slices(&self) -> (&[D], &[D]) { + as_slices!(&[][..], as_ptr, slice_from_raw_parts, self, &) + } + + #[cfg(test)] + #[inline] + pub(crate) fn blocks_capacity(&self) -> usize { + self.metadata.capacity() + } + + #[inline] + pub(crate) fn blocks_len(&self) -> usize { + self.metadata.len() + } + + #[inline] + pub(crate) fn clear(&mut self) { + let Self { data, head, metadata, tail } = self; + data.clear(); + *head = 0; + *tail = 0; + metadata.clear(); + } + + #[inline] + pub(crate) fn elements_capacity(&self) -> usize { + self.data.capacity() + } + + #[inline] + pub(crate) fn elements_len(&self) -> usize { + self.data.len() + } + + #[inline] + pub(crate) fn first(&self) -> Option> { + self.get(0) + } + + #[inline] + pub(crate) fn get(&self, idx: usize) -> Option> { + Some(Self::do_get(&self.data, self.metadata.get(idx)?)) + } + + #[inline] + pub(crate) fn get_mut(&mut self, idx: usize) -> Option> { + Some(Self::do_get_mut(&mut self.data, self.metadata.get_mut(idx)?)) + } + + #[inline] + pub(crate) fn iter(&self) -> impl Iterator> { + self.metadata.iter().map(|metadata| Self::do_get(&self.data, metadata)) + } + + #[inline] + pub(crate) fn last(&self) -> Option> { + self.get(self.data.len().checked_sub(1)?) + } + + #[inline] + pub(crate) fn pop_back(&mut self) -> Option<(M, &mut [D])> { + let metadata = self.metadata.pop_back()?; + if let Some(elem) = self.metadata.last() { + self.tail = elem.begin.wrapping_add(elem.len); + } else { + self.head = 0; + self.tail = 0; + }; + // SAFETY: structure is not empty + let slice = unsafe { + self.data.set_len(self.data.len().wrapping_sub(metadata.len)); + &mut *ptr::slice_from_raw_parts_mut(self.data.as_mut_ptr().add(metadata.begin), metadata.len) + }; + Some((metadata.misc, slice)) + } + + #[inline] + pub(crate) fn pop_front(&mut self) -> Option<(M, &mut [D])> { + let metadata = self.metadata.pop_front()?; + if let Some(elem) = self.metadata.first() { + self.head = elem.begin; + } else { + self.head = 0; + self.tail = 0; + }; + // SAFETY: structure is not empty + let slice = unsafe { + self.data.set_len(self.data.len().wrapping_sub(metadata.len)); + &mut *ptr::slice_from_raw_parts_mut(self.data.as_mut_ptr().add(metadata.begin), metadata.len) + }; + Some((metadata.misc, slice)) + } + + #[inline] + pub(crate) fn push_front_within_cap(&mut self, data: [&[D]; N], misc: M) { + let mut len: usize = 0; + for elem in data { + len = len.wrapping_add(elem.len()); + } + let mut tail = self.tail; + let (left_free, right_free) = self.free(|| { + tail = self.data.capacity(); + }); + let head = match (left_free >= len, right_free >= len) { + (true, _) => self.head_lhs(len), + (false, true) => self.head_rhs(len), + (false, false) => _unreachable(), + }; + self.metadata.push_front_within_cap(BlocksQueueMetadata { begin: head, len, misc }); + self.head = head; + self.tail = tail; + // SAFETY: indices point to valid memory locations + unsafe { + let mut start = self.head; + for value in data { + ptr::copy_nonoverlapping(value.as_ptr(), self.data.as_mut_ptr().add(start), value.len()); + start = start.wrapping_add(value.len()); + } + self.data.set_len(self.data.len().wrapping_add(len)); + } + } + + #[inline(always)] + pub(crate) fn reserve(&mut self, blocks: usize, elements: usize) { + let is_not_wrapped = !self.is_wrapped(); + let prev_head = self.head; + let diff_opt = reserve(elements, &mut self.data, &mut self.head); + self.metadata.reserve(blocks); + if let Some(diff) = diff_opt { + if is_not_wrapped { + self.tail = self.tail.wrapping_add(diff); + } + let mut iter = self.metadata.iter_mut(); + for elem in iter.by_ref() { + if elem.begin >= prev_head { + elem.begin = elem.begin.wrapping_add(diff); + break; + } + } + for elem in iter { + elem.begin = elem.begin.wrapping_add(diff); + } + } + } + + #[inline] + fn do_get<'this>( + data: &'this Vector, + metadata: &'this BlocksQueueMetadata, + ) -> BlockRef<'this, D, M> { + do_get!(BlockRef, metadata, data.as_ptr(), slice_from_raw_parts, &) + } + + #[inline] + fn do_get_mut<'this>( + data: &'this mut Vector, + metadata: &'this mut BlocksQueueMetadata, + ) -> BlockMut<'this, D, M> { + do_get!(BlockMut, metadata, data.as_mut_ptr(), slice_from_raw_parts_mut, &mut) + } + + #[inline] + fn free(&self, cb: impl FnOnce()) -> (usize, usize) { + self.wrap_logic( + || { + cb(); + (self.elements_capacity(), 0) + }, + || (self.head, self.data.capacity().wrapping_sub(self.tail)), + || (self.head.wrapping_sub(self.tail), 0), + ) + } + + #[inline] + fn head_lhs(&self, len: usize) -> usize { + wrap_sub(self.data.capacity(), self.head, len) + } + + #[inline] + fn head_rhs(&self, len: usize) -> usize { + self.data.capacity().wrapping_sub(len) + } + + #[inline] + fn is_wrapped(&self) -> bool { + self.wrap_logic(|| false, || false, || true) + } + + #[inline] + fn wrap_logic( + &self, + empty: impl FnOnce() -> R, + contiguous: impl FnOnce() -> R, + wraps: impl FnOnce() -> R, + ) -> R { + if self.head == 0 && self.tail == 0 { + empty() + } else if self.head < self.tail { + contiguous() + } else { + wraps() + } + } +} + +impl Debug for BlocksQueue +where + D: Copy + Debug, + M: Copy + Debug, +{ + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { + let (lhs, rhs) = self.as_slices(); + f.debug_struct("BlocksQueue").field("lhs", &lhs).field("rhs", &rhs).finish() + } +} + +impl Default for BlocksQueue +where + D: Copy, + M: Copy, +{ + #[inline] + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, PartialEq)] +pub(crate) struct Block +where + D: Lease<[D::Item]> + SingleTypeStorage, +{ + pub(crate) data: D, + pub(crate) misc: M, + pub(crate) range: Range, +} + +#[derive(Clone, Copy, Debug)] +struct BlocksQueueMetadata { + pub(crate) begin: usize, + pub(crate) len: usize, + pub(crate) misc: M, +} + +// H = Head (Inclusive) +// LF = Left Free +// LO = Left Occupied +// RF = Right Free +// RO = Right Occupied +// T = Tail (Exclusive) +#[cfg(test)] +mod tests { + use crate::misc::{blocks_queue::BlockRef, BlocksQueue}; + + // [. . . . . . . .]: Empty - (LF=8, LO=0,RF=0, RO=0) - (H=0, T=0) + // [. . . . . . . H]: Push front - (LF=7, LO=0, RF=0, RO=1) - (H=7, T=8) + // [. . . . . H * *]: Push front - (LF=5, LO=0, RF=0, RO=3) - (H=5, T=8) + // [. . H * * * * *]: Push front - (LF=2, LO=0, RF=0, RO=6) - (H=2, T=8) + // [H * * * * * * *]: Push front - (LF=0, LO=0, RF=0, RO=8) - (H=0, T=8) + // [H * * * * * * .]: Pop back - (LF=0, LO=0, RF=2, RO=6) - (H=0, T=7) + // [* * * * * * * H]: Push front - (LF=0, LO=7, RF=0, RO=0) - (H=7, T=7) + // [* * * * * . . H]: Pop back - (LF=2, LO=5, RF=0, RO=1) - (H=7, T=5) + // [* * . . . . . H]: Pop back - (LF=5, LO=2, RF=0, RO=1) - (H=7, T=2) + // [* * . . H * * *]: Push front - (LF=2, LO=2, RF=0, RO=4) - (H=4, T=2) + // [* * H * * * * *]: Push front - (LF=0, LO=2, RF=0, RO=6) - (H=2, T=2) + // [. . H * * * * *]: Pop back - (LF=2, LO=0, RF=0, RO=6) - (H=2, T=8) + // [. . H * * * * .]: Pop back - (LF=2, LO=0, RF=1, RO=5) - (H=2, T=7) + // [. . H * . . . .]: Pop back - (LF=1, LO=0, RF=4, RO=3) - (H=2, T=4) + // [. H * * . . . .]: Push front - (LF=1, LO=0, RF=4, RO=3) - (H=2, T=4) + // [H * * * . . . .]: Push front - (LF=0, LO=0, RF=4, RO=4) - (H=0, T=4) + // [H . . . . . . .]: Pop back - (LF=0, LO=0, RF=7, RO=1) - (H=0, T=1) + // [. . . . . . . .]: Pop back - (LF=8, LO=0, RF=0, RO=0) - (H=0, T=0) + #[test] + fn pop_back() { + let mut q = BlocksQueue::with_capacity(4, 8); + check_state(&q, 0, 0, 0, 0); + + q.push_front_within_cap([&[1]], ()); + check_state(&q, 1, 1, 7, 8); + + q.push_front_within_cap([&[2, 3]], ()); + check_state(&q, 2, 3, 5, 8); + + q.push_front_within_cap([&[4, 5], &[6]], ()); + check_state(&q, 3, 6, 2, 8); + + q.push_front_within_cap([&[7, 8]], ()); + check_state(&q, 4, 8, 0, 8); + + let _ = q.pop_back(); + check_state(&q, 3, 7, 0, 7); + + q.push_front_within_cap([&[9]], ()); + check_state(&q, 4, 8, 7, 7); + + let _ = q.pop_back(); + check_state(&q, 3, 6, 7, 5); + + let _ = q.pop_back(); + check_state(&q, 2, 3, 7, 2); + + q.push_front_within_cap([&[10], &[11, 12]], ()); + check_state(&q, 3, 6, 4, 2); + + q.push_front_within_cap([&[13, 14]], ()); + check_state(&q, 4, 8, 2, 2); + + let _ = q.pop_back(); + check_state(&q, 3, 6, 2, 8); + + let _ = q.pop_back(); + check_state(&q, 2, 5, 2, 7); + + let _ = q.pop_back(); + check_state(&q, 1, 2, 2, 4); + + q.push_front_within_cap([&[15]], ()); + check_state(&q, 2, 3, 1, 4); + + q.push_front_within_cap([&[16]], ()); + check_state(&q, 3, 4, 0, 4); + + let _ = q.pop_back(); + check_state(&q, 2, 2, 0, 2); + + let _ = q.pop_back(); + check_state(&q, 1, 1, 0, 1); + + let _ = q.pop_back(); + check_state(&q, 0, 0, 0, 0); + } + + // [. . . . . . . .]: Empty - (LF=8, LO=0,RF=0, RO=0) - (H=0, T=0) + // [. . . . . H * *]: Push front - (LF=5, LO=0, RF=0, RO=3) - (H=5, T=8) + // [H * * * * * * *]: Push front - (LF=0, LO=0, RF=0, RO=8) - (H=0, T=8) + // [. . . . . H * *]: Push front - (LF=5, LO=0, RF=0, RO=3) - (H=5, T=8) + // [. . . . . . . .]: Pop back - (LF=8, LO=0, RF=0, RO=0) - (H=0, T=0) + #[test] + fn pop_front() { + let mut q = BlocksQueue::with_capacity(2, 8); + check_state(&q, 0, 0, 0, 0); + + q.push_front_within_cap([&[1, 2, 3]], ()); + check_state(&q, 1, 3, 5, 8); + + q.push_front_within_cap([&[4, 5], &[6, 7, 8]], ()); + check_state(&q, 2, 8, 0, 8); + + let _ = q.pop_front(); + check_state(&q, 1, 3, 5, 8); + + let _ = q.pop_front(); + check_state(&q, 0, 0, 0, 0); + } + + // []: Empty - (LF=0, LO=0,RF=0, RO=0) - (H=0, T=0) + // [H * * *]: Push front - (LF=0, LO=0, RF=0, RO=4) - (H=0, T=4) + #[test] + fn push_reserve_and_push() { + let mut q = BlocksQueue::new(); + q.reserve(1, 4); + q.push_front_within_cap([&[0, 1, 2, 3]], ()); + check_state(&q, 1, 4, 0, 4); + assert_eq!(q.get(0), Some(BlockRef { data: &[0, 1, 2, 3], misc: &(), range: 0..4 })); + assert_eq!(q.get(1), None); + q.reserve(1, 6); + q.push_front_within_cap([&[4, 5, 6, 7, 8, 9]], ()); + check_state(&q, 2, 10, 0, 10); + assert_eq!(q.get(0), Some(BlockRef { data: &[4, 5, 6, 7, 8, 9], misc: &(), range: 0..6 })); + assert_eq!(q.get(1), Some(BlockRef { data: &[0, 1, 2, 3], misc: &(), range: 6..10 })); + assert_eq!(q.get(2), None); + } + + #[test] + fn reserve() { + let mut queue = BlocksQueue::::new(); + assert_eq!(queue.blocks_capacity(), 0); + assert_eq!(queue.elements_capacity(), 0); + queue.reserve(5, 10); + assert_eq!(queue.blocks_capacity(), 5); + assert_eq!(queue.elements_capacity(), 10); + } + + // [. . . . . H * * ]: Pop back - (LF=5, LO=0, RF=0, RO=3) - (H=5, T=8) + // [. . . . . . . . ]: Pop back - (LF=8, LO=0, RF=0, RO=0) - (H=0, T=0) + #[test] + fn wrap_pop_back() { + let mut q = wrap_initial(); + let _ = q.pop_back(); + let _ = q.pop_back(); + check_state(&q, 1, 3, 5, 8); + assert_eq!(q.get(0).unwrap().data, &[1, 2, 3]); + let _ = q.pop_back(); + check_state(&q, 0, 0, 0, 0); + } + + // [. . H * . . . . ]: Pop front - (LF=2, LO=0, RF=4, RO=0) - (H=2, T=4) + // [. . . . . . . . ]: Pop front - (LF=8, LO=0, RF=0, RO=0) - (H=0, T=0) + #[test] + fn wrap_pop_front() { + let mut q = wrap_initial(); + let _ = q.pop_front(); + check_state(&q, 2, 2, 2, 4); + assert_eq!(q.get(0).unwrap().data, &[0]); + assert_eq!(q.get(1).unwrap().data, &[0]); + let _ = q.pop_front(); + let _ = q.pop_front(); + check_state(&q, 0, 0, 0, 0); + } + + // [. . . . . . . . ]: Empty - (LF=8, LO=0, RF=0, RO=0) - (H=0, T=0) + // [. . H * * * * * ]: Push front - (LF=2, LO=0, RF=0, RO=6) - (H=2, T=8) + // [. . H * . . . . ]: Pop back - (LF=2, LO=0, RF=4, RO=0) - (H=2, T=4) + // [. . * * . H * * ]: Push front - (LF=1, LO=2, RF=0, RO=3) - (H=5, T=4) + fn wrap_initial() -> BlocksQueue { + let mut q = BlocksQueue::with_capacity(6, 8); + check_state(&q, 0, 0, 0, 0); + for _ in 0..6 { + q.push_front_within_cap([&[0]], ()); + } + check_state(&q, 6, 6, 2, 8); + for idx in 0..6 { + assert_eq!(q.get(idx).unwrap().data, &[0]); + } + let _ = q.pop_back(); + let _ = q.pop_back(); + let _ = q.pop_back(); + let _ = q.pop_back(); + check_state(&q, 2, 2, 2, 4); + assert_eq!(q.get(0).unwrap().data, &[0]); + assert_eq!(q.get(1).unwrap().data, &[0]); + q.push_front_within_cap([&[1, 2, 3]], ()); + check_state(&q, 3, 5, 5, 4); + assert_eq!(q.get(0).unwrap().data, &[1, 2, 3]); + assert_eq!(q.get(1).unwrap().data, &[0]); + assert_eq!(q.get(2).unwrap().data, &[0]); + q + } + + #[track_caller] + fn check_state( + q: &BlocksQueue, + blocks_len: usize, + elements_len: usize, + head: usize, + tail: usize, + ) { + assert_eq!(q.blocks_len(), blocks_len); + assert_eq!(q.elements_len(), elements_len); + assert_eq!(q.head, head); + assert_eq!(q.tail, tail); + } +} diff --git a/wtx/src/misc/connection_state.rs b/wtx/src/misc/connection_state.rs new file mode 100644 index 00000000..d9a0e1dd --- /dev/null +++ b/wtx/src/misc/connection_state.rs @@ -0,0 +1,33 @@ +/// The state of a connection between two parties. +#[derive(Clone, Copy, Debug)] +pub enum ConnectionState { + /// Is locally open. Does not necessary means that both parties are in the same state. + Open, + /// Is locally closed. Does not necessary means that both parties are in the same state. + Closed, +} + +impl ConnectionState { + /// Shortcut for [ConnectionState::Closed]. + #[inline] + pub fn is_closed(self) -> bool { + matches!(self, Self::Closed) + } + + /// Shortcut for [ConnectionState::Open]. + #[inline] + pub fn is_open(self) -> bool { + matches!(self, Self::Open) + } +} + +impl From for ConnectionState { + #[inline] + fn from(from: bool) -> Self { + if from { + Self::Open + } else { + Self::Closed + } + } +} diff --git a/wtx/src/misc/either.rs b/wtx/src/misc/either.rs index a36c4306..64e180d3 100644 --- a/wtx/src/misc/either.rs +++ b/wtx/src/misc/either.rs @@ -1,3 +1,5 @@ +use crate::misc::Lease; + /// An enum that can contain two different types. #[derive(Debug, PartialEq)] pub enum Either { @@ -7,58 +9,58 @@ pub enum Either { Right(R), } -impl AsRef<[u8]> for Either +impl Lease<[u8]> for Either where - L: AsRef<[u8]>, - R: AsRef<[u8]>, + L: Lease<[u8]>, + R: Lease<[u8]>, { #[inline] - fn as_ref(&self) -> &[u8] { + fn lease(&self) -> &[u8] { match self { - Either::Left(elem) => elem.as_ref(), - Either::Right(elem) => elem.as_ref(), + Either::Left(elem) => elem.lease(), + Either::Right(elem) => elem.lease(), } } } -impl<'any, L, R> AsRef<&'any [u8]> for Either +impl<'any, L, R> Lease<&'any [u8]> for Either where - L: AsRef<&'any [u8]>, - R: AsRef<&'any [u8]>, + L: Lease<&'any [u8]>, + R: Lease<&'any [u8]>, { #[inline] - fn as_ref(&self) -> &&'any [u8] { + fn lease(&self) -> &&'any [u8] { match self { - Either::Left(elem) => elem.as_ref(), - Either::Right(elem) => elem.as_ref(), + Either::Left(elem) => elem.lease(), + Either::Right(elem) => elem.lease(), } } } -impl AsRef for Either +impl Lease for Either where - L: AsRef, - R: AsRef, + L: Lease, + R: Lease, { #[inline] - fn as_ref(&self) -> &str { + fn lease(&self) -> &str { match self { - Either::Left(elem) => elem.as_ref(), - Either::Right(elem) => elem.as_ref(), + Either::Left(elem) => elem.lease(), + Either::Right(elem) => elem.lease(), } } } -impl<'any, L, R> AsRef<&'any str> for Either +impl<'any, L, R> Lease<&'any str> for Either where - L: AsRef<&'any str>, - R: AsRef<&'any str>, + L: Lease<&'any str>, + R: Lease<&'any str>, { #[inline] - fn as_ref(&self) -> &&'any str { + fn lease(&self) -> &&'any str { match self { - Either::Left(elem) => elem.as_ref(), - Either::Right(elem) => elem.as_ref(), + Either::Left(elem) => elem.lease(), + Either::Right(elem) => elem.lease(), } } } diff --git a/wtx/src/misc/enum_var_strings.rs b/wtx/src/misc/enum_var_strings.rs index 4a546c07..63c356f8 100644 --- a/wtx/src/misc/enum_var_strings.rs +++ b/wtx/src/misc/enum_var_strings.rs @@ -1,8 +1,8 @@ /// Enum Variant Strings #[derive(Debug)] -pub struct EnumVarStrings { +pub struct EnumVarStrings { /// Custom - pub custom: &'static str, + pub custom: [&'static str; N], /// Identifier pub ident: &'static str, /// Number diff --git a/wtx/src/misc/fn_fut.rs b/wtx/src/misc/fn_fut.rs new file mode 100644 index 00000000..fcb5515f --- /dev/null +++ b/wtx/src/misc/fn_fut.rs @@ -0,0 +1,50 @@ +use crate::misc::AsyncBounds; +use core::future::Future; + +/// Simulates `impl for<'any> Fn(&'any ..) -> impl Future + 'any` due to the lack of compiler +/// support. +/// +/// If applied as a function parameter, then callers must create their own async functions +/// instead of using closures. +/// +/// Credits to `Daniel Henry-Mantilla`. +pub trait FnFut: Fn(P) -> Self::Future { + /// Returning future. + type Future: AsyncBounds + Future; +} + +impl FnFut for F +where + F: Fn(P) -> FUT, + FUT: AsyncBounds + Future, +{ + type Future = FUT; +} + +/// Like [FnFut] but for [FnMut]. +pub trait FnMutFut: FnMut(P) -> Self::Future { + /// Returning future. + type Future: AsyncBounds + Future; +} + +impl FnMutFut for F +where + F: FnMut(P) -> FUT, + FUT: AsyncBounds + Future, +{ + type Future = FUT; +} + +/// Like [FnFut] but for [FnOnce]. +pub trait FnOnceFut: FnOnce(P) -> Self::Future { + /// Returning future. + type Future: AsyncBounds + Future; +} + +impl FnOnceFut for F +where + F: FnOnce(P) -> FUT, + FUT: AsyncBounds + Future, +{ + type Future = FUT; +} diff --git a/wtx/src/misc/fn_mut_fut.rs b/wtx/src/misc/fn_mut_fut.rs deleted file mode 100644 index 4fe7e005..00000000 --- a/wtx/src/misc/fn_mut_fut.rs +++ /dev/null @@ -1,21 +0,0 @@ -use core::future::Future; - -/// Simulates `impl for<'any> FnMut(&'any ..) -> impl Future + 'any` due to the lack of compiler -/// support. -/// -/// If applied as a function parameter, then callers should create their own async functions -/// instead of using closures. -/// -/// Credits to `Daniel Henry-Mantilla`. -pub trait FnMutFut: FnMut(P) -> Self::Future { - /// Returning future. - type Future: Future; -} - -impl FnMutFut for F -where - F: FnMut(P) -> FUT, - FUT: Future, -{ - type Future = FUT; -} diff --git a/wtx/src/misc/lease.rs b/wtx/src/misc/lease.rs new file mode 100644 index 00000000..2cb853ef --- /dev/null +++ b/wtx/src/misc/lease.rs @@ -0,0 +1,217 @@ +/// Copy of [core::borrow::Borrow] used to workaround orphan rules. +pub trait Lease +where + T: ?Sized, +{ + /// Immutable borrow. + fn lease(&self) -> &T; +} + +impl Lease for &T +where + T: Lease + ?Sized, + U: ?Sized, +{ + #[inline] + fn lease(&self) -> &U { + >::lease(*self) + } +} + +impl Lease for &mut T +where + T: Lease + ?Sized, + U: ?Sized, +{ + #[inline] + fn lease(&self) -> &U { + >::lease(*self) + } +} + +/// Copy of [core::borrow::BorrowMut] used to workaround orphan rules. +pub trait LeaseMut: Lease +where + T: ?Sized, +{ + /// Mutable borrow. + fn lease_mut(&mut self) -> &mut T; +} + +impl LeaseMut for &mut T +where + T: LeaseMut + ?Sized, + U: ?Sized, +{ + #[inline] + fn lease_mut(&mut self) -> &mut U { + >::lease_mut(*self) + } +} + +mod collections { + use crate::misc::{Lease, LeaseMut}; + use alloc::vec::Vec; + + impl Lease<[T]> for [T] { + #[inline] + fn lease(&self) -> &[T] { + self + } + } + + impl Lease<[T]> for [T; N] { + #[inline] + fn lease(&self) -> &[T] { + self + } + } + + impl Lease<[T]> for Vec { + #[inline] + fn lease(&self) -> &[T] { + self + } + } + + impl Lease> for Vec { + #[inline] + fn lease(&self) -> &Vec { + self + } + } + + impl LeaseMut<[T]> for [T] { + #[inline] + fn lease_mut(&mut self) -> &mut [T] { + self + } + } + + impl LeaseMut<[T]> for [T; N] { + #[inline] + fn lease_mut(&mut self) -> &mut [T] { + self + } + } + + impl LeaseMut<[T]> for Vec { + #[inline] + fn lease_mut(&mut self) -> &mut [T] { + self + } + } + + impl LeaseMut> for Vec { + #[inline] + fn lease_mut(&mut self) -> &mut Vec { + self + } + } +} + +#[cfg(feature = "ring")] +mod ring { + use crate::misc::Lease; + use ring::digest::Digest; + + impl Lease<[u8]> for Digest { + #[inline] + fn lease(&self) -> &[u8] { + self.as_ref() + } + } +} + +mod smart_pointers { + use crate::misc::{Lease, LeaseMut}; + use alloc::{boxed::Box, sync::Arc}; + + impl Lease for Arc { + #[inline] + fn lease(&self) -> &T { + self + } + } + + impl Lease for Box { + #[inline] + fn lease(&self) -> &T { + self + } + } + + impl LeaseMut for Box { + #[inline] + fn lease_mut(&mut self) -> &mut T { + self + } + } +} + +mod str { + use crate::misc::Lease; + use alloc::string::String; + + impl Lease<[u8]> for str { + #[inline] + fn lease(&self) -> &[u8] { + self.as_bytes() + } + } + + impl Lease for str { + #[inline] + fn lease(&self) -> &str { + self + } + } + + impl Lease<[u8]> for String { + #[inline] + fn lease(&self) -> &[u8] { + self.as_bytes() + } + } + + impl Lease for String { + #[inline] + fn lease(&self) -> &str { + self + } + } +} + +#[cfg(feature = "tokio")] +mod tokio { + use crate::misc::{Lease, LeaseMut}; + use tokio::sync::{MappedMutexGuard, MutexGuard}; + + impl Lease for MappedMutexGuard<'_, T> { + #[inline] + fn lease(&self) -> &T { + self + } + } + + impl LeaseMut for MappedMutexGuard<'_, T> { + #[inline] + fn lease_mut(&mut self) -> &mut T { + self + } + } + + impl Lease for MutexGuard<'_, T> { + #[inline] + fn lease(&self) -> &T { + self + } + } + + impl LeaseMut for MutexGuard<'_, T> { + #[inline] + fn lease_mut(&mut self) -> &mut T { + self + } + } +} diff --git a/wtx/src/misc/lock.rs b/wtx/src/misc/lock.rs new file mode 100644 index 00000000..9041a398 --- /dev/null +++ b/wtx/src/misc/lock.rs @@ -0,0 +1,222 @@ +use crate::misc::LockGuard; +use alloc::{rc::Rc, sync::Arc}; +use core::{future::Future, ops::DerefMut}; + +/// An asynchronous mutual exclusion primitive useful for protecting shared data. +pub trait Lock { + /// See [LockGuard]. + type Guard<'guard>: LockGuard<'guard, Self::Resource> + where + Self: 'guard; + /// Resource behind the lock. + type Resource; + + /// Generic way to build a lock. + fn new(resource: Self::Resource) -> Self; + + /// Locks this element, causing the current task to yield until the lock has been acquired. When + /// the lock has been acquired, returns a guard. + fn lock(&self) -> impl Future>; +} + +impl Lock for Arc +where + T: Lock, +{ + type Guard<'guard> = T::Guard<'guard> + where + Self: 'guard; + type Resource = T::Resource; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Arc::new(T::new(resource)) + } + + #[inline] + async fn lock(&self) -> Self::Guard<'_> { + (**self).lock().await + } +} + +impl Lock for Rc +where + T: Lock, +{ + type Guard<'guard> = T::Guard<'guard> + where + Self: 'guard; + type Resource = T::Resource; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Rc::new(T::new(resource)) + } + + #[inline] + async fn lock(&self) -> Self::Guard<'_> { + (**self).lock().await + } +} + +/// Synchronous counterpart of [Lock]. +pub trait SyncLock { + /// See [LockGuard]. + type Guard<'guard>: DerefMut + where + Self: 'guard; + /// Resource behind the lock. + type Resource; + + /// Generic way to build a lock. + fn new(resource: Self::Resource) -> Self; + + /// Locks this element, causing the current thread to yield until the lock has been acquired. When + /// the lock has been acquired, returns a guard. + fn lock(&self) -> Self::Guard<'_>; +} + +impl SyncLock for Arc +where + T: SyncLock, +{ + type Guard<'guard> = T::Guard<'guard> + where + Self: 'guard; + type Resource = T::Resource; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Arc::new(T::new(resource)) + } + + #[inline] + fn lock(&self) -> Self::Guard<'_> { + (**self).lock() + } +} + +impl SyncLock for Rc +where + T: SyncLock, +{ + type Guard<'guard> = T::Guard<'guard> + where + Self: 'guard; + type Resource = T::Resource; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Rc::new(T::new(resource)) + } + + #[inline] + fn lock(&self) -> Self::Guard<'_> { + (**self).lock() + } +} + +#[cfg(feature = "embassy-sync")] +mod embassy { + use crate::misc::Lock; + use embassy_sync::{ + blocking_mutex::raw::RawMutex, + mutex::{Mutex, MutexGuard}, + }; + + impl Lock for Mutex + where + M: RawMutex, + { + type Guard<'guard> = MutexGuard<'guard, M, Self::Resource> + where + Self: 'guard; + type Resource = T; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Mutex::new(resource) + } + + #[inline] + async fn lock(&self) -> Self::Guard<'_> { + (*self).lock().await + } + } +} + +#[cfg(feature = "parking_lot")] +mod parking_lot { + use crate::misc::SyncLock; + use parking_lot::{Mutex, MutexGuard}; + + impl SyncLock for Mutex { + type Guard<'guard> = MutexGuard<'guard, Self::Resource> + where + Self: 'guard; + type Resource = T; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Mutex::new(resource) + } + + #[inline] + fn lock(&self) -> Self::Guard<'_> { + (*self).lock() + } + } +} + +#[cfg(feature = "std")] +mod std { + use crate::misc::SyncLock; + use std::sync::{Mutex, MutexGuard, TryLockError}; + + impl SyncLock for Mutex { + type Guard<'guard> = MutexGuard<'guard, Self::Resource> + where + Self: 'guard; + type Resource = T; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Mutex::new(resource) + } + + #[inline] + fn lock(&self) -> Self::Guard<'_> { + loop { + let rslt = (*self).try_lock(); + match rslt { + Err(TryLockError::Poisoned(elem)) => return elem.into_inner(), + Err(TryLockError::WouldBlock) => {} + Ok(elem) => return elem, + } + } + } + } +} + +#[cfg(feature = "tokio")] +mod tokio { + use crate::misc::Lock; + use tokio::sync::{Mutex, MutexGuard}; + + impl Lock for Mutex { + type Guard<'guard> = MutexGuard<'guard, Self::Resource> + where + Self: 'guard; + type Resource = T; + + #[inline] + fn new(resource: Self::Resource) -> Self { + Mutex::new(resource) + } + + #[inline] + async fn lock(&self) -> Self::Guard<'_> { + (*self).lock().await + } + } +} diff --git a/wtx/src/misc/lock_guard.rs b/wtx/src/misc/lock_guard.rs new file mode 100644 index 00000000..a7756475 --- /dev/null +++ b/wtx/src/misc/lock_guard.rs @@ -0,0 +1,60 @@ +use core::ops::DerefMut; + +/// A handle to a held lock. +pub trait LockGuard<'guard, T>: DerefMut { + /// Sometimes it is desirable to return a type that differs from the original lock. + type Mapped: LockGuard<'guard, U> + where + U: 'guard; + + /// Makes a new mapped element for a component of the locked data. + fn map(this: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped; +} + +#[cfg(feature = "embassy-sync")] +mod embassy { + use crate::misc::LockGuard; + use embassy_sync::{blocking_mutex::raw::RawMutex, mutex::MutexGuard}; + + impl<'guard, M, T> LockGuard<'guard, T> for MutexGuard<'guard, M, T> + where + M: RawMutex, + { + type Mapped = MutexGuard<'guard, M, U> + where + U: 'guard; + + #[inline] + fn map(_: Self, _: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { + unimplemented!("Embassy needs to add support for `map` methods"); + } + } +} + +#[cfg(feature = "tokio")] +mod tokio { + use crate::misc::LockGuard; + use tokio::sync::{MappedMutexGuard, MutexGuard}; + + impl<'guard, T> LockGuard<'guard, T> for MappedMutexGuard<'guard, T> { + type Mapped = MappedMutexGuard<'guard, U> + where + U: 'guard; + + #[inline] + fn map(this: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { + MappedMutexGuard::map(this, f) + } + } + + impl<'guard, T> LockGuard<'guard, T> for MutexGuard<'guard, T> { + type Mapped = MappedMutexGuard<'guard, U> + where + U: 'guard; + + #[inline] + fn map(this: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { + MutexGuard::map(this, f) + } + } +} diff --git a/wtx/src/misc/mem_transfer.rs b/wtx/src/misc/mem_transfer.rs index 24645deb..edfb2eb3 100644 --- a/wtx/src/misc/mem_transfer.rs +++ b/wtx/src/misc/mem_transfer.rs @@ -1,173 +1,71 @@ -#![allow( - // See safety comments - unsafe_code -)] +use core::{ops::Range, ptr}; -//! On a fragmented vector of bytes, performs several experiments to see which is faster: -//! -//! 1. Transfer interval fragments to another vector (memcpy). -//! -//! ```ignore -//! // A vector fragmented in 3 pieces where the last two digits of each piece is copied -//! // to another vector. -//! -//! | | | | -//! A: |00|01|02|03|04|05|06|07|08|09|10|11| -//! | |_____| |_____| |_____| -//! | | | | | | | -//! | \|/ | \|/ | \|/ | -//! -//! B: |02|03|06|07|10|11| -//! ``` -//! -//! 2. In-place shifts of interval fragments (memmove). -//! -//! ```ignore -//! // A vector fragmented in 3 pieces where the last two digits of each piece are -//! // shifted to the left -//! -//! | | | | -//! A: |00|01|02|03|04|05|06|07|08|09|10|11| -//! | << <<| | | -//! -//! | | | | -//! A: |02|03|02|03|04|05|06|07|08|09|10|11| -//! |^^ ^^ | << <<| | -//! -//! | | | | -//! A: |02|03|06|07|04|05|06|07|08|09|10|11| -//! |^^ ^^ ^^ ^^| | << <<| -//! -//! | | | | -//! A: |02|03|06|07|10|11|06|07|08|09|10|11| -//! |^^ ^^ ^^ ^^|^^ ^^ | | -//! -//! A: |02|03|06|07|10|11| -//! ``` - -use crate::misc::{_unlikely_cb, _unlikely_elem}; -use core::ptr; - -#[cfg(test)] -pub(crate) fn _copy_bytes<'to>( - from: &[u8], - to: &'to mut [u8], - start: usize, - iter: impl IntoIterator, -) -> &'to mut [u8] { - if start >= from.len() || start >= to.len() { - return _unlikely_elem(to); - } - let mut new_len = start; - unsafe { - ptr::copy_nonoverlapping(from.as_ptr(), to.as_mut_ptr(), start); - } - for (begin, end) in iter { - let len = end.wrapping_sub(begin); - let to_end = new_len.wrapping_add(len); - let from_is_not_valid = from.get(begin..end).is_none(); - let to_is_not_valid = to.get(new_len..to_end).is_none(); - if from_is_not_valid || to_is_not_valid { - return _unlikely_cb(|| unsafe { to.get_unchecked_mut(..new_len) }); - } - unsafe { - ptr::copy_nonoverlapping(from.as_ptr().add(begin), to.as_mut_ptr().add(new_len), len); - } - new_len = to_end; - } - unsafe { to.get_unchecked_mut(..new_len) } -} - -pub(crate) fn _shift_bytes( - slice: &mut [u8], - start: usize, - iter: impl IntoIterator, -) -> &mut [u8] { - if start >= slice.len() { - return _unlikely_elem(slice); +/// Transfers sequential `iter` chunks delimited by indices to a region starting at `begin`. +/// +/// ```ignore +/// // For example, a vector fragmented in 3 pieces where the last two digits of each piece are +/// // shifted to the left +/// +/// | | | | +/// A: |00|01|02|03|04|05|06|07|08|09|10|11| +/// | << <<| | | +/// +/// | | | | +/// A: |02|03|02|03|04|05|06|07|08|09|10|11| +/// |^^ ^^ | << <<| | +/// +/// | | | | +/// A: |02|03|06|07|04|05|06|07|08|09|10|11| +/// |^^ ^^ ^^ ^^| | << <<| +/// +/// | | | | +/// A: |02|03|06|07|10|11|06|07|08|09|10|11| +/// |^^ ^^ ^^ ^^|^^ ^^ | | +/// +/// A: |02|03|06|07|10|11| +/// ``` +#[inline] +pub(crate) fn _shift_bytes( + begin: usize, + slice: &mut [T], + iter: impl IntoIterator>, +) -> &mut [T] +where + T: Copy, +{ + let mut new_len = begin; + if new_len > slice.len() { + return &mut []; } - let mut new_len = start; - for (begin, end) in iter { - if slice.get(begin..end).is_none() { - // SAFETY: Top-level check enforces bounds - return _unlikely_cb(|| unsafe { slice.get_unchecked_mut(..new_len) }); - } - let len = end.wrapping_sub(begin); + for Range { start, end } in iter { + let Some((diff, local_new_len)) = end.checked_sub(start).and_then(|diff| { + let local_new_len = new_len.checked_add(diff)?; + if local_new_len > slice.len() { + return None; + } + Some((diff, local_new_len)) + }) else { + // SAFETY: Top-level and loop-level checks enforce bounds + return unsafe { slice.get_unchecked_mut(..new_len) }; + }; + let ptr = slice.as_mut_ptr(); // SAFETY: Loop-level check enforces bounds unsafe { - ptr::copy(slice.as_ptr().add(begin), slice.as_mut_ptr().add(new_len), len); + ptr::copy(ptr.add(start), ptr.add(new_len), diff); } - new_len = new_len.wrapping_add(len); + new_len = local_new_len; } // SAFETY: Top-level check enforces bounds unsafe { slice.get_unchecked_mut(..new_len) } } -#[cfg(feature = "_bench")] -#[cfg(test)] -mod bench { - use crate::misc::mem_transfer::{_copy_bytes, _shift_bytes}; - use alloc::{vec, vec::Vec}; - use core::hint::black_box; - - const SHIFT_LEN: usize = 1000; - const SLICES_LEN: usize = 10000000; - const SLICES_NUM: usize = 1000; - - #[bench] - fn copy_bytes(b: &mut test::Bencher) { - let from: Vec = crate::bench::_data(SLICES_LEN * SLICES_NUM); - let mut to = vec![0; SLICES_LEN * SLICES_NUM]; - b.iter(|| { - black_box({ - let iter = (0..SLICES_NUM).skip(1).scan(SLICES_LEN, |begin, _| { - let end = begin.wrapping_add(SHIFT_LEN); - let rslt = (*begin, end); - *begin = begin.wrapping_add(SLICES_LEN); - Some(rslt) - }); - assert_eq!(_copy_bytes(&from, &mut to, 0, iter).len(), SHIFT_LEN * (SLICES_NUM - 1)); - }) - }); - } - - #[bench] - fn shift_bytes(b: &mut test::Bencher) { - let mut vector = crate::bench::_data(SLICES_LEN * SLICES_NUM); - b.iter(|| { - black_box({ - let iter = (0..SLICES_NUM).skip(1).scan(SLICES_LEN, |begin, _| { - let end = begin.wrapping_add(SHIFT_LEN); - let rslt = (*begin, end); - *begin = begin.wrapping_add(SLICES_LEN); - Some(rslt) - }); - assert_eq!(_shift_bytes(&mut vector, 0, iter).len(), SHIFT_LEN * (SLICES_NUM - 1)); - }) - }); - } -} - #[cfg(feature = "_proptest")] #[cfg(test)] mod proptest { - use crate::misc::{_shift_bytes, mem_transfer::_copy_bytes}; + use crate::misc::_shift_bytes; + use alloc::vec::Vec; use core::ops::Range; - #[test_strategy::proptest] - fn copy_bytes(from: Vec, range: Range) { - let mut begin: usize = range.start.into(); - let mut end: usize = range.end.into(); - let mut from_clone = from.clone(); - let mut to = vec![0; from.len()]; - begin = begin.min(from.len()); - end = end.min(from.len()); - let rslt = _copy_bytes(&from, &mut to, 0, [(begin, end)]); - from_clone.rotate_left(begin); - from_clone.truncate(rslt.len()); - assert_eq!(rslt, &from_clone); - } - #[test_strategy::proptest] fn shift_bytes(mut data: Vec, range: Range) { let mut begin: usize = range.start.into(); @@ -175,7 +73,7 @@ mod proptest { let mut data_clone = data.clone(); begin = begin.min(data.len()); end = end.min(data.len()); - let rslt = _shift_bytes(&mut data, 0, [(begin, end)]); + let rslt = _shift_bytes(0, &mut data, [begin..end]); data_clone.rotate_left(begin); data_clone.truncate(rslt.len()); assert_eq!(rslt, &data_clone); @@ -184,20 +82,12 @@ mod proptest { #[cfg(test)] mod test { - use crate::misc::mem_transfer::{_copy_bytes, _shift_bytes}; - - #[test] - fn _copy_bytes_has_correct_outputs() { - let from = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - let to = &mut [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - assert_eq!(_copy_bytes(from, to, 2, [(4, 6), (8, 10)]), &mut [0, 1, 4, 5, 8, 9]); - assert_eq!(to, &mut [0, 1, 4, 5, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - } + use crate::misc::mem_transfer::_shift_bytes; #[test] fn _shift_bytes_has_correct_outputs() { let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - assert_eq!(_shift_bytes(bytes, 2, [(4, 6), (8, 10)]), &mut [0, 1, 4, 5, 8, 9]); + assert_eq!(_shift_bytes(2, bytes, [4..6, 8..10]), &mut [0, 1, 4, 5, 8, 9]); assert_eq!(bytes, &mut [0, 1, 4, 5, 8, 9, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); } } diff --git a/wtx/src/misc/optimization.rs b/wtx/src/misc/optimization.rs index 171e7b9a..0778555d 100644 --- a/wtx/src/misc/optimization.rs +++ b/wtx/src/misc/optimization.rs @@ -1,35 +1,20 @@ -use crate::misc::{BasicUtf8Error, ExtUtf8Error, IncompleteUtf8Char, StdUtf8Error}; +#![allow( + // Used as fallbacks + clippy::disallowed_methods +)] -/// Internally uses `atoi` if the feature is active. -#[cfg(not(feature = "atoi"))] -#[inline] -pub fn atoi(bytes: &[u8]) -> crate::Result -where - T: core::str::FromStr, - T::Err: Into, -{ - Ok(from_utf8_basic(bytes)?.parse().map_err(Into::into)?) -} -/// Internally uses `atoi` if the feature is active. -#[cfg(feature = "atoi")] -#[inline] -pub fn atoi(bytes: &[u8]) -> crate::Result -where - T: atoi::FromRadix10SignedChecked, -{ - atoi::atoi(bytes).ok_or(crate::Error::AtoiInvalidBytes) -} +use crate::misc::{BasicUtf8Error, ExtUtf8Error, IncompleteUtf8Char, Lease, StdUtf8Error}; /// Internally uses `memchr` if the feature is active. #[inline] pub fn bytes_pos1(bytes: B, elem: u8) -> Option where - B: AsRef<[u8]>, + B: Lease<[u8]>, { #[cfg(feature = "memchr")] - return memchr::memchr(elem, bytes.as_ref()); + return memchr::memchr(elem, bytes.lease()); #[cfg(not(feature = "memchr"))] - return bytes.as_ref().iter().position(|byte| *byte == elem); + return bytes.lease().iter().position(|byte| *byte == elem); } /// Internally uses `memchr` if the feature is active. @@ -106,6 +91,26 @@ pub fn from_utf8_std(bytes: &[u8]) -> Result<&str, StdUtf8Error> { }); } +/// Internally uses `atoi` if the feature is active. +#[cfg(not(feature = "atoi"))] +#[inline] +pub fn atoi(bytes: &[u8]) -> crate::Result +where + T: core::str::FromStr, + T::Err: Into, +{ + from_utf8_basic(bytes)?.parse().map_err(Into::into) +} +/// Internally uses `atoi` if the feature is active. +#[cfg(feature = "atoi")] +#[inline] +pub fn atoi(bytes: &[u8]) -> crate::Result +where + T: atoi::FromRadix10SignedChecked, +{ + atoi::atoi(bytes).ok_or(crate::Error::AtoiInvalidBytes) +} + /// Internally uses `memchr` if the feature is active. #[inline] pub fn str_pos1(str: &str, elem: u8) -> Option { diff --git a/wtx/src/misc/partitioned_filled_buffer.rs b/wtx/src/misc/partitioned_filled_buffer.rs index df195f38..368dac2f 100644 --- a/wtx/src/misc/partitioned_filled_buffer.rs +++ b/wtx/src/misc/partitioned_filled_buffer.rs @@ -1,7 +1,4 @@ -use crate::{ - misc::{FilledBufferWriter, _unreachable}, - DFLT_PARTITIONED_BUFFER_LEN, -}; +use crate::misc::{FilledBufferWriter, _unreachable}; use alloc::{vec, vec::Vec}; use core::ops::Range; @@ -17,7 +14,11 @@ pub(crate) struct PartitionedFilledBuffer { } impl PartitionedFilledBuffer { - pub(crate) fn with_capacity(cap: usize) -> Self { + pub(crate) const fn new() -> Self { + Self { _antecedent_end_idx: 0, _buffer: Vec::new(), _current_end_idx: 0, _following_end_idx: 0 } + } + + pub(crate) fn _with_capacity(cap: usize) -> Self { Self { _antecedent_end_idx: 0, _buffer: vec![0; cap], @@ -26,10 +27,6 @@ impl PartitionedFilledBuffer { } } - pub(crate) fn _empty() -> Self { - Self { _antecedent_end_idx: 0, _buffer: Vec::new(), _current_end_idx: 0, _following_end_idx: 0 } - } - pub(crate) fn _antecedent_end_idx(&self) -> usize { self._antecedent_end_idx } @@ -94,7 +91,6 @@ impl PartitionedFilledBuffer { } } - /// Expands the buffer that can accommodate "following" but doesn't set its length. pub(crate) fn _expand_following(&mut self, new_len: usize) { self._expand_buffer(self._following_end_idx.wrapping_add(new_len)); } @@ -175,7 +171,7 @@ impl PartitionedFilledBuffer { impl Default for PartitionedFilledBuffer { #[inline] fn default() -> Self { - Self::with_capacity(DFLT_PARTITIONED_BUFFER_LEN) + Self::new() } } diff --git a/wtx/src/misc/query_writer.rs b/wtx/src/misc/query_writer.rs index 20729a99..580e20b9 100644 --- a/wtx/src/misc/query_writer.rs +++ b/wtx/src/misc/query_writer.rs @@ -1,7 +1,5 @@ -use core::{ - borrow::Borrow, - fmt::{Display, Write}, -}; +use crate::misc::Lease; +use core::fmt::{Display, Write}; /// Query parameters need special handling because of the initial `?`. #[derive(Debug)] @@ -12,10 +10,10 @@ pub struct QueryWriter<'str, S> { impl<'str, S> QueryWriter<'str, S> where - S: AsRef + Write, + S: Lease + Write, { pub(crate) fn new(s: &'str mut S) -> Self { - Self { initial_len: s.as_ref().len(), s } + Self { initial_len: s.lease().len(), s } } /// Writes `?param=value` or `¶m=value`. @@ -24,7 +22,7 @@ where where T: Display, { - if self.s.as_ref().len() == self.initial_len { + if self.s.lease().len() == self.initial_len { self.s.write_fmt(format_args!("?{param}={value}"))?; } else { self.s.write_fmt(format_args!("&{param}={value}"))?; @@ -37,9 +35,9 @@ where pub fn write_opt(self, param: &str, opt: U) -> crate::Result where T: Display, - U: Borrow>, + U: Lease>, { - if let Some(value) = opt.borrow() { + if let Some(value) = opt.lease() { self.write(param, value) } else { Ok(self) diff --git a/wtx/src/misc/queue.rs b/wtx/src/misc/queue.rs new file mode 100644 index 00000000..dd69c024 --- /dev/null +++ b/wtx/src/misc/queue.rs @@ -0,0 +1,307 @@ +macro_rules! as_slices { + ($empty:expr, $ptr:ident, $slice:ident, $this:expr, $($ref:tt)*) => {{ + let len = $this.data.len(); + let rhs_len = $this.capacity().wrapping_sub($this.head); + let ptr = $this.data.$ptr(); + if rhs_len < len { + let lhs_len = len.wrapping_sub(rhs_len); + // SAFETY: indices point to valid memory locations + unsafe { + ( + $($ref)* *ptr::$slice(ptr.add($this.head), rhs_len), + $($ref)* *ptr::$slice(ptr, lhs_len), + ) + } + } else { + // SAFETY: indices point to valid memory locations + unsafe { + ($($ref)* *ptr::$slice(ptr.add($this.head), len), $empty) + } + } + }} +} + +use crate::misc::{ + _unreachable, + queue_utils::{reserve, wrap_add, wrap_sub}, + Vector, +}; +use core::{ + fmt::{Debug, Formatter}, + ptr, +}; + +/// A circular buffer where elements are added in only one-way. +pub struct Queue { + data: Vector, + head: usize, +} + +impl Queue +where + D: Copy, +{ + #[inline] + pub(crate) const fn new() -> Self { + Self { data: Vector::new(), head: 0 } + } + + #[inline] + pub(crate) fn with_capacity(cap: usize) -> Self { + Self { data: Vector::with_capacity(cap), head: 0 } + } + + #[inline] + pub(crate) fn as_slices(&self) -> (&[D], &[D]) { + as_slices!(&[][..], as_ptr, slice_from_raw_parts, self, &) + } + + #[inline] + pub(crate) fn as_slices_mut(&mut self) -> (&mut [D], &mut [D]) { + as_slices!(&mut [][..], as_mut_ptr, slice_from_raw_parts_mut, self, &mut) + } + + #[inline] + pub(crate) fn capacity(&self) -> usize { + self.data.capacity() + } + + #[inline] + pub(crate) fn clear(&mut self) { + self.head = 0; + self.data.clear(); + } + + #[inline] + pub(crate) fn first(&self) -> Option<&D> { + self.get(0) + } + + #[inline] + pub(crate) fn get(&self, mut idx: usize) -> Option<&D> { + if idx >= self.data.len() { + return None; + } + idx = wrap_add(self.data.capacity(), self.head, idx); + // SAFETY: `idx` is less than the current length + unsafe { Some(&*self.data.as_ptr().add(idx)) } + } + + #[inline] + pub(crate) fn get_mut(&mut self, mut idx: usize) -> Option<&mut D> { + if idx >= self.data.len() { + return None; + } + idx = wrap_add(self.data.capacity(), self.head, idx); + // SAFETY: `idx` is less than the current length + unsafe { Some(&mut *self.data.as_mut_ptr().add(idx)) } + } + + #[inline] + pub(crate) fn is_empty(&self) -> bool { + self.data.is_empty() + } + + #[inline] + pub(crate) fn is_full(&self) -> bool { + self.data.len() >= self.data.capacity() + } + + #[inline] + pub(crate) fn iter(&self) -> impl Iterator { + let (lhs, rhs) = self.as_slices(); + rhs.iter().chain(lhs) + } + + #[inline] + pub(crate) fn iter_mut(&mut self) -> impl Iterator { + let (lhs, rhs) = self.as_slices_mut(); + rhs.iter_mut().chain(lhs) + } + + #[inline] + pub(crate) fn last(&self) -> Option<&D> { + self.get(self.len().checked_sub(1)?) + } + + #[inline] + pub(crate) fn len(&self) -> usize { + self.data.len() + } + + #[inline] + pub(crate) fn pop_back(&mut self) -> Option { + if self.is_empty() { + return None; + } + // SAFETY: Structure is not empty + unsafe { + self.data.set_len(self.data.len().unchecked_sub(1)); + let idx = wrap_add(self.data.capacity(), self.head, self.data.len()); + Some(ptr::read(self.data.as_mut_ptr().add(idx))) + } + } + + #[inline] + pub(crate) fn pop_front(&mut self) -> Option { + if self.is_empty() { + return None; + } + let len = self.data.len(); + let prev_head = self.head; + self.head = wrap_add(self.data.capacity(), self.head, 1); + // SAFETY: Structure is not empty + unsafe { + self.data.set_len(len.unchecked_sub(1)); + Some(ptr::read(self.data.as_mut_ptr().add(prev_head))) + } + } + + #[inline] + pub(crate) fn push_front_within_cap(&mut self, element: D) { + if self.is_full() { + _unreachable() + } + let len = self.data.len(); + self.head = wrap_sub(self.data.capacity(), self.head, 1); + // SAFETY: There is enough capacity + unsafe { + ptr::write(self.data.as_mut_ptr().add(self.head), element); + self.data.set_len(len.unchecked_add(1)); + } + } + + #[inline(always)] + pub(crate) fn reserve(&mut self, additional: usize) { + let _ = reserve(additional, &mut self.data, &mut self.head); + } +} + +impl Debug for Queue +where + D: Copy + Debug, +{ + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { + let (lhs, rhs) = self.as_slices(); + f.debug_struct("Queue").field("lhs", &lhs).field("rhs", &rhs).finish() + } +} + +impl Default for Queue +where + D: Copy, +{ + #[inline] + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "_proptest")] +#[cfg(test)] +mod _proptest { + use crate::misc::Queue; + use alloc::{collections::VecDeque, vec::Vec}; + + #[test_strategy::proptest] + fn queue(bytes: Vec) { + let mut queue = Queue::with_capacity(bytes.len()); + let mut vec_deque = VecDeque::with_capacity(bytes.len()); + + for byte in bytes.iter().copied() { + queue.push_front_within_cap(byte); + vec_deque.push_front(byte); + } + assert_eq!((queue.capacity(), queue.len()), (vec_deque.capacity(), vec_deque.len())); + for _ in 0..(bytes.len() / 2) { + assert_eq!(queue.as_slices(), vec_deque.as_slices()); + assert_eq!(queue.get(0), vec_deque.get(0)); + assert_eq!(queue.get_mut(0), vec_deque.get_mut(0)); + assert_eq!(queue.pop_back(), vec_deque.pop_back()); + assert_eq!(queue.as_slices(), vec_deque.as_slices()); + assert_eq!(queue.get(0), vec_deque.get(0)); + assert_eq!(queue.get_mut(0), vec_deque.get_mut(0)); + assert_eq!(queue.pop_front(), vec_deque.pop_front()); + } + queue.reserve(queue.capacity() + 10); + vec_deque.reserve(vec_deque.capacity() + 10); + loop { + if queue.is_empty() { + break; + } + assert_eq!(queue.as_slices(), vec_deque.as_slices()); + assert_eq!(queue.get(0), vec_deque.get(0)); + assert_eq!(queue.get_mut(0), vec_deque.get_mut(0)); + assert_eq!(queue.pop_back(), vec_deque.pop_back()); + if queue.is_empty() { + break; + } + assert_eq!(queue.as_slices(), vec_deque.as_slices()); + assert_eq!(queue.get(0), vec_deque.get(0)); + assert_eq!(queue.get_mut(0), vec_deque.get_mut(0)); + assert_eq!(queue.pop_front(), vec_deque.pop_front()); + } + assert_eq!((queue.capacity(), queue.len()), (vec_deque.capacity(), vec_deque.len())); + assert_eq!((queue.len(), vec_deque.len()), (0, 0)); + } +} + +#[cfg(test)] +mod tests { + use crate::misc::Queue; + + #[test] + fn clear() { + let mut queue = Queue::with_capacity(1); + assert_eq!(queue.len(), 0); + queue.push_front_within_cap(1); + assert_eq!(queue.len(), 1); + queue.clear(); + assert_eq!(queue.len(), 0); + } + + #[test] + fn get() { + let mut queue = Queue::with_capacity(1); + assert_eq!(queue.get(0), None); + assert_eq!(queue.get_mut(0), None); + queue.push_front_within_cap(1); + assert_eq!(queue.get(0), Some(&1i32)); + assert_eq!(queue.get_mut(0), Some(&mut 1i32)); + } + + #[test] + fn pop_back() { + let mut queue = Queue::with_capacity(1); + assert_eq!(queue.pop_back(), None); + queue.push_front_within_cap(1); + assert_eq!(queue.pop_back(), Some(1)); + assert_eq!(queue.pop_back(), None); + } + + #[test] + fn pop_front() { + let mut queue = Queue::with_capacity(1); + assert_eq!(queue.pop_front(), None); + queue.push_front_within_cap(1); + assert_eq!(queue.pop_front(), Some(1)); + assert_eq!(queue.pop_front(), None); + } + + #[test] + fn push_front_within_cap() { + let mut queue = Queue::with_capacity(1); + assert_eq!(queue.len(), 0); + queue.push_front_within_cap(1); + assert_eq!(queue.len(), 1); + } + + #[test] + fn reserve() { + let mut queue = Queue::::new(); + assert_eq!(queue.capacity(), 0); + queue.reserve(10); + assert_eq!(queue.capacity(), 10); + } +} diff --git a/wtx/src/misc/queue_utils.rs b/wtx/src/misc/queue_utils.rs new file mode 100644 index 00000000..210b11cd --- /dev/null +++ b/wtx/src/misc/queue_utils.rs @@ -0,0 +1,47 @@ +use crate::misc::{Vector, _shift_bytes}; +use core::{iter, ptr}; + +#[inline(always)] +pub(crate) fn reserve(additional: usize, data: &mut Vector, head: &mut usize) -> Option +where + D: Copy, +{ + let prev_cap = data.capacity(); + let prev_head = *head; + let rhs_len = prev_cap.wrapping_sub(prev_head); + data.reserve(additional); + let curr_cap = data.capacity(); + let cap_diff = curr_cap.wrapping_sub(prev_cap); + if prev_cap == 0 || cap_diff == 0 { + return None; + } + let curr_head = curr_cap.wrapping_sub(rhs_len); + // SAFETY: Slice is allocated but not initialized + let allocated = unsafe { + let rslt = &mut *ptr::slice_from_raw_parts_mut(data.as_mut_ptr(), curr_cap); + #[cfg(feature = "nightly")] + { + core::hint::assert_unchecked(curr_head <= rslt.len()); + core::hint::assert_unchecked(prev_head <= prev_cap); + } + rslt + }; + let _ = _shift_bytes(curr_head, allocated, iter::once(prev_head..prev_cap)); + *head = curr_head; + Some(cap_diff) +} + +#[inline] +pub(crate) fn wrap_add(cap: usize, idx: usize, value: usize) -> usize { + wrap_idx(idx.wrapping_add(value), cap) +} + +#[inline] +pub(crate) fn wrap_idx(idx: usize, cap: usize) -> usize { + idx.checked_sub(cap).unwrap_or(idx) +} + +#[inline] +pub(crate) fn wrap_sub(cap: usize, idx: usize, value: usize) -> usize { + wrap_idx(idx.wrapping_sub(value).wrapping_add(cap), cap) +} diff --git a/wtx/src/misc/ref_counter.rs b/wtx/src/misc/ref_counter.rs new file mode 100644 index 00000000..a0e5cb34 --- /dev/null +++ b/wtx/src/misc/ref_counter.rs @@ -0,0 +1,32 @@ +use alloc::{rc::Rc, sync::Arc}; +use core::ops::Deref; + +/// Reference Counter +/// +/// Stores the number of references, pointers, or handles to a resource, such as an object, a +/// block of memory, disk space and others. +pub trait RefCounter: Clone + Deref { + /// Item behind this counter. + type Item; + + /// Generic way to build a reference counter. + fn new(elem: Self::Item) -> Self; +} + +impl RefCounter for Arc { + type Item = T; + + #[inline] + fn new(elem: Self::Item) -> Self { + Arc::new(elem) + } +} + +impl RefCounter for Rc { + type Item = T; + + #[inline] + fn new(elem: Self::Item) -> Self { + Rc::new(elem) + } +} diff --git a/wtx/src/misc/traits.rs b/wtx/src/misc/single_type_storage.rs similarity index 61% rename from wtx/src/misc/traits.rs rename to wtx/src/misc/single_type_storage.rs index c520e629..6e77383f 100644 --- a/wtx/src/misc/traits.rs +++ b/wtx/src/misc/single_type_storage.rs @@ -1,4 +1,8 @@ -use alloc::vec::Vec; +use core::cell::RefCell; + +use alloc::{rc::Rc, sync::Arc, vec::Vec}; + +use crate::misc::ArrayVector; /// Internal trait not intended for public usage pub trait SingleTypeStorage { @@ -38,11 +42,28 @@ impl SingleTypeStorage for &'_ [T] { impl SingleTypeStorage for &'_ mut [T] { type Item = T; } + +impl SingleTypeStorage for Arc { + type Item = T; +} + +impl SingleTypeStorage for RefCell { + type Item = T; +} + +impl SingleTypeStorage for Rc { + type Item = T; +} + impl SingleTypeStorage for Vec { type Item = T; } -#[cfg(feature = "arrayvec")] -impl SingleTypeStorage for arrayvec::ArrayVec { +impl SingleTypeStorage for ArrayVector { + type Item = T; +} + +#[cfg(feature = "tokio")] +impl SingleTypeStorage for tokio::sync::Mutex { type Item = T; } diff --git a/wtx/src/misc/stream.rs b/wtx/src/misc/stream.rs index b1a9dff6..a8b67b2d 100644 --- a/wtx/src/misc/stream.rs +++ b/wtx/src/misc/stream.rs @@ -1,4 +1,30 @@ -use crate::misc::AsyncBounds; +macro_rules! _local_write_all { + ($bytes:expr, $write:expr) => {{ + while !$bytes.is_empty() { + match $write { + Err(e) => return Err(e.into()), + Ok(0) => return Err(crate::Error::UnexpectedEOF), + Ok(n) => $bytes = $bytes.get(n..).unwrap_or_default(), + } + } + }}; +} + +macro_rules! _local_write_all_vectored { + ($bytes:expr, |$io_slices:ident| $write:expr) => {{ + let mut buffer = [std::io::IoSlice::new(&[]); N]; + let $io_slices = crate::misc::stream::convert_to_io_slices(&mut buffer, $bytes); + while !$io_slices.is_empty() { + match $write { + Err(e) => return Err(e.into()), + Ok(0) => return Err(crate::Error::UnexpectedEOF), + Ok(n) => super::advance_slices(&mut &$bytes[..], &mut &mut *$io_slices, n), + } + } + }}; +} + +use crate::misc::{AsyncBounds, Lease}; use alloc::vec::Vec; use core::{cmp::Ordering, future::Future}; @@ -8,8 +34,18 @@ pub trait Stream { /// were read. fn read(&mut self, bytes: &mut [u8]) -> impl AsyncBounds + Future>; - /// Attempts to write all elements of `bytes`. + /// Attempts to write ***all*** `bytes`. fn write_all(&mut self, bytes: &[u8]) -> impl AsyncBounds + Future>; + + /// Attempts to write ***all*** `bytes` of all slices in a single syscall. + /// + /// # Panics + /// + /// If the length of the outermost slice is greater than 8. + fn write_all_vectored( + &mut self, + bytes: [&[u8]; N], + ) -> impl AsyncBounds + Future>; } /// Transport Layer Security @@ -17,7 +53,7 @@ pub trait TlsStream: Stream { /// Channel binding data defined in [RFC 5929]. /// /// [RFC 5929]: https://tools.ietf.org/html/rfc5929 - type TlsServerEndPoint: AsRef<[u8]>; + type TlsServerEndPoint: Lease<[u8]>; /// See `Self::TlsServerEndPoint`. fn tls_server_end_point(&self) -> crate::Result>; @@ -33,6 +69,11 @@ impl Stream for () { async fn write_all(&mut self, _: &[u8]) -> crate::Result<()> { Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, _: [&[u8]; N]) -> crate::Result<()> { + Ok(()) + } } impl Stream for &mut T @@ -48,6 +89,11 @@ where async fn write_all(&mut self, bytes: &[u8]) -> crate::Result<()> { (**self).write_all(bytes).await } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + (**self).write_all_vectored(bytes).await + } } /// Stores written data to transfer when read. @@ -95,6 +141,14 @@ impl Stream for BytesStream { self.buffer.extend_from_slice(bytes); Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + for elem in bytes { + self.buffer.extend_from_slice(elem); + } + Ok(()) + } } #[cfg(feature = "async-std")] @@ -116,6 +170,12 @@ mod async_std { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } } #[cfg(unix)] @@ -130,6 +190,12 @@ mod async_std { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } } } @@ -145,14 +211,15 @@ mod embassy_net { } #[inline] - async fn write_all(&mut self, bytes: &[u8]) -> crate::Result<()> { - let mut buf = bytes; - while !buf.is_empty() { - match self.write(buf).await { - Ok(0) => return Err(crate::Error::UnexpectedEOF), - Ok(n) => buf = buf.get(n..).unwrap_or_default(), - Err(e) => return Err(e.into()), - } + async fn write_all(&mut self, mut bytes: &[u8]) -> crate::Result<()> { + _local_write_all!(bytes, Self::write(self, bytes).await); + Ok(()) + } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + for elem in bytes { + self.write_all(elem).await?; } Ok(()) } @@ -181,6 +248,14 @@ mod embedded_tls { self.flush().await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + for elem in bytes { + ::write_all(self, elem).await?; + } + Ok(()) + } } impl<'any, S, C> TlsStream for TlsConnection<'any, S, C> @@ -214,6 +289,14 @@ mod glommio { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + for elem in bytes { + ::write_all(self, elem).await?; + } + Ok(()) + } } #[cfg(unix)] @@ -228,6 +311,14 @@ mod glommio { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + for elem in bytes { + ::write_all(self, elem).await?; + } + Ok(()) + } } } @@ -250,6 +341,12 @@ mod smol { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } } #[cfg(unix)] @@ -264,11 +361,44 @@ mod smol { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } + } +} + +#[cfg(feature = "smoltcp")] +mod smoltcp { + use crate::misc::Stream; + use smoltcp::socket::tcp::Socket; + + impl Stream for Socket<'_> { + #[inline] + async fn read(&mut self, bytes: &mut [u8]) -> crate::Result { + Ok(self.recv_slice(bytes)?) + } + + #[inline] + async fn write_all(&mut self, mut bytes: &[u8]) -> crate::Result<()> { + _local_write_all!(bytes, self.send_slice(bytes)); + Ok(()) + } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + for elem in bytes { + self.write_all(elem).await?; + } + Ok(()) + } } } #[cfg(feature = "std")] -mod std { +mod _std { use crate::misc::Stream; use std::{ io::{Read, Write}, @@ -286,6 +416,12 @@ mod std { ::write_all(self, bytes)?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices)); + Ok(()) + } } #[cfg(unix)] @@ -300,6 +436,12 @@ mod std { ::write_all(self, bytes)?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices)); + Ok(()) + } } } @@ -322,6 +464,12 @@ mod tokio { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } } #[cfg(unix)] @@ -336,13 +484,19 @@ mod tokio { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } } } #[cfg(feature = "tokio-rustls")] mod tokio_rustls { use crate::misc::{stream::TlsStream, AsyncBounds, Stream}; - use ring::digest; + use ring::digest::{self, Digest}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; impl Stream for tokio_rustls::client::TlsStream @@ -359,13 +513,19 @@ mod tokio_rustls { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } } impl TlsStream for tokio_rustls::client::TlsStream where T: AsyncBounds + AsyncRead + AsyncWrite + Unpin, { - type TlsServerEndPoint = digest::Digest; + type TlsServerEndPoint = Digest; #[inline] fn tls_server_end_point(&self) -> crate::Result> { @@ -416,13 +576,19 @@ mod tokio_rustls { ::write_all(self, bytes).await?; Ok(()) } + + #[inline] + async fn write_all_vectored(&mut self, bytes: [&[u8]; N]) -> crate::Result<()> { + _local_write_all_vectored!(bytes, |io_slices| self.write_vectored(io_slices).await); + Ok(()) + } } impl TlsStream for tokio_rustls::server::TlsStream where T: AsyncBounds + AsyncRead + AsyncWrite + Unpin, { - type TlsServerEndPoint = digest::Digest; + type TlsServerEndPoint = Digest; #[inline] fn tls_server_end_point(&self) -> crate::Result> { @@ -434,3 +600,111 @@ mod tokio_rustls { } } } + +#[allow( + // False-positive + clippy::mut_mut +)] +#[cfg(feature = "std")] +#[inline] +fn advance_slices<'bytes, 'buffer>( + bytes: &mut &[&'bytes [u8]], + io_slices: &'buffer mut &'buffer mut [std::io::IoSlice<'bytes>], + mut written: usize, +) { + let mut idx = 0; + for (local_idx, io_slice) in io_slices.iter().enumerate() { + let Some(diff) = written.checked_sub(io_slice.len()) else { + break; + }; + written = diff; + idx = local_idx; + } + let locals_opt = bytes.get(idx..).and_then(|el| Some((el, io_slices.get_mut(idx..)?))); + let Some((local_bytes, local_io_slices)) = locals_opt else { + return; + }; + *bytes = local_bytes; + *io_slices = local_io_slices; + let ([first_bytes, ..], [first_io_slices, ..]) = (bytes, io_slices) else { + return; + }; + *first_io_slices = std::io::IoSlice::new(first_bytes.get(written..).unwrap_or_default()); +} + +#[cfg(feature = "std")] +#[inline] +fn convert_to_io_slices<'buffer, 'bytes, const N: usize>( + buffer: &'buffer mut [std::io::IoSlice<'bytes>; N], + elems: [&'bytes [u8]; N], +) -> &'buffer mut [std::io::IoSlice<'bytes>] { + use std::io::IoSlice; + const { + if N > 8 { + panic!("It is not possible to vectored write more than 8 slices"); + } + } + match elems.as_slice() { + [a] => { + buffer[0] = IoSlice::new(a); + &mut buffer[..1] + } + [a, b] => { + buffer[0] = IoSlice::new(a); + buffer[1] = IoSlice::new(b); + &mut buffer[..2] + } + [a, b, c] => { + buffer[0] = IoSlice::new(a); + buffer[1] = IoSlice::new(b); + buffer[2] = IoSlice::new(c); + &mut buffer[..3] + } + [a, b, c, d] => { + buffer[0] = IoSlice::new(a); + buffer[1] = IoSlice::new(b); + buffer[2] = IoSlice::new(c); + buffer[3] = IoSlice::new(d); + &mut buffer[..4] + } + [a, b, c, d, e] => { + buffer[0] = IoSlice::new(a); + buffer[1] = IoSlice::new(b); + buffer[2] = IoSlice::new(c); + buffer[3] = IoSlice::new(d); + buffer[4] = IoSlice::new(e); + &mut buffer[..5] + } + [a, b, c, d, e, f] => { + buffer[0] = IoSlice::new(a); + buffer[1] = IoSlice::new(b); + buffer[2] = IoSlice::new(c); + buffer[3] = IoSlice::new(d); + buffer[4] = IoSlice::new(e); + buffer[5] = IoSlice::new(f); + &mut buffer[..6] + } + [a, b, c, d, e, f, g] => { + buffer[0] = IoSlice::new(a); + buffer[1] = IoSlice::new(b); + buffer[2] = IoSlice::new(c); + buffer[3] = IoSlice::new(d); + buffer[4] = IoSlice::new(e); + buffer[5] = IoSlice::new(f); + buffer[6] = IoSlice::new(g); + &mut buffer[..7] + } + [a, b, c, d, e, f, g, h] => { + buffer[0] = IoSlice::new(a); + buffer[1] = IoSlice::new(b); + buffer[2] = IoSlice::new(c); + buffer[3] = IoSlice::new(d); + buffer[4] = IoSlice::new(e); + buffer[5] = IoSlice::new(f); + buffer[6] = IoSlice::new(g); + buffer[7] = IoSlice::new(h); + &mut buffer[..8] + } + _ => &mut [], + } +} diff --git a/wtx/src/misc/tokio_rustls.rs b/wtx/src/misc/tokio_rustls.rs index fa531bab..98e886d5 100644 --- a/wtx/src/misc/tokio_rustls.rs +++ b/wtx/src/misc/tokio_rustls.rs @@ -1,3 +1,4 @@ +use alloc::{boxed::Box, string::String, vec::Vec}; use rustls_pki_types::ServerName; use std::{net::SocketAddr, sync::Arc}; use tokio::{ @@ -73,7 +74,7 @@ impl TokioRustlsConnector { } fn server_name(hostname: &str) -> crate::Result> { - Ok(ServerName::try_from(hostname.to_string()).map_err(invalid_input_err)?) + Ok(ServerName::try_from(String::from(hostname)).map_err(invalid_input_err)?) } fn tls_connector(self) -> TlsConnector { diff --git a/wtx/src/misc/uri.rs b/wtx/src/misc/uri.rs index 39ca3ad4..dacd69cc 100644 --- a/wtx/src/misc/uri.rs +++ b/wtx/src/misc/uri.rs @@ -1,7 +1,11 @@ -use crate::misc::{QueryWriter, _unlikely_dflt, str_rsplit_once1, str_split_once1}; +use crate::misc::{ + QueryWriter, _unlikely_dflt, str_rsplit_once1, str_split_once1, ArrayString, Lease, +}; use alloc::string::String; use core::fmt::{Arguments, Debug, Formatter, Write}; +/// [Uri] with an owned array. +pub type UriArrayString = Uri>; /// [Uri] with a string reference. pub type UriRef<'uri> = Uri<&'uri str>; /// [Uri] with an owned string. @@ -22,26 +26,12 @@ pub struct Uri { impl Uri where - S: AsRef, + S: Lease, { /// Analyzes the provided `uri` to create a new instance. #[inline] pub fn new(uri: S) -> Self { - let initial_len = uri.as_ref().len().try_into().unwrap_or(u16::MAX); - let valid_uri = uri.as_ref().get(..initial_len.into()).unwrap_or_else(_unlikely_dflt); - let authority_start_idx: u16 = valid_uri - .match_indices("://") - .next() - .and_then(|(element, _)| element.wrapping_add(3).try_into().ok()) - .unwrap_or_else(_unlikely_dflt); - let href_start_idx = valid_uri - .as_bytes() - .iter() - .copied() - .enumerate() - .skip(authority_start_idx.into()) - .find_map(|(idx, el)| (el == b'/').then_some(idx).and_then(|_usisze| _usisze.try_into().ok())) - .unwrap_or(initial_len); + let (authority_start_idx, href_start_idx, initial_len) = Self::parts(uri.lease()); Self { authority_start_idx, href_start_idx, initial_len, uri } } @@ -53,7 +43,7 @@ where pub fn authority(&self) -> &str { self .uri - .as_ref() + .lease() .get(self.authority_start_idx.into()..self.href_start_idx.into()) .unwrap_or_else(_unlikely_dflt) } @@ -103,7 +93,7 @@ where /// ``` #[inline] pub fn href(&self) -> &str { - if let Some(elem) = self.uri.as_ref().get(self.href_start_idx.into()..) { + if let Some(elem) = self.uri.lease().get(self.href_start_idx.into()..) { if !elem.is_empty() { return elem; } @@ -152,11 +142,7 @@ where pub fn query(&self) -> &str { let href = self.href(); let before_hash = if let Some((elem, _)) = str_rsplit_once1(href, b'#') { elem } else { href }; - if let Some((_, elem)) = str_rsplit_once1(before_hash, b'?') { - elem - } else { - "" - } + str_rsplit_once1(before_hash, b'?').map(|el| el.1).unwrap_or_default() } /// ```rust @@ -165,13 +151,11 @@ where /// ``` #[inline] pub fn schema(&self) -> &str { - let mut iter = self.uri.as_ref().split("://"); - let first_opt = iter.next(); - if iter.next().is_some() { - first_opt.unwrap_or_else(_unlikely_dflt) - } else { - "" - } + self + .authority_start_idx + .checked_sub(3) + .and_then(|index| self.uri.lease().get(..index.into())) + .unwrap_or_default() } /// See [UriPartsRef]. @@ -181,7 +165,7 @@ where authority_start_idx: self.authority_start_idx, href_start_idx: self.href_start_idx, initial_len: self.initial_len, - uri: self.uri.as_ref(), + uri: self.uri.lease(), } } @@ -192,14 +176,14 @@ where authority_start_idx: self.authority_start_idx, href_start_idx: self.href_start_idx, initial_len: self.initial_len, - uri: self.uri.as_ref().into(), + uri: self.uri.lease().into(), } } /// Full URI. #[inline] pub fn uri(&self) -> &str { - self.uri.as_ref() + self.uri.lease() } /// ```rust @@ -227,18 +211,29 @@ where "" } } -} -impl UriString { - /// Clears the internal storage. - #[inline] - pub fn clear(&mut self) { - self.authority_start_idx = 0; - self.href_start_idx = 0; - self.uri.clear(); + fn parts(uri: &str) -> (u16, u16, u16) { + let initial_len = uri.len().try_into().unwrap_or(u16::MAX); + let valid_uri = uri.get(..initial_len.into()).unwrap_or_else(_unlikely_dflt); + let authority_start_idx: u16 = valid_uri + .match_indices("://") + .next() + .and_then(|(element, _)| element.wrapping_add(3).try_into().ok()) + .unwrap_or_else(_unlikely_dflt); + let href_start_idx = valid_uri + .as_bytes() + .iter() + .copied() + .enumerate() + .skip(authority_start_idx.into()) + .find_map(|(idx, el)| (el == b'/').then_some(idx).and_then(|_usisze| _usisze.try_into().ok())) + .unwrap_or(initial_len); + (authority_start_idx, href_start_idx, initial_len) } +} - /// Pushes an additional path erasing any subsequent content. +impl UriString { + /// Pushes an additional path only if there is no query. #[inline] pub fn push_path(&mut self, args: Arguments<'_>) -> crate::Result<()> { if !self.query().is_empty() { @@ -257,11 +252,20 @@ impl UriString { Ok(QueryWriter::new(&mut self.uri)) } - /// Truncates the internal storage with the length of the URL initially created in this instance. - /// - /// If the current length is lesser than the original URL length, nothing will happen. + /// Clears the internal storage and makes room for a new base URI. + #[inline] + pub fn reset(&mut self, uri: &str) { + self.uri.clear(); + self.uri.push_str(uri); + let (authority_start_idx, href_start_idx, initial_len) = Self::parts(uri); + self.authority_start_idx = authority_start_idx; + self.href_start_idx = href_start_idx; + self.initial_len = initial_len; + } + + /// Truncates the internal storage with the length of the base URI created in this instance. #[inline] - pub fn retain_with_initial_len(&mut self) { + pub fn truncate_with_initial_len(&mut self) { self.uri.truncate(self.initial_len.into()); } } @@ -275,7 +279,7 @@ impl Debug for Uri { impl From for Uri where - S: AsRef, + S: Lease, { #[inline] fn from(value: S) -> Self { @@ -294,11 +298,11 @@ mod tests { assert_eq!(uri.path(), "/rewqd/tretre"); assert_eq!(uri.query(), ""); assert_eq!(uri.uri(), "http://dasdas.com/rewqd/tretre"); - uri.retain_with_initial_len(); + uri.truncate_with_initial_len(); assert_eq!(uri.path(), "/rewqd"); assert_eq!(uri.query(), ""); assert_eq!(uri.uri(), "http://dasdas.com/rewqd"); - uri.clear(); + uri.reset(""); assert_eq!(uri.path(), "/"); assert_eq!(uri.query(), ""); assert_eq!(uri.uri(), ""); diff --git a/wtx/src/misc/usize.rs b/wtx/src/misc/usize.rs index 3708dde8..aebba35b 100644 --- a/wtx/src/misc/usize.rs +++ b/wtx/src/misc/usize.rs @@ -5,13 +5,39 @@ use core::ops::{Deref, DerefMut}; -/// An `usize` that can be infallible converted from an `u32`. In other words, this effectively kills -/// the support for 16bit hardware. +/// An `usize` that can be infallible converted from an `u32`, which effectively kills the support +/// for 16bit hardware. /// /// Additionally, 128bit support is also dropped. #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Usize(usize); +impl Usize { + #[inline] + pub(crate) const fn from_u32(from: u32) -> Self { + #[cfg(target_pointer_width = "16")] + compile_error!("WTX does not support 16bit hardware"); + Self(from as usize) + } + + #[inline] + pub(crate) const fn from_usize(from: usize) -> Self { + Self(from) + } + + #[inline] + pub(crate) const fn into_usize(self) -> usize { + self.0 + } + + #[inline] + pub(crate) const fn into_u64(self) -> u64 { + #[cfg(target_pointer_width = "128")] + compile_error!("WTX does not support 128bit hardware"); + self.0 as u64 + } +} + impl Deref for Usize { type Target = usize; @@ -45,9 +71,7 @@ impl From for Usize { impl From for Usize { #[inline] fn from(from: u32) -> Self { - #[cfg(target_pointer_width = "16")] - compile_error!("WTX does not support 16bit hardware"); - Self(from as usize) + Self::from_u32(from) } } @@ -61,9 +85,7 @@ impl From for Usize { impl From for u64 { #[inline] fn from(from: Usize) -> Self { - #[cfg(target_pointer_width = "128")] - compile_error!("WTX does not support 128bit hardware"); - from.0 as u64 + from.into_u64() } } diff --git a/wtx/src/misc/vector.rs b/wtx/src/misc/vector.rs new file mode 100644 index 00000000..9e58f7ea --- /dev/null +++ b/wtx/src/misc/vector.rs @@ -0,0 +1,327 @@ +use crate::misc::{Lease, _unreachable}; +use alloc::vec::Vec; +use core::{ + fmt::{Debug, Formatter}, + ops::{Deref, DerefMut}, + ptr, +}; + +/// A wrapper around the std's vector with some additional methods to manipulate copyable data. +#[derive(Default, Eq, PartialEq)] +pub struct Vector { + data: Vec, +} + +impl Vector +where + D: Copy, +{ + /// Constructs a new, empty instance. + #[inline] + pub const fn new() -> Self { + Self { data: Vec::new() } + } + + /// Constructs a new, empty instance with at least the specified capacity. + #[inline] + pub fn with_capacity(cap: usize) -> Self { + let data = Vec::with_capacity(cap); + #[cfg(feature = "nightly")] + unsafe { + core::hint::assert_unchecked(data.len().unchecked_add(cap) <= data.capacity()); + } + Self { data } + } + + /// Returns an unsafe mutable pointer to the vector's buffer, or a dangling + /// raw pointer valid for zero sized reads if the vector didn't allocate. + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut D { + self.data.as_mut_ptr() + } + + /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer + /// valid for zero sized reads if the vector didn't allocate. + #[inline] + pub fn as_ptr(&self) -> *const D { + self.data.as_ptr() + } + + /// Returns the total number of elements the vector can hold without reallocating. + #[inline] + pub fn capacity(&self) -> usize { + self.data.capacity() + } + + /// Clears the vector, removing all values. + #[inline] + pub fn clear(&mut self) { + self.data.clear(); + } + + /// Iterates over the slice `other`, copies each element, and then appends + /// it to this vector. The `other` slice is traversed in-order. + /// + /// # Panics + /// + /// If memory reservation fails. + #[inline] + pub fn extend_from_slice(&mut self, other: &[D]) { + self.reserve(other.len()); + self.extend_from_slice_within_cap(other); + } + + /// Generalization of [Self::extend_from_slice_within_cap]. + /// + /// # Panics + /// + /// If memory reservation fails. + #[inline] + pub fn extend_from_slices_within_cap(&mut self, others: &[U; N]) + where + U: Lease<[D]>, + { + const { + if N > 8 { + panic!("It is not possible to extend more than 8 slices"); + } + } + let mut len: usize = 0; + for other in others { + // SAFETY: 8 slices is feasible by contract + unsafe { + len = len.unchecked_add(other.lease().len()); + } + } + self.reserve(len); + for other in others { + self.extend_from_slice_within_cap(other.lease()); + } + } + + /// Iterates over the slice `other`, copies each element, and then appends + /// it to this vector. The `other` slice is traversed in-order. + /// + /// # Panics + /// + /// If there is no available capacity. + #[allow( + // Programming error that should be handled by the caller + clippy::panic + )] + #[inline] + pub fn extend_from_slice_within_cap(&mut self, other: &[D]) { + let len = self.len(); + let other_len = other.len(); + // SAFETY: There is enough capacity + unsafe { + let new_len = len.unchecked_add(other_len); + if new_len > self.data.capacity() { + panic!("Must be called with sufficient capacity"); + } + ptr::copy_nonoverlapping(other.as_ptr(), self.data.as_mut_ptr().add(len), other_len); + self.set_len(new_len); + } + } + + /// Appends an element to the back of the collection. + /// + /// # Panics + /// + /// If memory reservation fails. + #[inline] + pub fn push(&mut self, value: D) { + self.reserve(1); + self.push_within_cap(value); + } + + /// Appends an element to the back of the collection. + /// + /// # Panics + /// + /// If there is no available capacity. + #[inline] + pub fn push_within_cap(&mut self, value: D) { + let len = self.data.len(); + if len >= self.data.capacity() { + _unreachable(); + } + // SAFETY: There is enough capacity + unsafe { + ptr::write(self.data.as_mut_ptr().add(len), value); + self.set_len(len.unchecked_add(1)); + } + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the given instance. The collection may reserve more space to + /// speculatively avoid frequent reallocations. After calling `reserve`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if capacity is already sufficient. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.data.reserve(additional); + #[cfg(feature = "nightly")] + unsafe { + core::hint::assert_unchecked( + self.data.len().unchecked_add(additional) <= self.data.capacity(), + ); + } + } + + /// Shortens the vector, keeping the first len elements and dropping the rest. + #[inline] + pub fn truncate(&mut self, len: usize) { + self.data.truncate(len); + } + + /// Forces the length of the vector to `new_len`. + /// + /// # Safety + /// + /// - `new_len` must be less than or equal to the capacity. + /// - The elements at `prev_len..new_len` must be initialized. + #[inline] + pub(crate) unsafe fn set_len(&mut self, new_len: usize) { + // Safety: up to the caller + unsafe { + self.data.set_len(new_len); + } + } +} + +impl Debug for Vector +where + D: Debug, +{ + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { + self.data.fmt(f) + } +} + +impl Deref for Vector +where + D: Copy, +{ + type Target = [D]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.data.as_slice() + } +} + +impl DerefMut for Vector +where + D: Copy, +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.data.as_mut_slice() + } +} + +impl Lease<[D]> for Vector { + #[inline] + fn lease(&self) -> &[D] { + self.data.as_slice() + } +} + +#[cfg(feature = "_bench")] +#[cfg(test)] +mod bench { + use crate::misc::vector::Vector; + use alloc::vec::Vec; + + macro_rules! extend_from_slice { + ( + $instance:expr, + $extend_from_slice_method:ident, + $reserve_method:ident + ) => { + $instance.$reserve_method(16 * 8); + extend_from_slice!( + @$instance, + $extend_from_slice_method, + $reserve_method, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + }; + ( + @$instance:expr, + $extend_from_slice_method:ident, + $reserve_method:ident, + $($n:literal),* + ) => { + $( + let _ = $n; + $instance.$extend_from_slice_method(&[0, 1, 2, 4, 5, 6, 7]); + )* + }; + } + + macro_rules! push { + ( + $instance:expr, + $push_method:ident, + $reserve_method:ident + ) => { + $instance.$reserve_method(64); + push!( + @$instance, + $push_method, + $reserve_method, + 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64 + ) + }; + ( + @$instance:expr, + $push_method:ident, + $reserve_method:ident, + $($n:literal),* + ) => { + $($instance.$push_method($n);)* + }; + } + + #[bench] + fn extend_from_slice(b: &mut test::Bencher) { + let mut vec = Vec::default(); + b.iter(|| { + extend_from_slice!(vec, extend_from_slice, reserve); + }); + } + + #[bench] + fn extend_from_slice_within_cap(b: &mut test::Bencher) { + let mut vec = Vector::default(); + b.iter(|| { + extend_from_slice!(vec, extend_from_slice_within_cap, reserve); + }); + } + + #[bench] + fn push(b: &mut test::Bencher) { + let mut vec = Vec::default(); + b.iter(|| { + push!(vec, push, reserve); + }); + } + + #[bench] + fn push_within_cap(b: &mut test::Bencher) { + let mut vec = Vector::default(); + b.iter(|| { + push!(vec, push_within_cap, reserve); + }); + } +} diff --git a/wtx/src/pool.rs b/wtx/src/pool.rs new file mode 100644 index 00000000..6acf895c --- /dev/null +++ b/wtx/src/pool.rs @@ -0,0 +1,65 @@ +//! Pool Manager + +mod fixed_pool; +mod resource_manager; + +use crate::misc::LockGuard; +use core::{future::Future, ops::DerefMut}; +pub use fixed_pool::*; +#[cfg(feature = "database")] +pub use resource_manager::database::PostgresRM; +#[cfg(feature = "http2")] +pub use resource_manager::http2::{Http2ClientBufferRM, Http2ServerBufferRM, ReqResBufferRM}; +#[cfg(feature = "web-socket")] +pub use resource_manager::web_socket::WebSocketRM; +pub use resource_manager::{ResourceManager, SimpleRM}; + +/// A pool contains a set of resources that are behind some synchronism mechanism. +pub trait Pool: Sized { + /// Result of the [Pool:get] method. + type GetRslt<'guard>: DerefMut> + where + Self: 'guard; + /// Synchronization mechanism. + type Guard<'guard>: LockGuard<'guard, Self::GuardElement> + where + Self: 'guard; + /// The element guarded by the synchronization mechanism. + type GuardElement; + /// See [ResourceManager]. + type ResourceManager: ResourceManager; + + /// Tries to retrieve a free resource. + /// + /// If the resource does not exist, a new one is created and if the pool is full, this method will + /// await until a free resource is available. + fn get( + &self, + ca: &::CreateAux, + ra: &::RecycleAux, + ) -> impl Future, ::Error>>; +} + +impl Pool for &T +where + T: Pool, +{ + type GetRslt<'guard> = T::GetRslt<'guard> + where + Self: 'guard; + type Guard<'guard> = T::Guard<'guard> + where + Self: 'guard; + type GuardElement = T::GuardElement; + type ResourceManager = T::ResourceManager; + + #[inline] + fn get( + &self, + ca: &::CreateAux, + ra: &::RecycleAux, + ) -> impl Future, ::Error>> + { + (**self).get(ca, ra) + } +} diff --git a/wtx/src/pool/fixed_pool.rs b/wtx/src/pool/fixed_pool.rs new file mode 100644 index 00000000..66e8a922 --- /dev/null +++ b/wtx/src/pool/fixed_pool.rs @@ -0,0 +1,225 @@ +use crate::{ + misc::{Lock, LockGuard, Queue, RefCounter, _unreachable}, + pool::{Pool, ResourceManager}, +}; +use alloc::vec::Vec; +use core::ops::{Deref, DerefMut}; + +/// A [FixedPool] synchronized by [tokio::sync::Mutex]. +#[cfg(feature = "tokio")] +pub type FixedPoolTokio = + FixedPool>>, tokio::sync::Mutex>, RM>; +/// A [FixedPoolGetRslt] synchronized by [tokio::sync::MappedMutexGuard]. +#[cfg(feature = "tokio")] +pub type FixedPoolGetRsltTokio<'guard, R> = FixedPoolGetRslt< + alloc::sync::Arc>>, + tokio::sync::MappedMutexGuard<'guard, R>, +>; + +/// A pool that does not dynamically change its size after initialization. All elements **must** be +/// of type `Option` to lazily evaluate resources. +/// +/// All `get` calls must end with a [FixedPoolController::release] invocation because otherwise +/// the underlying resource will be locked forever. +#[derive(Debug)] +pub struct FixedPool { + indcs: IC, + locks: Vec, + rm: RM, +} + +impl FixedPool +where + IC: RefCounter + Lock>, + IC::Item: Lock>, + RL: Lock>, + RM: ResourceManager, + for<'any> IC: 'any, + for<'any> R: 'any, + for<'any> RL: 'any, + for<'any> RM: 'any, +{ + /// Creates a new instance with a fixed amount of memory delimited by `len`. + /// + /// If `0` is received, then `len` will be stored as `1`. + #[inline] + pub fn new(mut len: usize, rm: RM) -> Self { + len = len.max(1); + Self { + indcs: { + let mut rslt = Queue::with_capacity(len); + for idx in 0..len { + rslt.push_front_within_cap(idx); + } + ::new(IC::Item::new(rslt)) + }, + locks: { + let mut rslt = Vec::with_capacity(len); + rslt.extend((0..len).map(|_| RL::new(None))); + rslt + }, + rm, + } + } + + /// Sometimes it is desired to eagerly initialize all instances. + #[inline] + pub async fn init_all<'this>( + &'this self, + ca: &RM::CreateAux, + ra: &RM::RecycleAux, + ) -> Result<(), RM::Error> { + for _ in 0..self.locks.len() { + let _guard = self.get(ca, ra).await?; + } + Ok(()) + } +} + +impl Pool for FixedPool +where + IC: RefCounter + Lock>, + RL: Lock>, + RM: ResourceManager, + for<'any> IC: 'any, + for<'any> R: 'any, + for<'any> RL: 'any, + for<'any> RM: 'any, +{ + type GetRslt<'guard> = FixedPoolGetRslt>; + type Guard<'guard> = as LockGuard<'guard, Option>>::Mapped; + type GuardElement = R; + type ResourceManager = RM; + + #[inline] + async fn get( + &self, + ca: &::CreateAux, + ra: &::RecycleAux, + ) -> Result, RM::Error> { + loop { + let mut indcs_lock = self.indcs.lock().await; + let Some(idx) = indcs_lock.pop_back() else { + drop(indcs_lock); + continue; + }; + drop(indcs_lock); + let lock = self.locks.get(idx).unwrap(); + let mut guard = lock.lock().await; + match &mut *guard { + None => { + *guard = Some(self.rm.create(ca).await?); + } + Some(resource) if self.rm.is_invalid(resource) => { + self.rm.recycle(ra, resource).await?; + } + _ => {} + } + let lock_guard = LockGuard::map(guard, |el| match el.as_mut() { + Some(elem) => elem, + None => _unreachable(), + }); + return Ok(FixedPoolGetRslt { idx, indcs: self.indcs.clone(), lock_guard }); + } + } +} + +/// Controls the guard locks related to [FixedPool]. +#[derive(Debug, PartialEq)] +pub struct FixedPoolGetRslt { + idx: usize, + indcs: IL, + lock_guard: LG, +} + +impl FixedPoolGetRslt +where + IL: Lock>, +{ + /// Releases the inner lock. + #[inline] + pub async fn release(self) -> LG { + self.indcs.lock().await.push_front_within_cap(self.idx); + self.lock_guard + } +} + +impl Deref for FixedPoolGetRslt +where + IL: Lock>, +{ + type Target = LG; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.lock_guard + } +} + +impl DerefMut for FixedPoolGetRslt +where + IL: Lock>, +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.lock_guard + } +} + +#[cfg(feature = "tokio")] +mod _tokio { + use crate::{ + misc::{Lease, LeaseMut}, + pool::FixedPoolGetRslt, + }; + use tokio::sync::MappedMutexGuard; + + impl Lease for FixedPoolGetRslt> { + #[inline] + fn lease(&self) -> &R { + &self.lock_guard + } + } + + impl LeaseMut for FixedPoolGetRslt> { + #[inline] + fn lease_mut(&mut self) -> &mut R { + &mut self.lock_guard + } + } +} + +#[cfg(all(feature = "tokio", test))] +mod tests { + use crate::pool::{fixed_pool::FixedPoolTokio, Pool, SimpleRM}; + + #[tokio::test] + async fn held_lock_is_not_modified() { + let pool = pool(); + let lhs_lock = pool.get(&(), &()).await.unwrap(); + + *pool.get(&(), &()).await.unwrap().release().await = 1; + assert_eq!([**lhs_lock, *pool.get(&(), &()).await.unwrap().release().await], [0, 1]); + + *pool.get(&(), &()).await.unwrap().release().await = 2; + assert_eq!([**lhs_lock, *pool.get(&(), &()).await.unwrap().release().await], [0, 2]); + + drop(lhs_lock.release().await); + + *pool.get(&(), &()).await.unwrap().release().await = 1; + assert_eq!( + [ + *pool.get(&(), &()).await.unwrap().release().await, + *pool.get(&(), &()).await.unwrap().release().await + ], + [0, 1] + ); + } + + fn pool() -> FixedPoolTokio> { + fn cb(_: &()) -> crate::Result { + Ok(0) + } + FixedPoolTokio::new(2, SimpleRM::new(cb, ())) + } +} diff --git a/wtx/src/pool/resource_manager.rs b/wtx/src/pool/resource_manager.rs new file mode 100644 index 00000000..06e7f7b4 --- /dev/null +++ b/wtx/src/pool/resource_manager.rs @@ -0,0 +1,216 @@ +use crate::misc::AsyncBounds; +use core::future::Future; + +/// Manager of a specific pool resource. +pub trait ResourceManager { + /// Auxiliary data used by the [Self::get] method. + type CreateAux; + /// Any custom error. + type Error: From; + /// Auxiliary data used by the [Self::recycle] method. + type RecycleAux; + /// Any pool resource. + type Resource; + + /// Creates a new resource instance based on the contents of this manager. + fn create( + &self, + aux: &Self::CreateAux, + ) -> impl AsyncBounds + Future>; + + /// If a resource is in an invalid state. + fn is_invalid(&self, resource: &Self::Resource) -> bool; + + /// Re-creates a new valid instance. Should be called if `resource` is invalid. + fn recycle( + &self, + aux: &Self::RecycleAux, + resource: &mut Self::Resource, + ) -> impl AsyncBounds + Future>; +} + +impl ResourceManager for () { + type CreateAux = (); + type Error = crate::Error; + type RecycleAux = (); + type Resource = (); + + async fn create(&self, _: &Self::CreateAux) -> Result { + Ok(()) + } + + fn is_invalid(&self, _: &Self::Resource) -> bool { + false + } + + async fn recycle(&self, _: &Self::RecycleAux, _: &mut Self::Resource) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Manages generic resources that are always valid and don't require logic for recycling. +#[derive(Debug)] +pub struct SimpleRM { + /// Create callback + pub cb: fn(&I) -> Result, + /// Input data + pub input: I, +} + +impl SimpleRM { + /// Shortcut constructor + #[inline] + pub fn new(cb: fn(&I) -> Result, input: I) -> Self { + Self { cb, input } + } +} + +impl ResourceManager for SimpleRM +where + E: From, + R: AsyncBounds, + for<'any> &'any Self: AsyncBounds, +{ + type CreateAux = (); + type Error = E; + type RecycleAux = (); + type Resource = R; + + #[inline] + async fn create(&self, _: &Self::CreateAux) -> Result { + (self.cb)(&self.input) + } + + #[inline] + fn is_invalid(&self, _: &Self::Resource) -> bool { + false + } + + #[inline] + async fn recycle(&self, _: &Self::RecycleAux, _: &mut Self::Resource) -> Result<(), Self::Error> { + Ok(()) + } +} + +#[cfg(feature = "postgres")] +pub(crate) mod database { + use crate::{ + database::client::postgres::{Executor, ExecutorBuffer}, + misc::AsyncBounds, + pool::ResourceManager, + }; + use core::{future::Future, mem}; + + /// Manages generic database executors. + #[derive(Debug)] + pub struct PostgresRM { + /// Create callback + pub cb: fn(&I) -> CF, + /// Input + pub input: I, + /// Recycle callback + pub rc: fn(&I, ExecutorBuffer) -> RF, + } + + impl ResourceManager for PostgresRM + where + CF: AsyncBounds + Future), E>>, + E: AsyncBounds + From, + O: AsyncBounds, + RF: AsyncBounds + Future, E>>, + S: AsyncBounds, + for<'any> &'any Self: AsyncBounds, + { + type CreateAux = (); + type Error = E; + type RecycleAux = (); + type Resource = (O, Executor); + + #[inline] + async fn create(&self, _: &Self::CreateAux) -> Result { + (self.cb)(&self.input).await + } + + #[inline] + fn is_invalid(&self, resource: &Self::Resource) -> bool { + resource.1.is_closed + } + + #[inline] + async fn recycle( + &self, + _: &Self::RecycleAux, + resource: &mut Self::Resource, + ) -> Result<(), Self::Error> { + let mut persistent = ExecutorBuffer::_empty(); + mem::swap(&mut persistent, &mut resource.1.eb); + resource.1 = (self.rc)(&self.input, persistent).await?; + Ok(()) + } + } +} + +#[cfg(feature = "http2")] +pub(crate) mod http2 { + use crate::{ + http2::{Http2Buffer, ReqResBuffer}, + pool::SimpleRM, + rng::Rng, + }; + + /// Manages HTTP/2 resources for clients. + pub type Http2ClientBufferRM = SimpleRM>; + /// Manages HTTP/2 resources for servers. + pub type Http2ServerBufferRM = SimpleRM>; + /// Manages resources for HTTP2 requests and responses. + pub type ReqResBufferRM = SimpleRM; + + type Http2RM = SimpleRM>; + + impl Http2RM + where + RNG: Clone + Rng, + { + /// Instance of [Http2ClientRM] or [Http2ServerRM]. + pub fn http2_buffer(rng: RNG) -> Self { + fn cb(rng: &RNG) -> crate::Result> + where + RNG: Clone + Rng, + { + Ok(Http2Buffer::new(rng.clone())) + } + Self { cb, input: rng } + } + } + + impl ReqResBufferRM { + /// Instance of [ReqResBufferRM]. + pub fn req_res_buffer() -> Self { + fn cb(_: &()) -> crate::Result { + Ok(ReqResBuffer::default()) + } + Self { cb, input: () } + } + } +} + +#[cfg(feature = "web-socket")] +pub(crate) mod web_socket { + use crate::{ + pool::SimpleRM, + web_socket::{FrameBufferVec, WebSocketBuffer}, + }; + + /// Manages WebSocket resources. + pub type WebSocketRM = SimpleRM; + + impl WebSocketRM { + /// Instance of [WebSocketRM]. + pub fn web_socket() -> Self { + fn cb(_: &()) -> crate::Result<(FrameBufferVec, WebSocketBuffer)> { + Ok((FrameBufferVec::default(), WebSocketBuffer::default())) + } + Self { cb, input: () } + } + } +} diff --git a/wtx/src/pool_manager.rs b/wtx/src/pool_manager.rs deleted file mode 100644 index 20b8fc1c..00000000 --- a/wtx/src/pool_manager.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! Pool Manager - -mod lock; -mod lock_guard; -mod resource_manager; -mod static_pool; - -use alloc::boxed::Box; -use core::future::Future; -pub use lock::Lock; -pub use lock_guard::LockGuard; -#[cfg(feature = "database")] -pub use resource_manager::database::PostgresRM; -#[cfg(feature = "web-socket")] -pub use resource_manager::web_socket::WebSocketRM; -pub use resource_manager::{ResourceManager, SimpleRM}; -pub use static_pool::*; - -/// A pool contains a set of resources that are behind some synchronism mechanism. -pub trait Pool: Sized { - /// Synchronization guard. - type Guard<'lock> - where - ::Resource: 'lock, - Self: 'lock; - - /// See [ResourceManager]. - type ResourceManager: ResourceManager; - - /// Initializes inner elements. - fn new(rm: Self::ResourceManager) -> crate::Result; - - /// Tries to retrieve a free resource. - /// - /// If the resource does not exist, a new one is created and if the pool is full, this method will - /// await until a free resource is available. - fn get<'this>( - &'this self, - ) -> impl Future, ::Error>> - where - ::Resource: 'this; -} - -impl Pool for Box -where - T: Pool, -{ - type Guard<'lock> = T::Guard<'lock> - where - ::Resource: 'lock, - Self: 'lock; - - type ResourceManager = T::ResourceManager; - - #[inline] - fn new(rm: Self::ResourceManager) -> crate::Result { - Ok(T::new(rm)?.into()) - } - - #[inline] - fn get<'this>( - &'this self, - ) -> impl Future, ::Error>> - where - ::Resource: 'this, - { - (**self).get() - } -} diff --git a/wtx/src/pool_manager/lock.rs b/wtx/src/pool_manager/lock.rs deleted file mode 100644 index 9984a0e4..00000000 --- a/wtx/src/pool_manager/lock.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::pool_manager::LockGuard; -use core::{ - cell::{RefCell, RefMut}, - future::{poll_fn, Future}, - task::Poll, -}; - -/// An asynchronous mutual exclusion primitive useful for protecting shared data. -pub trait Lock { - /// See [LockGuard]. - type Guard<'guard>: LockGuard<'guard, T> - where - Self: 'guard; - - /// Generic way to build a lock. - fn new(resource: T) -> Self; - - /// Locks this element, causing the current task to yield until the lock has been acquired. When - /// the lock has been acquired, returns a guard. - fn lock(&self) -> impl Future>; -} - -impl Lock for RefCell { - type Guard<'guard> = RefMut<'guard, T> - where - Self: 'guard; - - #[inline] - fn new(resource: T) -> Self { - RefCell::new(resource) - } - - #[inline] - fn lock(&self) -> impl Future> { - poll_fn( - |_| { - if let Ok(elem) = self.try_borrow_mut() { - Poll::Ready(elem) - } else { - Poll::Pending - } - }, - ) - } -} - -#[cfg(feature = "tokio")] -mod tokio { - use crate::pool_manager::Lock; - use tokio::sync::{Mutex, MutexGuard}; - - impl Lock for Mutex { - type Guard<'guard> = MutexGuard<'guard, T> - where - Self: 'guard; - - #[inline] - fn new(resource: T) -> Self { - Mutex::new(resource) - } - - #[inline] - async fn lock(&self) -> Self::Guard<'_> { - (*self).lock().await - } - } -} diff --git a/wtx/src/pool_manager/lock_guard.rs b/wtx/src/pool_manager/lock_guard.rs deleted file mode 100644 index 6347445e..00000000 --- a/wtx/src/pool_manager/lock_guard.rs +++ /dev/null @@ -1,57 +0,0 @@ -use core::{cell::RefMut, ops::DerefMut}; - -/// A handle to a held lock. -pub trait LockGuard<'guard, T>: DerefMut -where - T: ?Sized, -{ - /// Sometimes it is desirable to return a type that differs from the original lock. - type Mapped - where - U: ?Sized + 'guard; - - /// Makes a new mapped element for a component of the locked data. - fn map(this: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped - where - U: ?Sized; -} - -impl<'guard, T> LockGuard<'guard, T> for RefMut<'guard, T> -where - T: ?Sized, -{ - type Mapped = RefMut<'guard, U> - where - U: ?Sized + 'guard; - - #[inline] - fn map(this: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped - where - U: ?Sized, - { - RefMut::map(this, f) - } -} - -#[cfg(feature = "tokio")] -mod tokio { - use crate::pool_manager::LockGuard; - use tokio::sync::{MappedMutexGuard, MutexGuard}; - - impl<'guard, T> LockGuard<'guard, T> for MutexGuard<'guard, T> - where - T: ?Sized, - { - type Mapped = MappedMutexGuard<'guard, U> - where - U: ?Sized + 'guard; - - #[inline] - fn map(this: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped - where - U: ?Sized, - { - MutexGuard::map(this, f) - } - } -} diff --git a/wtx/src/pool_manager/resource_manager.rs b/wtx/src/pool_manager/resource_manager.rs deleted file mode 100644 index ede30988..00000000 --- a/wtx/src/pool_manager/resource_manager.rs +++ /dev/null @@ -1,145 +0,0 @@ -use core::future::Future; - -/// Manager of a specific pool resource. -pub trait ResourceManager { - /// Any custom error. - type Error; - /// Any pool resource. - type Resource; - - /// Creates a new resource instance based on the contents of this manager. - fn create(&self) -> impl Future>; - - /// If a resource is in an invalid state. - fn is_invalid(&self, resource: &Self::Resource) -> bool; - - /// Re-creates a new valid instance. Should be called if `resource` is invalid. - fn recycle(&self, resource: &mut Self::Resource) - -> impl Future>; -} - -impl ResourceManager for () { - type Error = crate::Error; - type Resource = (); - - async fn create(&self) -> Result { - Ok(()) - } - - fn is_invalid(&self, _: &Self::Resource) -> bool { - false - } - - async fn recycle(&self, _: &mut Self::Resource) -> Result<(), Self::Error> { - Ok(()) - } -} - -/// Manages generic resources that are always valid and don't require logic for recycling. -#[derive(Debug)] -pub struct SimpleRM { - /// Create callback - pub cb: fn(&I) -> Result, - /// Input data - pub input: I, -} - -impl SimpleRM { - /// Shortcut constructor - #[inline] - pub fn new(cb: fn(&I) -> Result, input: I) -> Self { - Self { cb, input } - } -} - -impl ResourceManager for SimpleRM -where - E: From, -{ - type Error = E; - type Resource = R; - - #[inline] - async fn create(&self) -> Result { - (self.cb)(&self.input) - } - - #[inline] - fn is_invalid(&self, _: &Self::Resource) -> bool { - false - } - - #[inline] - async fn recycle(&self, _: &mut Self::Resource) -> Result<(), Self::Error> { - Ok(()) - } -} - -#[cfg(feature = "postgres")] -pub(crate) mod database { - use crate::{ - database::client::postgres::{Executor, ExecutorBuffer}, - pool_manager::ResourceManager, - }; - use core::{future::Future, mem}; - - /// Manages generic database executors. - #[derive(Debug)] - pub struct PostgresRM { - /// Create callback - pub cb: fn(&I) -> CF, - /// Input - pub input: I, - /// Recycle callback - pub rc: fn(&I, ExecutorBuffer) -> RF, - } - - impl ResourceManager for PostgresRM - where - CF: Future), E>>, - E: From, - RF: Future, E>>, - { - type Error = E; - type Resource = (O, Executor); - - #[inline] - async fn create(&self) -> Result { - (self.cb)(&self.input).await - } - - #[inline] - fn is_invalid(&self, resource: &Self::Resource) -> bool { - resource.1.is_closed - } - - #[inline] - async fn recycle(&self, resource: &mut Self::Resource) -> Result<(), Self::Error> { - let mut persistent = ExecutorBuffer::_empty(); - mem::swap(&mut persistent, &mut resource.1.eb); - resource.1 = (self.rc)(&self.input, persistent).await?; - Ok(()) - } - } -} - -#[cfg(feature = "web-socket")] -pub(crate) mod web_socket { - use crate::{ - pool_manager::SimpleRM, - web_socket::{FrameBufferVec, WebSocketBuffer}, - }; - - /// Manages WebSocket resources. - pub type WebSocketRM = SimpleRM; - - impl WebSocketRM { - /// Instance of [WebSocketRM]. - pub fn web_socket_rm() -> WebSocketRM { - fn cb(_: &()) -> crate::Result<(FrameBufferVec, WebSocketBuffer)> { - Ok(<_>::default()) - } - Self { cb, input: () } - } - } -} diff --git a/wtx/src/pool_manager/static_pool.rs b/wtx/src/pool_manager/static_pool.rs deleted file mode 100644 index 3b4b1c0e..00000000 --- a/wtx/src/pool_manager/static_pool.rs +++ /dev/null @@ -1,135 +0,0 @@ -use crate::{ - misc::{PollOnce, _unreachable}, - pool_manager::{Lock, LockGuard, Pool, ResourceManager}, -}; -use alloc::collections::VecDeque; -use core::{ - array, - cell::RefCell, - pin::pin, - sync::atomic::{AtomicUsize, Ordering}, -}; - -/// A [StaticPool] synchronized by [RefCell]. -pub type StaticPoolRefCell = StaticPool>, RM, N>; -/// A [StaticPool] synchronized by [tokio::sync::Mutex]. -#[cfg(feature = "tokio")] -pub type StaticPoolTokioMutex = - StaticPool>, RM, N>; - -/// Fixed-size pool that does not require the use of reference-counters. On the other hand, all -/// elements of the pool **must** be of type `Option` to lazily evaluate resources. -/// -/// If stack memory becomes a problem, try heap allocation. -#[derive(Debug)] -pub struct StaticPool { - idx: AtomicUsize, - locks: [RL; N], - rm: RM, -} - -impl StaticPool -where - RL: Lock>, - RM: ResourceManager, -{ - /// Sometimes it is desired to eagerly initialize all instances. - pub async fn init_all(&self) -> Result<(), RM::Error> { - for _ in 0..N { - let _guard = self.get().await?; - } - Ok(()) - } -} - -impl Pool for StaticPool -where - RL: Lock>, - RM: ResourceManager, -{ - type Guard<'lock> = as LockGuard<'lock, Option>>::Mapped - where - ::Resource: 'lock, - Self: 'lock; - - type ResourceManager = RM; - - #[inline] - fn new(rm: RM) -> crate::Result { - if N == 0 { - return Err(crate::Error::StaticPoolMustHaveCapacityForAtLeastOneElement); - } - let mut available = VecDeque::new(); - available.extend(0..N); - Ok(Self { idx: AtomicUsize::new(0), locks: array::from_fn(|_| RL::new(None)), rm }) - } - - #[inline] - async fn get<'this>(&'this self) -> Result, RM::Error> - where - ::Resource: 'this, - { - loop { - #[allow( - // `N` will never be zero - clippy::arithmetic_side_effects, - )] - let local_idx = self.idx.fetch_add(1, Ordering::Release) % N; - let lock = match self.locks.get(local_idx) { - Some(elem) => elem, - None => _unreachable(), - }; - if let Some(mut guard) = PollOnce(pin!(lock.lock())).await { - match &mut *guard { - None => { - *guard = Some(self.rm.create().await?); - } - Some(resource) if self.rm.is_invalid(resource) => { - self.rm.recycle(resource).await?; - } - _ => {} - } - return Ok(LockGuard::map(guard, |el| match el.as_mut() { - Some(elem) => elem, - None => _unreachable(), - })); - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::pool_manager::{static_pool::StaticPoolRefCell, Pool, SimpleRM}; - - #[tokio::test] - async fn modifies_elements() { - let pool = pool(); - assert_eq!([*pool.get().await.unwrap(), *pool.get().await.unwrap()], [0, 0]); - *pool.get().await.unwrap() = 1; - *pool.get().await.unwrap() = 2; - assert_eq!([*pool.get().await.unwrap(), *pool.get().await.unwrap()], [1, 2]); - *pool.get().await.unwrap() = 3; - assert_eq!([*pool.get().await.unwrap(), *pool.get().await.unwrap()], [2, 3]); - } - - #[tokio::test] - async fn held_lock_is_not_modified() { - let pool = pool(); - let lock = pool.get().await.unwrap(); - *pool.get().await.unwrap() = 1; - assert_eq!([*lock, *pool.get().await.unwrap()], [0, 1]); - *pool.get().await.unwrap() = 2; - assert_eq!([*lock, *pool.get().await.unwrap()], [0, 2]); - drop(lock); - *pool.get().await.unwrap() = 1; - assert_eq!([*pool.get().await.unwrap(), *pool.get().await.unwrap()], [2, 1]); - } - - fn pool() -> StaticPoolRefCell, 2> { - fn cb(_: &()) -> crate::Result { - Ok(0) - } - StaticPoolRefCell::new(SimpleRM::new(cb, ())).unwrap() - } -} diff --git a/wtx/src/rng.rs b/wtx/src/rng.rs index fe6623bc..d7c0ba14 100644 --- a/wtx/src/rng.rs +++ b/wtx/src/rng.rs @@ -50,7 +50,7 @@ where /// The number generation is done using a simple XOR strategy. /// /// You probably shouldn't use this structure in a production environment. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct StaticRng(u64); impl Rng for StaticRng { @@ -80,7 +80,6 @@ impl Default for StaticRng { let elem = Box::new(Foo { _bar: 1, _baz: 2 }); let ref_ptr = ptr::addr_of!(elem).cast(); // SAFETY: Memory validation is not relevant - #[allow(unsafe_code)] let n: usize = unsafe { *ref_ptr }; if n == 0 { return Self(u64::from_be_bytes([55, 120, 216, 218, 191, 63, 200, 169])); diff --git a/wtx/src/rng/fastrand.rs b/wtx/src/rng/fastrand.rs index 1e83b2b0..11b1b415 100644 --- a/wtx/src/rng/fastrand.rs +++ b/wtx/src/rng/fastrand.rs @@ -1,37 +1,16 @@ -use core::ops::RangeInclusive; - -const RANGE: RangeInclusive = 0..=255; - impl crate::rng::Rng for fastrand::Rng { #[inline] fn u8(&mut self) -> u8 { - self.u8(RANGE) + self.u8(0..=u8::MAX) } #[inline] fn u8_4(&mut self) -> [u8; 4] { - [self.u8(RANGE), self.u8(RANGE), self.u8(RANGE), self.u8(RANGE)] + self.u32(0..=u32::MAX).to_be_bytes() } #[inline] fn u8_16(&mut self) -> [u8; 16] { - [ - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - self.u8(RANGE), - ] + self.u128(0..=u128::MAX).to_be_bytes() } } diff --git a/wtx/src/rng/rand.rs b/wtx/src/rng/rand.rs index 42c17a87..a8bba68c 100644 --- a/wtx/src/rng/rand.rs +++ b/wtx/src/rng/rand.rs @@ -5,17 +5,17 @@ macro_rules! implement { impl crate::rng::Rng for $struct { #[inline] fn u8(&mut self) -> u8 { - self.gen() + self.r#gen() } #[inline] fn u8_4(&mut self) -> [u8; 4] { - self.gen() + self.r#gen() } #[inline] fn u8_16(&mut self) -> [u8; 16] { - self.gen() + self.r#gen() } } }; diff --git a/wtx/src/rng/std.rs b/wtx/src/rng/std.rs index 3a5307df..9c789c65 100644 --- a/wtx/src/rng/std.rs +++ b/wtx/src/rng/std.rs @@ -5,7 +5,7 @@ use std::{ }; /// Derived from the tools provided by the standard library, uses a simple XOR strategy. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct StdRng(u64); impl Rng for StdRng { diff --git a/wtx/src/web_socket.rs b/wtx/src/web_socket.rs index 8473e06e..5b66171a 100644 --- a/wtx/src/web_socket.rs +++ b/wtx/src/web_socket.rs @@ -22,8 +22,8 @@ mod web_socket_buffer; use crate::web_socket::misc::Role; use crate::{ misc::{ - from_utf8_basic, from_utf8_ext, CompletionErr, ExtUtf8Error, IncompleteUtf8Char, - PartitionedFilledBuffer, Stream, _read_until, + from_utf8_basic, from_utf8_ext, CompletionErr, ConnectionState, ExtUtf8Error, + IncompleteUtf8Char, Lease, LeaseMut, PartitionedFilledBuffer, Stream, _read_until, }, rng::Rng, web_socket::{ @@ -35,7 +35,7 @@ use crate::{ use alloc::vec::Vec; pub use close_code::CloseCode; pub use compression::{Compression, CompressionLevel, DeflateConfig}; -use core::{borrow::BorrowMut, ops::Range}; +use core::ops::Range; pub use frame::{ Frame, FrameControlArray, FrameControlArrayMut, FrameMut, FrameMutControlArray, FrameMutControlArrayMut, FrameMutMut, FrameMutVec, FrameMutVecMut, FrameVec, FrameVecMut, @@ -83,9 +83,11 @@ type ReadContinuationFramesCbs = ( ); /// WebSocket protocol implementation over an asynchronous stream. +/// +/// #[derive(Debug)] pub struct WebSocket { - is_stream_closed: bool, + ct: ConnectionState, max_payload_len: usize, nc: NC, rng: RNG, @@ -107,15 +109,15 @@ where NC: NegotiatedCompression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { /// Creates a new instance from a stream that supposedly has already completed the WebSocket /// handshake. #[inline] pub fn new(nc: NC, rng: RNG, stream: S, mut wsb: WSB) -> Self { - wsb.borrow_mut().nb._clear_if_following_is_empty(); - wsb.borrow_mut().nb._expand_following(MAX_HDR_LEN_USIZE); - Self { is_stream_closed: false, max_payload_len: _MAX_PAYLOAD_LEN, nc, rng, stream, wsb } + wsb.lease_mut().nb._clear_if_following_is_empty(); + wsb.lease_mut().nb._expand_following(MAX_HDR_LEN_USIZE); + Self { ct: ConnectionState::Open, max_payload_len: _MAX_PAYLOAD_LEN, nc, rng, stream, wsb } } /// Reads a frame from the stream. @@ -128,7 +130,7 @@ where fb: &'fb mut FrameBuffer, ) -> crate::Result, IS_CLIENT>> where - B: AsMut<[u8]> + AsMut> + AsRef<[u8]>, + B: LeaseMut<[u8]> + LeaseMut>, { fb.clear(); let header_buffer_len = header_placeholder::(); @@ -155,14 +157,12 @@ where ) .await?; let payload_len = Self::copy_from_compressed_db_to_fb( - &mut self.wsb.borrow_mut().db, + &mut self.wsb.lease_mut().db, fb, &mut self.nc, payload_start_idx, )?; - let payload = fb - .buffer() - .as_ref() + let payload = lease_as_slice(fb.buffer()) .get(payload_start_idx..payload_start_idx.wrapping_add(payload_len)) .unwrap_or_default(); if matches!(first_rfi.op_code, OpCode::Text) && from_utf8_basic(payload).is_err() { @@ -219,14 +219,14 @@ where #[inline] pub async fn write_frame(&mut self, frame: &mut Frame) -> crate::Result<()> where - B: AsMut<[u8]> + AsRef<[u8]>, - FB: BorrowMut>, + B: LeaseMut<[u8]>, + FB: LeaseMut>, { Self::do_write_frame( + &mut self.ct, frame, - &mut self.is_stream_closed, &mut self.nc, - &mut self.wsb.borrow_mut().nb, + &mut self.wsb.lease_mut().nb, &mut self.rng, &mut self.stream, ) @@ -236,9 +236,9 @@ where fn begin_fb_bytes_mut(fb: &mut FrameBuffer, payload_start_idx: usize) -> &mut [u8] where - B: AsMut<[u8]> + AsMut>, + B: LeaseMut<[u8]> + LeaseMut>, { - AsMut::<[u8]>::as_mut(fb.buffer_mut()).get_mut(payload_start_idx..).unwrap_or_default() + LeaseMut::<[u8]>::lease_mut(fb.buffer_mut()).get_mut(payload_start_idx..).unwrap_or_default() } fn compress_frame<'pb, B, FB>( @@ -247,8 +247,8 @@ where pb: &'pb mut PartitionedFilledBuffer, ) -> crate::Result> where - B: AsMut<[u8]> + AsRef<[u8]>, - FB: BorrowMut>, + B: LeaseMut<[u8]>, + FB: LeaseMut>, { fn expand_pb<'pb, B>( len_with_header: usize, @@ -257,14 +257,14 @@ where written: usize, ) -> &'pb mut [u8] where - B: AsRef<[u8]>, + B: Lease<[u8]>, { let start = len_with_header.wrapping_add(written); local_pb._expand_following(start.wrapping_add(local_fb.frame().len()).wrapping_add(128)); local_pb._following_trail_mut().get_mut(start..).unwrap_or_default() } - let fb = frame.fb().borrow(); + let fb = frame.fb().lease(); let len = pb._following_trail_mut().len(); let len_with_header = len.wrapping_add(fb.header().len()); let mut payload_len = nc.compress( @@ -300,12 +300,12 @@ where payload_start_idx: usize, ) -> crate::Result where - B: AsMut<[u8]> + AsMut> + AsRef<[u8]>, + B: LeaseMut<[u8]> + LeaseMut>, { db.push_bytes(DECOMPRESSION_SUFFIX); let mut buffer_len = payload_start_idx .checked_add(db.len()) - .map(|element| element.max(fb.buffer().as_ref().len())); + .map(|element| element.max(lease_as_slice(fb.buffer()).len())); let payload_size = nc.decompress( db.get(payload_start_idx..).unwrap_or_default(), fb, @@ -343,11 +343,11 @@ where rfi: &ReadFrameInfo, ) -> crate::Result where - B: AsMut<[u8]> + AsMut> + AsRef<[u8]>, + B: LeaseMut<[u8]> + LeaseMut>, { let mut buffer_len = payload_start_idx .checked_add(rfi.payload_len) - .map(|element| element.max(fb.buffer().as_ref().len())); + .map(|element| element.max(lease_as_slice(fb.buffer()).len())); let payload_len = Self::copy_from_pb(fb, pb, rfi, |local_pb, local_fb| { local_pb._expand_buffer(local_pb._buffer().len().wrapping_add(4)); let curr_end_idx = local_pb._current().len(); @@ -406,7 +406,7 @@ where <&str>::from(Role::from_is_client(IS_CLIENT)), "Read", "Masked", - crate::web_socket::misc::truncated_slice(pb._current(), 0..32), + misc::truncated_slice(pb._current(), 0..32), rfi.op_code ); @@ -422,12 +422,12 @@ where <&str>::from(Role::from_is_client(IS_CLIENT)), "Read", "Unmasked", - crate::web_socket::misc::truncated_slice(pb._current(), 0..32), + misc::truncated_slice(pb._current(), 0..32), rfi.op_code ); let rslt = cb(pb, output)?; - pb.borrow_mut()._clear_if_following_is_empty(); + pb._clear_if_following_is_empty(); Ok(rslt) } @@ -440,12 +440,12 @@ where rfi: &ReadFrameInfo, ) -> crate::Result where - B: AsMut<[u8]> + AsMut>, + B: LeaseMut<[u8]> + LeaseMut>, { Self::copy_from_pb(fb, pb, rfi, |local_pb, local_fb| { let n = payload_start_idx.saturating_add(rfi.payload_len); local_fb.expand_buffer(n); - AsMut::<[u8]>::as_mut(local_fb.buffer_mut()) + LeaseMut::<[u8]>::lease_mut(local_fb.buffer_mut()) .get_mut(payload_start_idx..n) .unwrap_or_default() .copy_from_slice(local_pb._current().get(rfi.header_end_idx..).unwrap_or_default()); @@ -461,33 +461,33 @@ where should_use_db: bool, ) -> &'bytes [u8] where - B: AsMut<[u8]> + AsMut> + AsRef<[u8]>, + B: LeaseMut<[u8]>, { if should_use_db { db.get(range).unwrap_or_default() } else { - fb.buffer().as_ref().get(range).unwrap_or_default() + fb.buffer().lease().get(range).unwrap_or_default() } } async fn do_write_frame( + ct: &mut ConnectionState, frame: &mut Frame, - is_stream_closed: &mut bool, nc: &mut NC, pb: &mut PartitionedFilledBuffer, rng: &mut RNG, stream: &mut S, ) -> crate::Result<()> where - B: AsMut<[u8]> + AsRef<[u8]>, - FB: BorrowMut>, + B: LeaseMut<[u8]>, + FB: LeaseMut>, { let mut should_compress = false; if frame.op_code() == OpCode::Close { - *is_stream_closed = true; + *ct = ConnectionState::Closed; } if !frame.op_code().is_control() { - if let Some(first) = frame.fb_mut().borrow_mut().header_mut().first_mut() { + if let Some(first) = frame.fb_mut().lease_mut().header_mut().first_mut() { should_compress = nc.rsv1() != 0; *first |= nc.rsv1(); } @@ -498,7 +498,7 @@ where <&str>::from(Role::from_is_client(IS_CLIENT)), "Write", "Unmasked", - crate::web_socket::misc::truncated_slice(frame.fb().borrow().frame(), 0..32), + misc::truncated_slice(frame.fb().lease().frame(), 0..32), frame.op_code() ); Self::mask_frame(frame, rng); @@ -507,17 +507,17 @@ where <&str>::from(Role::from_is_client(IS_CLIENT)), "Write", "Masked", - crate::web_socket::misc::truncated_slice(frame.fb().borrow().frame(), 0..32), + misc::truncated_slice(frame.fb().lease().frame(), 0..32), frame.op_code() ); - stream.write_all(frame.fb().borrow().frame()).await?; + stream.write_all(frame.fb().lease().frame()).await?; } else { _debug!( "{:<5} - {:<5} - {:<25}: {:?}, {:?}", <&str>::from(Role::from_is_client(IS_CLIENT)), "Write", "Uncompressed, Unmasked", - crate::web_socket::misc::truncated_slice(frame.fb().borrow().frame(), 0..32), + misc::truncated_slice(frame.fb().lease().frame(), 0..32), frame.op_code() ); let mut compressed_frame = Self::compress_frame(frame, nc, pb)?; @@ -526,7 +526,7 @@ where <&str>::from(Role::from_is_client(IS_CLIENT)), "Write", "Compressed, Unmasked", - crate::web_socket::misc::truncated_slice(compressed_frame.fb().frame(), 0..32), + misc::truncated_slice(compressed_frame.fb().frame(), 0..32), frame.op_code() ); Self::mask_frame(&mut compressed_frame, rng); @@ -535,7 +535,7 @@ where <&str>::from(Role::from_is_client(IS_CLIENT)), "Write", "Compressed, Masked", - crate::web_socket::misc::truncated_slice(compressed_frame.fb().frame(), 0..32), + misc::truncated_slice(compressed_frame.fb().frame(), 0..32), frame.op_code() ); stream.write_all(compressed_frame.fb().frame()).await?; @@ -550,7 +550,7 @@ where written: usize, ) -> &'fb mut [u8] where - B: AsMut<[u8]> + AsMut>, + B: LeaseMut<[u8]> + LeaseMut>, { *buffer_len = buffer_len.and_then(|el| el.checked_mul(15)?.checked_div(10)); fb.expand_buffer(buffer_len.unwrap_or(usize::MAX)); @@ -559,27 +559,27 @@ where } async fn fetch_frame_from_stream(&mut self) -> crate::Result { - let mut read = self.wsb.borrow_mut().nb._following_len(); + let mut read = self.wsb.lease_mut().nb._following_len(); let rfi = Self::fetch_header_from_stream( self.max_payload_len, &self.nc, - &mut self.wsb.borrow_mut().nb, + &mut self.wsb.lease_mut().nb, &mut read, &mut self.stream, ) .await?; - if self.is_stream_closed && rfi.op_code != OpCode::Close { + if self.ct.is_closed() && rfi.op_code != OpCode::Close { return Err(crate::Error::ConnectionClosed); } Self::fetch_payload_from_stream( - &mut self.wsb.borrow_mut().nb, + &mut self.wsb.lease_mut().nb, &mut read, &rfi, &mut self.stream, ) .await?; - let current_end_idx = self.wsb.borrow().nb._current_end_idx(); - self.wsb.borrow_mut().nb._set_indices( + let current_end_idx = self.wsb.lease().nb._current_end_idx(); + self.wsb.lease_mut().nb._set_indices( current_end_idx, rfi.frame_len, read.wrapping_sub(rfi.frame_len), @@ -681,7 +681,7 @@ where /// If this method returns `false`, then a `ping` frame was received and the caller should fetch /// more external data in order to get the desired frame. async fn manage_auto_reply( - is_stream_closed: &mut bool, + ct: &mut ConnectionState, curr_payload: &[u8], nc: &mut NC, op_code: OpCode, @@ -690,7 +690,7 @@ where stream: &mut S, ) -> crate::Result { match op_code { - OpCode::Close if !*is_stream_closed => { + OpCode::Close if ct.is_open() => { match curr_payload { [] => {} [_] => return Err(crate::Error::InvalidCloseFrame), @@ -699,12 +699,12 @@ where let is_not_allowed = !CloseCode::try_from(u16::from_be_bytes([*a, *b]))?.is_allowed(); if is_not_allowed || rest.len() > MAX_CONTROL_FRAME_PAYLOAD_LEN - 2 { Self::write_control_frame( + ct, &mut FrameControlArray::close_from_params( CloseCode::Protocol, - <_>::default(), + FrameBuffer::default(), rest, )?, - is_stream_closed, nc, pb, rng, @@ -716,8 +716,8 @@ where } } Self::write_control_frame( - &mut FrameControlArray::new_fin(<_>::default(), OpCode::Close, curr_payload)?, - is_stream_closed, + ct, + &mut FrameControlArray::new_fin(FrameBuffer::default(), OpCode::Close, curr_payload)?, nc, pb, rng, @@ -728,8 +728,8 @@ where } OpCode::Ping => { Self::write_control_frame( - &mut FrameControlArray::new_fin(<_>::default(), OpCode::Pong, curr_payload)?, - is_stream_closed, + ct, + &mut FrameControlArray::new_fin(FrameBuffer::default(), OpCode::Pong, curr_payload)?, nc, pb, rng, @@ -777,11 +777,11 @@ where fn mask_frame(frame: &mut Frame, rng: &mut RNG) where - B: AsMut<[u8]> + AsRef<[u8]>, - FB: BorrowMut>, + B: LeaseMut<[u8]>, + FB: LeaseMut>, { if IS_CLIENT { - if let [_, second_byte, .., a, b, c, d] = frame.fb_mut().borrow_mut().header_mut() { + if let [_, second_byte, .., a, b, c, d] = frame.fb_mut().lease_mut().header_mut() { if !has_masked_frame(*second_byte) { *second_byte |= 0b1000_0000; let mask = rng.u8_4(); @@ -789,7 +789,7 @@ where *b = mask[1]; *c = mask[2]; *d = mask[3]; - unmask(frame.fb_mut().borrow_mut().payload_mut(), mask); + unmask(frame.fb_mut().lease_mut().payload_mut(), mask); } } } @@ -804,16 +804,16 @@ where (first_text_cb, continuation_cb, copy_cb): ReadContinuationFramesCbs, ) -> crate::Result<()> where - B: AsMut<[u8]> + AsMut> + AsRef<[u8]>, + B: LeaseMut<[u8]> + LeaseMut>, { let mut iuc = { let (should_use_db, payload_len) = - copy_cb(fb, first_rfi, *total_frame_len, self.wsb.borrow_mut())?; + copy_cb(fb, first_rfi, *total_frame_len, self.wsb.lease_mut())?; *total_frame_len = total_frame_len.wrapping_add(payload_len); match first_rfi.op_code { OpCode::Binary => None, OpCode::Text => first_text_cb(Self::curr_payload_bytes( - &self.wsb.borrow().db, + &self.wsb.lease().db, fb, payload_start_idx..*total_frame_len, should_use_db, @@ -829,12 +829,12 @@ where let mut rfi = self.fetch_frame_from_stream().await?; rfi.should_decompress = first_rfi.should_decompress; let (should_use_db, payload_len) = - copy_cb(fb, &rfi, *total_frame_len, self.wsb.borrow_mut())?; + copy_cb(fb, &rfi, *total_frame_len, self.wsb.lease_mut())?; *total_frame_len = total_frame_len.wrapping_add(payload_len); - let (db, nb) = self.wsb.borrow_mut().parts_mut(); + let (db, nb) = self.wsb.lease_mut().parts_mut(); let curr_payload = Self::curr_payload_bytes(db, fb, prev..*total_frame_len, should_use_db); if Self::manage_auto_reply( - &mut self.is_stream_closed, + &mut self.ct, curr_payload, &mut self.nc, rfi.op_code, @@ -873,14 +873,14 @@ where payload_start_idx: usize, ) -> crate::Result> where - B: AsMut<[u8]> + AsMut> + AsRef<[u8]>, + B: LeaseMut<[u8]> + LeaseMut>, { let first_rfi = 'auto_reply: loop { let rfi = self.fetch_frame_from_stream().await?; if !rfi.fin { break 'auto_reply rfi; } - let pb = &mut self.wsb.borrow_mut().nb; + let pb = &mut self.wsb.lease_mut().nb; let payload_len = if rfi.should_decompress { Self::copy_from_compressed_pb_to_fb(fb, &mut self.nc, payload_start_idx, pb, &rfi)? } else { @@ -895,11 +895,11 @@ where self.nc.rsv1(), )?; let should_stop = Self::manage_auto_reply( - &mut self.is_stream_closed, + &mut self.ct, fb.payload(), &mut self.nc, rfi.op_code, - &mut self.wsb.borrow_mut().nb, + &mut self.wsb.lease_mut().nb, &mut self.rng, &mut self.stream, ) @@ -921,14 +921,14 @@ where } async fn write_control_frame( + ct: &mut ConnectionState, frame: &mut FrameControlArray, - is_stream_closed: &mut bool, nc: &mut NC, pb: &mut PartitionedFilledBuffer, rng: &mut RNG, stream: &mut S, ) -> crate::Result<()> { - Self::do_write_frame(frame, is_stream_closed, nc, pb.borrow_mut(), rng, stream).await?; + Self::do_write_frame(ct, frame, nc, pb, rng, stream).await?; Ok(()) } } @@ -956,3 +956,10 @@ const fn header_placeholder() -> u8 { MAX_HDR_LEN_U8 - 4 } } + +fn lease_as_slice(instance: &T) -> &[U] +where + T: Lease<[U]>, +{ + instance.lease() +} diff --git a/wtx/src/web_socket/close_code.rs b/wtx/src/web_socket/close_code.rs index da877868..5429cf51 100644 --- a/wtx/src/web_socket/close_code.rs +++ b/wtx/src/web_socket/close_code.rs @@ -27,7 +27,9 @@ pub enum CloseCode { Restart, /// Server is busy and the client should reconnect. Again, - #[doc(hidden)] + /// MUST NOT be set as a status code in a close control frame by an endpoint. It is designated + /// for use in applications expecting a status code to indicate that the connection was closed + /// due to a failure to perform a TLS handshake Tls, /// Spaces without meaning reserved by the specification. Reserved(u16), diff --git a/wtx/src/web_socket/compression/flate2.rs b/wtx/src/web_socket/compression/flate2.rs index 80560cc1..5261e120 100644 --- a/wtx/src/web_socket/compression/flate2.rs +++ b/wtx/src/web_socket/compression/flate2.rs @@ -44,7 +44,7 @@ impl Compression for Flate2 { let mut client_max_window_bits_flag = false; let mut permessage_deflate_flag = false; let mut server_max_window_bits_flag = false; - for param in bytes_split1(permessage_deflate_option, b';').map(|elem| _trim_bytes(elem)) { + for param in bytes_split1(permessage_deflate_option, b';').map(_trim_bytes) { if param == b"client_no_context_takeover" || param == b"server_no_context_takeover" { } else if param == b"permessage-deflate" { _manage_header_uniqueness(&mut permessage_deflate_flag, || Ok(()))? @@ -63,11 +63,11 @@ impl Compression for Flate2 { Ok(()) })?; } else { - return Err(crate::Error::InvalidCompressionHeaderParameter.into()); + return Err(crate::Error::InvalidCompressionHeaderParameter); } } if !permessage_deflate_flag { - return Err(crate::Error::InvalidCompressionHeaderParameter.into()); + return Err(crate::Error::InvalidCompressionHeaderParameter); } has_extension = true; } diff --git a/wtx/src/web_socket/frame.rs b/wtx/src/web_socket/frame.rs index c82505eb..9c609aa3 100644 --- a/wtx/src/web_socket/frame.rs +++ b/wtx/src/web_socket/frame.rs @@ -1,5 +1,5 @@ use crate::{ - misc::SingleTypeStorage, + misc::{Lease, LeaseMut, SingleTypeStorage}, web_socket::{ close_code::CloseCode, frame_buffer::{ @@ -10,10 +10,7 @@ use crate::{ MIN_HEADER_LEN_USIZE, }, }; -use core::{ - borrow::{Borrow, BorrowMut}, - str, -}; +use core::str; /// Composed by a [FrameBufferControlArray]. pub type FrameControlArray = Frame; @@ -76,13 +73,13 @@ impl Frame { impl Frame where - B: AsRef<[u8]>, - FB: Borrow> + SingleTypeStorage, + B: Lease<[u8]>, + FB: Lease> + SingleTypeStorage, { /// Creates a new instance based on the contained bytes of `fb`. #[inline] pub fn from_fb(fb: FB) -> crate::Result { - let header = fb.borrow().header(); + let header = fb.lease().header(); let len = header.len(); let has_valid_header = (MIN_HEADER_LEN_USIZE..=MAX_HDR_LEN_USIZE).contains(&len); let (true, Some(first_header_byte)) = (has_valid_header, header.first().copied()) else { @@ -98,19 +95,16 @@ where B: 'this, { self.op_code.is_text().then(|| { - #[allow(unsafe_code)] // SAFETY: UTF-8 data is always verified when read from a stream. - unsafe { - str::from_utf8_unchecked(self.fb.borrow().payload()) - } + unsafe { str::from_utf8_unchecked(self.fb.lease().payload()) } }) } } impl Frame where - B: AsMut<[u8]> + AsRef<[u8]> + Expand, - FB: BorrowMut> + SingleTypeStorage, + B: Expand + LeaseMut<[u8]>, + FB: LeaseMut> + SingleTypeStorage, { /// Creates based on the individual parameters that compose a close frame. /// @@ -120,7 +114,7 @@ where let reason_len = reason.len().min(MAX_CONTROL_FRAME_PAYLOAD_LEN - 2); let payload_len = reason_len.wrapping_add(2); Self::build_frame(fb, true, OpCode::Close, payload_len, |local_fb| { - let payload = local_fb.borrow_mut().payload_mut(); + let payload = local_fb.lease_mut().payload_mut(); payload.get_mut(..2).unwrap_or_default().copy_from_slice(&u16::from(code).to_be_bytes()); payload .get_mut(2..) @@ -149,10 +143,10 @@ where payload_len: usize, cb: impl FnOnce(&mut FB) -> crate::Result<()>, ) -> crate::Result { - fb.borrow_mut().clear(); - fb.borrow_mut().buffer_mut().expand(MAX_HDR_LEN_USIZE.saturating_add(payload_len)); + fb.lease_mut().clear(); + fb.lease_mut().buffer_mut().expand(MAX_HDR_LEN_USIZE.saturating_add(payload_len)); define_fb_from_header_params::<_, IS_CLIENT>( - fb.borrow_mut(), + fb.lease_mut(), fin, None, op_code, @@ -171,7 +165,7 @@ where }; Self::build_frame(fb, fin, op_code, payload_len, |local_fb| { local_fb - .borrow_mut() + .lease_mut() .payload_mut() .copy_from_slice(payload.get(..payload_len).unwrap_or_default()); Ok(()) diff --git a/wtx/src/web_socket/frame_buffer.rs b/wtx/src/web_socket/frame_buffer.rs index 659c9b6d..518dc43c 100644 --- a/wtx/src/web_socket/frame_buffer.rs +++ b/wtx/src/web_socket/frame_buffer.rs @@ -1,5 +1,5 @@ use crate::{ - misc::{SingleTypeStorage, _unreachable}, + misc::{Lease, LeaseMut, SingleTypeStorage, _unreachable}, web_socket::{DFLT_FRAME_BUFFER_VEC_LEN, MAX_CONTROL_FRAME_LEN, MAX_HDR_LEN_U8}, }; use alloc::{vec, vec::Vec}; @@ -78,7 +78,7 @@ impl FrameBuffer { impl FrameBuffer where - B: AsRef<[u8]>, + B: Lease<[u8]>, { /// Creates a new instance from the given `buffer`. #[inline] @@ -90,7 +90,7 @@ where #[inline] pub fn header(&self) -> &[u8] { if let Some(el) = - self.buffer.as_ref().get(self.header_begin_idx.into()..self.header_end_idx.into()) + self.buffer.lease().get(self.header_begin_idx.into()..self.header_end_idx.into()) { el } else { @@ -101,7 +101,7 @@ where /// Sequence of bytes that composes the frame payload. #[inline] pub fn payload(&self) -> &[u8] { - if let Some(el) = self.buffer.as_ref().get(self.header_end_idx.into()..self.payload_end_idx) { + if let Some(el) = self.buffer.lease().get(self.header_end_idx.into()..self.payload_end_idx) { el } else { _unreachable() @@ -109,7 +109,7 @@ where } pub(crate) fn frame(&self) -> &[u8] { - if let Some(el) = self.buffer.as_ref().get(self.header_begin_idx.into()..self.payload_end_idx) { + if let Some(el) = self.buffer.lease().get(self.header_begin_idx.into()..self.payload_end_idx) { el } else { _unreachable() @@ -124,7 +124,7 @@ where ) -> crate::Result<()> { let header_end_idx = Self::header_end_idx_from_parts(header_begin_idx, header_len); let payload_end_idx = Self::payload_end_idx_from_parts(header_end_idx, payload_len); - if header_len > MAX_HDR_LEN_U8 || payload_end_idx > self.buffer.as_ref().len() { + if header_len > MAX_HDR_LEN_U8 || payload_end_idx > self.buffer.lease().len() { return Err(crate::Error::InvalidPayloadBounds); } self.header_begin_idx = header_begin_idx; @@ -136,11 +136,11 @@ where impl FrameBuffer where - B: AsMut<[u8]>, + B: LeaseMut<[u8]>, { pub(crate) fn header_mut(&mut self) -> &mut [u8] { let range = self.header_begin_idx.into()..self.header_end_idx.into(); - if let Some(el) = self.buffer.as_mut().get_mut(range) { + if let Some(el) = self.buffer.lease_mut().get_mut(range) { el } else { _unreachable() @@ -149,7 +149,7 @@ where pub(crate) fn payload_mut(&mut self) -> &mut [u8] { let range = self.header_end_idx.into()..self.payload_end_idx; - if let Some(el) = self.buffer.as_mut().get_mut(range) { + if let Some(el) = self.buffer.lease_mut().get_mut(range) { el } else { _unreachable() @@ -159,11 +159,11 @@ where impl FrameBuffer where - B: AsMut>, + B: LeaseMut>, { pub(crate) fn expand_buffer(&mut self, new_len: usize) { - if new_len > self.buffer.as_mut().len() { - self.buffer.as_mut().resize(new_len, 0); + if new_len > self.buffer.lease_mut().len() { + self.buffer.lease_mut().resize(new_len, 0); } } @@ -221,7 +221,7 @@ impl Default for FrameBufferVec { impl<'fb, B> From<&'fb mut FrameBuffer> for FrameBufferMut<'fb> where - B: AsMut<[u8]>, + B: LeaseMut<[u8]>, { #[inline] fn from(from: &'fb mut FrameBuffer) -> Self { @@ -229,7 +229,7 @@ where header_begin_idx: from.header_begin_idx, header_end_idx: from.header_end_idx, payload_end_idx: from.payload_end_idx, - buffer: from.buffer.as_mut(), + buffer: from.buffer.lease_mut(), } } } @@ -262,3 +262,17 @@ impl<'bytes> From<&'bytes mut Vec> for FrameBufferVecMut<'bytes> { Self::new(from) } } + +impl Lease> for FrameBuffer { + #[inline] + fn lease(&self) -> &FrameBuffer { + self + } +} + +impl LeaseMut> for FrameBuffer { + #[inline] + fn lease_mut(&mut self) -> &mut FrameBuffer { + self + } +} diff --git a/wtx/src/web_socket/handshake.rs b/wtx/src/web_socket/handshake.rs index d46c35f5..5c066200 100644 --- a/wtx/src/web_socket/handshake.rs +++ b/wtx/src/web_socket/handshake.rs @@ -7,7 +7,7 @@ mod raw; mod tests; use crate::{ - http::Request, + http::GenericRequest, web_socket::{WebSocketClient, WebSocketServer}, }; use core::future::Future; @@ -18,7 +18,7 @@ pub trait WebSocketAccept { /// Reads external data to figure out if incoming requests can be accepted as WebSocket connections. fn accept( self, - cb: impl FnOnce(&dyn Request) -> bool, + cb: impl FnOnce(&dyn GenericRequest) -> bool, ) -> impl Future>>; } diff --git a/wtx/src/web_socket/handshake/misc.rs b/wtx/src/web_socket/handshake/misc.rs index d58a08a0..7247b165 100644 --- a/wtx/src/web_socket/handshake/misc.rs +++ b/wtx/src/web_socket/handshake/misc.rs @@ -14,23 +14,17 @@ pub(crate) fn gen_key<'buffer>(buffer: &'buffer mut [u8; 26], rng: &mut impl Rng } #[allow( - // False positive - clippy::arithmetic_side_effects, - // Buffer has enough capacity and `base64` already returns a valid string + // Buffer has enough capacity. clippy::unwrap_used )] fn base64_from_array<'output, const I: usize, const O: usize>( input: &[u8; I], output: &'output mut [u8; O], ) -> &'output [u8] { - fn div_ceil(x: usize, y: usize) -> usize { - let fun = || { - let num = x.checked_add(y)?.checked_sub(1)?; - num.checked_div(y) - }; - fun().unwrap_or_default() + const { + let rslt = if let Some(elem) = base64::encoded_len(I, false) { elem } else { 0 }; + assert!(O >= rslt); } - assert!(O >= div_ceil(I, 3).wrapping_mul(4)); let len = STANDARD.encode_slice(input, output).unwrap(); output.get(..len).unwrap_or_default() } diff --git a/wtx/src/web_socket/handshake/raw.rs b/wtx/src/web_socket/handshake/raw.rs index 03d62d56..f1df7a94 100644 --- a/wtx/src/web_socket/handshake/raw.rs +++ b/wtx/src/web_socket/handshake/raw.rs @@ -40,8 +40,8 @@ pub struct WebSocketConnectRaw<'fb, 'hb, 'uri, B, C, H, RNG, S, WSB> { #[cfg(feature = "web-socket-handshake")] mod httparse_impls { use crate::{ - http::{ExpectedHeader, GenericHeader as _, Request as _}, - misc::{bytes_split1, FilledBufferWriter, Stream, UriRef}, + http::{ExpectedHeader, GenericHeader as _, GenericRequest as _}, + misc::{bytes_split1, FilledBufferWriter, LeaseMut, Stream, UriRef}, rng::Rng, web_socket::{ compression::NegotiatedCompression, @@ -55,7 +55,6 @@ mod httparse_impls { }, }; use alloc::vec::Vec; - use core::borrow::BorrowMut; use httparse::{Header, Request, Response, Status, EMPTY_HEADER}; const MAX_READ_LEN: usize = 2 * 1024; @@ -66,14 +65,14 @@ mod httparse_impls { C: Compression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, { #[inline] async fn accept( mut self, - cb: impl FnOnce(&dyn crate::http::Request) -> bool, + cb: impl FnOnce(&dyn crate::http::GenericRequest) -> bool, ) -> crate::Result> { - let nb = &mut self.wsb.borrow_mut().nb; + let nb = &mut self.wsb.lease_mut().nb; nb._set_indices_through_expansion(0, 0, MAX_READ_LEN); let mut read = 0; loop { @@ -131,11 +130,11 @@ mod httparse_impls { impl<'fb, 'hb, B, C, RNG, S, WSB> WebSocketConnect for WebSocketConnectRaw<'fb, 'hb, '_, B, C, Header<'fb>, RNG, S, WSB> where - B: AsMut<[u8]> + AsMut> + AsRef<[u8]>, + B: LeaseMut<[u8]> + LeaseMut>, C: Compression, RNG: Rng, S: Stream, - WSB: BorrowMut, + WSB: LeaseMut, 'fb: 'hb, { type Response = Response<'hb, 'fb>; @@ -146,8 +145,8 @@ mod httparse_impls { headers: impl IntoIterator, ) -> crate::Result<(Self::Response, WebSocketClient)> { - let key_buffer = &mut <_>::default(); - let nb = &mut self.wsb.borrow_mut().nb; + let key_buffer = &mut [0; 26]; + let nb = &mut self.wsb.lease_mut().nb; nb._clear(); let mut fbw = nb.into(); let key = @@ -177,14 +176,12 @@ mod httparse_impls { if !has_header_key_and_value( res.headers, b"sec-websocket-accept", - derived_key(&mut <_>::default(), key), + derived_key(&mut [0; 30], key), ) { - return Err(crate::Error::MissingHeader { - expected: crate::http::ExpectedHeader::SecWebSocketKey, - }); + return Err(crate::Error::MissingHeader { expected: ExpectedHeader::SecWebSocketKey }); } let compression = self.compression.negotiate(res.headers.iter())?; - nb.borrow_mut()._set_indices_through_expansion(0, 0, read.wrapping_sub(len)); + nb._set_indices_through_expansion(0, 0, read.wrapping_sub(len)); nb._following_mut().copy_from_slice(self.fb.payload().get(len..read).unwrap_or_default()); Ok((res, WebSocketClient::new(compression, self.rng, self.stream, self.wsb))) } diff --git a/wtx/src/web_socket/handshake/tests.rs b/wtx/src/web_socket/handshake/tests.rs index cb123e13..bd5d86c7 100644 --- a/wtx/src/web_socket/handshake/tests.rs +++ b/wtx/src/web_socket/handshake/tests.rs @@ -13,7 +13,9 @@ use crate::{ web_socket::{ compression::NegotiatedCompression, frame::FrameMutVec, - handshake::{WebSocketAccept, WebSocketAcceptRaw, WebSocketConnect, WebSocketConnectRaw}, + handshake::{ + HeadersBuffer, WebSocketAccept, WebSocketAcceptRaw, WebSocketConnect, WebSocketConnectRaw, + }, Compression, FrameBufferVec, OpCode, WebSocket, WebSocketBuffer, WebSocketClientOwned, WebSocketServerOwned, }, @@ -88,7 +90,7 @@ where let (_, mut ws) = WebSocketConnectRaw { compression: client_compression, fb: &mut fb, - headers_buffer: &mut <_>::default(), + headers_buffer: &mut HeadersBuffer::default(), rng: StaticRng::default(), stream: TcpStream::connect(uri.host()).await.unwrap(), uri: &uri.to_ref(), diff --git a/wtx/src/web_socket/misc.rs b/wtx/src/web_socket/misc.rs index f1e0e8e0..de366ae2 100644 --- a/wtx/src/web_socket/misc.rs +++ b/wtx/src/web_socket/misc.rs @@ -3,7 +3,10 @@ mod filled_buffer; mod role; mod traits; -use crate::web_socket::{FrameBuffer, OpCode}; +use crate::{ + misc::LeaseMut, + web_socket::{FrameBuffer, OpCode}, +}; pub(crate) use filled_buffer::FilledBuffer; #[cfg(feature = "tracing")] pub(crate) use role::Role; @@ -18,15 +21,15 @@ pub(crate) fn define_fb_from_header_params( rsv1: u8, ) -> crate::Result<()> where - B: AsMut<[u8]> + AsRef<[u8]>, + B: LeaseMut<[u8]>, { let new_header_len = header_len_from_payload_len::(payload_len); let (buffer, header_begin_idx) = if let Some(el) = header_buffer_len { let header_begin_idx = el.saturating_sub(new_header_len); - let buffer = fb.buffer_mut().as_mut().get_mut(header_begin_idx.into()..).unwrap_or_default(); + let buffer = fb.buffer_mut().lease_mut().get_mut(header_begin_idx.into()..).unwrap_or_default(); (buffer, header_begin_idx) } else { - (fb.buffer_mut().as_mut(), 0) + (fb.buffer_mut().lease_mut(), 0) }; let _ = copy_header_params_to_buffer::(buffer, fin, op_code, payload_len, rsv1)?; fb.set_indices(header_begin_idx, new_header_len, payload_len)?; diff --git a/wtx/src/web_socket/misc/traits.rs b/wtx/src/web_socket/misc/traits.rs index 772b2ee1..889b8d72 100644 --- a/wtx/src/web_socket/misc/traits.rs +++ b/wtx/src/web_socket/misc/traits.rs @@ -21,7 +21,7 @@ where { fn expand(&mut self, len: usize) { if len > self.len() { - self.resize(len, <_>::default()); + self.resize(len, T::default()); } } } diff --git a/wtx/src/web_socket/unmask.rs b/wtx/src/web_socket/unmask.rs index 61b2c8dc..f3a40f78 100644 --- a/wtx/src/web_socket/unmask.rs +++ b/wtx/src/web_socket/unmask.rs @@ -2,7 +2,6 @@ #[inline] pub(crate) fn unmask(bytes: &mut [u8], mask: [u8; 4]) { let mut mask_u32 = u32::from_ne_bytes(mask); - #[allow(unsafe_code)] // SAFETY: Changing a sequence of `u8` to `u32` should be fine let (prefix, words, suffix) = unsafe { bytes.align_to_mut::() }; unmask_u8_slice(prefix, mask); @@ -30,7 +29,7 @@ fn unmask_u8_slice(bytes: &mut [u8], mask: [u8; 4]) { } fn unmask_u32_slice(bytes: &mut [u32], mask: u32) { - _iter4_mut!(bytes, |elem| { + _iter4_mut!(bytes, {}, |elem| { *elem ^= mask; }); } @@ -38,11 +37,11 @@ fn unmask_u32_slice(bytes: &mut [u32], mask: u32) { #[cfg(feature = "_bench")] #[cfg(test)] mod bench { - use crate::web_socket::unmask; + use crate::{bench::_data, web_socket::unmask}; #[bench] fn bench_unmask(b: &mut test::Bencher) { - let mut data = crate::bench::_data(64 << 20); + let mut data = _data(1024 * 1024 * 8); b.iter(|| unmask(&mut data, [3, 5, 7, 11])); } } @@ -50,6 +49,8 @@ mod bench { #[cfg(test)] #[cfg(feature = "_proptest")] mod proptest { + use alloc::vec::Vec; + #[test_strategy::proptest] fn unmask(mut data: Vec, mask: [u8; 4]) { crate::web_socket::unmask(&mut data, mask); diff --git a/wtx/src/web_socket/web_socket_buffer.rs b/wtx/src/web_socket/web_socket_buffer.rs index 865522ef..c80abc53 100644 --- a/wtx/src/web_socket/web_socket_buffer.rs +++ b/wtx/src/web_socket/web_socket_buffer.rs @@ -1,4 +1,7 @@ -use crate::{misc::PartitionedFilledBuffer, web_socket::misc::FilledBuffer}; +use crate::{ + misc::{Lease, LeaseMut, PartitionedFilledBuffer}, + web_socket::misc::FilledBuffer, +}; #[derive(Debug, Default)] #[doc = _internal_buffer_doc!()] @@ -15,7 +18,7 @@ impl WebSocketBuffer { pub fn with_capacity(decompression_buffer_cap: usize, network_buffer_cap: usize) -> Self { Self { db: FilledBuffer::with_capacity(decompression_buffer_cap), - nb: PartitionedFilledBuffer::with_capacity(network_buffer_cap), + nb: PartitionedFilledBuffer::_with_capacity(network_buffer_cap), } } @@ -23,3 +26,17 @@ impl WebSocketBuffer { (&mut self.db, &mut self.nb) } } + +impl Lease for WebSocketBuffer { + #[inline] + fn lease(&self) -> &WebSocketBuffer { + self + } +} + +impl LeaseMut for WebSocketBuffer { + #[inline] + fn lease_mut(&mut self) -> &mut WebSocketBuffer { + self + } +}