From 66e8989394f2cf75566465c9e6a0f54c78cdb3f5 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Tue, 22 Apr 2025 14:00:48 +0900 Subject: [PATCH] Import cxx 1.0.157 --- .bazelignore | 2 + .bazelrc | 21 + .bcr/README.md | 9 + .bcr/config.yml | 3 + .bcr/metadata.template.json | 16 + .bcr/presubmit.yml | 15 + .bcr/source.template.json | 5 + .buckconfig | 30 + .buckroot | 0 .cargo_vcs_info.json | 6 + .clang-format | 3 + .clang-tidy | 20 + .devcontainer/Dockerfile | 1 + .devcontainer/README.md | 4 + .devcontainer/build.Dockerfile | 14 + .devcontainer/devcontainer.json | 20 + .gitattributes | 3 + .github/FUNDING.yml | 1 + .github/workflows/buck2.yml | 28 + .github/workflows/ci.yml | 224 +++ .github/workflows/install.yml | 18 + .github/workflows/release.yml | 23 + .github/workflows/site.yml | 37 + .gitignore | 11 + .vscode/README.md | 4 + .vscode/launch.json | 29 + .vscode/settings.json | 5 + .vscode/tasks.json | 30 + .watchmanconfig | 3 + BUCK | 100 ++ BUILD.bazel | 99 ++ Cargo.toml | 152 ++ Cargo.toml.orig | 76 + LICENSE-APACHE | 176 +++ LICENSE-MIT | 23 + MODULE.bazel | 21 + MODULE.bazel.lock | 243 +++ README.md | 388 +++++ book/.gitignore | 3 + book/README.md | 9 + book/book.toml | 22 + book/build.js | 137 ++ book/build.sh | 17 + book/css/cxx.css | 49 + book/diagram/.gitignore | 7 + book/diagram/Makefile | 8 + book/diagram/overview.tex | 45 + book/eslint.config.mjs | 8 + book/package-lock.json | 1365 +++++++++++++++++ book/package.json | 16 + book/src/404.md | 5 + book/src/SUMMARY.md | 37 + book/src/async.md | 86 ++ book/src/attributes.md | 75 + book/src/binding/box.md | 120 ++ book/src/binding/cxxstring.md | 140 ++ book/src/binding/cxxvector.md | 62 + book/src/binding/fn.md | 34 + book/src/binding/rawptr.md | 100 ++ book/src/binding/result.md | 148 ++ book/src/binding/sharedptr.md | 80 + book/src/binding/slice.md | 178 +++ book/src/binding/str.md | 121 ++ book/src/binding/string.md | 134 ++ book/src/binding/uniqueptr.md | 63 + book/src/binding/vec.md | 200 +++ book/src/bindings.md | 56 + book/src/build/bazel.md | 109 ++ book/src/build/cargo.md | 306 ++++ book/src/build/cmake.md | 47 + book/src/build/other.md | 87 ++ book/src/building.md | 20 + book/src/concepts.md | 85 + book/src/context.md | 118 ++ book/src/cxx.png | Bin 0 -> 71692 bytes book/src/extern-c++.md | 352 +++++ book/src/extern-rust.md | 165 ++ book/src/index.md | 83 + book/src/overview.svg | 444 ++++++ book/src/reference.md | 29 + book/src/shared.md | 246 +++ book/src/tutorial.md | 692 +++++++++ book/theme/head.hbs | 7 + build.rs | 75 + compile_flags.txt | 1 + include/cxx.h | 1149 ++++++++++++++ reindeer.toml | 1 + rust-toolchain.toml | 2 + src/cxx.cc | 815 ++++++++++ src/cxx_string.rs | 329 ++++ src/cxx_vector.rs | 503 ++++++ src/exception.rs | 32 + src/extern_type.rs | 225 +++ src/fmt.rs | 16 + src/function.rs | 9 + src/hash.rs | 6 + src/lib.rs | 538 +++++++ src/lossy.rs | 67 + src/macros/assert.rs | 7 + src/macros/mod.rs | 2 + src/memory.rs | 9 + src/opaque.rs | 33 + src/result.rs | 70 + src/rust_slice.rs | 66 + src/rust_str.rs | 28 + src/rust_string.rs | 48 + src/rust_type.rs | 5 + src/rust_vec.rs | 115 ++ src/shared_ptr.rs | 316 ++++ src/symbols/exception.rs | 18 + src/symbols/mod.rs | 5 + src/symbols/rust_slice.rs | 20 + src/symbols/rust_str.rs | 43 + src/symbols/rust_string.rs | 114 ++ src/symbols/rust_vec.rs | 75 + src/type_id.rs | 9 + src/unique_ptr.rs | 469 ++++++ src/unwind.rs | 39 + src/vector.rs | 9 + src/weak_ptr.rs | 179 +++ tests/BUCK | 58 + tests/BUILD.bazel | 60 + tests/compiletest.rs | 9 + tests/cxx_gen.rs | 32 + tests/cxx_string.rs | 54 + tests/cxx_vector.rs | 7 + tests/test.rs | 397 +++++ tests/ui/array_len_expr.rs | 10 + tests/ui/array_len_expr.stderr | 17 + tests/ui/array_len_suffix.rs | 8 + tests/ui/array_len_suffix.stderr | 11 + tests/ui/async_fn.rs | 14 + tests/ui/async_fn.stderr | 11 + tests/ui/bad_explicit_impl.rs | 10 + tests/ui/bad_explicit_impl.stderr | 5 + tests/ui/by_value_not_supported.rs | 22 + tests/ui/by_value_not_supported.stderr | 59 + tests/ui/const_fn.rs | 10 + tests/ui/const_fn.stderr | 5 + .../ui/cxx_crate_name_qualified_cxx_string.rs | 17 + ...cxx_crate_name_qualified_cxx_string.stderr | 5 + tests/ui/data_enums.rs | 8 + tests/ui/data_enums.stderr | 5 + tests/ui/deny_elided_lifetimes.rs | 27 + tests/ui/deny_elided_lifetimes.stderr | 15 + tests/ui/deny_missing_docs.rs | 94 ++ tests/ui/deny_missing_docs.stderr | 47 + tests/ui/derive_duplicate.rs | 9 + tests/ui/derive_duplicate.stderr | 7 + tests/ui/derive_noncopy.rs | 13 + tests/ui/derive_noncopy.stderr | 7 + tests/ui/drop_shared.rs | 14 + tests/ui/drop_shared.stderr | 8 + tests/ui/empty_enum.rs | 6 + tests/ui/empty_enum.stderr | 5 + tests/ui/empty_struct.rs | 6 + tests/ui/empty_struct.stderr | 5 + tests/ui/enum_inconsistent.rs | 9 + tests/ui/enum_inconsistent.stderr | 5 + tests/ui/enum_match_without_wildcard.rs | 16 + tests/ui/enum_match_without_wildcard.stderr | 17 + tests/ui/enum_out_of_bounds.rs | 13 + tests/ui/enum_out_of_bounds.stderr | 11 + tests/ui/enum_overflows.rs | 17 + tests/ui/enum_overflows.stderr | 5 + tests/ui/enum_receiver.rs | 11 + tests/ui/enum_receiver.stderr | 5 + tests/ui/enum_unsatisfiable.rs | 9 + tests/ui/enum_unsatisfiable.stderr | 8 + tests/ui/expected_named.rs | 9 + tests/ui/expected_named.stderr | 11 + tests/ui/extern_fn_abi.rs | 8 + tests/ui/extern_fn_abi.stderr | 5 + tests/ui/extern_type_bound.rs | 15 + tests/ui/extern_type_bound.stderr | 11 + tests/ui/extern_type_generic.rs | 8 + tests/ui/extern_type_generic.stderr | 5 + tests/ui/extern_type_lifetime_bound.rs | 8 + tests/ui/extern_type_lifetime_bound.stderr | 5 + tests/ui/fallible_fnptr.rs | 8 + tests/ui/fallible_fnptr.stderr | 5 + tests/ui/function_with_body.rs | 8 + tests/ui/function_with_body.stderr | 5 + tests/ui/generic_enum.rs | 16 + tests/ui/generic_enum.stderr | 17 + tests/ui/impl_trait_for_type.rs | 10 + tests/ui/impl_trait_for_type.stderr | 5 + tests/ui/include.rs | 12 + tests/ui/include.stderr | 29 + tests/ui/lifetime_extern_cxx.rs | 9 + tests/ui/lifetime_extern_cxx.stderr | 5 + tests/ui/lifetime_extern_rust.rs | 17 + tests/ui/lifetime_extern_rust.stderr | 5 + tests/ui/missing_unsafe.rs | 10 + tests/ui/missing_unsafe.stderr | 9 + tests/ui/multiple_parse_error.rs | 8 + tests/ui/multiple_parse_error.stderr | 11 + tests/ui/mut_return.rs | 18 + tests/ui/mut_return.stderr | 11 + tests/ui/non_integer_discriminant_enum.rs | 8 + tests/ui/non_integer_discriminant_enum.stderr | 5 + tests/ui/nonempty_impl_block.rs | 12 + tests/ui/nonempty_impl_block.stderr | 8 + tests/ui/nonlocal_rust_type.rs | 18 + tests/ui/nonlocal_rust_type.stderr | 23 + tests/ui/opaque_autotraits.rs | 16 + tests/ui/opaque_autotraits.stderr | 76 + tests/ui/opaque_not_sized.rs | 10 + tests/ui/opaque_not_sized.stderr | 17 + tests/ui/pin_mut_opaque.rs | 14 + tests/ui/pin_mut_opaque.stderr | 35 + tests/ui/ptr_in_fnptr.rs | 8 + tests/ui/ptr_in_fnptr.stderr | 5 + tests/ui/ptr_missing_unsafe.rs | 10 + tests/ui/ptr_missing_unsafe.stderr | 5 + tests/ui/ptr_no_const_mut.rs | 10 + tests/ui/ptr_no_const_mut.stderr | 18 + tests/ui/ptr_unsupported.rs | 12 + tests/ui/ptr_unsupported.stderr | 17 + tests/ui/raw_ident_namespace.rs | 53 + tests/ui/raw_ident_namespace.stderr | 11 + tests/ui/reference_to_reference.rs | 13 + tests/ui/reference_to_reference.stderr | 11 + tests/ui/reserved_lifetime.rs | 10 + tests/ui/reserved_lifetime.stderr | 5 + tests/ui/reserved_name.rs | 16 + tests/ui/reserved_name.stderr | 17 + tests/ui/result_no_display.rs | 14 + tests/ui/result_no_display.stderr | 8 + tests/ui/root_namespace.rs | 13 + tests/ui/root_namespace.stderr | 5 + tests/ui/rust_pinned.rs | 14 + tests/ui/rust_pinned.stderr | 18 + tests/ui/slice_of_type_alias.rs | 30 + tests/ui/slice_of_type_alias.stderr | 16 + tests/ui/slice_unsupported.rs | 10 + tests/ui/slice_unsupported.stderr | 11 + tests/ui/struct_cycle.rs | 34 + tests/ui/struct_cycle.stderr | 23 + tests/ui/type_alias_rust.rs | 9 + tests/ui/type_alias_rust.stderr | 5 + tests/ui/unique_ptr_as_mut.rs | 23 + tests/ui/unique_ptr_as_mut.stderr | 7 + tests/ui/unique_ptr_to_opaque.rs | 23 + tests/ui/unique_ptr_to_opaque.stderr | 21 + tests/ui/unique_ptr_twice.rs | 19 + tests/ui/unique_ptr_twice.stderr | 8 + tests/ui/unnamed_receiver.rs | 14 + tests/ui/unnamed_receiver.stderr | 11 + tests/ui/unpin_impl.rs | 10 + tests/ui/unpin_impl.stderr | 12 + tests/ui/unrecognized_receiver.rs | 8 + tests/ui/unrecognized_receiver.stderr | 5 + tests/ui/unsupported_elided.rs | 20 + tests/ui/unsupported_elided.stderr | 22 + tests/ui/vec_opaque.rs | 34 + tests/ui/vec_opaque.stderr | 23 + tests/ui/vector_autotraits.rs | 21 + tests/ui/vector_autotraits.stderr | 34 + tests/ui/wrong_type_id.rs | 15 + tests/ui/wrong_type_id.stderr | 13 + tests/unique_ptr.rs | 8 + tools/bazel/BUILD.bazel | 19 + tools/bazel/extension.bzl | 30 + tools/bazel/rust_cxx_bridge.bzl | 57 + tools/buck/rust_cxx_bridge.bzl | 38 + tools/buck/toolchains/BUCK | 48 + tools/cargo/build.rs | 79 + 268 files changed, 16246 insertions(+) create mode 100644 .bazelignore create mode 100644 .bazelrc create mode 100644 .bcr/README.md create mode 100644 .bcr/config.yml create mode 100644 .bcr/metadata.template.json create mode 100644 .bcr/presubmit.yml create mode 100644 .bcr/source.template.json create mode 100644 .buckconfig create mode 100644 .buckroot create mode 100644 .cargo_vcs_info.json create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/build.Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitattributes create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/buck2.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/install.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/site.yml create mode 100644 .gitignore create mode 100644 .vscode/README.md create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 .watchmanconfig create mode 100644 BUCK create mode 100644 BUILD.bazel create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 MODULE.bazel create mode 100644 MODULE.bazel.lock create mode 100644 README.md create mode 100644 book/.gitignore create mode 100644 book/README.md create mode 100644 book/book.toml create mode 100755 book/build.js create mode 100755 book/build.sh create mode 100644 book/css/cxx.css create mode 100644 book/diagram/.gitignore create mode 100644 book/diagram/Makefile create mode 100644 book/diagram/overview.tex create mode 100644 book/eslint.config.mjs create mode 100644 book/package-lock.json create mode 100644 book/package.json create mode 100644 book/src/404.md create mode 100644 book/src/SUMMARY.md create mode 100644 book/src/async.md create mode 100644 book/src/attributes.md create mode 100644 book/src/binding/box.md create mode 100644 book/src/binding/cxxstring.md create mode 100644 book/src/binding/cxxvector.md create mode 100644 book/src/binding/fn.md create mode 100644 book/src/binding/rawptr.md create mode 100644 book/src/binding/result.md create mode 100644 book/src/binding/sharedptr.md create mode 100644 book/src/binding/slice.md create mode 100644 book/src/binding/str.md create mode 100644 book/src/binding/string.md create mode 100644 book/src/binding/uniqueptr.md create mode 100644 book/src/binding/vec.md create mode 100644 book/src/bindings.md create mode 100644 book/src/build/bazel.md create mode 100644 book/src/build/cargo.md create mode 100644 book/src/build/cmake.md create mode 100644 book/src/build/other.md create mode 100644 book/src/building.md create mode 100644 book/src/concepts.md create mode 100644 book/src/context.md create mode 100644 book/src/cxx.png create mode 100644 book/src/extern-c++.md create mode 100644 book/src/extern-rust.md create mode 100644 book/src/index.md create mode 100644 book/src/overview.svg create mode 100644 book/src/reference.md create mode 100644 book/src/shared.md create mode 100644 book/src/tutorial.md create mode 100644 book/theme/head.hbs create mode 100644 build.rs create mode 100644 compile_flags.txt create mode 100644 include/cxx.h create mode 100644 reindeer.toml create mode 100644 rust-toolchain.toml create mode 100644 src/cxx.cc create mode 100644 src/cxx_string.rs create mode 100644 src/cxx_vector.rs create mode 100644 src/exception.rs create mode 100644 src/extern_type.rs create mode 100644 src/fmt.rs create mode 100644 src/function.rs create mode 100644 src/hash.rs create mode 100644 src/lib.rs create mode 100644 src/lossy.rs create mode 100644 src/macros/assert.rs create mode 100644 src/macros/mod.rs create mode 100644 src/memory.rs create mode 100644 src/opaque.rs create mode 100644 src/result.rs create mode 100644 src/rust_slice.rs create mode 100644 src/rust_str.rs create mode 100644 src/rust_string.rs create mode 100644 src/rust_type.rs create mode 100644 src/rust_vec.rs create mode 100644 src/shared_ptr.rs create mode 100644 src/symbols/exception.rs create mode 100644 src/symbols/mod.rs create mode 100644 src/symbols/rust_slice.rs create mode 100644 src/symbols/rust_str.rs create mode 100644 src/symbols/rust_string.rs create mode 100644 src/symbols/rust_vec.rs create mode 100644 src/type_id.rs create mode 100644 src/unique_ptr.rs create mode 100644 src/unwind.rs create mode 100644 src/vector.rs create mode 100644 src/weak_ptr.rs create mode 100644 tests/BUCK create mode 100644 tests/BUILD.bazel create mode 100644 tests/compiletest.rs create mode 100644 tests/cxx_gen.rs create mode 100644 tests/cxx_string.rs create mode 100644 tests/cxx_vector.rs create mode 100644 tests/test.rs create mode 100644 tests/ui/array_len_expr.rs create mode 100644 tests/ui/array_len_expr.stderr create mode 100644 tests/ui/array_len_suffix.rs create mode 100644 tests/ui/array_len_suffix.stderr create mode 100644 tests/ui/async_fn.rs create mode 100644 tests/ui/async_fn.stderr create mode 100644 tests/ui/bad_explicit_impl.rs create mode 100644 tests/ui/bad_explicit_impl.stderr create mode 100644 tests/ui/by_value_not_supported.rs create mode 100644 tests/ui/by_value_not_supported.stderr create mode 100644 tests/ui/const_fn.rs create mode 100644 tests/ui/const_fn.stderr create mode 100644 tests/ui/cxx_crate_name_qualified_cxx_string.rs create mode 100644 tests/ui/cxx_crate_name_qualified_cxx_string.stderr create mode 100644 tests/ui/data_enums.rs create mode 100644 tests/ui/data_enums.stderr create mode 100644 tests/ui/deny_elided_lifetimes.rs create mode 100644 tests/ui/deny_elided_lifetimes.stderr create mode 100644 tests/ui/deny_missing_docs.rs create mode 100644 tests/ui/deny_missing_docs.stderr create mode 100644 tests/ui/derive_duplicate.rs create mode 100644 tests/ui/derive_duplicate.stderr create mode 100644 tests/ui/derive_noncopy.rs create mode 100644 tests/ui/derive_noncopy.stderr create mode 100644 tests/ui/drop_shared.rs create mode 100644 tests/ui/drop_shared.stderr create mode 100644 tests/ui/empty_enum.rs create mode 100644 tests/ui/empty_enum.stderr create mode 100644 tests/ui/empty_struct.rs create mode 100644 tests/ui/empty_struct.stderr create mode 100644 tests/ui/enum_inconsistent.rs create mode 100644 tests/ui/enum_inconsistent.stderr create mode 100644 tests/ui/enum_match_without_wildcard.rs create mode 100644 tests/ui/enum_match_without_wildcard.stderr create mode 100644 tests/ui/enum_out_of_bounds.rs create mode 100644 tests/ui/enum_out_of_bounds.stderr create mode 100644 tests/ui/enum_overflows.rs create mode 100644 tests/ui/enum_overflows.stderr create mode 100644 tests/ui/enum_receiver.rs create mode 100644 tests/ui/enum_receiver.stderr create mode 100644 tests/ui/enum_unsatisfiable.rs create mode 100644 tests/ui/enum_unsatisfiable.stderr create mode 100644 tests/ui/expected_named.rs create mode 100644 tests/ui/expected_named.stderr create mode 100644 tests/ui/extern_fn_abi.rs create mode 100644 tests/ui/extern_fn_abi.stderr create mode 100644 tests/ui/extern_type_bound.rs create mode 100644 tests/ui/extern_type_bound.stderr create mode 100644 tests/ui/extern_type_generic.rs create mode 100644 tests/ui/extern_type_generic.stderr create mode 100644 tests/ui/extern_type_lifetime_bound.rs create mode 100644 tests/ui/extern_type_lifetime_bound.stderr create mode 100644 tests/ui/fallible_fnptr.rs create mode 100644 tests/ui/fallible_fnptr.stderr create mode 100644 tests/ui/function_with_body.rs create mode 100644 tests/ui/function_with_body.stderr create mode 100644 tests/ui/generic_enum.rs create mode 100644 tests/ui/generic_enum.stderr create mode 100644 tests/ui/impl_trait_for_type.rs create mode 100644 tests/ui/impl_trait_for_type.stderr create mode 100644 tests/ui/include.rs create mode 100644 tests/ui/include.stderr create mode 100644 tests/ui/lifetime_extern_cxx.rs create mode 100644 tests/ui/lifetime_extern_cxx.stderr create mode 100644 tests/ui/lifetime_extern_rust.rs create mode 100644 tests/ui/lifetime_extern_rust.stderr create mode 100644 tests/ui/missing_unsafe.rs create mode 100644 tests/ui/missing_unsafe.stderr create mode 100644 tests/ui/multiple_parse_error.rs create mode 100644 tests/ui/multiple_parse_error.stderr create mode 100644 tests/ui/mut_return.rs create mode 100644 tests/ui/mut_return.stderr create mode 100644 tests/ui/non_integer_discriminant_enum.rs create mode 100644 tests/ui/non_integer_discriminant_enum.stderr create mode 100644 tests/ui/nonempty_impl_block.rs create mode 100644 tests/ui/nonempty_impl_block.stderr create mode 100644 tests/ui/nonlocal_rust_type.rs create mode 100644 tests/ui/nonlocal_rust_type.stderr create mode 100644 tests/ui/opaque_autotraits.rs create mode 100644 tests/ui/opaque_autotraits.stderr create mode 100644 tests/ui/opaque_not_sized.rs create mode 100644 tests/ui/opaque_not_sized.stderr create mode 100644 tests/ui/pin_mut_opaque.rs create mode 100644 tests/ui/pin_mut_opaque.stderr create mode 100644 tests/ui/ptr_in_fnptr.rs create mode 100644 tests/ui/ptr_in_fnptr.stderr create mode 100644 tests/ui/ptr_missing_unsafe.rs create mode 100644 tests/ui/ptr_missing_unsafe.stderr create mode 100644 tests/ui/ptr_no_const_mut.rs create mode 100644 tests/ui/ptr_no_const_mut.stderr create mode 100644 tests/ui/ptr_unsupported.rs create mode 100644 tests/ui/ptr_unsupported.stderr create mode 100644 tests/ui/raw_ident_namespace.rs create mode 100644 tests/ui/raw_ident_namespace.stderr create mode 100644 tests/ui/reference_to_reference.rs create mode 100644 tests/ui/reference_to_reference.stderr create mode 100644 tests/ui/reserved_lifetime.rs create mode 100644 tests/ui/reserved_lifetime.stderr create mode 100644 tests/ui/reserved_name.rs create mode 100644 tests/ui/reserved_name.stderr create mode 100644 tests/ui/result_no_display.rs create mode 100644 tests/ui/result_no_display.stderr create mode 100644 tests/ui/root_namespace.rs create mode 100644 tests/ui/root_namespace.stderr create mode 100644 tests/ui/rust_pinned.rs create mode 100644 tests/ui/rust_pinned.stderr create mode 100644 tests/ui/slice_of_type_alias.rs create mode 100644 tests/ui/slice_of_type_alias.stderr create mode 100644 tests/ui/slice_unsupported.rs create mode 100644 tests/ui/slice_unsupported.stderr create mode 100644 tests/ui/struct_cycle.rs create mode 100644 tests/ui/struct_cycle.stderr create mode 100644 tests/ui/type_alias_rust.rs create mode 100644 tests/ui/type_alias_rust.stderr create mode 100644 tests/ui/unique_ptr_as_mut.rs create mode 100644 tests/ui/unique_ptr_as_mut.stderr create mode 100644 tests/ui/unique_ptr_to_opaque.rs create mode 100644 tests/ui/unique_ptr_to_opaque.stderr create mode 100644 tests/ui/unique_ptr_twice.rs create mode 100644 tests/ui/unique_ptr_twice.stderr create mode 100644 tests/ui/unnamed_receiver.rs create mode 100644 tests/ui/unnamed_receiver.stderr create mode 100644 tests/ui/unpin_impl.rs create mode 100644 tests/ui/unpin_impl.stderr create mode 100644 tests/ui/unrecognized_receiver.rs create mode 100644 tests/ui/unrecognized_receiver.stderr create mode 100644 tests/ui/unsupported_elided.rs create mode 100644 tests/ui/unsupported_elided.stderr create mode 100644 tests/ui/vec_opaque.rs create mode 100644 tests/ui/vec_opaque.stderr create mode 100644 tests/ui/vector_autotraits.rs create mode 100644 tests/ui/vector_autotraits.stderr create mode 100644 tests/ui/wrong_type_id.rs create mode 100644 tests/ui/wrong_type_id.stderr create mode 100644 tests/unique_ptr.rs create mode 100644 tools/bazel/BUILD.bazel create mode 100644 tools/bazel/extension.bzl create mode 100644 tools/bazel/rust_cxx_bridge.bzl create mode 100644 tools/buck/rust_cxx_bridge.bzl create mode 100644 tools/buck/toolchains/BUCK create mode 100644 tools/cargo/build.rs diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..c42dab3 --- /dev/null +++ b/.bazelignore @@ -0,0 +1,2 @@ +target/ +tools/buck/buck2/ diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..09d078e --- /dev/null +++ b/.bazelrc @@ -0,0 +1,21 @@ +############################################################################### +## Bazel Configuration Flags +## +## `.bazelrc` is a Bazel configuration file. +## https://bazel.build/docs/best-practices#bazelrc-file +############################################################################### + +build --enable_platform_specific_config +build:linux --@rules_rust//:extra_rustc_flags=-Clink-arg=-fuse-ld=lld +build:linux --cxxopt=-std=c++17 +build:macos --cxxopt=-std=c++17 + +############################################################################### +## Custom user flags +## +## This should always be the last thing in the `.bazelrc` file to ensure +## consistent behavior when setting flags in that file as `.bazelrc` files are +## evaluated top to bottom. +############################################################################### + +try-import %workspace%/user.bazelrc diff --git a/.bcr/README.md b/.bcr/README.md new file mode 100644 index 0000000..44ae7fe --- /dev/null +++ b/.bcr/README.md @@ -0,0 +1,9 @@ +# Bazel Central Registry + +When the ruleset is released, we want it to be published to the +Bazel Central Registry automatically: + + +This folder contains configuration files to automate the publish step. +See +for authoritative documentation about these files. diff --git a/.bcr/config.yml b/.bcr/config.yml new file mode 100644 index 0000000..8531afc --- /dev/null +++ b/.bcr/config.yml @@ -0,0 +1,3 @@ +fixedReleaser: + login: dtolnay + email: dtolnay@gmail.com diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json new file mode 100644 index 0000000..0982309 --- /dev/null +++ b/.bcr/metadata.template.json @@ -0,0 +1,16 @@ +{ + "homepage": "https://cxx.rs", + "maintainers": [ + { + "github": "dtolnay", + "github_user_id": 1940490, + "email": "dtolnay@gmail.com", + "name": "David Tolnay" + } + ], + "repository": [ + "github:dtolnay/cxx" + ], + "versions": [], + "yanked_versions": {} +} diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml new file mode 100644 index 0000000..b5083f5 --- /dev/null +++ b/.bcr/presubmit.yml @@ -0,0 +1,15 @@ +matrix: + platform: + - macos_arm64 + - ubuntu2404 + - windows + bazel: [7.x, 8.x] +tasks: + verify_targets: + name: Verify build targets + platform: ${{ platform }} + bazel: ${{ bazel }} + build_targets: + - '@cxx.rs//...' + test_targets: + - '@cxx.rs//...' diff --git a/.bcr/source.template.json b/.bcr/source.template.json new file mode 100644 index 0000000..902c238 --- /dev/null +++ b/.bcr/source.template.json @@ -0,0 +1,5 @@ +{ + "integrity": "", + "strip_prefix": "{REPO}-{VERSION}", + "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{VERSION}.tar.gz" +} diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 0000000..1878b58 --- /dev/null +++ b/.buckconfig @@ -0,0 +1,30 @@ +[cells] +root = . +prelude = tools/buck/prelude +toolchains = tools/buck/toolchains +none = none + +[external_cells] +prelude = bundled + +[cell_aliases] +config = prelude +fbcode = none +fbsource = none + +[project] +# Hide BUCK files under target/package/ from `buck build ...`. Otherwise: +# $ buck build ... +# //target/package/cxx-0.3.0/tests:ffi references non-existing file or directory 'target/package/cxx-0.3.0/tests/ffi/lib.rs' +# +# Also hide some Bazel-managed directories that contain symlinks to the repo root. +ignore = \ + .git, \ + bazel-bin, \ + bazel-cxx, \ + bazel-out, \ + bazel-testlogs, \ + target + +[parser] +target_platform_detector_spec = target:root//...->prelude//platforms:default diff --git a/.buckroot b/.buckroot new file mode 100644 index 0000000..e69de29 diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..76a464f --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "f9d547b60324bc02d9983622159973a75d06ea10" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8ea286f --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +AlwaysBreakTemplateDeclarations: true +MaxEmptyLinesToKeep: 3 +ReflowComments: false diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..671d539 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,20 @@ +Checks: + clang-analyzer-*, + clang-diagnostic-*, + cppcoreguidelines-*, + modernize-*, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-const-cast, + -cppcoreguidelines-pro-type-member-init, + -cppcoreguidelines-pro-type-reinterpret-cast, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-special-member-functions, + -modernize-return-braced-init-list, + -modernize-use-default-member-init, + -modernize-use-equals-default, + -modernize-use-trailing-return-type, +HeaderFilterRegex: cxx\.h diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..93cfc05 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1 @@ +FROM dtolnay/devcontainer:latest diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000..b30aebc --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,4 @@ +This directory contains the container setup used when developing CXX inside of +GitHub [Codespaces]. + +[Codespaces]: https://github.com/features/codespaces diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile new file mode 100644 index 0000000..74ddd7b --- /dev/null +++ b/.devcontainer/build.Dockerfile @@ -0,0 +1,14 @@ +FROM mcr.microsoft.com/devcontainers/rust:bookworm + +RUN apt-get update \ + && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends clang lld zstd \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && wget -q -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 \ + && wget -q -O /tmp/buck.zst https://github.com/facebook/buck2/releases/download/latest/buck2-x86_64-unknown-linux-gnu.zst \ + && wget -q -O /usr/local/bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier-linux-amd64 \ + && unzstd /tmp/buck.zst -o /usr/local/bin/buck \ + && chmod +x /usr/local/bin/bazel /usr/local/bin/buck /usr/local/bin/buildifier \ + && rm /tmp/buck.zst \ + && rustup component add rust-analyzer rust-src diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b5b2911 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,20 @@ +{ + "name": "Rust", + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "lldb.executable": "/usr/bin/lldb", + "files.watcherExclude": { + "**/target/**": true + } + }, + "extensions": [ + "BazelBuild.vscode-bazel", + "ms-vscode.cpptools", + "rust-lang.rust-analyzer", + "vadimcn.vscode-lldb" + ] +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1cdc71c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +MODULE.bazel.lock linguist-generated +third-party/BUCK linguist-generated +third-party/bazel/** linguist-generated diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7507077 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: dtolnay diff --git a/.github/workflows/buck2.yml b/.github/workflows/buck2.yml new file mode 100644 index 0000000..9c3c54a --- /dev/null +++ b/.github/workflows/buck2.yml @@ -0,0 +1,28 @@ +name: Buck2 + +on: + push: + workflow_dispatch: + schedule: [cron: "40 1,13 * * *"] + +permissions: + contents: read + +jobs: + buck2: + name: Buck2 on ${{matrix.os == 'ubuntu' && 'Linux' || matrix.os == 'macos' && 'macOS' || matrix.os == 'windows' && 'Windows' || '???'}} + runs-on: ${{matrix.os}}-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu, macos, windows] + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rust-src + - uses: dtolnay/install-buck2@latest + - run: buck2 run demo + - run: buck2 build ... + - run: buck2 test ... diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b52099e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,224 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + schedule: [cron: "40 1 * * *"] + +permissions: + contents: read + +jobs: + pre_ci: + uses: dtolnay/.github/.github/workflows/pre_ci.yml@master + + test: + name: ${{matrix.name || format('Rust {0}', matrix.rust)}} + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ${{matrix.os}}-latest + strategy: + fail-fast: false + matrix: + rust: [nightly, beta, stable, 1.82.0, 1.80.0, 1.77.0, 1.74.0, 1.73.0] + os: [ubuntu] + cc: [''] + flags: [''] + include: + - name: Cargo on macOS + rust: nightly + os: macos + - name: Cargo on Windows (msvc) + rust: nightly-x86_64-pc-windows-msvc + os: windows + flags: /EHsc + - name: Clang + rust: nightly + cc: clang++ + os: ubuntu + flags: -std=c++20 -Werror -Wall + - name: C++14 + rust: nightly + os: ubuntu + flags: -std=c++14 -Werror -Wall + - name: C++17 + rust: nightly + os: ubuntu + flags: -std=c++17 -Werror -Wall + - name: C++20 + rust: nightly + os: ubuntu + flags: -std=c++20 -Werror -Wall + env: + CXX: ${{matrix.cc}} + CXXFLAGS: ${{matrix.flags}} + RUSTFLAGS: --cfg deny_warnings -Dwarnings + timeout-minutes: 45 + steps: + - name: Enable symlinks (windows) + if: matrix.os == 'windows' + run: git config --global core.symlinks true + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + components: rust-src + - name: Determine test suite subset + # Our Windows and macOS jobs are the longest running, so exclude the + # relatively slow compiletest from them to speed up end-to-end CI time, + # except during cron builds when no human is presumably waiting on the + # build. The extra coverage is not particularly valuable and we can + # still ensure the test is kept passing on the basis of the scheduled + # builds. + run: | + echo RUSTFLAGS=$RUSTFLAGS >> $GITHUB_ENV + echo exclude=--exclude cxx-test-suite ${{matrix.rust == '1.73.0' && '--exclude cxxbridge-cmd' || ''}} >> $GITHUB_OUTPUT + env: + RUSTFLAGS: ${{env.RUSTFLAGS}} ${{matrix.os != 'ubuntu' && github.event_name != 'schedule' && '--cfg skip_ui_tests' || ''}} + id: testsuite + shell: bash + - name: Ignore macOS linker warning + run: echo RUSTFLAGS=${RUSTFLAGS}\ -Alinker_messages >> $GITHUB_ENV + if: matrix.os == 'macos' + - run: cargo run --manifest-path demo/Cargo.toml + - run: cargo test --workspace ${{steps.testsuite.outputs.exclude}} + if: matrix.rust != '1.74.0' && matrix.rust != '1.73.0' + - run: cargo check --no-default-features --features alloc + env: + RUSTFLAGS: --cfg compile_error_if_std ${{env.RUSTFLAGS}} + - run: cargo check --no-default-features + env: + RUSTFLAGS: --cfg compile_error_if_alloc --cfg cxx_experimental_no_alloc ${{env.RUSTFLAGS}} + - uses: actions/upload-artifact@v4 + if: matrix.os == 'ubuntu' && matrix.rust == 'nightly' && always() + with: + name: Cargo.lock + path: Cargo.lock + continue-on-error: true + + reindeer: + name: Reindeer + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rust-src + - uses: dtolnay/install@reindeer + - run: reindeer buckify + working-directory: third-party + - name: Check reindeer-generated BUCK file up to date + run: git diff --exit-code + + bazel: + name: Bazel on ${{matrix.os == 'ubuntu' && 'Linux' || matrix.os == 'macos' && 'macOS' || matrix.os == 'windows' && 'Windows' || '???'}} + runs-on: ${{matrix.os}}-latest + if: github.event_name != 'pull_request' + strategy: + fail-fast: false + matrix: + os: [ubuntu, macos, windows] + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - name: Install lld + run: sudo apt-get install lld + if: matrix.os == 'ubuntu' + - name: Set bazelrc for Windows + run: echo "startup --output_user_root=D:/bzl" > user.bazelrc + if: matrix.os == 'windows' + - run: bazel --version + - run: bazel run demo --verbose_failures --noshow_progress ${{matrix.os == 'macos' && '--xcode_version_config=tools/bazel:github_actions_xcodes' || ''}} + - run: bazel test ... --verbose_failures --noshow_progress ${{matrix.os == 'macos' && '--xcode_version_config=tools/bazel:github_actions_xcodes' || ''}} + - name: Check MODULE.bazel.lock up to date + run: git diff --exit-code + - run: bazel run //third-party:vendor + if: matrix.os == 'ubuntu' || matrix.os == 'macos' + - name: Check third-party/bazel up to date + run: git diff --exit-code + if: matrix.os == 'ubuntu' || matrix.os == 'macos' + + minimal: + name: Minimal versions + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo generate-lockfile -Z minimal-versions + - run: cargo check --locked --workspace + + doc: + name: Documentation + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + RUSTDOCFLAGS: -Dwarnings + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rust-src + - uses: dtolnay/install@cargo-docs-rs + - run: cargo docs-rs + - run: cargo docs-rs -p cxx-build + - run: cargo docs-rs -p cxx-gen + - run: cargo docs-rs -p cxxbridge-flags + - run: cargo docs-rs -p cxxbridge-macro + + clippy: + name: Clippy + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + env: + RUSTFLAGS: -Dwarnings + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy, rust-src + - run: cargo clippy --workspace --tests --exclude demo -- -Dclippy::all -Dclippy::pedantic + - run: cargo clippy --manifest-path demo/Cargo.toml -- -Dclippy::all + + clang-tidy: + name: Clang Tidy + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - name: Install clang-tidy + run: sudo apt-get install clang-tidy-18 + - name: Run clang-tidy + run: clang-tidy-18 src/cxx.cc --warnings-as-errors=* + + eslint: + name: ESLint + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - run: npm install + working-directory: book + - run: npx eslint + working-directory: book + + outdated: + name: Outdated + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/install@cargo-outdated + - run: cargo outdated --workspace --exit-code 1 diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml new file mode 100644 index 0000000..025fe23 --- /dev/null +++ b/.github/workflows/install.yml @@ -0,0 +1,18 @@ +name: Install + +on: + workflow_dispatch: + schedule: [cron: "40 1 * * *"] + push: {tags: ['*']} + +permissions: {} + +env: + RUSTFLAGS: -Dwarnings + +jobs: + install: + name: Install + uses: dtolnay/.github/.github/workflows/check_install.yml@master + with: + crate: cxxbridge-cmd diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..196e389 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,23 @@ +name: Release + +on: + release: + types: [released] + +permissions: + attestations: write + contents: write + id-token: write + +jobs: + upload: + uses: dtolnay/.github/.github/workflows/release_tgz.yml@master + + publish-to-bcr: + needs: upload + uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@47913235f61615d02c989d652c4d10c45c0c4f0b + with: + tag_name: ${{github.event.release.tag_name}} + registry_fork: dtolnay-contrib/bazel-central-registry + secrets: + publish_token: ${{secrets.PUBLISH_TOKEN}} diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml new file mode 100644 index 0000000..555be19 --- /dev/null +++ b/.github/workflows/site.yml @@ -0,0 +1,37 @@ +name: Deploy + +on: + push: + branches: + - master + paths: + - book/** + - .github/workflows/site.yml + workflow_dispatch: + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + permissions: + contents: write + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/install@mdbook + - run: mdbook --version + + - name: Build + run: book/build.sh + + - name: Push to gh-pages + working-directory: book/build + run: | + REV=$(git rev-parse --short HEAD) + git init + git remote add upstream https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/dtolnay/cxx + git config user.name "CXX" + git config user.email "dtolnay+cxx@gmail.com" + git add -A . + git commit -qm "Website @ ${{github.repository}}@${REV}" + git push -q upstream HEAD:refs/heads/gh-pages --force diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b6f5c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/.buckd +/bazel-bin +/bazel-cxx +/bazel-out +/bazel-testlogs +/user.bazelrc +/buck-out +/expand.cc +/expand.rs +/target/ +/Cargo.lock diff --git a/.vscode/README.md b/.vscode/README.md new file mode 100644 index 0000000..5ed5b27 --- /dev/null +++ b/.vscode/README.md @@ -0,0 +1,4 @@ +VS Code actions and configuration. Applicable when developing CXX inside of +GitHub [Codespaces]. + +[Codespaces]: https://github.com/features/codespaces diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0218f47 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run cxx demo", + "type": "lldb", + "request": "launch", + "cargo": { + "args": ["build", "--manifest-path", "demo/Cargo.toml"], + "filter": { + "name": "demo", + "kind": "bin" + } + } + }, + { + "name": "Debug cargo tests", + "type": "lldb", + "request": "launch", + "cargo": { + "args": ["test", "--no-run"], + "filter": { + "name": "test", + "kind": "test" + } + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8a1c2c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "search.exclude": { + "**/target": true + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..44a2ab7 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Cargo test", + "type": "shell", + "command": "cargo test", + "group": "test" + }, + { + "label": "Bazel test", + "type": "shell", + "command": "bazel test ...", + "group": "test", + "dependsOn": ["Vendor"] + }, + { + "label": "Buck test", + "type": "shell", + "command": "buck test ...", + "group": "test", + "dependsOn": ["Vendor"] + }, + { + "label": "Vendor", + "type": "shell", + "command": "cp third-party/Cargo.lock . && cargo vendor --versioned-dirs --locked third-party/vendor" + } + ] +} diff --git a/.watchmanconfig b/.watchmanconfig new file mode 100644 index 0000000..d93f308 --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1,3 @@ +{ + "ignore_dirs": ["buck-out"] +} diff --git a/BUCK b/BUCK new file mode 100644 index 0000000..dccc386 --- /dev/null +++ b/BUCK @@ -0,0 +1,100 @@ +rust_library( + name = "cxx", + srcs = glob(["src/**/*.rs"]), + doc_deps = [ + ":cxx-build", + ], + edition = "2021", + features = [ + "alloc", + "std", + ], + visibility = ["PUBLIC"], + deps = [ + ":core", + ":cxxbridge-macro", + "//third-party:foldhash", + ], +) + +alias( + name = "codegen", + actual = ":cxxbridge", + visibility = ["PUBLIC"], +) + +rust_binary( + name = "cxxbridge", + srcs = glob(["gen/cmd/src/**/*.rs"]) + [ + "gen/cmd/src/gen", + "gen/cmd/src/syntax", + ], + edition = "2021", + deps = [ + "//third-party:clap", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) + +cxx_library( + name = "core", + srcs = ["src/cxx.cc"], + exported_headers = { + "cxx.h": "include/cxx.h", + }, + header_namespace = "rust", + preferred_linkage = "static", + visibility = ["PUBLIC"], +) + +rust_library( + name = "cxxbridge-macro", + srcs = glob(["macro/src/**/*.rs"]) + ["macro/src/syntax"], + doctests = False, + edition = "2021", + proc_macro = True, + deps = [ + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:rustversion", + "//third-party:syn", + ], +) + +rust_library( + name = "cxx-build", + srcs = glob(["gen/build/src/**/*.rs"]) + [ + "gen/build/src/gen", + "gen/build/src/syntax", + ], + doctests = False, + edition = "2021", + deps = [ + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:scratch", + "//third-party:syn", + ], +) + +rust_library( + name = "cxx-gen", + srcs = glob(["gen/lib/src/**/*.rs"]) + [ + "gen/lib/src/gen", + "gen/lib/src/syntax", + ], + edition = "2021", + visibility = ["PUBLIC"], + deps = [ + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..f1e2f92 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,99 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro") + +rust_library( + name = "cxx", + srcs = glob(["src/**/*.rs"]), + crate_features = [ + "alloc", + "std", + ], + edition = "2021", + proc_macro_deps = [ + ":cxxbridge-macro", + ], + visibility = ["//visibility:public"], + deps = [ + ":core-lib", + "@crates.io//:foldhash", + ], +) + +alias( + name = "codegen", + actual = ":cxxbridge", + visibility = ["//visibility:public"], +) + +rust_binary( + name = "cxxbridge", + srcs = glob(["gen/cmd/src/**/*.rs"]), + compile_data = ["gen/cmd/src/gen/include/cxx.h"], + edition = "2021", + deps = [ + "@crates.io//:clap", + "@crates.io//:codespan-reporting", + "@crates.io//:proc-macro2", + "@crates.io//:quote", + "@crates.io//:syn", + ], +) + +cc_library( + name = "core", + hdrs = ["include/cxx.h"], + include_prefix = "rust", + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) + +cc_library( + name = "core-lib", + srcs = ["src/cxx.cc"], + hdrs = ["include/cxx.h"], + linkstatic = True, +) + +rust_proc_macro( + name = "cxxbridge-macro", + srcs = glob(["macro/src/**/*.rs"]), + edition = "2021", + proc_macro_deps = [ + "@crates.io//:rustversion", + ], + deps = [ + "@crates.io//:proc-macro2", + "@crates.io//:quote", + "@crates.io//:syn", + ], +) + +rust_library( + name = "cxx-build", + srcs = glob(["gen/build/src/**/*.rs"]), + compile_data = ["gen/build/src/gen/include/cxx.h"], + edition = "2021", + deps = [ + "@crates.io//:cc", + "@crates.io//:codespan-reporting", + "@crates.io//:proc-macro2", + "@crates.io//:quote", + "@crates.io//:scratch", + "@crates.io//:syn", + ], +) + +rust_library( + name = "cxx-gen", + srcs = glob(["gen/lib/src/**/*.rs"]), + compile_data = ["gen/lib/src/gen/include/cxx.h"], + edition = "2021", + visibility = ["//visibility:public"], + deps = [ + "@crates.io//:cc", + "@crates.io//:codespan-reporting", + "@crates.io//:proc-macro2", + "@crates.io//:quote", + "@crates.io//:syn", + ], +) diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9400c2e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,152 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.73" +name = "cxx" +version = "1.0.157" +authors = ["David Tolnay "] +build = "build.rs" +links = "cxxbridge1" +exclude = [ + "/demo", + "/gen", + "/syntax", + "/third-party", + "/tools/buck/prelude", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Safe interop between Rust and C++" +homepage = "https://cxx.rs" +documentation = "https://docs.rs/cxx" +readme = "README.md" +keywords = [ + "ffi", + "c++", +] +categories = [ + "development-tools::ffi", + "api-bindings", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/cxx" + +[package.metadata.bazel] +additive_build_file_content = ''' +cc_library( + name = "cxx_cc", + srcs = ["src/cxx.cc"], + hdrs = ["include/cxx.h"], + include_prefix = "rust", + includes = ["include"], + linkstatic = True, + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +''' +deps = [":cxx_cc"] +gen_build_script = false + +[package.metadata.bazel.extra_aliased_targets] +cxx_cc = "cxx_cc" + +[package.metadata.docs.rs] +rustdoc-args = [ + "--generate-link-to-definition", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", +] +targets = ["x86_64-unknown-linux-gnu"] + +[features] +alloc = [] +"c++14" = ["cxxbridge-flags/c++14"] +"c++17" = ["cxxbridge-flags/c++17"] +"c++20" = ["cxxbridge-flags/c++20"] +default = [ + "std", + "cxxbridge-flags/default", +] +std = [ + "alloc", + "foldhash/std", +] + +[lib] +name = "cxx" +path = "src/lib.rs" + +[[test]] +name = "compiletest" +path = "tests/compiletest.rs" + +[[test]] +name = "cxx_gen" +path = "tests/cxx_gen.rs" + +[[test]] +name = "cxx_string" +path = "tests/cxx_string.rs" + +[[test]] +name = "cxx_vector" +path = "tests/cxx_vector.rs" + +[[test]] +name = "test" +path = "tests/test.rs" + +[[test]] +name = "unique_ptr" +path = "tests/unique_ptr.rs" + +[dependencies.cxxbridge-macro] +version = "=1.0.157" + +[dependencies.foldhash] +version = "0.1" +default-features = false + +[dependencies.link-cplusplus] +version = "1.0.9" + +[dev-dependencies.cxx-build] +version = "=1.0.157" + +[dev-dependencies.cxx-gen] +version = "0.7" + +[dev-dependencies.cxx-test-suite] +version = "0" + +[dev-dependencies.rustversion] +version = "1.0.13" + +[dev-dependencies.trybuild] +version = "1.0.81" +features = ["diff"] + +[build-dependencies.cc] +version = "1.0.83" + +[build-dependencies.cxxbridge-flags] +version = "=1.0.157" +default-features = false + +[target."cfg(any())".build-dependencies.cxxbridge-cmd] +version = "=1.0.157" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..c67f252 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,76 @@ +[package] +name = "cxx" +version = "1.0.157" +authors = ["David Tolnay "] +categories = ["development-tools::ffi", "api-bindings", "no-std"] +description = "Safe interop between Rust and C++" +documentation = "https://docs.rs/cxx" +edition = "2021" +exclude = ["/demo", "/gen", "/syntax", "/third-party", "/tools/buck/prelude"] +homepage = "https://cxx.rs" +keywords = ["ffi", "c++"] +license = "MIT OR Apache-2.0" +links = "cxxbridge1" +repository = "https://github.com/dtolnay/cxx" +rust-version = "1.73" + +[features] +default = ["std", "cxxbridge-flags/default"] # c++11 +"c++14" = ["cxxbridge-flags/c++14"] +"c++17" = ["cxxbridge-flags/c++17"] +"c++20" = ["cxxbridge-flags/c++20"] +alloc = [] +std = ["alloc", "foldhash/std"] + +[dependencies] +cxxbridge-macro = { version = "=1.0.157", path = "macro" } +foldhash = { version = "0.1", default-features = false } +link-cplusplus = "1.0.9" + +[build-dependencies] +cc = "1.0.83" +cxxbridge-flags = { version = "=1.0.157", path = "flags", default-features = false } + +[dev-dependencies] +cxx-build = { version = "=1.0.157", path = "gen/build" } +cxx-gen = { version = "0.7", path = "gen/lib" } +cxx-test-suite = { version = "0", path = "tests/ffi" } +rustversion = "1.0.13" +trybuild = { version = "1.0.81", features = ["diff"] } + +# Disallow incompatible cxxbridge-cmd version appearing in the same lockfile. +[target.'cfg(any())'.build-dependencies] +cxxbridge-cmd = { version = "=1.0.157", path = "gen/cmd" } + +[workspace] +members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = [ + "--generate-link-to-definition", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", +] + +[package.metadata.bazel] +additive_build_file_content = """ +cc_library( + name = "cxx_cc", + srcs = ["src/cxx.cc"], + hdrs = ["include/cxx.h"], + include_prefix = "rust", + includes = ["include"], + linkstatic = True, + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""" +deps = [":cxx_cc"] +extra_aliased_targets = { cxx_cc = "cxx_cc" } +gen_build_script = false + +[patch.crates-io] +cxx = { path = "." } +cxx-build = { path = "gen/build" } diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..c14a5c9 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,21 @@ +module( + name = "cxx.rs", + version = "0.0.0", + bazel_compatibility = [">=7.2.1"], + compatibility_level = 1, +) + +bazel_dep(name = "bazel_features", version = "1.21.0") +bazel_dep(name = "bazel_skylib", version = "1.7.1") +bazel_dep(name = "platforms", version = "0.0.11") +bazel_dep(name = "rules_cc", version = "0.1.1") +bazel_dep(name = "rules_rust", version = "0.60.0") + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain(versions = ["1.86.0"]) +use_repo(rust, "rust_toolchains") + +register_toolchains("@rust_toolchains//:all") + +crate_repositories = use_extension("//tools/bazel:extension.bzl", "crate_repositories") +use_repo(crate_repositories, "crates.io", "vendor") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock new file mode 100644 index 0000000..e84b165 --- /dev/null +++ b/MODULE.bazel.lock @@ -0,0 +1,243 @@ +{ + "lockFileVersion": 18, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", + "https://bcr.bazel.build/modules/apple_support/1.17.1/MODULE.bazel": "655c922ab1209978a94ef6ca7d9d43e940cd97d9c172fb55f94d91ac53f8610b", + "https://bcr.bazel.build/modules/apple_support/1.17.1/source.json": "6b2b8c74d14e8d485528a938e44bdb72a5ba17632b9e14ef6e68a5ee96c8347f", + "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://bcr.bazel.build/modules/bazel_features/1.21.0/source.json": "3e8379efaaef53ce35b7b8ba419df829315a880cb0a030e5bb45c96d6d5ecb5f", + "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", + "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", + "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", + "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4", + "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", + "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", + "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", + "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", + "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://bcr.bazel.build/modules/rules_java/8.11.0/MODULE.bazel": "c3d280bc5ff1038dcb3bacb95d3f6b83da8dd27bba57820ec89ea4085da767ad", + "https://bcr.bazel.build/modules/rules_java/8.11.0/source.json": "302b52a39259a85aa06ca3addb9787864ca3e03b432a5f964ea68244397e7544", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/0.40.0/source.json": "939d4bd2e3110f27bfb360292986bb79fd8dcefb874358ccd6cdaa7bda029320", + "https://bcr.bazel.build/modules/rules_rust/0.60.0/MODULE.bazel": "911ff2a12d01ac574fd6dfec0b05fa976ff8693d8c2420db637a9f98f697b0ae", + "https://bcr.bazel.build/modules/rules_rust/0.60.0/source.json": "2b17f77e27489aa1b86b765a141642a1966a2a35fed0207277f3327fd09ef3d4", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", + "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", + "https://bcr.bazel.build/modules/stardoc/0.7.1/source.json": "b6500ffcd7b48cd72c29bb67bcac781e12701cc0d6d55d266a652583cfcdab01", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d", + "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" + }, + "selectedYankedVersions": {}, + "moduleExtensions": { + "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "xcBTf2+GaloFpg7YEh/Bv+1yAczRkiCt3DGws4K7kSk=", + "usagesDigest": "3L+PK6aRnliv0iIS8m3kdo+LjmvjJWoFCm3qZcPSg+8=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_apple_cc_toolchains": { + "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf_toolchains", + "attributes": {} + }, + "local_config_apple_cc": { + "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support+", + "bazel_tools", + "bazel_tools" + ], + [ + "bazel_tools", + "rules_cc", + "rules_cc+" + ] + ] + } + }, + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "general": { + "bzlTransitiveDigest": "sFhcgPbDQehmbD1EOXzX4H1q/CD5df8zwG4kp4jbvr8=", + "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_jetbrains_kotlin_git": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", + "attributes": { + "urls": [ + "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip" + ], + "sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88" + } + }, + "com_github_jetbrains_kotlin": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", + "attributes": { + "git_repository_name": "com_github_jetbrains_kotlin_git", + "compiler_version": "1.9.23" + } + }, + "com_github_google_ksp": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", + "attributes": { + "urls": [ + "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip" + ], + "sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d", + "strip_version": "1.9.23-1.0.20" + } + }, + "com_github_pinterest_ktlint": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", + "urls": [ + "https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint" + ], + "executable": true + } + }, + "rules_android": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", + "strip_prefix": "rules_android-0.1.1", + "urls": [ + "https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_kotlin+", + "bazel_tools", + "bazel_tools" + ] + ] + } + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..9c6dab1 --- /dev/null +++ b/README.md @@ -0,0 +1,388 @@ +CXX — safe FFI between Rust and C++ +========================================= + +[github](https://github.com/dtolnay/cxx) +[crates.io](https://crates.io/crates/cxx) +[docs.rs](https://docs.rs/cxx) +[build status](https://github.com/dtolnay/cxx/actions?query=branch%3Amaster) + +This library provides a **safe** mechanism for calling C++ code from Rust and +Rust code from C++, not subject to the many ways that things can go wrong when +using bindgen or cbindgen to generate unsafe C-style bindings. + +This doesn't change the fact that 100% of C++ code is unsafe. When auditing a +project, you would be on the hook for auditing all the unsafe Rust code and +*all* the C++ code. The core safety claim under this new model is that auditing +just the C++ side would be sufficient to catch all problems, i.e. the Rust side +can be 100% safe. + +```toml +[dependencies] +cxx = "1.0" + +[build-dependencies] +cxx-build = "1.0" +``` + +*Compiler support: requires rustc 1.73+ and c++11 or newer*
+*[Release notes](https://github.com/dtolnay/cxx/releases)* + +
+ +## Guide + +Please see **** for a tutorial, reference material, and example +code. + +
+ +## Overview + +The idea is that we define the signatures of both sides of our FFI boundary +embedded together in one Rust module (the next section shows an example). From +this, CXX receives a complete picture of the boundary to perform static analyses +against the types and function signatures to uphold both Rust's and C++'s +invariants and requirements. + +If everything checks out statically, then CXX uses a pair of code generators to +emit the relevant `extern "C"` signatures on both sides together with any +necessary static assertions for later in the build process to verify +correctness. On the Rust side this code generator is simply an attribute +procedural macro. On the C++ side it can be a small Cargo build script if your +build is managed by Cargo, or for other build systems like Bazel or Buck we +provide a command line tool which generates the header and source file and +should be easy to integrate. + +The resulting FFI bridge operates at zero or negligible overhead, i.e. no +copying, no serialization, no memory allocation, no runtime checks needed. + +The FFI signatures are able to use native types from whichever side they please, +such as Rust's `String` or C++'s `std::string`, Rust's `Box` or C++'s +`std::unique_ptr`, Rust's `Vec` or C++'s `std::vector`, etc in any combination. +CXX guarantees an ABI-compatible signature that both sides understand, based on +builtin bindings for key standard library types to expose an idiomatic API on +those types to the other language. For example when manipulating a C++ string +from Rust, its `len()` method becomes a call of the `size()` member function +defined by C++; when manipulating a Rust string from C++, its `size()` member +function calls Rust's `len()`. + +
+ +## Example + +In this example we are writing a Rust application that wishes to take advantage +of an existing C++ client for a large-file blobstore service. The blobstore +supports a `put` operation for a discontiguous buffer upload. For example we +might be uploading snapshots of a circular buffer which would tend to consist of +2 chunks, or fragments of a file spread across memory for some other reason. + +A runnable version of this example is provided under the *demo* directory of +this repo. To try it out, run `cargo run` from that directory. + +```rust +#[cxx::bridge] +mod ffi { + // Any shared structs, whose fields will be visible to both languages. + struct BlobMetadata { + size: usize, + tags: Vec, + } + + extern "Rust" { + // Zero or more opaque types which both languages can pass around but + // only Rust can see the fields. + type MultiBuf; + + // Functions implemented in Rust. + fn next_chunk(buf: &mut MultiBuf) -> &[u8]; + } + + unsafe extern "C++" { + // One or more headers with the matching C++ declarations. Our code + // generators don't read it but it gets #include'd and used in static + // assertions to ensure our picture of the FFI boundary is accurate. + include!("demo/include/blobstore.h"); + + // Zero or more opaque types which both languages can pass around but + // only C++ can see the fields. + type BlobstoreClient; + + // Functions implemented in C++. + fn new_blobstore_client() -> UniquePtr; + fn put(&self, parts: &mut MultiBuf) -> u64; + fn tag(&self, blobid: u64, tag: &str); + fn metadata(&self, blobid: u64) -> BlobMetadata; + } +} +``` + +Now we simply provide Rust definitions of all the things in the `extern "Rust"` +block and C++ definitions of all the things in the `extern "C++"` block, and get +to call back and forth safely. + +Here are links to the complete set of source files involved in the demo: + +- [demo/src/main.rs](demo/src/main.rs) +- [demo/build.rs](demo/build.rs) +- [demo/include/blobstore.h](demo/include/blobstore.h) +- [demo/src/blobstore.cc](demo/src/blobstore.cc) + +To look at the code generated in both languages for the example by the CXX code +generators: + +```console + # run Rust code generator and print to stdout + # (requires https://github.com/dtolnay/cargo-expand) +$ cargo expand --manifest-path demo/Cargo.toml + + # run C++ code generator and print to stdout +$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs +``` + +
+ +## Details + +As seen in the example, the language of the FFI boundary involves 3 kinds of +items: + +- **Shared structs** — their fields are made visible to both languages. + The definition written within cxx::bridge is the single source of truth. + +- **Opaque types** — their fields are secret from the other language. + These cannot be passed across the FFI by value but only behind an indirection, + such as a reference `&`, a Rust `Box`, or a `UniquePtr`. Can be a type alias + for an arbitrarily complicated generic language-specific type depending on + your use case. + +- **Functions** — implemented in either language, callable from the other + language. + +Within the `extern "Rust"` part of the CXX bridge we list the types and +functions for which Rust is the source of truth. These all implicitly refer to +the `super` module, the parent module of the CXX bridge. You can think of the +two items listed in the example above as being like `use super::MultiBuf` and +`use super::next_chunk` except re-exported to C++. The parent module will either +contain the definitions directly for simple things, or contain the relevant +`use` statements to bring them into scope from elsewhere. + +Within the `extern "C++"` part, we list types and functions for which C++ is the +source of truth, as well as the header(s) that declare those APIs. In the future +it's possible that this section could be generated bindgen-style from the +headers but for now we need the signatures written out; static assertions will +verify that they are accurate. + +Your function implementations themselves, whether in C++ or Rust, *do not* need +to be defined as `extern "C"` ABI or no\_mangle. CXX will put in the right shims +where necessary to make it all work. + +
+ +## Comparison vs bindgen and cbindgen + +Notice that with CXX there is repetition of all the function signatures: they +are typed out once where the implementation is defined (in C++ or Rust) and +again inside the cxx::bridge module, though compile-time assertions guarantee +these are kept in sync. This is different from [bindgen] and [cbindgen] where +function signatures are typed by a human once and the tool consumes them in one +language and emits them in the other language. + +[bindgen]: https://github.com/rust-lang/rust-bindgen +[cbindgen]: https://github.com/eqrion/cbindgen/ + +This is because CXX fills a somewhat different role. It is a lower level tool +than bindgen or cbindgen in a sense; you can think of it as being a replacement +for the concept of `extern "C"` signatures as we know them, rather than a +replacement for a bindgen. It would be reasonable to build a higher level +bindgen-like tool on top of CXX which consumes a C++ header and/or Rust module +(and/or IDL like Thrift) as source of truth and generates the cxx::bridge, +eliminating the repetition while leveraging the static analysis safety +guarantees of CXX. + +But note in other ways CXX is higher level than the bindgens, with rich support +for common standard library types. Frequently with bindgen when we are dealing +with an idiomatic C++ API we would end up manually wrapping that API in C-style +raw pointer functions, applying bindgen to get unsafe raw pointer Rust +functions, and replicating the API again to expose those idiomatically in Rust. +That's a much worse form of repetition because it is unsafe all the way through. + +By using a CXX bridge as the shared understanding between the languages, rather +than `extern "C"` C-style signatures as the shared understanding, common FFI use +cases become expressible using 100% safe code. + +It would also be reasonable to mix and match, using CXX bridge for the 95% of +your FFI that is straightforward and doing the remaining few oddball signatures +the old fashioned way with bindgen and cbindgen, if for some reason CXX's static +restrictions get in the way. Please file an issue if you end up taking this +approach so that we know what ways it would be worthwhile to make the tool more +expressive. + +
+ +## Cargo-based setup + +For builds that are orchestrated by Cargo, you will use a build script that runs +CXX's C++ code generator and compiles the resulting C++ code along with any +other C++ code for your crate. + +The canonical build script is as follows. The indicated line returns a +[`cc::Build`] instance (from the usual widely used `cc` crate) on which you can +set up any additional source files and compiler flags as normal. + +[`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html + +```toml +# Cargo.toml + +[build-dependencies] +cxx-build = "1.0" +``` + +```rust +// build.rs + +fn main() { + cxx_build::bridge("src/main.rs") // returns a cc::Build + .file("src/demo.cc") + .std("c++11") + .compile("cxxbridge-demo"); + + println!("cargo:rerun-if-changed=src/main.rs"); + println!("cargo:rerun-if-changed=src/demo.cc"); + println!("cargo:rerun-if-changed=include/demo.h"); +} +``` + +
+ +## Non-Cargo setup + +For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate way of +invoking the C++ code generator as a standalone command line tool. The tool is +packaged as the `cxxbridge-cmd` crate on crates.io or can be built from the +*gen/cmd* directory of this repo. + +```bash +$ cargo install cxxbridge-cmd + +$ cxxbridge src/main.rs --header > path/to/mybridge.h +$ cxxbridge src/main.rs > path/to/mybridge.cc +``` + +
+ +## Safety + +Be aware that the design of this library is intentionally restrictive and +opinionated! It isn't a goal to be powerful enough to handle arbitrary +signatures in either language. Instead this project is about carving out a +reasonably expressive set of functionality about which we can make useful safety +guarantees today and maybe extend over time. You may find that it takes some +practice to use CXX bridge effectively as it won't work in all the ways that you +are used to. + +Some of the considerations that go into ensuring safety are: + +- By design, our paired code generators work together to control both sides of + the FFI boundary. Ordinarily in Rust writing your own `extern "C"` blocks is + unsafe because the Rust compiler has no way to know whether the signatures + you've written actually match the signatures implemented in the other + language. With CXX we achieve that visibility and know what's on the other + side. + +- Our static analysis detects and prevents passing types by value that shouldn't + be passed by value from C++ to Rust, for example because they may contain + internal pointers that would be screwed up by Rust's move behavior. + +- To many people's surprise, it is possible to have a struct in Rust and a + struct in C++ with exactly the same layout / fields / alignment / everything, + and still not the same ABI when passed by value. This is a longstanding + bindgen bug that leads to segfaults in absolutely correct-looking code + ([rust-lang/rust-bindgen#778]). CXX knows about this and can insert the + necessary zero-cost workaround transparently where needed, so go ahead and + pass your structs by value without worries. This is made possible by owning + both sides of the boundary rather than just one. + +- Template instantiations: for example in order to expose a UniquePtr\ type + in Rust backed by a real C++ unique\_ptr, we have a way of using a Rust trait + to connect the behavior back to the template instantiations performed by the + other language. + +[rust-lang/rust-bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778 + +
+ +## Builtin types + +In addition to all the primitive types (i32 <=> int32_t), the following +common types may be used in the fields of shared structs and the arguments and +returns of functions. + + + + + + + + + + + + + + + + + +
name in Rustname in C++restrictions
Stringrust::String
&strrust::Str
&[T]rust::Slice<const T>cannot hold opaque C++ type
&mut [T]rust::Slice<T>cannot hold opaque C++ type
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
SharedPtr<T>std::shared_ptr<T>cannot hold opaque Rust type
[T; N]std::array<T, N>cannot hold opaque C++ type
Vec<T>rust::Vec<T>cannot hold opaque C++ type
CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type
*mut T, *const TT*, const T*fn with a raw pointer argument must be declared unsafe to call
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
+ +The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in +this repo. You will need to include this header in your C++ code when working +with those types. + +The following types are intended to be supported "soon" but are just not +implemented yet. I don't expect any of these to be hard to make work but it's a +matter of designing a nice API for each in its non-native language. + + + + + + + + + +
name in Rustname in C++
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
Option<T>tbd
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>
+ +
+ +## Remaining work + +This is still early days for CXX; I am releasing it as a minimum viable product +to collect feedback on the direction and invite collaborators. Please check the +open issues. + +Especially please report issues if you run into trouble building or linking any +of this stuff. I'm sure there are ways to make the build aspects friendlier or +more robust. + +Finally, I know more about Rust library design than C++ library design so I +would appreciate help making the C++ APIs in this project more idiomatic where +anyone has suggestions. + +
+ +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this project by you, as defined in the Apache-2.0 license, +shall be dual licensed as above, without any additional terms or conditions. + diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 0000000..3c7d187 --- /dev/null +++ b/book/.gitignore @@ -0,0 +1,3 @@ +/build/ +/mdbook +/node_modules/ diff --git a/book/README.md b/book/README.md new file mode 100644 index 0000000..e4916e0 --- /dev/null +++ b/book/README.md @@ -0,0 +1,9 @@ +Published automatically to https://cxx.rs from master branch. + +To build and view locally: + +- Install [mdBook]: `cargo install mdbook`. +- Run `mdbook build` in this directory. +- Open the generated *build/index.html*. + +[mdBook]: https://github.com/rust-lang/mdBook diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 0000000..a8148fe --- /dev/null +++ b/book/book.toml @@ -0,0 +1,22 @@ +[book] +#title = "Rust ♡ C++" +authors = ["David Tolnay"] +description = "CXX — safe interop between Rust and C++ by David Tolnay. This library provides a safe mechanism for calling C++ code from Rust and Rust code from C++." + +[rust] +edition = "2021" + +[build] +build-dir = "build" +create-missing = false + +[output.html] +additional-css = ["css/cxx.css"] +cname = "cxx.rs" +git-repository-url = "https://github.com/dtolnay/cxx" +playground = { copyable = false } +print = { enable = false } + +[output.html.redirect] +"binding/index.html" = "../bindings.html" +"build/index.html" = "../building.html" diff --git a/book/build.js b/book/build.js new file mode 100755 index 0000000..85da7bf --- /dev/null +++ b/book/build.js @@ -0,0 +1,137 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const cheerio = require('cheerio'); +const entities = require('html-entities'); +const hljs = require('./build/highlight.js'); + +const githublink = `\ +
  • \ +\ +\ +https://github.com/dtolnay/cxx\ +\ +
  • `; + +const opengraph = `\ +\ +\ +\ +\ +\ +\ +`; + +const themejs = `\ +var theme; +try { theme = localStorage.getItem('mdbook-theme'); } catch(e) {} +if (theme === null || theme === undefined) { theme = default_theme; } +const html = document.documentElement; +html.classList.remove('light') +html.classList.add(theme); +html.classList.add("js");`; + +const themejsReplacement = `\ +const html = document.documentElement; +html.classList.add('js');`; + +const dirs = ['build']; +while (dirs.length) { + const dir = dirs.pop(); + fs.readdirSync(dir).forEach((entry) => { + const path = dir + '/' + entry; + const stat = fs.statSync(path); + if (stat.isDirectory()) { + dirs.push(path); + return; + } + + if (!path.endsWith('.html')) { + return; + } + + const index = fs.readFileSync(path, 'utf8'); + const $ = cheerio.load(index, { + decodeEntities: false, + xml: { xmlMode: false }, + }); + + $('head').append(opengraph); + $('nav#sidebar ol.chapter').append(githublink); + $('head link[href="tomorrow-night.css"]').attr('disabled', true); + $('head link[href="ayu-highlight.css"]').attr('disabled', true); + $('button#theme-toggle').attr('style', 'display:none'); + $('pre code').each(function () { + const node = $(this); + const langClass = node.attr('class').split(' ', 2)[0]; + if (!langClass.startsWith('language-')) { + return; + } + const lang = langClass.replace('language-', ''); + const originalLines = node.html().split('\n'); + const boring = originalLines.map((line) => + line.includes(''), + ); + const ellipsis = originalLines.map((line) => line.includes('// ...')); + const target = entities.decode(node.text()); + const highlightedLines = hljs.highlight(lang, target).value.split('\n'); + const result = highlightedLines + .map(function (line, i) { + if (boring[i]) { + line = '' + line; + } else if (ellipsis[i]) { + line = '' + line; + } + if (i > 0 && (boring[i - 1] || ellipsis[i - 1])) { + line = '' + line; + } + if (i + 1 === highlightedLines.length && (boring[i] || ellipsis[i])) { + line = line + ''; + } + return line; + }) + .join('\n'); + node.text(result); + node.removeClass(langClass); + if (!node.hasClass('focuscomment')) { + node.addClass('hidelines'); + node.addClass('hide-boring'); + } + }); + $('code').each(function () { + $(this).addClass('hljs'); + }); + + var foundScript = false; + $('body script').each(function () { + const node = $(this); + if (node.text().replace(/\s/g, '') === themejs.replace(/\s/g, '')) { + node.text(themejsReplacement); + foundScript = true; + } + }); + const pathsWithoutScript = [ + 'build/toc.html', + 'build/build/index.html', + 'build/binding/index.html', + ]; + if (!foundScript && !pathsWithoutScript.includes(path)) { + throw new Error('theme script not found'); + } + + const out = $.html(); + fs.writeFileSync(path, out); + }); +} + +fs.copyFileSync('build/highlight.css', 'build/tomorrow-night.css'); +fs.copyFileSync('build/highlight.css', 'build/ayu-highlight.css'); + +var bookjs = fs.readFileSync('build/book.js', 'utf8'); +bookjs = bookjs + .replace('set_theme(theme, false);', '') + .replace( + 'document.querySelectorAll("code.hljs")', + 'document.querySelectorAll("code.hidelines")', + ); +fs.writeFileSync('build/book.js', bookjs); diff --git a/book/build.sh b/book/build.sh new file mode 100755 index 0000000..783d304 --- /dev/null +++ b/book/build.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +cd "$(dirname "$0")" + +if [ -f ./mdbook ]; then + ./mdbook build +else + mdbook build +fi + +if [ ! -d node_modules ]; then + npm install +fi + +./build.js diff --git a/book/css/cxx.css b/book/css/cxx.css new file mode 100644 index 0000000..68d32db --- /dev/null +++ b/book/css/cxx.css @@ -0,0 +1,49 @@ +:root { + --sidebar-width: 310px; +} + +.badges img { + margin: 0 7px 7px 0; +} + +.badges { + margin: 16px 0 120px; +} + +.boring { + opacity: 0.5; +} + +.no-js code:not(.focuscomment) .boring { + display: none; +} + +.js code:not(.hide-boring) .ellipsis { + display: none; +} + +.focuscomment .hljs-comment { + font-weight: bold; + color: black; +} + +.focuscomment .boring { + opacity: 0.5; +} + +nav.sidebar li.part-title i.fa-github { + font-size: 20px; + padding-right: 5px; + padding-top: 12px; + position: relative; + top: 1px; +} + +.sidebar .sidebar-scrollbox { + padding: 10px 0 10px 10px; +} + +pre > .buttons { + visibility: visible; + opacity: 0.3; +} diff --git a/book/diagram/.gitignore b/book/diagram/.gitignore new file mode 100644 index 0000000..0017281 --- /dev/null +++ b/book/diagram/.gitignore @@ -0,0 +1,7 @@ +/*.aux +/*.fdb_latexmk +/*.fls +/*.log +/*.pdf +/*.png +/*.svg diff --git a/book/diagram/Makefile b/book/diagram/Makefile new file mode 100644 index 0000000..1723a9b --- /dev/null +++ b/book/diagram/Makefile @@ -0,0 +1,8 @@ +overview.svg: overview.pdf + pdf2svg $< $@ + +overview.pdf: overview.tex + latexmk $< + +overview.png: overview.svg + svgexport $< $@ 3x diff --git a/book/diagram/overview.tex b/book/diagram/overview.tex new file mode 100644 index 0000000..a613bb7 --- /dev/null +++ b/book/diagram/overview.tex @@ -0,0 +1,45 @@ +\documentclass{standalone} +\usepackage{makecell} +\usepackage{pgfplots} +\usepackage{sansmath} +\usetikzlibrary{arrows.meta} +\pgfplotsset{compat=1.16} +\begin{document} +\pagecolor{white} +\begin{tikzpicture}[ + x=1cm, + y=-.6cm, + every node/.append style={ + line width=1.5pt, + font=\Large\sansmath\sffamily, + }, + every path/.append style={ + >={Latex[length=10pt,width=8pt]}, + line width=1.5pt, + }, + execute at end node={\vphantom{bg}}, +] +\node[draw, rounded corners=5, inner xsep=30pt, inner ysep=2pt] + (bridge) at (0, .25) {\makecell{\texttt{\#\hspace{-1pt}[}cxx::bridge\texttt{]} mod\\[-4pt]description of boundary}}; +\node[draw, rounded corners, inner xsep=10pt, inner ysep=6pt, text depth=1pt] + (rust-bindings) at (-3.5, 6.5) {Rust bindings}; +\node[draw, rounded corners, inner xsep=10pt, inner ysep=6pt, text depth=1pt] + (cpp-bindings) at (3.5, 6.5) {C\texttt{++} bindings}; +\node[inner xsep=4pt, inner ysep=-0pt] + (rust-code) at (-9, 6.5) {\makecell[r]{\\[-8pt]Rust\\[-4pt]code}}; +\node[inner xsep=4pt, inner ysep=-0pt] + (cpp-code) at (9, 6.5) {\makecell[l]{\\[-8pt]C\texttt{++}\\[-4pt]code}}; +\draw (bridge) -- (0, 4); +\draw[<->] (rust-bindings) |- (0, 4) -| (cpp-bindings); +\draw[<->] (rust-code) -- (rust-bindings); +\draw[<->, dash pattern=on 8pt off 6pt] (rust-bindings) -- (cpp-bindings); +\draw[<->] (cpp-bindings) -- (cpp-code); +\draw (-.75, 4) node[anchor=south east] {Macro expansion}; +\draw (.75, 4) node[anchor=south west] {Code generation}; +\draw (0, 6.5) node[anchor=south, inner ysep=4pt] {Hidden C ABI}; +\draw (-6.75, 6.5) node[anchor=south, inner ysep=1pt] {\makecell{Safe\\[-4pt]straightforward\\[-4pt]Rust APIs}}; +\draw (6.75, 6.5) node[anchor=south, inner ysep=1pt] {\makecell{Straightforward\\[-4pt]C\texttt{++} APIs}}; +\pgfresetboundingbox\path + (-9.5, 0) -- (rust-bindings.south)+(0, .3) -- (9.5, 0) -- (bridge.north); +\end{tikzpicture} +\end{document} diff --git a/book/eslint.config.mjs b/book/eslint.config.mjs new file mode 100644 index 0000000..6ad9c6c --- /dev/null +++ b/book/eslint.config.mjs @@ -0,0 +1,8 @@ +import pluginJs from '@eslint/js'; + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + { ignores: ['build/*'] }, + { files: ['**/*.js'], languageOptions: { sourceType: 'commonjs' } }, + pluginJs.configs.recommended, +]; diff --git a/book/package-lock.json b/book/package-lock.json new file mode 100644 index 0000000..a6af7c2 --- /dev/null +++ b/book/package-lock.json @@ -0,0 +1,1365 @@ +{ + "name": "cxx-book-build", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cxx-book-build", + "version": "0.0.0", + "dependencies": { + "cheerio": "^1.0.0", + "html-entities": "^2.5.2" + }, + "devDependencies": { + "@eslint/js": "^9.19.0", + "eslint": "^9.19.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", + "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.10.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", + "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.10.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.19.0", + "@eslint/plugin-kit": "^0.2.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/undici": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/book/package.json b/book/package.json new file mode 100644 index 0000000..bbed765 --- /dev/null +++ b/book/package.json @@ -0,0 +1,16 @@ +{ + "name": "cxx-book-build", + "version": "0.0.0", + "main": "build.js", + "dependencies": { + "cheerio": "^1.0.0", + "html-entities": "^2.5.2" + }, + "devDependencies": { + "@eslint/js": "^9.19.0", + "eslint": "^9.19.0" + }, + "prettier": { + "singleQuote": true + } +} diff --git a/book/src/404.md b/book/src/404.md new file mode 100644 index 0000000..c5b71f2 --- /dev/null +++ b/book/src/404.md @@ -0,0 +1,5 @@ +### Whoops, this page doesn’t exist :-( + +
    + +ferris diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 0000000..2d2502e --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,37 @@ +# Summary + +- [Rust ❤️ C++](index.md) + +- [Core concepts](concepts.md) + +- [Tutorial](tutorial.md) + +- [Other Rust–C++ interop tools](context.md) + +- [Multi-language build system options](building.md) + - [Cargo](build/cargo.md) + - [Bazel or Buck2](build/bazel.md) + - [CMake](build/cmake.md) + - [More...](build/other.md) + +- [Reference: the bridge module](reference.md) + - [extern "Rust"](extern-rust.md) + - [extern "C++"](extern-c++.md) + - [Shared types](shared.md) + - [Attributes](attributes.md) + - [Async functions](async.md) + - [Error handling](binding/result.md) + +- [Reference: built-in bindings](bindings.md) + - [String — rust::String](binding/string.md) + - [&str — rust::Str](binding/str.md) + - [&[T], &mut [T] — rust::Slice\](binding/slice.md) + - [CxxString — std::string](binding/cxxstring.md) + - [Box\ — rust::Box\](binding/box.md) + - [UniquePtr\ — std::unique\_ptr\](binding/uniqueptr.md) + - [SharedPtr\ — std::shared\_ptr\](binding/sharedptr.md) + - [Vec\ — rust::Vec\](binding/vec.md) + - [CxxVector\ — std::vector\](binding/cxxvector.md) + - [*mut T, *const T raw pointers](binding/rawptr.md) + - [Function pointers](binding/fn.md) + - [Result\](binding/result.md) diff --git a/book/src/async.md b/book/src/async.md new file mode 100644 index 0000000..0f3fed1 --- /dev/null +++ b/book/src/async.md @@ -0,0 +1,86 @@ +{{#title Async functions — Rust ♡ C++}} +# Async functions + +Direct FFI of async functions is absolutely in scope for CXX (on C++20 and up) +but is not implemented yet in the current release. We are aiming for an +implementation that is as easy as: + +```rust,noplayground +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + async fn doThing(arg: Arg) -> Ret; + } +} +``` + +```cpp +rust::Future doThing(Arg arg) { + auto v1 = co_await f(); + auto v2 = co_await g(arg); + co_return v1 + v2; +} +``` + +## Workaround + +For now the recommended approach is to handle the return codepath over a oneshot +channel (such as [`futures::channel::oneshot`]) represented in an opaque Rust +type on the FFI. + +[`futures::channel::oneshot`]: https://docs.rs/futures/0.3.8/futures/channel/oneshot/index.html + +```rust,noplayground +// bridge.rs + +use futures::channel::oneshot; + +#[cxx::bridge] +mod ffi { + extern "Rust" { + type DoThingContext; + } + + unsafe extern "C++" { + include!("path/to/bridge_shim.h"); + + fn shim_doThing( + arg: Arg, + done: fn(Box, ret: Ret), + ctx: Box, + ); + } +} + +struct DoThingContext(oneshot::Sender); + +pub async fn do_thing(arg: Arg) -> Ret { + let (tx, rx) = oneshot::channel(); + let context = Box::new(DoThingContext(tx)); + + ffi::shim_doThing( + arg, + |context, ret| { let _ = context.0.send(ret); }, + context, + ); + + rx.await.unwrap() +} +``` + +```cpp +// bridge_shim.cc + +#include "path/to/bridge.rs.h" +#include "rust/cxx.h" + +void shim_doThing( + Arg arg, + rust::Fn ctx, Ret ret)> done, + rust::Box ctx) noexcept { + doThing(arg) + .then([done, ctx(std::move(ctx))](auto &&res) mutable { + (*done)(std::move(ctx), std::move(res)); + }); +} +``` diff --git a/book/src/attributes.md b/book/src/attributes.md new file mode 100644 index 0000000..9c33b77 --- /dev/null +++ b/book/src/attributes.md @@ -0,0 +1,75 @@ +{{#title Attributes — Rust ♡ C++}} +# Attributes + +## namespace + +The top-level cxx::bridge attribute macro takes an optional `namespace` argument +to control the C++ namespace into which to emit extern Rust items and the +namespace in which to expect to find the extern C++ items. + +```rust,noplayground +#[cxx::bridge(namespace = "path::of::my::company")] +mod ffi { + extern "Rust" { + type MyType; // emitted to path::of::my::company::MyType + } + + extern "C++" { + type TheirType; // refers to path::of::my::company::TheirType + } +} +``` + +Additionally, a `#[namespace = "..."]` attribute may be used inside the bridge +module on any extern block or individual item. An item will inherit the +namespace specified on its surrounding extern block if any, otherwise the +namespace specified with the top level cxx::bridge attribute if any, otherwise +the global namespace. + +```rust,noplayground +#[cxx::bridge(namespace = "third_priority")] +mod ffi { + #[namespace = "second_priority"] + extern "Rust" { + fn f(); + + #[namespace = "first_priority"] + fn g(); + } + + extern "Rust" { + fn h(); + } +} +``` + +The above would result in functions `::second_priority::f`, +`::first_priority::g`, `::third_priority::h`. + +## rust\_name, cxx\_name + +Sometimes you want the Rust name of a function or type to differ from its C++ +name. Importantly, this enables binding multiple overloads of the same C++ +function name using distinct Rust names. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + #[rust_name = "i32_overloaded_function"] + fn cOverloadedFunction(x: i32) -> String; + #[rust_name = "str_overloaded_function"] + fn cOverloadedFunction(x: &str) -> String; + } +} +``` + +The `#[rust_name = "..."]` attribute replaces the name that Rust should use for +this function, and an analogous `#[cxx_name = "..."]` attribute replaces the +name that C++ should use. + +Either of the two attributes may be used on extern "Rust" as well as extern +"C++" functions, according to which one you find clearer in context. + +The same attribute works for renaming functions, opaque types, shared +structs and enums, and enum variants. diff --git a/book/src/binding/box.md b/book/src/binding/box.md new file mode 100644 index 0000000..abd40d6 --- /dev/null +++ b/book/src/binding/box.md @@ -0,0 +1,120 @@ +{{#title rust::Box — Rust ♡ C++}} +# rust::Box\ + +### Public API: + +```cpp,hidelines=... +// rust/cxx.h +... +...#include +... +...namespace rust { + +template +class Box final { +public: + using element_type = T; + using const_pointer = + typename std::add_pointer::type>::type; + using pointer = typename std::add_pointer::type; + + Box(Box &&) noexcept; + ~Box() noexcept; + + explicit Box(const T &); + explicit Box(T &&); + + Box &operator=(Box &&) & noexcept; + + const T *operator->() const noexcept; + const T &operator*() const noexcept; + T *operator->() noexcept; + T &operator*() noexcept; + + template + static Box in_place(Fields &&...); + + void swap(Box &) noexcept; + + // Important: requires that `raw` came from an into_raw call. Do not + // pass a pointer from `new` or any other source. + static Box from_raw(T *) noexcept; + + T *into_raw() noexcept; +}; +... +...} // namespace rust +``` + +### Restrictions: + +Box\ does not support T being an opaque C++ type. You should use +[UniquePtr\](uniqueptr.md) or [SharedPtr\](sharedptr.md) instead for +transferring ownership of opaque C++ types on the language boundary. + +If T is an opaque Rust type, the Rust type is required to be [Sized] i.e. size +known at compile time. In the future we may introduce support for dynamically +sized opaque Rust types. + +[Sized]: https://doc.rust-lang.org/std/marker/trait.Sized.html + +## Example + +This program uses a Box to pass ownership of some opaque piece of Rust state +over to C++ and then back to a Rust callback, which is a useful pattern for +implementing [async functions over FFI](../async.md). + +```rust,noplayground +// src/main.rs + +use std::io::Write; + +#[cxx::bridge] +mod ffi { + extern "Rust" { + type File; + } + + unsafe extern "C++" { + include!("example/include/example.h"); + + fn f( + callback: fn(Box, fst: &str, snd: &str), + out: Box, + ); + } +} + +pub struct File(std::fs::File); + +fn main() { + let out = std::fs::File::create("example.log").unwrap(); + + ffi::f( + |mut out, fst, snd| { let _ = write!(out.0, "{}{}\n", fst, snd); }, + Box::new(File(out)), + ); +} +``` + +```cpp +// include/example.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +void f(rust::Fn, rust::Str, rust::Str)> callback, + rust::Box out); +``` + +```cpp +// include/example.cc + +#include "example/include/example.h" + +void f(rust::Fn, rust::Str, rust::Str)> callback, + rust::Box out) { + callback(std::move(out), "fearless", "concurrency"); +} +``` diff --git a/book/src/binding/cxxstring.md b/book/src/binding/cxxstring.md new file mode 100644 index 0000000..dc2619c --- /dev/null +++ b/book/src/binding/cxxstring.md @@ -0,0 +1,140 @@ +{{#title std::string — Rust ♡ C++}} +# std::string + +The Rust binding of std::string is called **[`CxxString`]**. See the link for +documentation of the Rust API. + +[`CxxString`]: https://docs.rs/cxx/*/cxx/struct.CxxString.html + +### Restrictions: + +Rust code can never obtain a CxxString by value. C++'s string requires a move +constructor and may hold internal pointers, which is not compatible with Rust's +move behavior. Instead in Rust code we will only ever look at a CxxString +through a reference or smart pointer, as in &CxxString or Pin\<&mut CxxString\> +or UniquePtr\. + +In order to construct a CxxString on the stack from Rust, you must use the +[`let_cxx_string!`] macro which will pin the string properly. The code below +uses this in one place, and the link covers the syntax. + +[`let_cxx_string!`]: https://docs.rs/cxx/*/cxx/macro.let_cxx_string.html + +## Example + +This example uses C++17's std::variant to build a toy JSON type. JSON can hold +various types including strings, and JSON's object type is a map with string +keys. The example demonstrates Rust indexing into one of those maps. + +```rust,noplayground +// src/main.rs + +use cxx::let_cxx_string; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/json.h"); + + #[cxx_name = "json"] + type Json; + #[cxx_name = "object"] + type Object; + + fn isNull(self: &Json) -> bool; + fn isNumber(self: &Json) -> bool; + fn isString(self: &Json) -> bool; + fn isArray(self: &Json) -> bool; + fn isObject(self: &Json) -> bool; + + fn getNumber(self: &Json) -> f64; + fn getString(self: &Json) -> &CxxString; + fn getArray(self: &Json) -> &CxxVector; + fn getObject(self: &Json) -> &Object; + + #[cxx_name = "at"] + fn get<'a>(self: &'a Object, key: &CxxString) -> &'a Json; + + fn load_config() -> UniquePtr; + } +} + +fn main() { + let config = ffi::load_config(); + + let_cxx_string!(key = "name"); + println!("{}", config.getObject().get(&key).getString()); +} +``` + +```cpp +// include/json.h + +#pragma once +#include +#include +#include +#include +#include + +class json final { +public: + static const json null; + using number = double; + using string = std::string; + using array = std::vector; + using object = std::map; + + json() noexcept = default; + json(const json &) = default; + json(json &&) = default; + template + json(T &&...value) : value(std::forward(value)...) {} + + bool isNull() const; + bool isNumber() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + number getNumber() const; + const string &getString() const; + const array &getArray() const; + const object &getObject() const; + +private: + std::variant value; +}; + +using object = json::object; + +std::unique_ptr load_config(); +``` + +```cpp +// include/json.cc + +#include "example/include/json.h" +#include +#include + +const json json::null{}; +bool json::isNull() const { return std::holds_alternative(value); } +bool json::isNumber() const { return std::holds_alternative(value); } +bool json::isString() const { return std::holds_alternative(value); } +bool json::isArray() const { return std::holds_alternative(value); } +bool json::isObject() const { return std::holds_alternative(value); } +json::number json::getNumber() const { return std::get(value); } +const json::string &json::getString() const { return std::get(value); } +const json::array &json::getArray() const { return std::get(value); } +const json::object &json::getObject() const { return std::get(value); } + +std::unique_ptr load_config() { + return std::make_unique( + std::in_place_type, + std::initializer_list>{ + {"name", "cxx-example"}, + {"edition", 2021.}, + {"repository", json::null}}); +} +``` diff --git a/book/src/binding/cxxvector.md b/book/src/binding/cxxvector.md new file mode 100644 index 0000000..fd95a2d --- /dev/null +++ b/book/src/binding/cxxvector.md @@ -0,0 +1,62 @@ +{{#title std::vector — Rust ♡ C++}} +# std::vector\ + +The Rust binding of std::vector\ is called **[`CxxVector`]**. See the +link for documentation of the Rust API. + +[`CxxVector`]: https://docs.rs/cxx/*/cxx/struct.CxxVector.html + +### Restrictions: + +Rust code can never obtain a CxxVector by value. Instead in Rust code we will +only ever look at a vector behind a reference or smart pointer, as in +&CxxVector\ or UniquePtr\\>. + +CxxVector\ does not support T being an opaque Rust type. You should use a +Vec\ (C++ rust::Vec\) instead for collections of opaque Rust types on +the language boundary. + +## Example + +This program involves Rust code converting a `CxxVector` (i.e. +`std::vector`) into a Rust `Vec`. + +```rust,noplayground +// src/main.rs + +#![no_main] // main defined in C++ by main.cc + +use cxx::{CxxString, CxxVector}; + +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn f(vec: &CxxVector); + } +} + +fn f(vec: &CxxVector) { + let vec: Vec = vec + .iter() + .map(|s| s.to_string_lossy().into_owned()) + .collect(); + g(&vec); +} + +fn g(vec: &[String]) { + println!("{:?}", vec); +} +``` + +```cpp +// src/main.cc + +#include "example/src/main.rs.h" +#include +#include + +int main() { + std::vector vec{"fearless", "concurrency"}; + f(vec); +} +``` diff --git a/book/src/binding/fn.md b/book/src/binding/fn.md new file mode 100644 index 0000000..a32ad52 --- /dev/null +++ b/book/src/binding/fn.md @@ -0,0 +1,34 @@ +{{#title Function pointers — Rust ♡ C++}} +# Function pointers + +### Public API: + +```cpp,hidelines=... +// rust/cxx.h +... +...namespace rust { + +template +class Fn; + +template +class Fn final { +public: + Ret operator()(Args... args) const noexcept; + Fn operator*() const noexcept; +}; +... +...} // namespace rust +``` + +### Restrictions: + +Function pointers with a Result return type are not implemented yet. + +Passing a function pointer from C++ to Rust is not implemented yet, only from +Rust to an `extern "C++"` function is implemented. + +## Example + +Function pointers are commonly useful for implementing [async functions over +FFI](../async.md). See the example code on that page. diff --git a/book/src/binding/rawptr.md b/book/src/binding/rawptr.md new file mode 100644 index 0000000..1794211 --- /dev/null +++ b/book/src/binding/rawptr.md @@ -0,0 +1,100 @@ +{{#title *mut T, *const T — Rust ♡ C++}} +# *mut T, *const T + +Generally you should use references (`&mut T`, `&T`) or [std::unique_ptr\] +where possible over raw pointers, but raw pointers are available too as an +unsafe fallback option. + +[std::unique_ptr\]: uniqueptr.md + +### Restrictions: + +Extern functions and function pointers taking a raw pointer as an argument must +be declared `unsafe fn` i.e. unsafe to call. The same does not apply to +functions which only *return* a raw pointer, though presumably doing anything +useful with the returned pointer is going to involve unsafe code elsewhere +anyway. + +## Example + +This example illustrates making a Rust call to a canonical C-style `main` +signature involving `char *argv[]`. + +```cpp +// include/args.h + +#pragma once + +void parseArgs(int argc, char *argv[]); +``` + +```cpp +// src/args.cc + +#include "example/include/args.h" +#include + +void parseArgs(int argc, char *argv[]) { + std::cout << argc << std::endl; + for (int i = 0; i < argc; i++) { + std::cout << '"' << argv[i] << '"' << std::endl; + } +} +``` + +```rust,noplayground +// src/main.rs + +use std::env; +use std::ffi::CString; +use std::os::raw::c_char; +use std::os::unix::ffi::OsStrExt; +use std::ptr; + +#[cxx::bridge] +mod ffi { + extern "C++" { + include!("example/include/args.h"); + + unsafe fn parseArgs(argc: i32, argv: *mut *mut c_char); + } +} + +fn main() { + // Convert from OsString to nul-terminated CString, truncating each argument + // at the first inner nul byte if present. + let args: Vec = env::args_os() + .map(|os_str| { + let bytes = os_str.as_bytes(); + CString::new(bytes).unwrap_or_else(|nul_error| { + let nul_position = nul_error.nul_position(); + let mut bytes = nul_error.into_vec(); + bytes.truncate(nul_position); + CString::new(bytes).unwrap() + }) + }) + .collect(); + + // Convert from Vec of owned strings to Vec<*mut c_char> of + // borrowed string pointers. + // + // Once extern type stabilizes (https://github.com/rust-lang/rust/issues/43467) + // and https://internals.rust-lang.org/t/pre-rfc-make-cstr-a-thin-pointer/6258 + // is implemented, and CStr pointers become thin, we can sidestep this step + // by accumulating the args as Vec> up front, then simply casting + // from *mut [Box] to *mut [*mut CStr] to *mut *mut c_char. + let argc = args.len(); + let mut argv: Vec<*mut c_char> = Vec::with_capacity(argc + 1); + for arg in &args { + argv.push(arg.as_ptr() as *mut c_char); + } + argv.push(ptr::null_mut()); // Nul terminator. + + unsafe { + ffi::parseArgs(argc as i32, argv.as_mut_ptr()); + } + + // The CStrings go out of scope here. C function must not have held on to + // the pointers beyond this point. +} +``` diff --git a/book/src/binding/result.md b/book/src/binding/result.md new file mode 100644 index 0000000..2a47531 --- /dev/null +++ b/book/src/binding/result.md @@ -0,0 +1,148 @@ +{{#title Result — Rust ♡ C++}} +# Result\ + +Result\ is allowed as the return type of an extern function in either +direction. Its behavior is to translate to/from C++ exceptions. If your codebase +does not use C++ exceptions, or prefers to represent fallibility using something +like outcome\, leaf::result\, StatusOr\, etc then you'll need to +handle the translation of those to Rust Result\ using your own shims for +now. Better support for this is planned. + +If an exception is thrown from an `extern "C++"` function that is *not* declared +by the CXX bridge to return Result, the program calls C++'s `std::terminate`. +The behavior is equivalent to the same exception being thrown through a +`noexcept` C++ function. + +If a panic occurs in *any* `extern "Rust"` function, regardless of whether it is +declared by the CXX bridge to return Result, a message is logged and the program +calls Rust's `std::process::abort`. + +## Returning Result from Rust to C++ + +An `extern "Rust"` function returning a Result turns into a `throw` in C++ if +the Rust side produces an error. + +Note that the return type written inside of cxx::bridge must be written without +a second type parameter. Only the Ok type is specified for the purpose of the +FFI. The Rust *implementation* (outside of the bridge module) may pick any error +type as long as it has a std::fmt::Display impl. + +```rust,noplayground +# use std::io; +# +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn fallible1(depth: usize) -> Result; + fn fallible2() -> Result<()>; + } +} + +fn fallible1(depth: usize) -> anyhow::Result { + if depth == 0 { + return Err(anyhow::Error::msg("fallible1 requires depth > 0")); + } + ... +} + +fn fallible2() -> Result<(), io::Error> { + ... + Ok(()) +} +``` + +The exception that gets thrown by CXX on the C++ side is always of type +`rust::Error` and has the following C++ public API. The `what()` member function +gives the error message according to the Rust error's std::fmt::Display impl. + +```cpp,hidelines=... +// rust/cxx.h +... +...namespace rust { + +class Error final : public std::exception { +public: + Error(const Error &); + Error(Error &&) noexcept; + ~Error() noexcept; + + Error &operator=(const Error &) &; + Error &operator=(Error &&) & noexcept; + + const char *what() const noexcept override; +}; +... +...} // namespace rust +``` + +## Returning Result from C++ to Rust + +An `extern "C++"` function returning a Result turns into a `catch` in C++ that +converts the exception into an Err for Rust. + +Note that the return type written inside of cxx::bridge must be written without +a second type parameter. Only the Ok type is specified for the purpose of the +FFI. The resulting error type created by CXX when an `extern "C++"` function +throws will always be of type **[`cxx::Exception`]**. + +[`cxx::Exception`]: https://docs.rs/cxx/*/cxx/struct.Exception.html + +```rust,noplayground +# use std::process; +# +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/example.h"); + fn fallible1(depth: usize) -> Result; + fn fallible2() -> Result<()>; + } +} + +fn main() { + if let Err(err) = ffi::fallible1(99) { + eprintln!("Error: {}", err); + process::exit(1); + } +} +``` + +The specific set of caught exceptions and the conversion to error message are +both customizable. The way you do this is by defining a template function +`rust::behavior::trycatch` with a suitable signature inside any one of the +headers `include!`'d by your cxx::bridge. + +The template signature is required to be: + +```cpp +namespace rust { +namespace behavior { + +template +static void trycatch(Try &&func, Fail &&fail) noexcept; + +} // namespace behavior +} // namespace rust +``` + +The default `trycatch` used by CXX if you have not provided your own is the +following. You must follow the same pattern: invoke `func` with no arguments, +catch whatever exception(s) you want, and invoke `fail` with the error message +you'd like for the Rust error to have. + +```cpp,hidelines=... +...#include +... +...namespace rust { +...namespace behavior { +... +template +static void trycatch(Try &&func, Fail &&fail) noexcept try { + func(); +} catch (const std::exception &e) { + fail(e.what()); +} +... +...} // namespace behavior +...} // namespace rust +``` diff --git a/book/src/binding/sharedptr.md b/book/src/binding/sharedptr.md new file mode 100644 index 0000000..a3b7070 --- /dev/null +++ b/book/src/binding/sharedptr.md @@ -0,0 +1,80 @@ +{{#title std::shared_ptr — Rust ♡ C++}} +# std::shared\_ptr\ + +The Rust binding of std::shared\_ptr\ is called **[`SharedPtr`]**. See +the link for documentation of the Rust API. + +[`SharedPtr`]: https://docs.rs/cxx/*/cxx/struct.SharedPtr.html + +### Restrictions: + +SharedPtr\ does not support T being an opaque Rust type. You should use a +Box\ (C++ [rust::Box\](box.md)) instead for transferring ownership of +opaque Rust types on the language boundary. + +## Example + +```rust,noplayground +// src/main.rs + +use std::ops::Deref; +use std::ptr; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/example.h"); + + type Object; + + fn create_shared_ptr() -> SharedPtr; + } +} + +fn main() { + let ptr1 = ffi::create_shared_ptr(); + + { + // Create a second shared_ptr holding shared ownership of the same + // object. There is still only one Object but two SharedPtr. + // Both pointers point to the same object on the heap. + let ptr2 = ptr1.clone(); + assert!(ptr::eq(ptr1.deref(), ptr2.deref())); + + // ptr2 goes out of scope, but Object is not destroyed yet. + } + + println!("say goodbye to Object"); + + // ptr1 goes out of scope and Object is destroyed. +} +``` + +```cpp +// include/example.h + +#pragma once +#include + +class Object { +public: + Object(); + ~Object(); +}; + +std::shared_ptr create_shared_ptr(); +``` + +```cpp +// src/example.cc + +#include "example/include/example.h" +#include + +Object::Object() { std::cout << "construct Object" << std::endl; } +Object::~Object() { std::cout << "~Object" << std::endl; } + +std::shared_ptr create_shared_ptr() { + return std::make_shared(); +} +``` diff --git a/book/src/binding/slice.md b/book/src/binding/slice.md new file mode 100644 index 0000000..4054bce --- /dev/null +++ b/book/src/binding/slice.md @@ -0,0 +1,178 @@ +{{#title rust::Slice — Rust ♡ C++}} +# rust::Slice\, rust::Slice\ + +- Rust `&[T]` is written `rust::Slice` in C++ +- Rust `&mut [T]` is written `rust::Slice` in C++ + +### Public API: + +```cpp,hidelines=... +// rust/cxx.h +... +...#include +...#include +... +...namespace rust { + +template +class Slice final { +public: + using value_type = T; + + Slice() noexcept; + Slice(const Slice &) noexcept; + Slice(T *, size_t count) noexcept; + + template + explicit Slice(C &c) : Slice(c.data(), c.size()); + + Slice &operator=(Slice &&) & noexcept; + Slice &operator=(const Slice &) & noexcept + requires std::is_const_v; + + T *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + bool empty() const noexcept; + + T &operator[](size_t n) const noexcept; + T &at(size_t n) const; + T &front() const noexcept; + T &back() const noexcept; + + class iterator; + iterator begin() const noexcept; + iterator end() const noexcept; + + void swap(Slice &) noexcept; +}; +... +...template +...class Slice::iterator final { +...public: +...#if __cplusplus >= 202002L +... using iterator_category = std::contiguous_iterator_tag; +...#else +... using iterator_category = std::random_access_iterator_tag; +...#endif +... using value_type = T; +... using pointer = T *; +... using reference = T &; +... +... T &operator*() const noexcept; +... T *operator->() const noexcept; +... T &operator[](ptrdiff_t) const noexcept; +... +... iterator &operator++() noexcept; +... iterator operator++(int) noexcept; +... iterator &operator--() noexcept; +... iterator operator--(int) noexcept; +... +... iterator &operator+=(ptrdiff_t) noexcept; +... iterator &operator-=(ptrdiff_t) noexcept; +... iterator operator+(ptrdiff_t) const noexcept; +... iterator operator-(ptrdiff_t) const noexcept; +... ptrdiff_t operator-(const iterator &) const noexcept; +... +... bool operator==(const iterator &) const noexcept; +... bool operator!=(const iterator &) const noexcept; +... bool operator<(const iterator &) const noexcept; +... bool operator>(const iterator &) const noexcept; +... bool operator<=(const iterator &) const noexcept; +... bool operator>=(const iterator &) const noexcept; +...}; +... +...} // namespace rust +``` + +### Restrictions: + +T must not be an opaque Rust type or opaque C++ type. Support for opaque Rust +types in slices is coming. + +Allowed as function argument or return value. Not supported in shared structs. + +Only rust::Slice\ is copy-assignable, not rust::Slice\. (Both are +move-assignable.) You'll need to write std::move occasionally as a reminder that +accidentally exposing overlapping &mut \[T\] to Rust is UB. + +## Example + +This example is a C++ program that constructs a slice containing JSON data (by +reading from stdin, but it could be from anywhere), then calls into Rust to +pretty-print that JSON data into a std::string via the [serde_json] and +[serde_transcode] crates. + +[serde_json]: https://github.com/serde-rs/json +[serde_transcode]: https://github.com/sfackler/serde-transcode + +```rust,noplayground +// src/main.rs + +#![no_main] // main defined in C++ by main.cc + +use cxx::CxxString; +use std::io::{self, Write}; +use std::pin::Pin; + +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> Result<()>; + } +} + +struct WriteToCxxString<'a>(Pin<&'a mut CxxString>); + +impl<'a> Write for WriteToCxxString<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.as_mut().push_bytes(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> serde_json::Result<()> { + let writer = WriteToCxxString(output); + let mut deserializer = serde_json::Deserializer::from_slice(input); + let mut serializer = serde_json::Serializer::pretty(writer); + serde_transcode::transcode(&mut deserializer, &mut serializer) +} +``` + +```cpp +// src/main.cc + +#include "example/src/main.rs.h" +#include +#include +#include +#include + +int main() { + // Read json from stdin. + std::istreambuf_iterator begin{std::cin}, end; + std::vector input{begin, end}; + rust::Slice slice{input.data(), input.size()}; + + // Prettify using serde_json and serde_transcode. + std::string output; + prettify_json(slice, output); + + // Write to stdout. + std::cout << output << std::endl; +} +``` + +Testing the example: + +```console +$ echo '{"fearless":"concurrency"}' | cargo run + Finished dev [unoptimized + debuginfo] target(s) in 0.02s + Running `target/debug/example` +{ + "fearless": "concurrency" +} +``` diff --git a/book/src/binding/str.md b/book/src/binding/str.md new file mode 100644 index 0000000..214d12d --- /dev/null +++ b/book/src/binding/str.md @@ -0,0 +1,121 @@ +{{#title rust::Str — Rust ♡ C++}} +# rust::Str + +### Public API: + +```cpp,hidelines=... +// rust/cxx.h +... +...#include +...#include +... +...namespace rust { + +class Str final { +public: + Str() noexcept; + Str(const Str &) noexcept; + Str(const String &) noexcept; + + // Throws std::invalid_argument if not utf-8. + Str(const std::string &); + Str(const char *); + Str(const char *, size_t); + + Str &operator=(const Str &) & noexcept; + + explicit operator std::string() const; +#if __cplusplus >= 201703L + explicit operator std::string_view() const; +#endif + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + bool empty() const noexcept; + + using iterator = const char *; + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; + + void swap(Str &) noexcept; +}; + +std::ostream &operator<<(std::ostream &, const Str &); +... +...} // namespace rust +``` + +### Notes: + +**Be aware that rust::Str behaves like &str i.e. it is a borrow!** C++ +needs to be mindful of the lifetimes at play. + +Just to reiterate: &str is rust::Str. Do not try to write &str as `const +rust::Str &`. A language-level C++ reference is not able to capture the fat +pointer nature of &str. + +### Restrictions: + +Allowed as function argument or return value. Not supported in shared structs +yet. `&mut str` is not supported yet, but is also extremely obscure so this is +fine. + +## Example + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn r(greeting: &str); + } + + unsafe extern "C++" { + include!("example/include/greeting.h"); + fn c(greeting: &str); + } +} + +fn r(greeting: &str) { + println!("{}", greeting); +} + +fn main() { + ffi::c("hello from Rust"); +} +``` + +```cpp +// include/greeting.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +void c(rust::Str greeting); +``` + +```cpp +// src/greeting.cc + +#include "example/include/greeting.h" +#include + +void c(rust::Str greeting) { + std::cout << greeting << std::endl; + r("hello from C++"); +} +``` diff --git a/book/src/binding/string.md b/book/src/binding/string.md new file mode 100644 index 0000000..7875685 --- /dev/null +++ b/book/src/binding/string.md @@ -0,0 +1,134 @@ +{{#title rust::String — Rust ♡ C++}} +# rust::String + +### Public API: + +```cpp,hidelines=... +// rust/cxx.h +... +...#include +...#include +... +...namespace rust { + +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + // Throws std::invalid_argument if not UTF-8. + String(const std::string &); + String(const char *); + String(const char *, size_t); + String(const char8_t *); + String(const char8_t *, size_t); + + // Replaces invalid UTF-8 data with the replacement character (U+FFFD). + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, size_t) noexcept; + + // Throws std::invalid_argument if not UTF-16. + String(const char16_t *); + String(const char16_t *, size_t); + + // Replaces invalid UTF-16 data with the replacement character (U+FFFD). + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, size_t) noexcept; + + String &operator=(const String &) & noexcept; + String &operator=(String &&) & noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; +}; + +std::ostream &operator<<(std::ostream &, const String &); +... +...} // namespace rust +``` + +### Restrictions: + +None. Strings may be used as function arguments and function return values, by +value or by reference, as well as fields of shared structs. + +## Example + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + struct ConcatRequest { + fst: String, + snd: String, + } + + unsafe extern "C++" { + include!("example/include/concat.h"); + fn concat(r: ConcatRequest) -> String; + } +} + +fn main() { + let concatenated = ffi::concat(ffi::ConcatRequest { + fst: "fearless".to_owned(), + snd: "concurrency".to_owned(), + }); + println!("concatenated: {:?}", concatenated); +} +``` + +```cpp +// include/concat.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +rust::String concat(ConcatRequest r); +``` + +```cpp +// src/concat.cc + +#include "example/include/concat.h" + +rust::String concat(ConcatRequest r) { + // The full suite of operator overloads hasn't been added + // yet on rust::String, but we can get it done like this: + return std::string(r.fst) + std::string(r.snd); +} +``` diff --git a/book/src/binding/uniqueptr.md b/book/src/binding/uniqueptr.md new file mode 100644 index 0000000..eefbc34 --- /dev/null +++ b/book/src/binding/uniqueptr.md @@ -0,0 +1,63 @@ +{{#title std::unique_ptr — Rust ♡ C++}} +# std::unique\_ptr\ + +The Rust binding of std::unique\_ptr\ is called **[`UniquePtr`]**. See +the link for documentation of the Rust API. + +[`UniquePtr`]: https://docs.rs/cxx/*/cxx/struct.UniquePtr.html + +### Restrictions: + +Only `std::unique_ptr>` is currently supported. Custom +deleters may be supported in the future. + +UniquePtr\ does not support T being an opaque Rust type. You should use a +Box\ (C++ [rust::Box\](box.md)) instead for transferring ownership of +opaque Rust types on the language boundary. + +## Example + +UniquePtr is commonly useful for returning opaque C++ objects to Rust. This use +case was featured in the [*blobstore tutorial*](../tutorial.md). + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/blobstore.h"); + + type BlobstoreClient; + + fn new_blobstore_client() -> UniquePtr; + // ... + } +} + +fn main() { + let client = ffi::new_blobstore_client(); + // ... +} +``` + +```cpp +// include/blobstore.h + +#pragma once +#include + +class BlobstoreClient; + +std::unique_ptr new_blobstore_client(); +``` + +```cpp +// src/blobstore.cc + +#include "example/include/blobstore.h" + +std::unique_ptr new_blobstore_client() { + return std::make_unique(); +} +``` diff --git a/book/src/binding/vec.md b/book/src/binding/vec.md new file mode 100644 index 0000000..3e883a2 --- /dev/null +++ b/book/src/binding/vec.md @@ -0,0 +1,200 @@ +{{#title rust::Vec — Rust ♡ C++}} +# rust::Vec\ + +### Public API: + +```cpp,hidelines=... +// rust/cxx.h +... +...#include +...#include +...#include +... +...namespace rust { + +template +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) & noexcept; + Vec &operator=(const Vec &) &; + + size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + size_t capacity() const noexcept; + + const T &operator[](size_t n) const noexcept; + const T &at(size_t n) const; + const T &front() const; + const T &back() const; + + T &operator[](size_t n) noexcept; + T &at(size_t n); + T &front(); + T &back(); + + void reserve(size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template + void emplace_back(Args &&...args); + void truncate(size_t len); + void clear(); + + class iterator; + iterator begin() noexcept; + iterator end() noexcept; + + class const_iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; +}; +... +...template +...class Vec::iterator final { +...public: +...#if __cplusplus >= 202002L +... using iterator_category = std::contiguous_iterator_tag; +...#else +... using iterator_category = std::random_access_iterator_tag; +...#endif +... using value_type = T; +... using pointer = T *; +... using reference = T &; +... +... T &operator*() const noexcept; +... T *operator->() const noexcept; +... T &operator[](ptrdiff_t) const noexcept; +... +... iterator &operator++() noexcept; +... iterator operator++(int) noexcept; +... iterator &operator--() noexcept; +... iterator operator--(int) noexcept; +... +... iterator &operator+=(ptrdiff_t) noexcept; +... iterator &operator-=(ptrdiff_t) noexcept; +... iterator operator+(ptrdiff_t) const noexcept; +... iterator operator-(ptrdiff_t) const noexcept; +... ptrdiff_t operator-(const iterator &) const noexcept; +... +... bool operator==(const iterator &) const noexcept; +... bool operator!=(const iterator &) const noexcept; +... bool operator<(const iterator &) const noexcept; +... bool operator<=(const iterator &) const noexcept; +... bool operator>(const iterator &) const noexcept; +... bool operator>=(const iterator &) const noexcept; +...}; +... +...template +...class Vec::const_iterator final { +...public: +...#if __cplusplus >= 202002L +... using iterator_category = std::contiguous_iterator_tag; +...#else +... using iterator_category = std::random_access_iterator_tag; +...#endif +... using value_type = const T; +... using pointer = const T *; +... using reference = const T &; +... +... const T &operator*() const noexcept; +... const T *operator->() const noexcept; +... const T &operator[](ptrdiff_t) const noexcept; +... +... const_iterator &operator++() noexcept; +... const_iterator operator++(int) noexcept; +... const_iterator &operator--() noexcept; +... const_iterator operator--(int) noexcept; +... +... const_iterator &operator+=(ptrdiff_t) noexcept; +... const_iterator &operator-=(ptrdiff_t) noexcept; +... const_iterator operator+(ptrdiff_t) const noexcept; +... const_iterator operator-(ptrdiff_t) const noexcept; +... ptrdiff_t operator-(const const_iterator &) const noexcept; +... +... bool operator==(const const_iterator &) const noexcept; +... bool operator!=(const const_iterator &) const noexcept; +... bool operator<(const const_iterator &) const noexcept; +... bool operator<=(const const_iterator &) const noexcept; +... bool operator>(const const_iterator &) const noexcept; +... bool operator>=(const const_iterator &) const noexcept; +...}; +... +...} // namespace rust +``` + +### Restrictions: + +Vec\ does not support T being an opaque C++ type. You should use +CxxVector\ (C++ std::vector\) instead for collections of opaque C++ +types on the language boundary. + +## Example + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + struct Shared { + v: u32, + } + + unsafe extern "C++" { + include!("example/include/example.h"); + + fn f(elements: Vec); + } +} + +fn main() { + let shared = |v| ffi::Shared { v }; + let elements = vec![shared(3), shared(2), shared(1)]; + ffi::f(elements); +} +``` + +```cpp +// include/example.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +void f(rust::Vec elements); +``` + +```cpp +// src/example.cc + +#include "example/include/example.h" +#include +#include +#include +#include +#include + +void f(rust::Vec v) { + for (auto shared : v) { + std::cout << shared.v << std::endl; + } + + // Copy the elements to a C++ std::vector using STL algorithm. + std::vector stdv; + std::copy(v.begin(), v.end(), std::back_inserter(stdv)); + assert(v.size() == stdv.size()); +} +``` diff --git a/book/src/bindings.md b/book/src/bindings.md new file mode 100644 index 0000000..bcb51d8 --- /dev/null +++ b/book/src/bindings.md @@ -0,0 +1,56 @@ +{{#title Built-in bindings — Rust ♡ C++}} +# Built-in bindings reference + +In addition to all the primitive types (i32 <=> int32_t), the following +common types may be used in the fields of shared structs and the arguments and +returns of extern functions. + +
    + + + + + + + + + + + + + + + + + +
    name in Rustname in C++restrictions
    Stringrust::String
    &strrust::Str
    &[T]rust::Slice<const T>cannot hold opaque C++ type
    &mut [T]rust::Slice<T>cannot hold opaque C++ type
    CxxStringstd::stringcannot be passed by value
    Box<T>rust::Box<T>cannot hold opaque C++ type
    UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
    SharedPtr<T>std::shared_ptr<T>cannot hold opaque Rust type
    [T; N]std::array<T, N>cannot hold opaque C++ type
    Vec<T>rust::Vec<T>cannot hold opaque C++ type
    CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type
    *mut T, *const TT*, const T*fn with a raw pointer argument must be declared unsafe to call
    fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
    Result<T>throw/catchallowed as return type only
    + +
    + +The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in +the CXX GitHub repo. You will need to include this header in your C++ code when +working with those types. **When using Cargo and the cxx-build crate, the header +is made available to you at `#include "rust/cxx.h"`.** + +The `rust` namespace additionally provides lowercase type aliases of all the +types mentioned in the table, for use in codebases preferring that style. For +example `rust::String`, `rust::Vec` may alternatively be written `rust::string`, +`rust::vec` etc. + +## Pending bindings + +The following types are intended to be supported "soon" but are just not +implemented yet. I don't expect any of these to be hard to make work but it's a +matter of designing a nice API for each in its non-native language. + +
    + + + + + + + + + +
    name in Rustname in C++
    BTreeMap<K, V>tbd
    HashMap<K, V>tbd
    Arc<T>tbd
    Option<T>tbd
    tbdstd::map<K, V>
    tbdstd::unordered_map<K, V>
    diff --git a/book/src/build/bazel.md b/book/src/build/bazel.md new file mode 100644 index 0000000..698bded --- /dev/null +++ b/book/src/build/bazel.md @@ -0,0 +1,109 @@ +{{#title Bazel, Buck2 — Rust ♡ C++}} +## Bazel, Buck2, potentially other similar environments + +Starlark-based build systems with the ability to compile a code generator and +invoke it as a `genrule` will run CXX's C++ code generator via its `cxxbridge` +command line interface. + +The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be built +from the *gen/cmd/* directory of the CXX GitHub repo. + +```console +$ cargo install cxxbridge-cmd + +$ cxxbridge src/bridge.rs --header > path/to/bridge.rs.h +$ cxxbridge src/bridge.rs > path/to/bridge.rs.cc +``` + +The CXX repo maintains working [Bazel] `BUILD.bazel` and [Buck2] `BUCK` targets +for the complete blobstore tutorial (chapter 3) for your reference, tested in +CI. These aren't meant to be directly what you use in your codebase, but serve +as an illustration of one possible working pattern. + +[Bazel]: https://bazel.build +[Buck2]: https://buck2.build + +```python +# tools/bazel/rust_cxx_bridge.bzl + +load("@bazel_skylib//rules:run_binary.bzl", "run_binary") +load("@rules_cc//cc:defs.bzl", "cc_library") + +def rust_cxx_bridge(name, src, deps = []): + native.alias( + name = "%s/header" % name, + actual = src + ".h", + ) + + native.alias( + name = "%s/source" % name, + actual = src + ".cc", + ) + + run_binary( + name = "%s/generated" % name, + srcs = [src], + outs = [ + src + ".h", + src + ".cc", + ], + args = [ + "$(location %s)" % src, + "-o", + "$(location %s.h)" % src, + "-o", + "$(location %s.cc)" % src, + ], + tool = "//:codegen", + ) + + cc_library( + name = name, + srcs = [src + ".cc"], + deps = deps + [":%s/include" % name], + ) + + cc_library( + name = "%s/include" % name, + hdrs = [src + ".h"], + ) +``` + +```python +# demo/BUILD.bazel + +load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_rust//rust:defs.bzl", "rust_binary") +load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") + +rust_binary( + name = "demo", + srcs = glob(["src/**/*.rs"]), + deps = [ + ":blobstore-sys", + ":bridge", + "//:cxx", + ], +) + +rust_cxx_bridge( + name = "bridge", + src = "src/main.rs", + deps = [":blobstore-include"], +) + +cc_library( + name = "blobstore-sys", + srcs = ["src/blobstore.cc"], + deps = [ + ":blobstore-include", + ":bridge/include", + ], +) + +cc_library( + name = "blobstore-include", + hdrs = ["include/blobstore.h"], + deps = ["//:core"], +) +``` diff --git a/book/src/build/cargo.md b/book/src/build/cargo.md new file mode 100644 index 0000000..6e9af80 --- /dev/null +++ b/book/src/build/cargo.md @@ -0,0 +1,306 @@ +{{#title Cargo-based setup — Rust ♡ C++}} +# Cargo-based builds + +As one aspect of delivering a good Rust–C++ interop experience, CXX turns +Cargo into a quite usable build system for C++ projects published as a +collection of crates.io packages, including a consistent and frictionless +experience `#include`-ing C++ headers across dependencies. + +## Canonical setup + +CXX's integration with Cargo is handled through the [cxx-build] crate. + +[cxx-build]: https://docs.rs/cxx-build + +```toml,hidelines=... +# Cargo.toml +...[package] +...name = "..." +...version = "..." +...edition = "2021" + +[dependencies] +cxx = "1.0" + +[build-dependencies] +cxx-build = "1.0" +``` + +The canonical build script is as follows. The indicated line returns a +[`cc::Build`] instance (from the usual widely used `cc` crate) on which you can +set up any additional source files and compiler flags as normal. + +[`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html + +```rust,noplayground +// build.rs + +fn main() { + cxx_build::bridge("src/main.rs") // returns a cc::Build + .file("src/demo.cc") + .std("c++11") + .compile("cxxbridge-demo"); + + println!("cargo:rerun-if-changed=src/main.rs"); + println!("cargo:rerun-if-changed=src/demo.cc"); + println!("cargo:rerun-if-changed=include/demo.h"); +} +``` + +The `rerun-if-changed` lines are optional but make it so that Cargo does not +spend time recompiling your C++ code when only non-C++ code has changed since +the previous Cargo build. By default without any `rerun-if-changed`, Cargo will +re-execute the build script after *any* file changed in the project. + +If stuck, try comparing what you have against the *demo/* directory of the CXX +GitHub repo, which maintains a working Cargo-based setup for the blobstore +tutorial (chapter 3). + +## Header include paths + +With cxx-build, by default your include paths always start with the crate name. +This applies to both `#include` within your C++ code, and `include!` in the +`extern "C++"` section of your Rust cxx::bridge. + +Your crate name is determined by the `name` entry in Cargo.toml. + +For example if your crate is named `yourcratename` and contains a C++ header +file `path/to/header.h` relative to Cargo.toml, that file will be includable as: + +```cpp +#include "yourcratename/path/to/header.h" +``` + +A crate can choose a prefix for its headers that is different from the crate +name by modifying **[`CFG.include_prefix`][CFG]** from build.rs: + +[CFG]: https://docs.rs/cxx-build/*/cxx_build/static.CFG.html + +```rust,noplayground +// build.rs + +use cxx_build::CFG; + +fn main() { + CFG.include_prefix = "my/project"; + + cxx_build::bridge(...)... +} +``` + +Subsequently the header located at `path/to/header.h` would now be includable +as: + +```cpp +#include "my/project/path/to/header.h" +``` + +The empty string `""` is a valid include prefix and will make it possible to +have `#include "path/to/header.h"`. However, if your crate is a library, be +considerate of possible name collisions that may occur in downstream crates. If +using an empty include prefix, you'll want to make sure your headers' local path +within the crate is sufficiently namespaced or unique. + +## Including generated code + +If your `#[cxx::bridge]` module contains an `extern "Rust"` block i.e. types or +functions exposed from Rust to C++, or any shared data structures, the +CXX-generated C++ header declaring those things is available using a `.rs.h` +extension on the Rust source file's name. + +```cpp +// the header generated from path/to/lib.rs +#include "yourcratename/path/to/lib.rs.h" +``` + +For giggles, it's also available using just a plain `.rs` extension as if you +were including the Rust file directly. Use whichever you find more palatable. + +```cpp +#include "yourcratename/path/to/lib.rs" +``` + +## Including headers from dependencies + +You get to include headers from your dependencies, both handwritten ones +contained as `.h` files in their Cargo package, as well as CXX-generated ones. + +It works the same as an include of a local header: use the crate name (or their +include\_prefix if their crate changed it) followed by the relative path of the +header within the crate. + +```cpp +#include "dependencycratename/path/to/their/header.h` +``` + +Note that cross-crate imports are only made available between **direct +dependencies**. You must directly depend on the other crate in order to #include +its headers; a transitive dependency is not sufficient. + +Additionally, headers from a direct dependency are only importable if the +dependency's Cargo.toml manifest contains a `links` key. If not, its headers +will not be importable from outside of the same crate. See *[the `links` +manifest key][links]* in the Cargo reference. + +[links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key + +


    + +# Advanced features + +The following CFG settings are only relevant to you if you are writing a library +that needs to support downstream crates `#include`-ing its C++ public headers. + +## Publicly exporting header directories + +**[`CFG.exported_header_dirs`][CFG]** (vector of absolute paths) defines a set +of additional directories from which the current crate, directly dependent +crates, and further crates to which this crate's headers are exported (more +below) will be able to `#include` headers. + +Adding a directory to `exported_header_dirs` is similar to adding it to the +current build via the `cc` crate's [`Build::include`], but *also* makes the +directory available to downstream crates that want to `#include` one of the +headers from your crate. If the dir were added only using `Build::include`, the +downstream crate including your header would need to manually add the same +directory to their own build as well. + +[`Build::include`]: https://docs.rs/cc/1/cc/struct.Build.html#method.include + +When using `exported_header_dirs`, your crate must also set a `links` key for +itself in Cargo.toml. See [*the `links` manifest key*][links]. The reason is +that Cargo imposes no ordering on the execution of build scripts without a +`links` key, which means the downstream crate's build script might otherwise +execute before yours decides what to put into `exported_header_dirs`. + +### Example + +One of your crate's headers wants to include a system library, such as `#include +"Python.h"`. + +```rust,noplayground +// build.rs + +use cxx_build::CFG; +use std::path::PathBuf; + +fn main() { + let python3 = pkg_config::probe_library("python3").unwrap(); + let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path); + CFG.exported_header_dirs.extend(python_include_paths); + + cxx_build::bridge("src/bridge.rs").compile("demo"); +} +``` + +### Example + +Your crate wants to rearrange the headers that it exports vs how they're laid +out locally inside the crate's source directory. + +Suppose the crate as published contains a file at `./include/myheader.h` but +wants it available to downstream crates as `#include "foo/v1/public.h"`. + +```rust,noplayground +// build.rs + +use cxx_build::CFG; +use std::path::Path; +use std::{env, fs}; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let headers = Path::new(&out_dir).join("headers"); + CFG.exported_header_dirs.push(&headers); + + // We contain `include/myheader.h` locally, but + // downstream will use `#include "foo/v1/public.h"` + let foo = headers.join("foo").join("v1"); + fs::create_dir_all(&foo).unwrap(); + fs::copy("include/myheader.h", foo.join("public.h")).unwrap(); + + cxx_build::bridge("src/bridge.rs").compile("demo"); +} +``` + +## Publicly exporting dependencies + +**[`CFG.exported_header_prefixes`][CFG]** (vector of strings) each refer to the +`include_prefix` of one of your direct dependencies, or a prefix thereof. They +describe which of your dependencies participate in your crate's C++ public API, +as opposed to private use by your crate's implementation. + +As a general rule, if one of your headers `#include`s something from one of your +dependencies, you need to put that dependency's `include_prefix` into +`CFG.exported_header_prefixes` (*or* their `links` key into +`CFG.exported_header_links`; see below). On the other hand if only your C++ +implementation files and *not* your headers are importing from the dependency, +you do not export that dependency. + +The significance of exported headers is that if downstream code (crate **𝒜**) +contains an `#include` of a header from your crate (**ℬ**) and your header +contains an `#include` of something from your dependency (**𝒞**), the exported +dependency **𝒞** becomes available during the downstream crate **𝒜**'s build. +Otherwise the downstream crate **𝒜** doesn't know about **𝒞** and wouldn't be +able to find what header your header is referring to, and would fail to build. + +When using `exported_header_prefixes`, your crate must also set a `links` key +for itself in Cargo.toml. + +### Example + +Suppose you have a crate with 5 direct dependencies and the `include_prefix` for +each one are: + +- "crate0" +- "group/api/crate1" +- "group/api/crate2" +- "group/api/contrib/crate3" +- "detail/crate4" + +Your header involves types from the first four so we re-export those as part of +your public API, while crate4 is only used internally by your cc file not your +header, so we do not export: + +```rust,noplayground +// build.rs + +use cxx_build::CFG; + +fn main() { + CFG.exported_header_prefixes = vec!["crate0", "group/api"]; + + cxx_build::bridge("src/bridge.rs") + .file("src/impl.cc") + .compile("demo"); +} +``` + +
    + +For more fine grained control, there is **[`CFG.exported_header_links`][CFG]** +(vector of strings) which each refer to the `links` attribute ([*the `links` +manifest key*][links]) of one of your crate's direct dependencies. + +This achieves an equivalent result to `CFG.exported_header_prefixes` by +re-exporting a C++ dependency as part of your crate's public API, except with +finer control for cases when multiple crates might be sharing the same +`include_prefix` and you'd like to export some but not others. Links attributes +are guaranteed to be unique identifiers by Cargo. + +When using `exported_header_links`, your crate must also set a `links` key for +itself in Cargo.toml. + +### Example + +```rust,noplayground +// build.rs + +use cxx_build::CFG; + +fn main() { + CFG.exported_header_links.push("git2"); + + cxx_build::bridge("src/bridge.rs").compile("demo"); +} +``` diff --git a/book/src/build/cmake.md b/book/src/build/cmake.md new file mode 100644 index 0000000..478552e --- /dev/null +++ b/book/src/build/cmake.md @@ -0,0 +1,47 @@ +{{#title CMake — Rust ♡ C++}} +# CMake + +There is not an officially endorsed CMake setup for CXX, but a few developers +have shared one that they got working. You can try one of these as a starting +point. If you feel that you have arrived at a CMake setup that is superior to +what is available in these links, feel free to make a PR adding it to this list. + +
    + +--- + +- **** + + - Supports cross-language link time optimization (LTO) + +--- + +- **** + + - Includes a cbindgen component + - Tested on Windows 10 with MSVC, and on Linux + +--- + +- **** + + - Alias target that can be linked into a C++ project + - Tested on Windows 10 with GNU target, and on Linux + +--- + +- **** + + - Improved rusty_cmake CMake file to use modern C++ + - Rich examples of using different primitive types and Rust's Result return to C++ + - MacOS and Linux only + +--- + +- **** + + - Same blobstore example as the official demo, but inverted languages + - Minimal CMake configuration + - Tested on Linux, macOS, and Windows + +--- diff --git a/book/src/build/other.md b/book/src/build/other.md new file mode 100644 index 0000000..c0c6e91 --- /dev/null +++ b/book/src/build/other.md @@ -0,0 +1,87 @@ +{{#title Other build systems — Rust ♡ C++}} +# Some other build system + +You will need to achieve at least these three things: + +- Produce the CXX-generated C++ bindings code. +- Compile the generated C++ code. +- Link the resulting objects together with your other C++ and Rust objects. + +*Not all build systems are created equal. If you're hoping to use a build system +from the '90s, especially if you're hoping to overlaying the limitations of 2 or +more build systems (like automake+cargo) and expect to solve them +simultaneously, then be mindful that your expectations are set accordingly and +seek sympathy from those who have imposed the same approach on themselves.* + +### Producing the generated code + +CXX's Rust code generation automatically happens when the `#[cxx::bridge]` +procedural macro is expanded during the normal Rust compilation process, so no +special build steps are required there. + +But the C++ side of the bindings needs to be generated. Your options are: + +- Use the `cxxbridge` command, which is a standalone command line interface to + the CXX C++ code generator. Wire up your build system to compile and invoke + this tool. + + ```console + $ cxxbridge src/bridge.rs --header > path/to/bridge.rs.h + $ cxxbridge src/bridge.rs > path/to/bridge.rs.cc + ``` + + It's packaged as the `cxxbridge-cmd` crate on crates.io or can be built from + the *gen/cmd/* directory of the CXX GitHub repo. + +- Or, build your own code generator frontend on top of the [cxx-gen] crate. This + is currently unofficial and unsupported. + +[cxx-gen]: https://docs.rs/cxx-gen + +### Compiling C++ + +However you like. We can provide no guidance. + +### Linking the C++ and Rust together + +When linking a binary which contains mixed Rust and C++ code, you will have to +choose between using the Rust toolchain (`rustc`) or the C++ toolchain which you +may already have extensively tuned. + +The generated C++ code and the Rust code generated by the procedural macro both +depend on each other. Simple examples may only require one or the other, but in +general your linking will need to handle both directions. For some linkers, such +as llvm-ld, this is not a problem at all. For others, such as GNU ld, flags like +`--start-lib`/`--end-lib` may help. + +Rust does not generate simple standalone `.o` files, so you can't just throw the +Rust-generated code into your existing C++ toolchain linker. Instead you need to +choose one of these options: + +* Use `rustc` as the final linker. Pass any non-Rust libraries using `-L + ` and `-l` rustc arguments, and/or `#[link]` directives in + your Rust code. If you need to link against C/C++ `.o` files you can use + `-Clink-arg=file.o`. + +* Use your C++ linker. In this case, you first need to use `rustc` and/or + `cargo` to generate a _single_ Rust `staticlib` target and pass that into your + foreign linker invocation. + + * If you need to link multiple Rust subsystems, you will need to generate a + _single_ `staticlib` perhaps using lots of `extern crate` statements to + include multiple Rust `rlib`s. Multiple Rust `staticlib` files are likely + to conflict. + +Passing Rust `rlib`s directly into your non-Rust linker is not supported (but +apparently sometimes works). + +See the [Rust reference's *Linkage*][linkage] page for some general information +here. + +[linkage]: https://doc.rust-lang.org/reference/linkage.html + +The following open rust-lang issues might hold more recent guidance or +inspiration: [rust-lang/rust#73632], [rust-lang/rust#73295]. + +[rust-lang/rust#73632]: https://github.com/rust-lang/rust/issues/73632 +[rust-lang/rust#73295]: https://github.com/rust-lang/rust/issues/73295 diff --git a/book/src/building.md b/book/src/building.md new file mode 100644 index 0000000..c75939e --- /dev/null +++ b/book/src/building.md @@ -0,0 +1,20 @@ +{{#title Multi-language build system options — Rust ♡ C++}} +# Multi-language build system options + +CXX is designed to be convenient to integrate into a variety of build systems. + +If you are working in a project that does not already have a preferred build +system for its C++ code *or* which will be relying heavily on open source +libraries from the Rust package registry, you're likely to have the easiest +experience with Cargo which is the build system commonly used by open source +Rust projects. Refer to the ***[Cargo](build/cargo.md)*** chapter about CXX's +Cargo support. + +Among build systems designed for first class multi-language support, Bazel is a +solid choice. Refer to the ***[Bazel](build/bazel.md)*** chapter. + +If your codebase is already invested in CMake, refer to the +***[CMake](build/cmake.md)*** chapter. + +If you have some other build system that you'd like to try to make work with +CXX, see [this page](build/other.md) for notes. diff --git a/book/src/concepts.md b/book/src/concepts.md new file mode 100644 index 0000000..75daedd --- /dev/null +++ b/book/src/concepts.md @@ -0,0 +1,85 @@ +{{#title Core concepts — Rust ♡ C++}} +# Core concepts + +This page is a brief overview of the major concepts of CXX, enough so that you +recognize the shape of things as you read the tutorial and following chapters. + +In CXX, the language of the FFI boundary involves 3 kinds of items: + +- **Shared structs** — data structures whose fields are made visible to + both languages. The definition written within cxx::bridge in Rust is usually + the single source of truth, though there are ways to do sharing based on a + bindgen-generated definition with C++ as source of truth. + +- **Opaque types** — their fields are secret from the other language. + These cannot be passed across the FFI by value but only behind an indirection, + such as a reference `&`, a Rust `Box`, or a C++ `unique_ptr`. Can be a type + alias for an arbitrarily complicated generic language-specific type depending + on your use case. + +- **Functions** — implemented in either language, callable from the other + language. + +```rust,noplayground,focuscomment +# #[cxx::bridge] +# mod ffi { + // Any shared structs, whose fields will be visible to both languages. +# struct BlobMetadata { +# size: usize, +# tags: Vec, +# } +# +# extern "Rust" { + // Zero or more opaque types which both languages can pass around + // but only Rust can see the fields. +# type MultiBuf; +# + // Functions implemented in Rust. +# fn next_chunk(buf: &mut MultiBuf) -> &[u8]; +# } +# +# unsafe extern "C++" { + // One or more headers with the matching C++ declarations for the + // enclosing extern "C++" block. Our code generators don't read it + // but it gets #include'd and used in static assertions to ensure + // our picture of the FFI boundary is accurate. +# include!("demo/include/blobstore.h"); +# + // Zero or more opaque types which both languages can pass around + // but only C++ can see the fields. +# type BlobstoreClient; +# + // Functions implemented in C++. +# fn new_blobstore_client() -> UniquePtr; +# fn put(&self, parts: &mut MultiBuf) -> u64; +# fn tag(&self, blobid: u64, tag: &str); +# fn metadata(&self, blobid: u64) -> BlobMetadata; +# } +# } +``` + +Within the `extern "Rust"` part of the CXX bridge we list the types and +functions for which Rust is the source of truth. These all implicitly refer to +the `super` module, the parent module of the CXX bridge. You can think of the +two items listed in the example above as being like `use super::MultiBuf` and +`use super::next_chunk` except re-exported to C++. The parent module will either +contain the definitions directly for simple things, or contain the relevant +`use` statements to bring them into scope from elsewhere. + +Within the `extern "C++"` part, we list types and functions for which C++ is the +source of truth, as well as the header(s) that declare those APIs. In the future +it's possible that this section could be generated bindgen-style from the +headers but for now we need the signatures written out; static assertions verify +that they are accurate. + +

    + +Be aware that the design of this library is intentionally restrictive and +opinionated! It isn't a goal to be flexible enough to handle an arbitrary +signature in either language. Instead this project is about carving out a highly +expressive set of functionality about which we can make powerful safety +guarantees today and extend over time. You may find that it takes some practice +to use CXX bridge effectively as it won't work in all the ways that you may be +used to. + +
    diff --git a/book/src/context.md b/book/src/context.md new file mode 100644 index 0000000..516ee91 --- /dev/null +++ b/book/src/context.md @@ -0,0 +1,118 @@ +{{#title Other Rust–C++ interop tools — Rust ♡ C++}} +# Context: other Rust–C++ interop tools + +When it comes to interacting with an idiomatic Rust API or idiomatic C++ API +from the other language, the generally applicable approaches outside of the CXX +crate are: + +- Build a C-compatible wrapper around the code (expressed using `extern "C"` + signatures, primitives, C-compatible structs, raw pointers). Translate that + manually to equivalent `extern "C"` declarations in the other language and + keep them in sync. Preferably, build a safe/idiomatic wrapper around the + translated `extern "C"` signatures for callers to use. + +- Build a C wrapper around the C++ code and use **[bindgen]** to translate that + programmatically to `extern "C"` Rust signatures. Preferably, build a + safe/idiomatic Rust wrapper on top. + +- Build a C-compatible Rust wrapper around the Rust code and use **[cbindgen]** + to translate that programmatically to an `extern "C"` C++ header. Preferably, + build an idiomatic C++ wrapper. + +**If the code you are binding is already *"effectively C"*, the above has you +covered.** You should use bindgen or cbindgen, or manually translated C +signatures if there aren't too many and they seldom change. + +[bindgen]: https://github.com/rust-lang/rust-bindgen +[cbindgen]: https://github.com/eqrion/cbindgen + +## C++ vs C + +Bindgen has some basic support for C++. It can reason about classes, member +functions, and the layout of templated types. However, everything it does +related to C++ is best-effort only. Bindgen starts from a point of wanting to +generate declarations for everything, so any C++ detail that it hasn't +implemented will cause a crash if you are lucky ([bindgen#388]) or more likely +silently emit an incompatible signature ([bindgen#380], [bindgen#607], +[bindgen#652], [bindgen#778], [bindgen#1194]) which will do arbitrary +memory-unsafe things at runtime whenever called. + +[bindgen#388]: https://github.com/rust-lang/rust-bindgen/issues/388 +[bindgen#380]: https://github.com/rust-lang/rust-bindgen/issues/380 +[bindgen#607]: https://github.com/rust-lang/rust-bindgen/issues/607 +[bindgen#652]: https://github.com/rust-lang/rust-bindgen/issues/652 +[bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778 +[bindgen#1194]: https://github.com/rust-lang/rust-bindgen/issues/1194 + +Thus using bindgen correctly requires not just juggling all your pointers +correctly at the language boundary, but also understanding ABI details and their +workarounds and reliably applying them. For example, the programmer will +discover that their program sometimes segfaults if they call a function that +returns std::unique\_ptr\ through bindgen. Why? Because unique\_ptr, despite +being "just a pointer", has a different ABI than a pointer or a C struct +containing a pointer ([bindgen#778]) and is not directly expressible in Rust. +Bindgen emitted something that *looks* reasonable and you will have a hell of a +time in gdb working out what went wrong. Eventually people learn to avoid +anything involving a non-trivial copy constructor, destructor, or inheritance, +and instead stick to raw pointers and primitives and trivial structs only +— in other words C. + +## Geometric intuition for why there is so much opportunity for improvement + +The CXX project attempts a different approach to C++ FFI. + +Imagine Rust and C and C++ as three vertices of a scalene triangle, with length +of the edges being related to similarity of the languages when it comes to +library design. + +The most similar pair (the shortest edge) is Rust–C++. These languages +have largely compatible concepts of things like ownership, vectors, strings, +fallibility, etc that translate clearly from signatures in either language to +signatures in the other language. + +When we make a binding for an idiomatic C++ API using bindgen, and we fall down +to raw pointers and primitives and trivial structs as described above, what we +are really doing is coding the two longest edges of the triangle: getting from +C++ down to C, and C back up to Rust. The Rust–C edge always involves a +great deal of `unsafe` code, and the C++–C edge similarly requires care +just for basic memory safety. Something as basic as "how do I pass ownership of +a string to the other language?" becomes a strap-yourself-in moment, +particularly for someone not already an expert in one or both sides. + +You should think of the `cxx` crate as being the midpoint of the Rust–C++ +edge. Rather than coding the two long edges, you will code half the short edge +in Rust and half the short edge in C++, in both cases with the library playing +to the strengths of the Rust type system *and* the C++ type system to help +assure correctness. + +If you've already been through the tutorial in the previous chapter, take a +moment to appreciate that the C++ side *really* looks like we are just writing +C++ and the Rust side *really* looks like we are just writing Rust. Anything you +could do wrong in Rust, and almost anything you could reasonably do wrong in +C++, will be caught by the compiler. This highlights that we are on the "short +edge of the triangle". + +But it all still boils down to the same things: it's still FFI from one piece of +native code to another, nothing is getting serialized or allocated or +runtime-checked in between. + +## Role of CXX + +The role of CXX is to capture the language boundary with more fidelity than what +`extern "C"` is able to represent. You can think of CXX as being a replacement +for `extern "C"` in a sense. + +From this perspective, CXX is a lower level tool than the bindgens. Just as +bindgen and cbindgen are built on top of `extern "C"`, it makes sense to think +about higher level tools built on top of CXX. Such a tool might consume a C++ +header and/or Rust module (and/or IDL like Thrift) and emit the corresponding +safe cxx::bridge language boundary, leveraging CXX's static analysis and +underlying implementation of that boundary. We are beginning to see this space +explored by the [autocxx] tool, though nothing yet ready for broad use in the +way that CXX on its own is. + +[autocxx]: https://github.com/google/autocxx + +But note in other ways CXX is higher level than the bindgens, with rich support +for common standard library types. CXX's types serve as an intuitive vocabulary +for designing a good boundary between components in different languages. diff --git a/book/src/cxx.png b/book/src/cxx.png new file mode 100644 index 0000000000000000000000000000000000000000..07118aafb0a6df27f3a327e946de93e3e0844a2d GIT binary patch literal 71692 zcmeFZXH-+$_cj_mmSg2eQIzIUniR31BGp2#g7hwk6bVT0<%miX5Rf29kuD%0Ep#G> z4hqr?5}LFKNG}0G$UArN_q%tz@3`ZBxntZfe?AEA?CibP+H=kFJkQ+0_f+MM9X@jy zg+d)ukeAj#q5gD7q4wVV^8g(AD$d~szxFt3$lXEZcd$;QP-jsJ(zi9;6MhYOz2vv_ z*`RhNJ$r9$_2_uYtDt>e|6reD+?B1f_5W$ksSPO~IP&~yxw~PhhW(6ZuJN-0n;Olt zTJj&tw5WG-EDi1{DU=i&=Dr@&JMb?5c?2nX>-0MX2= z>9N8sZ_1_~FEt8W=nR_ra&8O~yORvF(pd$od`x!Z5enpo84-5BLCOdFSat z_;vAho#|OJnY?_;Z|T(XVm%#^?Km9v-(S8t^>&hux=1&fxZs^N=-4jk5%>puqWpXx z9sDw6mXniPCgZ5&<-P*nYYX?_7;0ivuhSFr>({Rqd&)Qa)?+sYU%83E@mt)S@OV(8 zcQ&G>HW@q?yhh%-F(Y5V+{)01tgpYT&d5c(f`704A{_oyu^zy&eQS7Uy{tV@YWqj^ z-92#hqqOgorLU~yTDDqiqT6ts8!sHZ#kYIu!L4CZEl+{>B>!Y_GUZo;kA2OWw#VFK z6aE4}%4~+NKpVPjh`Vga^{qH%TFvBEI(!$^F$UwuO%^t@N6T^eXi9Fjs{x<`F4oNsxTwq_l9Uh@@l^Em2GA-Q_-xzKAj9C@gyu(0sm zd*rGD|DM8a$ZY1~w)iU+YROIWZsfVL_So80E{_hUq~doiaE|}9VVf9ovMKxZ8#kJ{ z-53w=-z56&Sb8kh9aNY{KIup?*?4a9%4?xUr*@-JsIA>(XE~BAvGp@;c}54PpX5Fk zyk8c1gb$k!BZeU2RymZv-DJPr)Y56PUKHEPBekhe;k~)MHw_v4tJuf*{8oGFx_vu& zgt`++uAX-(U}!ITh(@FTiA2_SI&u_#`8!R{^yMz!ms-2OJQ1zN?Ypyu#~JS|aZuIm zDyK8|%OS>h*1oE$YFW;2RnBp4ptPY=rRTM5THz=%eShcQk8ru^My>jM4QaTcb7zD~ z_FERBE(<~BOG zu}zecx>7OzT*y0xm6i3?Ao8i<6TiiSerIu;Z*g%O-*4bHRS=`98V#ZiK(`JtRAQ5o z@iP5QNy%`u-_+IQ3(d{Vj>JT_slS+zlPp$x@2e?*^>zBL7Z-NgRZO1r_?Z$o0S2sn zoVFZkODi&PX2OwV>Nb%fT1;9REQ}OMyZ_lI^~Y|s9F*F)U9r@}VxH_nC6C0Ca8$}o z>HYAXmvXyy@;t(O;(Rb6nLJ2rGZ@+WwqFhznCW+>!}~p&#fBp{zQ3o|!V%Q6^zSd! z*Vi}9;YeOMp4f{^)OF;+1(6d+HWM4>sIbp3j%;?gon$=*hcB^Hm2SXq?WEbt<Q2VqX3xw@yFCl(hkt?g9iE9Gf3 zsPs%RGhrgFXql1b5kpuh;Pt$WCyeJyM8$*4mt3g;fUT*7v6F z`KYX)hY2z?xnM!x>@cyq>OA~6axUsg$}41x!#Iu#WznJ3^R)TpSDEGTYxi!lAcs*; zTw|9J_m4s?p3k&atT4ocg@qMb+}ozaf8m~lFpK>7AMs}rNB%GkF3}gfDIvks?2xXJ z&AFp7ydrLW{p~r&nxl8JH5J!FJx9ei1N}A*dTcK>-z6k1y|kQQL~IvKahv9BP$;E; zv1qu&vEc~)xO#_#uU^`VpJY};aT}o?fwVJGAMd@kLI&;6G+t@jH6TA7QuMo%P>^Q0 z;cTQfD1<)u=k7=TD*-gSN9oen2eX8vz8tg{Igzf(_vIS*?g^;&fBtgz$kw2W)cSDGF2Z+ybagQfpVJ9)NpPzU*r2+{5{&EKQS<;6Rr)6&v{TaW1!7{??g zCN_)*CMG2vAZO?BmFla6G>-k+n{)#2GP$^TPoSNifHhCf&EbpBRH!kIl}$ULWawa| zlW$yeWcp1}m1wzI}QuduW5eWvo$$IX)8T+Z851lbFL=Q)I zcU~+ybhdJ=GCwGYF;u3@#Rvk={p4m?eo@iGu+7369+wF4GxH=7Tj%HPK0NHg<91vS zpZJs%k}6+e*x~JyY2EhYM~ZtAPyVc8q3+TAJnh^sT)}M|NAhRk&Z*Y%aN%tHtVV91 z8JUBPEf`I>?FDCtq;iI5aAGX>6Ip#O3MN_i@9g+qdbI5&aIUVf*j<%;dCSN8O{TRObJJ%kcRyATd`4aIJ6(YT6e~UI z4zj+VDy}deW#>TtwBox6kk76X4zk&sX|&>2Z!w3azxTDyH*X|F4R?a z5nS--^twad?iQkG-5!Y1if5YPgNI5wn7J8|?ia*J3zdXuZK$j7~dYA_u>1i*{dK%#~2y;zl zmsPIzc+9MMT4g-Dlk&>5^tr{?Y3Fpqi=o!>3`&yl%)dNx${~wyv-R0%#eaEZ%k|Xb zr?aR=p7#Bi;!7&`+a`;C&CMlFCxDYF)lYQy0fQ>lkFhBgEu=L2#7e#BUKb-^GYl`9 z{OnfPsZs5D6h8iJK8xr9S=Qg+wX|?HFydqr-KgHw6U^u8G?`h2b*9yb128>7f|;bbUm35Y4-;|*z`!6l`9jFK%KXqw>)Y00{5ttTM2~Kp(hld2pAUT@JMpYxPv9RQO8aVN%Jq~e zQ+TGm&*7EEepX&=G`qZ%;2ZVt($Z4fDqb%btCyj1sHtjXA#L(4co^3zHcq1rA9qbD zMbRujwaj1#5pAlsZ#Bl^dB=1_v2j7{%*`n$R=Mlfugh{j-XN}vtFbm8O7clmnO!sEGEPmlGjw95%mqboQr{&n>Y!o+LXBN;2K1tQ$i;?w} zE5L`wd|o108OB9eB^*3>FxY~FFQ)AsVJ=+ohOkO72JRb)7oCyh3ab(z@n$cC@ma&Y z4}_#XTmPcr*otdPU%r(#uY?;)FnqB7?ce&6~k& zS=IwBlxXg6J>1riSu{ksbgTY*`~aRuD@$cvL2*(3YF%=#WOR6#>MJ>(h-R)BX_e^F z%QN~srG2ff3S))V^Y$eLs-lnF+(^o;Cl!1ozO2baw0BdZKA1fXOyy-D*l0${#e2Cp zI!3)=j+xqdsB(`*@_M_IZFgz7lK*LJMTNrCgWAVw<)QBWSR|7}y%NK_1o)jg{5+C; zEOsXdb5rjC7Tv7))wya&nQM=9e(cTDIy3dcD&QaMygFCXzuR(SPFy;AUNh(2J>DQR zfx-NqmKKB~8TF^=@NrIzyu7GiT9>?*Xp>ZI{%nd4hHM~k(!5#Cz${H8JIFdd+YE~q z`({ESk>oH=L1*SkVX}8wn-z@m&+)ex-5Hmgki&~+=WW+sva=1RXQPW&tSQmY`|43t zl4TvAQk%~k(vMBt+DaX9>2elL3`Y0al-}t?HY-DW)cc(H4dpkQN$>k?o^Rb6xIB~G zgPrS#*)Js#8x{38DHpyJXZK9YBai$%GWW9*zeD{Y!y3ss@Gq-6J3&_VLkS6p{Om& zG$qGoOxoewguej8x1JDRTq`vgUWqmGl&`Z_DAl2oIgo(J>$~YZ)-5MObx+*swXSnQqVH<@u2 zL&7T2c4aIB-)yf3JPTLNVyK)i$>mNsRhi$cjSb2_a5+5L`KlVM*;XbvM3enImCLdN ztrRQ6Xl?gJQgFUz^tB2bcfV!_jRICj_TJgGUNZOE+Y)r5^RM!KNmgdEdD?GtM$WuA zaV9H&Hk{3^YhQ#$ws2XW3$o_Ft4S2K7WM7cJFW4hb z;Ocbs3O(Dwp4Bz0<(_lnod%eOf^^H(YWn#{4kkW&+AmJ4o}}0N>`u#bm0it;X@7i@ zaWmFl4B9#4t-iF$Z+kS>G8kiHEqtb~jlBm#Ns8kkSci_DIoH_vT7jqevxQM~;p6Ex zp&mSYf;o9O1ji(~O526IIX_7kEdBf`pVK!J*D)*7@m#O;hd^${(b$l9uFe&+;FFdW z9F_TjZ8fay?A(|rMaAuMUJ7kFFl@B#h`9XgcUSv=jwCq{gv`dyAa@lwuULZozSWUwRA>I(S3S|~W}s@; zd9cJ174_F~7-e-WEnC7#8TC7a$E5?GqvW-_N}@YL6(6pBx=E zwZrjp5TOeBK}?ufo@{s899~;n`}%CFx}&)5qJ~EVL{JO_?EMR6SnLFxKd}Q*Xw}=Q zR6o0efz7YRSjarxA-6LAt4E}3V{GvgBeyZ1j`0OrmVfs2?z(=wRnJhfVoJb*<$G$l zV-7!m3Y*>K2XW0slD z`)m}ldJZ+cu+Zzay>8*=cHBH8 zeyyh9Wjj1hOX4n_;YTnv)Th1Qv4XH(Z>(0t)$$E1=)H}38R;2>yXSQZ@EOJhoqsR4 zhBTV-ZvI^v|EU_X5f&DfI`W;AzeczEEc#H*hwd^RhT*>WlD~hV>k7pGsG@V>qs!p#kizk#yM z`(-SY8zT~am82yO`OBl?)1nc$ncG!EN#75%gpbP?e?K}LtC%r5tcPLJkIT@{OPbbM z&*ilqbC1(b3gQ-ZPb&MUCHErw=@z9{_)J(K8Gr)^RvxER?bS1MbaYqQA|@gK5I9s{ z^dTE3B&sUMh2la-updDN*mCgq=1lv^x$@pbK5Ixw4ZNh{*UwcCgC8)bV80Iy9V=B4 z-ZXpsn17|AmgoVQYyfFz=CJSHyHE%j?PjKhU+J%TEQqu@bOC*{EkaA_#CIB%ksqP zVz{6_Jchn{)O=#6zSUvJgXYLKcM$}XBV*!wmeQHq&N4w3`fPPsIX1Re94##^?ShX( zek?q#3pE)38z{EoTS(E@y7NE&Sdj8QHs>maF}+`aw}6_)N?10mHi~M3p1l8ctsyvxA|8v{?*5q{O&;wJSQmh ze0rO?rZ2&i-ml7}++(iL4}wf$B2<9i7Rd!TEi~wZh4ks0uyfh@683u9e?n^S>gsxj zz^%ec2ECystm2u(A;&W`7Y&7A4frMk`&h&?3)9M? z#-a}mCjMgK31jy_t~T@yb=CqXxjN9RI=_;7lR*)g#S3?+9P~FCL79h zWV8hWI1GtF@P&z9^obYd3dPF-7J9)7B8jS&IV&9oHO3J!iHqbZPIxk}@4eP`mI)Ua zH$9V+(=;$5{6)F-%+AJJHxI6KJXBYje9y6HTif(Xxfc(fC7GYY>lDXn$|`8}-7`yQ zS+l(BER)7*-Hf1{sIy|f69&0@Tu!*EBpu7eV-DZ09AEinQ))JONxA2>V}*E;064m$ z(o(KiA(P`OPvuA|d~pzh6Kxv*>=C}(dU>4tZ;E>=fjg!t81w8zg=DtW1-*I~Bd8Tq zbiVD2__@@>?H;uK)0eApl&6_9?`hP!@dVd}%?33qd42RJEEfO+WNNf{R@s;y1h5Fka@cjv#NWw|Z$zhOqy%TtM? zM(g|NP8{O?E0E`u5k3ZN^|eTsFnVmlMOfiBe(h_+gA9#0U7Jrt9+TJYllKy!WYgUH zx3x6D{7Cl?3p+Nw=BbppJ&M43YHE!Rtl8SR8UiN+B#9y&Ex`oYw8$;v z37H<@Y1gVk@@!>(@2rcu*0plEuE%+rd}AKQA9@=(VM|`$vrswTurJ}qL_C-mvJuNK zx4mB?#ttzX*E|@MaACFN+WIB)&N?0dj>`OYz>_`>VdvfzY#g#mo43r?Yjv@P87{~&i$|_zqjuv&^T;4^t{1c>kesBxMEf~Zc(&-U4SK6jyi5*l2bO=BD zoL=Z?{huoN{2}xPg68xpJ!V`FU+DBNiVI<%LVl#o^xf8S`imo(!X)zMk97En7^D;U2E;Lf*zni512JB#$JsP z9T*-C&)Yh?oR;pdPAbvJmdA~l#94lk`N7i|ra#xZQXNS#2dtO1xpgp*(h!yI%ekCZ zE1WQ>o~bQ=ZcsdyejzR@z0am`4D9bOKqid~0_oLYW5Xkb;NjI9Q+Y-Q~ixpBs4At(5NlGSF(uFFk3M@gD`;Mx-VoSp!l(E+V zr8*V)cI7x?u5TbRL?M(_;T)a#qJQ;I8KJhn&z)uIT+vW@kufB61k$IUKY87yn2$c% zqST9b0$uYoW7`OStj*6Vo;yA>EI~6I!^C_s<1$*Hi%A**SX&S->MQogGG~uGUt@~3 z{x3=H#^}jG>&|FS{nYcG#(w+hsMIZV!K~+umZfZ?2Zp?{R^VsE?)fuiv?i4|Wm(L6 zlmD-cxp%XbjG>LA)Z=e^wd)P>VR_|XL=Bf-29q9@*AxK$NcN6ry3ZXZWrVlf^46Ou z=E_Z9!njrkCDd@pC0km0bclpAte*0Few?Z|6U!FQg?E9x{@t-{r|C-e#{qibZl)*Z zD`ziyZ2Sl`CvbR7oT#9z6P$t&3^wRDOb+yesb`l$Kcr|&Z+7{O}9B!o3BIa1L!yWv!kmP-%2AKfR%J0S=?Yb}zgeQa4jNO`ETw2=I z%`KzP=F819rq?R+Srnh%W3{1x04@p)Yf1sqUpeqv8HMN+aFeEH92lOP}SATt)MvtIAHSQ8RC_~dZR|CEn#mqzf ziW&dTuzh-($aVXnHY9;l%gQl|GNOqc3Fl~V_NZBkG%Xo1GzB;aP4EcZlfd8g@Z~Nh zWU1IK0x)fjiL=Ma$XUFF-nV}{X=kJI&G#dGEbN%m-NGtxt3^QJ!9|UE3~P?yZLi-A z)&=;s##qWJfp(f{>2D4Ib;%skM$N4j2i6O*?LeU*M!2_$mWJaE@gwvIB`}7~u zY8RAgtNJb^jWX>p#K%x5lu;j;uriGR2mbN-F42TiBA(~ilpp?kVl2b0CUCRgfJFB>6|-7673=i=h0GYWM%EH zLVaFFv(9vNL;Nv`PU$fSj?qm`?}_;%SO8xv6@wNb=UiWJDEiQU)o#Q7$A>6I2nH02 zPa)cCVk8n0xEBvvrluZcuBh55Ma~}5+u5YdvK>8ee*~wV>SKMn=>qatj@Vpc7MDcK zLc4#+ZA9X)$o|=X4zjn12@A9UY!8G@gYREHr(awK(D*a}6aAKYzv-Wj3+Do~bFZyD z4z4{D)?{c#GHEn(c=F`QrDZS3lkbuNF*}$H`1MiXCdNqX@KrasflrFhMU&cunE_cJ zw=x17gl|p!_i5**B!Fa5{IGm&!atzw9D;p`IyWF1YP-wZzw z+1o?`E)9y9wKW$oG4Y<8?URll_sX0}R7LoVrgvkY=fwlS7g(##DZNG$?vsKafVbY-@|;$H#>~E+atIf3O56|Z6btowgOy0jEd`KJ^_pE;ZXP6{+BCQOie(qD z)7A4GyVdh<0u)s#;b1dJAdrP9y)*5Xt~ znEOiN3poe%MtS%T=IvPPcv`H!;-E)WF9i7H-miKxnM>sb>5JK*1bs#AXUYe~{hR^TLoCR=QScSm| z-{>+lT00N;5qXoL0LUMgL!rp(b#$mcNd`oTmZt8vkc=Ae1dtcT_*sc3BiOB1WS!jd z<2;YETEq+i?3?TqwrqTl$ zeU@bG1Lnf+(+ZT{g{$?QBAqKwFH5LMeF*^1x2_^~9SUzqRW&>1fNSWiggWoS1`Dj) z*JLDd9V8SllV6(bZ6ZPtb+g~t0>u33^_Di_FOm2hpgqM5fY39pkx?&=zWKZc9$9zu^_`SUSle6On^A2KU|03K zqzlR+8~42?pLy)d1roWCYl=zcw1K0=hOXafs)N#?TtxSsD}byEnIV5|X75=bnRET6sio4lARCi?8gH+r zs!AV07#vJ+uhkO;7AgkA3xrgGn6wYB$0#BV%g{e0TauA`zEuWvM(d3yu7Ny0vmK6QT~1o zabv?hr!ZRhQPe;wP;~-VnS8F>#9v>k?&?n?e%9|koEtz(k97C{Rvbl+I$Y*bQH@c| z&}0as_)1-FHIIVH_CIuu4^H|i{XiqCp{}mZkf$b%!9tH-9^t#q8Scn(DQ*-1IP7`O z9dn4?n%g3p`)slDzm{`K;}2C9gVqyE@V`P;ld|GaY}bM%rmMka1>8w_l6eSU9Aa!g zKx{I7HK^9T9%-SvC{Gp#C#Ik*E$<|#aEQS=>JRU(;4QO4k)<~lii>Z0YvH?vwK3eo z=@a=SodfBQl?k$XWc%)sUkd0eIf4|O_g&VZSlS?m{RSJYCyjdwxV2J%l*ki2c&wjM@sNvY>(u>k+d@`j$$ z<)e><&mi=1dm$%4jZ8*(p7|_meY0fwN}YCY{flSku93mr0ccxIQldWWvl8Y`(gA#n zBVHyrs3bK*wn+SjbUq2n!#e~BTe6t5`b;pOzLi^AT7_r>MwvtC2+T)jm+%J!0wGS( z0vy!6U|w8r3tvCjOMk6``FVZE3w+ZH8X(&l%5pJ^MGTS56+GOjaI`*XjfH^C5-kgo zFTg8l@yX!Vbgc^oDBSw-R$7XGAPI;FZZ}8z=m-S_=vQB0L6)F==!hP}G00u5=*u*+ zAI3{ZsJ@Wl2~K--^8u@b+qeGC9PQl6>nGl3r~zVvKxydwS=DMCk1Hxyg-;?BnL_6S zz`?c$Xn4FOFt6)v+Cyb0Fx3(R~pKa1! zXb~;+JQ}~KueVhY}m%ApyeqMtb2xJm# zLSb^jSS?KOh92;`YZFsfz6uLs6R78uf*vvnay-F!vCQ@8MO}P!*+I;{Yqs^u5<3%` zAF6hjp=@&lnXc>1H&h?pRQJXB#s_+Y14}tv6iXM^X!Caur&CVOna6JxES`&ZhDw;6 ziesfwGim=B*ijyXpwGC8yUt_lAT5O=2eWz2?v8?%*7w1|qiCQlpcZV86;cAZ*`wkM z2r@{2JT*EwG%3iW(L`L1eBYVgMUPX(HhFSKHsZ?_huW4abUh*5rxPYCfQ5aptkg|P zcC&7Vv=@S%=z70Lf}7sw*|@jDlCd6Z;d)Ts&pdv~ZK8?bW1o5aS0;t?Sb3(G&0SHRS5&+Nw?9nLx%T$BbAF0Vgo_n7#@v!P} zOFCR16Z^6iRq}bOk!6GA3>-Bn4XQfdp2_2`?uUBHh66hOzi5fE* z7d0W!IR)7x_>dB`VI0QldxnJx8^Y&&YP?Ouq5WeVliJU|&*1}ho;xK9z$##*LEF+F zb9*_#S(}lo%qdx>+lH}S#DgPj`a`ajbc9LMW8+7}&F`rgvZM8CG8q5{2O8$(sr|TO9Ynso;GP6GRn_VE%-mB2Lq|%nKm)fqOs! zQ7G_uI!etOYO{5>Yt~E=R_7FJj7@^CXd-vHcwo0;GCg$q#or+O!HvWY4HaT0wQhY0QY)w2sWNOSEyB(+Kk-yR?=@TrLp{`Hzdia_u)5j5Ujx? z2G$K08X^FKZSu51_Q1W9^HyQIRXZarm4TQS9{2;?FZe{LiohR}(itP&<4yud zMY+5MFh*oLf&!Xm|7V{_^Q=xKJ~?5ufBff9t9&AhoR-$*qjzb*BFon2mFk9!$-v3e z|7}GOX2Wo0+;D;Z4aP*8bhS{Et+jzna~=P25|wMZ&|sVX_Yu%M=u znbI@;Y?wd9ai0EURMVZ+N*cXSD%D5Nlxu@jxR6P?iA$ezt;ag)JK$Vfr0T8d)V9O~ znUXJAY*anzXnF0Wh2TuXU$B&>O3;z7d>5r!}&eXUTV1#{7WPpBwx?4up_g%U!A&kGeP(y;3k=XPTjzK z>;;wAWWyt80jL5Vu^#~_4m0?d(FxPOho?b;X~7OPNrU2J3!g_kiDg|@oEW~i=N7sd z)=@tYN1)FeAYo)l3PxSY~u^}##t zl|W}bY(u3#*zPVmO!#AhGb|ts`7p%=bez#RkcwZu*1l3-qs3lZbkdRlCc-VS& zx(UK60A~U?Y`eDuiw3d^oZo$+;sp`aU!HO*!gA)$ta3ShHaV>~@h%zPZ9(H~oMU2| z^+Tkifl1JopjwU9$bMaZM>eX&;>q^25IwAUiAjhd$C0kS2Be5r7ru;-wum~^~Zk_QQd(Ue8p#`wcQr{;yKu7<#p@pQ2CHnSi@HQ6`L!|Ifh zo@r&}(I8*n$+nnahb?>sU(}Y#z)pe-6VnL=?{CVUNnBh%1pJ^lT*prgIt|^;65%JQFvXVjT~9u&>vpS^*`z3p{jy({9a) zV3oVh{Vrs6Z8rt}=n+N;Iafb`MIdIwU|dx~QXlfUytCH-N!i({1KAjIFp za+;>HLgB>{!`IaD29wf;g{u0Za7c5_4(;9X0JX?dU(xLZrI{=cwDj%s7Otx|$+qkr zK~ePz6@zUd`e0LIMv^I(2@W-^7LhB6{ z-CdE%c0+-ftYeU=mD_iv`T5FhhuDJhuiOanW%=P0B)Q+SODuluhS&^brzcfEj~f)N z+J-HuG+Z8pP!NAX-(a50$r&RgucXchO*g78Dgde=>P|+5diyFZ{U6|%<;w_Rd3KjU zb@LZEBT@t~Xq75VGB@v2S%5%=PzjRst86;FjGzKKtC>G~4g!uXp+kKB(b7@9>%gQO zKYn~lQdL@7Q%mB$)E5nUIjz1(GC&}nn|v`LtQ;H#y@!wczLnLHJ8cQgK^#&B;_f>i~qaQ(zA8*y?VgwEXNWdlWkW@eiGgrwz zAR@fHZehv%0`d!}z0OBp+Qufhju}H;Ch*ucN-j?uBzpWbl&Hfyyu`s-lnoXysb5Gb zBkI@ue8_Aie@w|$C0`9?Sh|>spfkQNK8%UH0SM&a_(M(H%~_G9O-jkgLe_j%4|gm; ziRN2^Ybr4YG#9h? z7IX8x7*6KKcLfZ8b_S#Fs7oU!(?&vNO6naYHIA7#N z{ji)CZ&K(zw`J{I8m=dv7TdR2x`h{sn0035DRo@8%;Akn5Ov@NQ z@$F(i$QO<@3x63M$D@dAJ{-Ms+PLPf zG(0yVBds_}-)#}2UDCqdUTDKDt?Mr>EvJ_>qPY7v`3-f!bHMZ=OjZK~^Pj8!Zk>Uv zBE2BsrW;qPzq&R%JcO|BZj##veAz`&T64>rw*Qb3aHHZ0nm7SiDq%t1Ix~Xn$B0y3 zq-83Y7GN`X3lQz$up8V|Tq!=^$bi2A6JUQ6Y{WV4cEH0tB?J-EX(EYJ!>$czdP`gAjMQHMWu;D&txz>bIST|_u5NPw8OVP zRI49iho;KdQ?z>3U+d}&jzyN@hCG2+yL9Ep<+e%3vGoYQ?eW)-;V|mbw;w+qjIGC3 zPjLP2e6#=GHcVEMry}?p=#M+WpMXQK+ZyoQP6GU^ktz?tka z<;=TjLBf#&k1vnu%SNDEbU8!F$NG00peuBGk`S@iQZc@I>KZHoN^TZQk&7ETC0F7)<+FP9 zo*)kah3TX9xRR|bv8hwL%;d2YYEr%1XWIIo zHcyosL&8Nb)}h&N;+VaA0MbLJ`-E0$Vj7WVIntn0XUjx*S@onKC(;;hYQ1ZE9Wh8p ztORNBeDYRr7QGa1I`i$s9~>M0)k|UahEEN$zYzO7BK<11j_QsbT=2&EX&X_p1b=GOw7=(Hja9T z6+X}1jWMPz^>k9Ko*qfDZt_5JZ+$DPUNCdxK5<1_1UwhZu=WXz9MkCeR1ezwoL!4?C9RRnofDV<;SJhj!RE+ z{Ds#M`SKvUskQD;VZQs;C-*q2{PhL?jd{dUrd)XT{Becgbl%8rNxEC6mae|uE94&U zg=$*ii~2aeZ&xe=6aOW$2YJ;2?MF=2AK8J(&!=z(NZodS{^xYm|E$da7{UK%M&MgH z2l~WnnSi>pwZ0V5tF`)hO{UkCi?;0&{=7R@<+E63FH?Pn2WH=Vl31d z8$S!LdHO6XMR9`K5xU0&U$0xdo_z+?2Z$a|DnY97>HZ<;#&?I@$n?Q0ncTjRN5z}A zvy$+}4uSTmon#cbWKnm99NOiItSRt7EV=PL;tK}11DB}crO%h#=1$w%dE%KhV1OU zzsJVLvY2qgmqoXOt> zb9q%2td!7BBd%Y=+8>`kve3ED4U{7wD1hF!Z~Bk2dV)bt#B}z^V!}?n-v-?hQsLYm z-|53`=@{@qlX^iUiVo#}DvGm}^V9h;uUk!($LK9&)ElzYlDX^Q^$0l*rn#`>*icmU z33$FBqx!s&wa@LPR<71*asPvp9+E3%-zrH-QczGvApeYL!W&UG_DqA$aP;|Wlf6mF z$@aG2Cx5Yl{K>ri0IDC8$~(5W_~g;{_wV1E_FC*=zcRnqX8SE(3-ZuvM&v09K{qVY z?0D2t4_o+pbkY>&ToXCD7iD`NHpr+xHL=8jS|7_n<-7GAh?&S2hk}jw{|C9;ap0rAzgV zJoJRRB$U9&;Rsl!I%D)pz<#g6&H|_zY5smxTv6yU<-ra{2?l!pTxL*)N57o|M0o@^5a8S&UdCQ~`{liM zZXpgwO?N_zur^IjPAa>mEUg(A;LpRYpSB{eIl8>_gJb8xmF+CQt*j-a(taEREz29L z(>)K9M@FWWYK=*6E;{)!b3`u{szyC5!tL*?TVclUnJ1JASd0^YZfquJPWMulFLXW) z{I)sWlhwMyGe(svyHS%*p7mDr`H>Nm+%s-|Wx?ajk`EaiCJ$*tpgk2B6D>McskEk9 zL*nUczD*I(@*F3+7N*+}w*&@U{d0Y}; zp#gI5+WNWSK2pFV-SD}z+}5Kub1jmuqC)KHfE+-fVadH{>0mrWqrei1Nx~R z#i>+q^_(p9J~?}Nkw+al2IxT1nPyyYqd0HUe7$%1`r9vEaE<8oPHvDIeNK>C@l}Wt zFZXLFI+p7Dth#@`y5KD#Dypx$F4H^%`pe#FXZ9hiU0Id(X~NukH}Mg$%(HJ|7dS|< zOPec1xq|jb#WyP}D)g)qPn|lYlt?$}G`>RYJQ9l(V`cYyAecy*c$gR(8HEAA-%Wbw z(orKr3WPOMFC!Su7<$gsL=XYcpxDhXkpT%Pol7nJ1a|d#X&{F9vwZ&J=bCK&yl{wM zvfsYXxpElcDV|+-JKHVLIkk)QnC#fF*<56XzBt_CY3RG-FdDhi{4mk7@ow`R2z9zD zj@n~b>j^fifEgmc+@UM6habC- zolBXW$lk*aMAPG;0PUfXm3cb4A(knMwgl0`W3RYzvIlE(FvoR=M$5kWxKsY8gmqaGA2F z;FTZ-@B)}|aQs)q=jk8BXksUOC*LX?_~N{R3c=Q=NwwhgF&0;|F`=nJH8nNmH8n;T zELKb}y&2#zA75=6r(97j6BpuI&aw7C7;qcN2ery?x-tOMudJ-ZErrRN&xpJxsYy%2 zt0^GDz)I7=R)^}Nw+ASOgFsK z@A`Bq|CrbJ!1^D(6i4v|usFq`xR(o$CoM@7mk7KUm3?T=6+dj!sczz~3c(tNatc$* z8^U}%Fj};ZLWWe!{d>9C$08YG9mUhsBauX`0z>d%X?K~FX{OXdAC7#~!)eR4)U6H5 z+!?pGj($~5E?ActY-mTR#T0*)-{)o=jP(+;@?0{!W(4AJ<T+w@79Q;AlFGIt_xh7f4FKM8Y&WBCgcn3ssg9V>KZ7v74mzJBG z``qMR2ife2pP%1@nQ?F;6{qPjlmUn6wjp?}M$B{K5PakFszQ{2;d=E7I?kzbyYUfR zb#-wd0}&qh_UbwIDro8?#MBp&dY8S&DK$SO zORRh%h64L9o)$Nb_2`^-sXX=FIj$}47-Wk9Mv{hxhMx!D>QGW^ww25Ma7m=<>dMM^ zj{|v?bjiiFAFWr4`~}+#)${A~mTi5ld}0(Q>+Gc4h@Z^kuC=WtkY|Bya{6Dyz4lZ$*c^-8#g%VO3B12`$i3&-Sc`P=WBhxmYdRH=}jG50NWDX&-5@OR%LNaCynPZ!M zu1Dv5U*FGP@Yz3{*Qwag^W4vUuXU|!U2CnD8JOP!rn%7i{bw@ST5b!orR@pJAIahm z9w1FTck*a4&qWZ?@B_v;h}Xn7R4w_&MVN{`jxt2XlNRB8I|l2SY%t%>#QsRG?9p)c zh{?qBu>QV(%47~UX@u7QRE-(;7>Fa&?!&P>2-nQ(aHpp$*Ew3gfv%g0m%f`Y|KVXK zloMsf5?HA40T@5ZMn{PoG2WUd<{B#Gu%*Gn&%;{@3-;8vwNUVX zd;7UE&D4*U_d0}YJZd%ZsxBT=2u;sy@9g|2;ffwlsv~*_HWt<8*mX*phr7@1pLw8k zBFw3&`!U(!$D`ob21?Wx)B{|9%Mh?Bly|$ayQSWAVZE<&@rox_v2pa%o5i9>!6WAa zHq8d-pOg;_8L=}Wwkl$Pgc0;Na5}y2c{-Wq)|BrLEOa>fmyNh|KuB%_A^6L9HC!hqFwiHF<`gNUoDh!Q0cdU@~ zB{J_u)h@=hTJno@71ITfSoRN)RyAKNz4L6P3nryAcMFcE{!rV%UAyQOp7~+0gCVq) zfw=$a^l#TbQ1*3C7XH-sx-?&qV}5EFhpe`B_r>_u!X}}&2Wfmd?5wUe3-1zxV_5=N z!|Wux{~0vanlIMvcMcwO|1qD(gPrS;-uT==zw5-zq+Q4CKMsyy>pf%#rxbTDzqenLdzhXt^p4p6^lSt=WbbMpZB=?pVHiv>|~E2^wLmp zFOUwK%9Cgga|D!zR6+#7P}%L+QQ#1SLZK0kuv#->`l(IoW6FhyyHcAXb~4CieG&9b znK}oXV)~?o1M#ytyPm$j)R|@Yoz^Od7?Mjr5BiIvT6IA)7$Vz*!QZ~aw2C@HEtcp? z8PKDrWp=3X7P-p@UfD$*iTAMV$d(90c&OH?_GDz41VdDFCnE7#;*bIb@nQo|OLT^I z`qk)>eF0rGua+bppEJ_ZvCR4A?vTfPl<-wnR%VYfJ;nGw-=-_E3IR;Wuv@?BJ0tz< z(pTHGLX{-NaJTG|`~Vf^We4Q4e%+008ByBhRMe3Wa4C@UI@M2Yg5?GR2ccF|4G)~7i9&k7;(_3Bp*x9g&O1CWtN)WTgs zj>&Y}{Uj{MPi;6JxjTaVD?b>*vM-Fxr0+3bdgVf&;>(ti>lOXo{@GKG#Ia-9Cwdd6 zIH9U{O7WA!{e897sY|NMXM-uF!;0S*N&G%N4HtiYzrzSuEfhu4BC0VSibsF^B82!- zTOhne&DxkL%(ijeo_-6CTg!j@IBbw$Q|0e6lHsSOyD4kVNchin-q9$nQr{J)05^3p zkzCE_T&P!=2RL}6*EqgG^Xwgt9@k#Gvvi~X44M`EQ;ZYh8a#ltiSNxKkDWn^~RX51R}oFnEtR15_Erbz%-0GjH6~A^^e3&!i&@asN?%_;9$)Ypu`U6aYp7;wH8)Z4adFBq zIyN>lsv)(Q74-sonH?cl{#TUZ=7uVoZ!-g6W3X{gX!0vHCfs-Vy&gpTZ>fitDcdLB z6c_7$^-{sf@bzz?Jg4P;RZN`Hb0`ZbyXZE&<0GehBC|ux>HBi?39d1N$}8Hotwiwk z=FOYu!!iA(Zw+5H9r)Pa{ZMc8(ehb?m$b!YAhARl>q9>T(M}3T6-0n@sF8YURZQU% zmlCO_Q^l!o#mjG@?cV@phL@uM`k8I#-S5X~Np`IaB=P4h6RYiSP2180(kxn+weqf( zjGdjO@=HVx=ou;JT6QZWs5XRD9~F((%Q$o>8~QT1aYLsc;A{*j!!rwY3{%sp+6;1n zGk=%?FHKr2W%K$1W}g972x$HlEtH6y+T>VAQAp`6w=t0 z0ucg4E1T^u_LyGJ>?pF=rZI2wXQ!k4O;)^6$uflTVJSHsd&YG06koNx@np*xT^PN) z(W!5ZCdtgxS69!lkrerqT1@TgA@4$a)u*UcHQWNSGC8b1R?0m>UQ2f@X*q$px>1;j zATBrlYSrIZCd;r_885NGpI=UtMDHG*dCn8BcPNI$a8JB8{6;7=oKZ0`7b6mFI6gsO z1W!JR%x~a1+@6hJDuHYnLcuqnw}k@E$Wr`4& zScX6vS_V|Ydw%%`jto=-Rq_&HpUa7j1gQz=Y|)vhVf%C9u0KQ7q)H9SJoH>Vf8ImL z23m}J>k41njCBwwRi;98q5ZlJ7HnY0yG8#GdU2O2{TvT7WM(0O=y|?w$Rr3YsXSp-NH2~W&MRp>p;$+Q2_j) z8^egzIy*bBZlXxoM?iBo1Ku%81o5DgLK;0S>3Am zJG=exu6L89B_!NJ?IdOZwN@i?#>xZIqUw2XiFVHI~jko6bm-|??Ff^;BtM@K1 z(ysSHIMj=cZ;P6Vp^Io~YBLbtG)@c4=nYA7-N96>p94+_KX~`PI0$_7F)V)UqScy6 zEozw+gYcn?;O@Wf>lfHHE@l|a5)wO2X}X$pdOF)4t#F{bN-T|13N#+Q*hm&=bUK@0 zM;4Ebuzk^fdn!smgF^DGS#BS)Pb@7Sqb*(^R7adWL^DvGKf`a{$F#kee@d zA6?u6T9u11V3#=Mg=XSB+m#gGQyi%9Hoa3*cINGsm2x{DRrqf{0zOrq`%#vj$3GI8 zp0f@tVN(*Z6z1KeuzwzOvaO*MgX-k3LzE2QOtg0TZ5{Gu1>i3O~k4v|;sQ1^g zBC1m?qch0;Ct=BMc4^5iVctNsL_gN-lqPM{T=9+1)9Wt-Pt3;g#FS)C2`eRi{u^)v z`GTPDz^qM%jxH_>J!rk01W;_2OiycCn{9g@G61h#}qIF(n54)2IOwfpt$Tx~#c z4(c>CVRiUK`vmr$NkKqt0k|b zBqVgjul?O_IQtyzjZiq~-#V*9JYt<`pLt$Wc5y0C9`ffQOp`XSy8)TqL`$@BtM=~8 z>-1ysE&0!;Jx+bU&R5tKJbpw@CG8I{b&rFJ}?YtFDO1R}cBd@UWw z`4}tTY6`oHB#$bqOXpql$c{QI1vs8o+_3g^X_+92`8kg`uCk za*GLF#+^OZJOLVx#4ad^AQ}p50YRW;P!(d(lQ}hYr-{K|KCsU1nJZFYvl>}c)J5;)fYLXe-?o$IB9yxH5v4yrpVX3to=r!hjzDge$=ad zA|&w2v}56|;+99jMUrkGxP_ji=X53kD4t$t-M{eBsFs$mkibEQOv@jH65*9mt-!Bw zv8@uOvroKR`A>u{-}5-dSm*Xfo*$NW=3O?APYCloctyCmxry%;7Y)Agt9qD4leA#< z`Emo0AZ@RT4qF(0r8}#}QVF~gz%E_&{rmTKy^UC{LVA}U{kHJ6V1;TX-u&eeeK?lH zTyiiPX+evdCQqg&3rb7%hjl5_5@<}q9&H^77z3x7_QjA<(3RgRWfdI*q#UGV-E1Uu z1|;m7@En-B?V*tgY4Dd{N*&xxf@wL+pAPUuCn`mO!Qo+Zla*?MzN%|PU6_W3#@(bc zi0=`z7LfEz+_3<`}6I3=8n z$hL*Qq7X7plUl(1`-{E*B^^BbNjO0dsx#BvN;u`Gx_?bSk&*QSR()r6R$M&toIzC? z`h66FG=7soFjUp9&yX-F7}1_>KRF&i(0PMQI{k$L9T%4zRmVOaMUX3c5hj7s38#u)VV40$_unrsL0_kAd_Y*W1{bnNTT+q^&=`oXP z#o+%(eX5wB=cuFWSV>PQXnhsbDIoI|GR%i%y4!+0#h}zBc<777kK8qF9qf%e_x*xH z&Z8I^N@siwLGdB77$D<;-x(w|GGY0MuShT}Y(7V!4ow!&uDSPsxI0b#Bsb7>S3T?F z^aN4`$Yq-hjLx|1ptek&o?CI0w0~kX9Da5@!ue$sw$v;KagW-Ne#BQy@ z5c$PA9~tW^1aI8ERaz&Ip(J3SXk<_ZtLQTkqoqwF%bCM~4`_l5u7O*;JY|_`qYd9a zR9(p9Q{&h!V1vASSsE&NyA(|R0??OJxf0^uF19@P6~PdX&n-K*u=;M*s~e;b?G9TJ zx&vhc>{^r)xNIE8c7fbM@Vb+uZTTOj-Rv^B<@X~``dQfhj`+f|2X5s7C8K2o*+%IQ zcamE%iZ}3g2&1y8dF~z=ac;}IH{Y)zYmGH}G=!x@3KA-P(nbG3MW!gIlR+>lJ} z8b?d4*g_lrLQ^!C1>zrCG|AQW2a2pb-HEO6rIa<+wbz|UVx_YnpfzOeb026I@f&yFA!yK$?m5Cg@7!Bj5PH%A`)5ZOV4D) zca-rXvkg%LvV+4*diEcY0xxzJJ4Z(+asSy)a2qVN;GJyo4NI95)Q=xyiv36uzeqph z43G+5%pE)dICjv4pZpVtl5NR6TVk2Jlt)rrTqiY+;vqtd8emN&1>BvNC@v@npI3Em zy6Yeu;aRE=QxWahsqq&~#mQ*t5CzirDT=j?P0Km4LHECkC`=OS%eW1+%6B;J?3aEw zCc{p*I>yd(;=%C9k^9wccVS0nr!6~|EnCx=iNkf8RbrN~O_}kQO@%NvEXZ9+%(LZ! ze!Pnesm}8(<0c#_t>ZEtqG$Y!za~>i;DG!B(sCdOEM@AWnINdP>V|BdPA!@#;T^)1#L| zRCKgbX@=*p3zn2f9uJ}eZU7>X8ioXcWE>fonk%vWzNlP8=B@Q{<^VP<;50}`ZSw{6 z48m@Fww5ianX@hHq?M@?$%zOPEx7K{7LQgxM&vNuSMci}vMq-!QuH$~MI;7zbg)h? z`wroH$dz@>?<8P47PGgnj}wW7&(mmy2OCSC2Hz3MrEu|DAmMf_sY@nXS8NV%+vVnboLa2B za)y9J7=9QYi-p35Sr8_}BEEj7^|3a5%R5U9Z1hESJ}9!7mIB~vE<|97g6=LAGHSx*R7I(7VflAIM2LPE7&mCM0FfkSy+K@%DqA#+cZX^T>BTR&u}edlXPk44kbOK zW?S0nQd;x@-0VH4pF>aW>E}83$n*Km)uGrGv!#B6m3~#yaUJ_B)q2|gMK)U1*T+D1 z8nVz_Lebu7+VhDgZG5GpZq8tNSYxuSYsGN~SI=3*E|-cZ%q<3?C1HFyA1CkLbhGi9 z^AKQiI+i1k2!x2P(L`MAMAr^6a)1j24`^2)gv3b1Y0=0&Tf?q3Oh8p4&>MY5>p4*sgto$=Ch!Em}!73*l#1TyMHb`h}6p z^wm8Qr4ycr=$j(H3QU|qn-rP&>fR4Wl;Rp2yVQ4VQp0mbLow_-sLEr5dh<4kNh`bJ zJT}t3Dq7Q%X4V>kF(s{+=(a+apGiWeafhI66>HyQm3EC+uXzLc<^yn!|5bEaDE%%6KkMq;hAu^{SYvf537?~)_JijudOr# ztP0>P9N8K%bUj>>qjZZsLTNbuXPoi?2z()Eer_m9#35c9TtHNZ5t`agKy_8Fli<77 zEfwGF#d_@bZR7t9s|hmW_vfpvbcZ9Zrt@2Cf=FU%Hxyawe1 zIQ*O4KqoIkju*%&aP4zFp%HX(DLm!P#R2=r2~uuHC^0_@ALsNnZJ$jSzJ!Pt{%9AZ z;!FOeLF0Htbqnj!0ZPhai4lb#do~jlFT^M9&&_+fwOEnm2Y_7k1ZC8KTtQWW88bPi z8i{|?MCC^N!6IS#K+aOPd7;y#4Yy@w>z1W||F&24)(N^8C7^?CV?@?yoCbxJG-DdJ z1Qe5N;watJCYQWy>p$@`+q*HF0kT3+DOZ%l#_pN4+*kYedpfZHoeyFS=I7_Xyn)@d zKllW6D(&%jqzkiR;`Gazv9$mN9IZ4TAW;ANIaL7VY z%Tc99up&MXcGPfK-vHoYM&+ZB{lCA%a`gU|SGJIwMI>JDg;@$(&sMwcKv80G36QTM z*Sa~|_KprE2meiUzNpcemmYkw3Zd!h$1nZs3}ZYOlvmA`ay(~Y>QMyaap(VM`MXDd zI2K*+2*FXr>-Xhy@fhzciDYK;zo>I!TX{h59?!W8OQ??d>L`ef#$HYg5t}1QZ$@t+ zoq8WM0dZSOyN=0cCj=Jz#qs3n5tTFebo(QIlmP8nI?5rDYIlAIoj`n)x&#kdj8ld=SqLsQkZ})PD(f593g~#zq$a?YFj04xBy?u0@+qCdiI;QPfpr$%I^oU zf06`ABlgCA+`7MwXk>$VE<$YU1l>ItVMFYhrb?v}{Qy+_BUbn2+VFLkZ{h8EvDJaD z_e3=u13%=Q69*B7FJ9gTjaD%7bpb?)ebk0~Jq9Mg9ViTlvL)Kpq1+ae)*-!jiR@%(sGE}~9RjU8K(4zA= zxUD#4-gBK!LmX>j;UKdhP6%mPVTLVCX*^DU+1q9->Az`r1JOPH!(6oSnO3C((fLxo&H^iBjE$>si!h(u0?A=~iwe``{l zJ8*~u2xJUJ7=S6LVkys+Ls+MP&2DMTeOW1xL0o?r_Msbg(&VAWP~d>aYyfniJU;#* zI4H#DE9-g*uXN}tf-)|`oq$GwgZ4(k!f62s71LdXJX_R_wPr0xeCF8BtA6ZM8O!Jw zglRLM zp??)Y?#5DMEm8Vk|L(zJuzFt6Uq&54&=8^TmpMWMjubHrRYIWE)w3+}kQ8v%1cfI5 z=U|;9u>j&&by;8)q!>iNfPq(c{hW;im(zHsl7pFDSy6D<#38+M38(!3-h?CkjH5Qz z47i?$+tpM_A36Q5T|k<*E%Yo}jC1Wm}{ zs`LyD1Vn6bc@GaZbTd-3*FzP2Y&%HWwaXPRnLNr7r=BW@>t0RDdBqYGw)+k43>zxdS2f;% zNIU9pueOcMBtAsci#U5!_s%8RI^z+PUg#fxPIQjKXU-O@CTe}=khP^2Fa@Toe0c+o z!08)s8|=XKUId0!wb^EPCPE2X6|!NiJQlynfybbBqmhi1+uW;XmInZ?2wX~os?r^N zazY(z|4i}!?9Nw1f8RWoLSpIvcYo_Yq}yZIi(UCU=pyiQZrEwYojo|U=It1?1t3_^ z7*-8Qi@oSGaEb6yBY*tb26S}gAVFt-v{#%4nN9Ejk-Tt2#OKuo+zyL_^eLEXSc=J& z<;9QbxjO59N>|jE-t+(>ZYl z*)hW010zAhesash-u)|{8f=?(xp7?FaGB7n5p zVJC_7v9j{93al(FI`>G1dMet~_h4axevqR{-y#R@3!+BAPq)QcE7dVP9ud6az`<>* zj(LwS-a2#WW3Zmse@=tgd7>$}B(h0*seY(sNhnfcVlf@Vwh6cjeRn(_W&M#VzE+gxwAVx=_KJ{vFa+fm90$gT+Pq z6i=#{DXr8900)KwE%K2>{XF8#kFUgwMD!gpfKDE;>j$+${{zHcVhUbcCU_Ba;zkhH z`y&+zM0~Q|080R0J0)Ro;%>EjuO(wYzu_1lo2|Y50cg^mC7KcGt$e}WuIv$Ll&()p zzV0N2itK)hag#oT4J$-R08l95_T^M~qx;ul2;i6KPC|2j@qMgS(c$v8ut73njy0#Z zZyx|6>%Oic2AD*ESEvvWYOv^-#ta61pFF3JUMk#+5Ts7{U16o$=Q&rw4hS|^iSQLQ z{*xGb?6Cv2K6Ls;NUs;Le_p~|<0oB917bQnsIpk{{0;lD2|OpQuXX-;3bhdaobqen zkJvhfzW;`Q#6JChUy`^ul=rV0D<26QB)csyh#*ZML4|=Pww^aA4VXv=3fD%j{7ZO) z9C*VvU8PD7@rrOX`9g2!;BOG2fEqMknhS^kZ6lUjQk(`;i=%qI7m37B1>&8Q2b=)J zl9mntKqALTpRvL*0oXxW5;*Ih{n@(!=8K%hxhrh=%8#KYthgAgO=4Bc$V|=RYW{0{ zrR}SBP0(~3DQ_hZZii4Xcm$H+H<qpbp~;xm`L{L;)uzw(m<&q8WT5b)#dj2(!OfR*7Xb z@E~$CHFprxysF=}(YgF&7^(G(zcdE{@&?ySbjHDSK>&p^w>)2LDgZ%VtS8XiC6)qq zybu@`IO|V$3hu4p>ZPRBU$Kc*C7!DuYrl=V1h8o1-(0>Ep}ocPFqI=cuEr$1IlMxo zu`@t;68>H3MxUVwBeA*IvX=GTSZWamN}nDm3lZ&`b-Ph$$X(~}Kths!q@V!97p zKh7i)8|@ym*}Or&&y=$oOnT&7XT+wk34~pq_V0>*Qgj;=G=c)U?Ty}MAXD@tAmCU9 z^4h51fm14IOfoB+(57?VE#2z4j!r-FKez)84UrvZX-69oOVQ=;YtLSM0)7L@Yi1`L zX7B^an@3@ze+TN&ntFRDTe80bR_MHOtr657WBa~I$=mYSm4U_+UK$y#mQ_Ibzc&Qx z#yoZ+MPokqPc2alM^|nUv=$F2`-@|1z`ssq6UGQoa>ix7g!-=*&*hd$q5|GL%6+a_ z%6&n2-U*rint@%ftkz37&%js`Xyc(ydh8UFBQbB{5!RxJ};3R|M%n%+47?fTi z{`p-UF1YKRQ}TaW09-w@0sI;fZxUGPb)CZihQ5QSSh_R*&U2n(P$hu>S}_=zlC(sJ zR*rF?ibrC|-F?0|ES%-59C3MKw?4cS-w>+sjvvsOKtfDxPD$ArycAKDHH7UI?I7(d zBWua$`*xYOiq6fSg*z0^epQ0vfhZ2D05`3?y_TnJwPj}eE8pLReZ<1N60hCWA;d&i zZv8LgMEu?VXP90+%}8BmC}%P zuM4GQ>t2hasO~CFYZ*aa!3gQYbPKRaL@k)HbHn_h$>sL4($Xj1-UN-^Zl13b}pMsJ8DJnQ_6PPw!Z3SBnl#C=WyN*OxcI(Qvt$Qyoon z9n&yUT5nisT_XI`rHcifO4i?sou1)^5LrEPRb#*)#E8y!7OI=2trdBEIDW&!EnPZg ze@uU?!sB9nAl5Okh|TyMBO)aoW=8!c*Lzotk{v!4SJl#Tj+QWJPJamI55aQwXOS(6 zO#T-=54}B&q6a1}c_AO~1P_>OpzKRvw69apm{L)|sA2p_fRd|&g9t|RTyI1W_kdDV zSjdbjy~B{$(BgW~9{=rn@kD2YByYHgbLqgVb1TzS~H6iZL%q&yGn~ zIlL}pUoAMGwqyC`o?B*rS&trnbuVwPa>5FKZv2DnPu9KrUOqTzzU7+lx6iic6aLUS ze)TVoe*zdylVx|@JEPs8`eJ{;m-V-jdy=oEu6RvuQZc+KUb=(#6>ncb(eOKIo-XOJ zmLncNIUV;`U!C%hkMD>Kf3JM#7}+hR_pYv37JFQ*V^MVBu9e)#l_I&eV{ut#5j$x0 zch2a}&vjRKS?BLK+svWW#gfe%px09W>v_)XS(|1L_JHc%w}wyONK&)NE-n#g9!_-| zD9Y?LS?NeK5*(Gx83nn!b+3X)ec=A&F^(QI(ZN)x9i&U(&K_ zNIgZplm2HoRyF;ybOTVTGg!qTq7<1{sjIL5gta<+p4M<~UBGJ5(3#;lM(fvXuRol~ zTF{#8AhjUzWU*8_7?7-$Dbwi?EjQUi9?^1J(hu#|Z4=#j)NvKtSC=-JFKyu&6BA?d z%~Mva`7k;j4nhTGq!r5G!6rNDx$^a+i`&~3v?M-&gd*QENe9E-0@T#+Ro@nlIJ+)< zMyBYe<{xr3(S2FB1BH}hS}-d3LCpy*+9hGZfwB*S%ieKkN{fr5=5_P<-V*5;nvr=# z==|LCux>${wtX^tZ{^cmO0KlAVwut5&ADBoT0~k3^E%4Pii_()ghiKwxU_hL1l}H{ zNL>jUeC~7gIof(W{XJk7M#uW{1l?TBYv1xXcCFCrP?EoUmqFXCy8kGDWVfI|^YhT| ztmLnK&c!bG9+9-dt3$c8Nb2u)tU9S25>eji*6{6vD3^1c&UNjU$+ul%l)QM`Bvy^3 zO)b+CBeGKfLt^n3x7tlV>m0Dqg?*sq@93 ztu{ymg%9oCVWWuV$vrdS{7MWVS9~Y>`@Ow656L=BP%<5BP3h{ zi#PTe`9;)oqan*&$FG$>%I5tY&byGbz70lcX2sA?1r6ka1r_7Xw9|IRk1vKR1PE5` z?`L`JEkUK|7W#RvzL;5xa+0_hD{zw$h#`x5u*KoA;6=A076?BOOd0tsnA6YXo!C2Py-Sbi9%(S25H3@YYa$_F5N-lRke2 za&k3$W*0lf9~5tlYo68`i_P=+d&K2uSLt|CMvdH}fVRNadxs}AA)IrTK)u7kCtON< zH$r$G=Ci63n)@Y!p=Xt7pY``Ov@l-LOKtivT^!D?-tfO_+e3wpquM>=R~oPI{+jYl z0AAU3wCuqxgU}piaKgM)YL*O+n$u<$?SdFd*-n~1s=_zRvhl?%F2u= zxDHMj)w{x=N=sr)JXCnzBn{& z?@^KslJd;ar^g7dAn~|K^j=)_yQ3F&sQU5DkADLk@H(6rUKHB@fPs%3BPPH1DTy3- zDDYV8{eYaQEKT{qfvV{#{=kpgJHt31+v^=uIY4hK?;+msT=b8~x)a|n%I@80cj#D_ zQj0@PcADmXg>UC%^pP{)-)kkz+;`@x<*Mw+dAF$Iw$YEp-F$o*?l9cYK+g%d&z(6?$noG>3JgY_= ztG%m_ma1?jH+@%>*mmz}>Mu>qKDx2r$0Ycg_U2VhHyZ)@K%x9(WD0s=1na10({HpE zJ917}MJLGZF!#^bYNT%7G#+iaan z$tIZ(GcHq$7w{g&l~<&eO#c*qC>L;=*xyqc#OqIbbm#O?GxE~RO6mPr@X~ChHK4_cS<+Q1u?Er64g`r+Be7E&bn>)R#9={ zL&UF&E-~uH9EEK}N&n+Tljy(ws^3OQ_HDajmU(>VF{X0vOE{z@irc79%)2TQWs_^TB%lE$59P|=X z{O_mG@juF;Ol}*S8?UtDp}+v?0rwJ<%D)-peaVC5=+xlux8qJSdxf~1WIyYVkg2Af zlTi+mzFjomC-NTt_XAcU0>RFX{){@|DFHY0 zUcEZf`$6aTdL;5}M^*1vE3K*^ZmN{AAD2Q7{;X^D#D}+K>u(H>=5G&peEJIc1*=f0 z^z0@hr@U(7B7{aEZ5|8>7|C%fpD^CK#^FfFh>d7Qe#lmCY?e!GtF)wFnLqa=C{^xR z9M5A>IbZFg2{kz}mH8!F_E)a5Go++&$8^OlPLFI+S#*x!@hNCI(D-DU)MZ4Ty~+6S zu|p7?`q@P3)IScCOI)7tdN;xTbL8??o(r81zN$?Gy_54DqVyay)#7G~1gXG7YhpKJuY|v=p_vh~uR~ljaj_t3%&>yd^V~bJ4+B;=%e?xWN9iuKwk%T& zeEr3iq^15-vTxBNV;McIGiKIHWgS*tLYIv|(pkaIG@5eT%85A<-njlwqd?C7Z;#Bf zJl?PVb9?Ri4Qer`GN+Z-OqkNvx5c)Jo~ik4gSNx>(wD>B7*WZelES~dq*0UjQCoy7 zjz{5^s<4e~<73^1NeRhk)S$!fx|~>>pYaAdu}>{32ENFZ7WnT0y|O=KOUTa|`VlHU z-6i7TLQ*~z*UYV+6EKc6W!<(vRjR7AQhJ_oBKz?Uv!+8+oGg7MFN3?(86Vy?SRC5; z^oK}{M>>PvkLX+%T6Xg!8^!gl*y=YiW`F;ZmYbev3%Wf>-LB3-4+6^bJwkeY%4}kW&xDftJK3abB0@-1Mqj0s&)m+_W6HsG zdoylX1QsveiO!V#@7~Wh!S(7 z7~lA#Hsqwey}fL`Nf2m=1R*MgnTGVExcpUDWuvvGsB4e5_)b`9%3b!|*$f*>lJEea(LWA)k?mV|fkfo%>n@2_IXr_Z3_u;eKMb~X(yLP5g_?YC}9PdRo zBaF-ArcPrKLRMtnWNg#NuQ@sbE0a=QUlVQ=2I{D}=U&!ztSxWw>fSkX-XiUe7h5AQ zw{N@m_a}Lp{k_c%{xx5VYuT2@=UzZuzrXs^ukm;*lRq9FaVXN8a`#LRF>xl-Q+*Eo zOJIWI5q)JPzHc%0&vr|_kv)+x7mzP{k4WH~&)9eoR9PMCCFitgN?=B;?6>fGlzQ9j=kn~Bwd z*!zeBX71yN4Kd!;3&YnY>t~`(J4@=lpg%f~T|3C>Tz)Y;Q67Pdvqa+eJC`A3H)h-{ z9~eBRQ}wl7{!&vxh9Yu|$;(x!55%7RG&FR4*P{!NaN3i}P-opS>W#cQakx=b%XWhq~-TtipC3s}(pB#;92P(hHAL>dZ zg|E2gSzSkH6FrfVqA=l{uNZ6}%*t$oh_@$=h*YoR2ybD}lX ztMLz}pniY6N?UY?_3}%ig~XhF2Eh&t5N2BpThE#u9k*;HxpL!b@yIeiw|6kacE1& zZS=~-+VbeR>N(}X8hsG14-aZpGGYIg7hUM?(Idte(SEbxoHTqmpRIQw*T<>L;$}Px z8|4qksj7*HW=%`jsMe5NuH?joWsC&ToNj#PQ@Bzi{`R!eX_aObH8mlnAbY*cU)zit zM*q{A(9xw_R0)0`H<|a@LDF`ePSdTRrGcUaDSBf-5U1(eBqq11zse*95fg>cfUO_(gT{T>~I6aQm_7HzK0bzM${9ao>3 zsD1BFC7V9R3F4+6RXF(gg&JL~NrcJI=|A^w#3t-3v`z>V_x^m+dA!z?^#=ON{;evj za^*(oOKs(Jxw$El|Hb>dE$Q#y^Y<*S^zCRBRu4>nAqbnv`sO0 z*wBw_zqjZgtAqLUmE%bPNo0n|kA>%jM;6wpEKFy*gm_B*<-;z0ejmEdhUAROZ*+6i zkMUuM9Ig)umEoZXoqcjaX748)+DYdtHyNwG-6@}%>)RanOZ2Son+!!X+xG3qOK!Oo z+W&RidB%rXS9If8YM7=2!xN9)A~=vm((+>VWUrB*l^%)X-_u3+RR95))uefg1ATzn z%=1p(wx=(JSZ(}o39DG@u)QZDiR2e2YF$dr zn#|z8c^4h_RWU*r`_(EX#oB&->Fw<7AOAC1p!sEK7L=z2yroP>477HQAS$-Jr5L=r zksNM(_`e(XBAu$j*x{6xk(2o>SXk|d)Re*-x!xz#tI}O8v&~1i51T za3EFm8)bS*Fd?lfrNTFkG48Z2@D|Wj51krn+SwlG9y6RSNTdmleyu zx7ZHc#20_wzuQuCb|9&>>@(~kAxRmw(qiqbuiv8C9%YVI6$v#-XMcIU{6ZKGpNaxv4*j~I;}jP1Z^m+DLQOQvV}<7wZZ z&=c)kc@!}28pF_LO72~a8LaHgAxa|NZC@>t2)R~Ucv*{-UfBG!>P(*c{({h&`D*$?DeWuTO#89FD>PuYAg+^ZADkKWvkB%dvwu`Ra=Gj z?bHf28rSl;_q@L(8LZRYcMf`iB=W$DZ|5p$7LX_iT#CE8T+l2?!7qcXYL+sy{6Fa% zh3Tzc4j}4I&R<32=u15>oS)$b(ic3|SQjr#dp)YdrU^)l`oFYEqmT2aV^Wikb=%O1 zf~9DR*)Tehp@zxGxJ?x2G?WAj8lGY^SE>y1KkJPZBlRo=(0#FhZe|YPD z>f31v;8$bDi#H$BAwM@#XpdPT1$;X%;3x#mc`~=j zE+A;M8^LfG&Ojax#4$;Y$8-4#d(U7bv}NhO+(alOm6q!;QVkhB zvB2k>i?#sXJs+z40k$WbNKzVWaRWjcylRLOU)|JvTlnBr?gMi2yb%|-?Jx@%CF_6R z#+GMwOzMLq^(ZJgs`S?N+bf>Bb#*yA#>DZwNEtTnp$}=NTxfCl?BG5SX0G;vRT>Zo zk)2j3=*!aa>g>{o~SK2?C^*MJ?YFy0*i7Y)o7(U-Qr}K)L66 z!#mPz9+r*kz}ULzqw|WLz`H31XAoAbDcN=(S+3ab=1a%vpAt{F1Z_wN6|L8Pwxh z=;LLy?I1bD=@NuwoMY57jqIw`B%P)!2AA1NG8!yc+&hDn4#9XX<9&q@;Xw<6W@<9j;0(>97`< ziBeNjY7eax>cwV0St8W{6W$>>BZ}Ns6U5j?Ac5i!kmK>hSDz6-@neF7Kn$0hNq8&Sj()o;i?dLX}D=hIkyG=r5Ama65SMO#SIl-b|GU zXaT+~UfTP=D4@uVCO2~uh-H3KG*G>)o*l%BT7UW`Yvx3!fCXKI~Y8@$X%qY?5Ap;j zLZ;3PR7q-mcgiod5P=P#wp$#q zttZ_qa+#0&2r+30XVn*Vm({(AKPXzvORrj`na0`jYZM)9!L%1xH3}O&rY8sT7;EVd zW24MZ#wXu4^G=sy#|khzL4X)y zUbm=ki&Hc#NS%{a;q0Q-*dD^=n}Y&=xGwU2bIQrfy4^1Kv*M*}{2&?l^XKzmPBXhJ zR|8z5t69%C$yG1fAFqnrdhb;O+nY6fa&0&0F0NkR7b$;dnO*-+3vl9~g`uhJq+(m% zF{=;mUDPiM=lA|kUVK`OLC?MG7$tlEDBBIS==YxwR*p8awsgo^273RvyafDQ8%2kCr3XLrS^NnLO2_p1Q`0fXF^CXDLnFX@R4Fz1fG;yo$4GNd{3>)e%? ztTelyKtSLhNLqc1Qm|Q?Csox#;Kz!k1g)Y+v>IWwB^+aNA8U>0N_6?F?37&i8NO2Y z_{rdFnn$Oz$G*jDEi>{uoESoDVcrmtkTG(%TFWtR`;D%UN#`Y=eUceJ|Dr25nP|;R zNx8b;INLkW0WhD_mGPmM7aZ>EQmmG5~VOyWMwl=ODJ1-!1<8>2{OxeBT zS|SoIH1}J6DvS?CI>q)V8zYgPNMR;6*%%Romk?J9~KO z=$gDX?(vfBA+sTwro6%@zkid=EBhL#tBtf8F_Y%Dur@gg7tFI_(5}1?_(GQJ$lB$< zTDkFBblgzm>Z0v%b))kl&uJ>4W~Y!OY2=4+O0VxR^$eYtx$~9Q4Rl`h-;WuDlG4BI z2_Cl+i)Mm`|J8Ody|iz-53SlBNQ$m~AZvzDp$j}d~R{9#5R}WREqAw z-~(6VLXjORrKM(OvMMi#?7}W4=*nO1R*CK6xE%Puz_{Owf-XuJxO}`X3sI_e4&C|W zIfaDOs?~X_@Qpt=(Y@Tv=}h-?pU0p1mgKyxM!f2A{yb-g082DO%_1J$nC#9xzlIXM zmT$I+ZoBttwz`tMnJ!ofp+K2N31g7*51#DL7!YHOaT#8caYQB#igcBxqlD6aF+U|m zQ5rQZ=OY|6CP6Yyy8mXI7cU&B4Ec8O1t#!hz1mLq%514J3|S&12>k@lVZzkn939=Y zCntL+=TtacbXzy9^Qs3w0;%P()q}(!aQ%!*{whXD2xK=s{eH(=9vdPpnwMfRGW~62 z=2Og?-7=&KR{iMiHna+GB9k9uG6N5*k)N?vB1(mnt*;+=KzxfMpUmECkTvNYI8K%jrLWX`jBU3JhqCrQKU}?a zSXAp5KRO@=f`ZagN=rzHfHVp!eP~hXk{0P0Kv0wrq@@)J>5`5CDUlLJ7+?r#kjNlWnBb7Dji1hT?irdmMnqUpZ9%A`qW!RM5}3o9^rAoDN-qpTiAI zIeKBHA)^o_qD;%cQa`VCOwI|VWU&GGNNFX*5+%IN1&IhUVY2`3cmvW!@pDTPval}hnNA^ zfa`2O!wF?wGs+DCWw6cTs9c)%o8>Y3j2{P6f@M>DVzbW4Gl2s%t=)k>qWz711lL|d zZmtbWH6o;$7V>%3!>8e}OjzX%VF6!RO`+D8zOPFI(=~`9@e3d;pJh2FjPvPaW&;tl!=E6G+zIEjm%RVRoi*KND%$4fPfj0fZ0XI`LzI z_?{pJxk&H-12kd62rUfo^6-fSuf0aU04X#$1=w|@UyjJRNLS|2RgZQF(Ac&*9H3>6 zw7f>AKNwv=?9K3{g@GilIIPK7wg|awYgRetIiIpMCyJ`hxYASzCdL`D9!6T?-7NEd@_w_1a0Cqh{#IL~AMnCR=i z`aL68QZ4J(GnbQ2ozG-qo?iE`Z&z)>9cAvfZ$Q7HiUw9KwlJf9v9R_pex%b< zh0OjJ8F*h1GaQ{lP&MtnJ{`vVAN%Qc@yufYBpZNB9qn(wiO?^fxy+KtmX_hf@Rp%{ z_|NkV6FejiOxkdT>atty(hY*fbAM~pfJ}8b4^3keE1|zVHNef{j4usD(Gw4yo4+O+ z#=NMpQK8nQA(FFM@)f>TfK;n3{CHdwo`yQYqB7NeH=;|hhU+1E#`PCJ5i)7*z)DY9 zJupw?U}R!~CQHt9k5PczVQX|AF+Nk$O)gY)6|1bSVm#< z-?zK21-f8{e+cE2FjEAyMPY*1BHBTM_FRlXvv!O_G0GG-ePTcb8`a$E#Z+%%3)6%^s1E)@u-#di(2-5cqI~| zB`l@XV(c!?CdRgu1m9gQO`kq5i1Z(ObCC&gHk$3Gb50?2o_fy(+h8*SH0@j<0d;PL z_HQEzRoWm*BDu*T`vTogO8*PZ0^1?6U-=IKF)n;}-d_{gAb<@fCnn3ld6@ehy7g5c z`5uH%ch#3V)I6)S$HJ}x?J^+9v63=jJ&r6svjxy&;*KLJ$T-WAm08`=Jj;CuJ zW`Dl#ES0R*)WvK+_LqE848kq;aB@V*{|MpVXG`B*zkn|=UUS_Lws)|kJqIYsixrAi z(`+-wM{8W5Z!f{bL@YS6G_k6~6^rOUor=aQ`$2%IRH;Pdzw|5p>?c#GtvX9#_rTj9 zw+zEUX;^&}_Bk=j=(b|iRYh?fo&|V~fEZ|41%d4&hPR-&Uwcgn6%uudERcs?RNp{Z z4-OYVHJ9dfioA4&n*;a&AhdZ2E)?SG!dgv*3us}U1Q)4#E^y;Mikof0xefIAWkHdT z-~1~)Qr|i|`AZf5P3Ok%^o}6-fE5mHW>;{*DcT5Y`-cE2%e{by!&%c4Wv2T&N>xxv z08>7JgGbL{xP~ za0u;TeUVClKz|odQC)#Obmrh#llU4u<;5CMct!rvU}+oDZQr`13I!^KVrRD{1^y%9 ztI-fGN_IiTAPw4ghM-$~}_PtZkJvmp<1rp($JoEb<}3#4OdHr3$&a$F>yW zDVK*Ur1plX(d;4Yiizfi*|9eZH*9mcE&|a5I4SNRmgzQPsY1Ug{3`>PpJnxF&A_30 zwX$d=F7sbj(aRZBMCJ0~TP~>t;R^v*9}psvgcl{xbTA?Wx+|7j>OSe_>3B6UmnIdC zilqFB7s)PosX(frag~PTpuuQC@JypA{u5obX1BlcIJW_G5Jl*%#gEjqU-yY+pYV_5 zM$jSLF!=}`e6R4I2q>V6=0<0E1o!5<(j+pFGJ4e^HxNrj`tuaEhjf<_d#_DCj?{EJ zC8wy(jnO~_7PjDM=Ho-(PKwwBn%h`8V;g(lbLijnnK*RxO;vP7VYc%VI&rpZ1hV=- z8NgxK$b&${@D*gvi0IsScFu35ng2r;@N11&c>F> z@BWb`CyM2dXk!kATL8VnS@m4n|6o|?QKU$AJezekDvj368NRS$;qa|s+=ao-0xbe# z_nh2o2cSi7*4abI%6E9GXXJL&0U`$F*uimI6dybtJCP0?@}+_MVy=QpF9H5n_0N6N ziHO{sFwt?0z3_kTgoXB0ODUMSLeKJ`3%6twOcDMAYoS1ve94GJAiCxbq#-F34UXzI zLO(Qiw1v=%^6v^bEz=w-pYB#W`9!Q0nc`@0;LBAo`HVE&|81>Z?2?o5*%rO)IWlgXPKC$Z&OlC40M4|6n8{e z=;Qwfkek(oBb&&y!e`Y&dy_I)D=GE=X$#x2?OgUXCyh!9m;7-#oWCP2XCHUQJcebr&dqxD)cIFO8oGkQiz0oLO)IsEer&r ztIFXJx~uk{I{9l&Z`RGs{keKW0MnzyfquSzfqNKxm$bPhy^*7hF34o*V`5KKyhXw( zD9h1KnTtg%(dg)KP7uTWYZUi^;S;J|cbGkABD-+z3~xc$-Bnuoe@Z;KZ~)>n*RR4% zmDtDTX;AY3{|k*an6GF_b;q;pv7jcWiNiq&2xxGJuqvq3l^yxT`JZU7-{3xj^zwS5 zR4lbDX%UY~OW%zDk-x(m$(?p0-*Nx&u6hxs`TM2DR1KI7EcdY`%S}py3{g5wvw?o% z(YTZ7i=3~ofUD|3eHQ?Xf!mXJZPPWqJ2_3IeQVG4Y|9pbE~v2l`HJnVA9t3THnjqt zRhRT2tz&RN73sMKw@!6w z7s1*09;wx;*=n6R7n1xhWl zAjroatw5+>!pS%e37z2+Yc-2R%R=rKa7u!AP$?~8vqB$=bkb(|?{31`PdPe36AV@d zXN&N!7X#C|=#Ly8Nsz(&L67yN+pXSDH+CA?icXcT zvQh|2b+oX!ghZzgG^I-o-#l(lk=MeZJ|WyKAAr=MknEouR^Z_0r$9%vz5XaID*}7T z*J3WNj(8+Q=Ev4~aXA{_ojQgszLYU-4{tdx`d+Kg>z zlPPF;GZMC$$cbK^rh&K}Z=dlWPA7^mDEv-@*vsf4J<%_)-bDo0V0a6Rm8 z#U06$#2NgvV2EW31!=(ncg0sbcO7b;FXpU0 z7&mOV{T+fJaQj>^ZMF z0vOwK$=%cNXXQ*+zf#QC-OV%$ijP2PL#bU;sK+fphM0ZV!=HOKiNJp&Q^&U1bj#t- z(?Toj`sPaOU^p16#YB}V9%qRkmbw_IC!_Onl@haVWRVp=fIGuA1e#mwifh3?H5BB& z>dP&|b8A@DyY(w3?yMJNiLB*H0)XJeATA@L&iiw{18^C(!k}b#cEf}NF7@s31=J*k zd2ZAn!8|h8$l3m9nD=}yF9d*;qIb+t_*I2{QlUX@P&ALf)EDb(07!h&j)8Ip3`@~`=|r8qu*;}S)Rjta5%rT6jo@2QiEucY%sW@ix?5S zr#1DR78-=HDRW-=a;Syrei^(QH{jhMghNE_{Q?b@%qCK%f*QaaSpV+cJ}S&5QvP;; zcKj!Ram8plu_}M6;02BiwlX+I4K`IEL-!rt@#t6hMo^swYHHlNPuqR_AktQ0h)poHlXMC?4)F>?Y4 z#n31HLEOjVd=WkiU2;jt9{ff)OT~zuucXs?nQSGVg|fqH`Xlj&b;xbJ_Px+WHn{g$ zPm+hmfc$$4A~|v_v%XTbf~C&-W4$&o+w5p~e$%&%{bR@}eIX5JlK`)YuG0h(2a?85 z>d1duF01vp{y;M6M|kh-!sJ`#TX<*9hi1TP*Z{Tr#3?yR475T06jjkD{FH7BvOIzC z>HOu|XW&X1*c!aS*YWx9-c^*U9Qnlw4%ClW_r6XLT`gWAk}GUIl;pdeMWL8$tQQ?2 z)f>vbZ)M+m_2$MQXKq+@?CJq@oSvyPB)=Zg*olSThgXj$!WJX7)_BGRH|CChD|2YF z-zM512XPi)e?`tqOx(nOHt*$!zB3O2sq|0XZQ;cC?UrSVP-s|?*;;)HXy1YU;|f_X z1zJ6CDzfs)B=2I#0!_-O+_!ykjp2bqLN0C*Wd7T@@Cwxrv42#lFA{a{RsG0#(a`^jO|g3Z&Ee<} z?x<_if^AK=mwR5iZ(?&^X3U{7CTdV4PZW$Gu49V9f8SZ?QxXw?LgsyhJ^d&WH?}p7 zYjC%UP5>-@41+p$-3QaNn!G&o$!`0Cs-qXh(P-}xL;XfU+wS}M#4k$FK2GK4V(uiA=eclLw4<|*GrZq=vf8dO%LC)2#giOBHAqrC`YdqmZaNEq%10j$SmxiSNUVHm6eiB>a5x zSgDNqilqU=PLN~vd0C=~*`YoJJzMI5mH4A%&d2(*AwlSV!}ASif7~(-;~Pt5k8(Q( zEy=>XOqR7KIT0}NOK#A-E&J_^Nlfgid5xe`AE0pFsJG4{2!B2ZLEB5)O2pI!r9z+> z8auut;x!Zb@F&yNnLnQ6KS@pim;Cr-xv66L9>o)e_L~;Bs#Lt=9{tX;xMnn4OKQ=w z<@vmt@IMA^IJgiX6opu2Lh}^0w<_q-5H>QPndquO6tuoB2v4kl_N2xml_tr5&bs$% zf=-qPBOOZrU^q1W5wQMTA#aR`#Y5A%N=irI`ys^PKWfh)n4~QM>ObV4Gt6AxT(A|G z;HhzVM!yW)#}x3%Dhv3r;hjwV zbHB2SF{D(e@jdcd85Dz?7~JCjHQ+&lyhgG%SAw(*h!KIu9*>V`I5|F8fLv1PF&K4j zK?Xd4?pApb^8N8r?{U~l&El>7UvH)5dp&!ce==!iW|sT&ehFyjFkP{B*3*w$_&1RE z#=#xENNi2q*4eVP#}@gudm@)j@uv%uAR`(h%;(;@>H5o6YUdbIH(Xx4kOhm-v>CyW zEB@%e0IM8#r1;{aAgsvDoz22)a7%UbS~=WP=i$JK)~tj(m7B{RUg}|OQM#FkzlX$3 zi%V8U5QTj;+7xtvP7F{DQ7U`}GwVlS zW`V=tBWB97*0pq~1JQ^%)@_OK0=5E5(+=?~ru&FRJl+!W!6JdTGeqY*1Z$fo>_0 zlnB2Mm987T8LyK?8qf*W*XV*uw2Wq~*+YtqP^meUfAHE8aw3Ux6m_IU0;H3fo@BaIo@aUK&@ug`bOediNCSmMr+coM-azH^kLoVx?m0D=ux~bSw%k zi}n`wIJ!$Uv5L#w++WXo=>w7sKtZDYZnwe4mEtM2x%=ax^gTRU-^m1e0?L`bhs>E+ zM^wrtzK6SEa$g&1IJV-a>ga27; zoK%0k+6)6@)qGxgr^Z)#IWt=1u`Y5iRH%Ys%-lOWc&E%BYpy>y9^>P^JNb6u4Zn{` z)=W6oq4vG+5ymfNrE2=J*Azusm0kf145%2ZL><|C{dsl%@2m4npS)M9-}dzM{MAa^ z|2=w82cbsU+0xrNTUbD!>94UbeX-BITxnfX$VsiReBu6=^EOvR6~--?Q_GoPFod)dji1tAw74Y$&1`uHkG@8Mx=Me^5|!pISbe#!m6 zU$$b4eBLx1&hn$BaHIE`5C5EBfL-knJ_3Hzf9g>E2IhOs+dq%N-q(ov?MYx}$2sSD zz`>Wbdf;o*+G4@%mv&VW$m%URv)~Ypy5}-aaIY=ggJ;p>}B)wHlgwyp?U1PKqeDV8T_k_n}jFguPar)0ANxr=KE#!kH0% zhtrU{GB>^onx6%=u9Av@8o~gb@4Yv6Od860p->YDYgyJ&eEgV-IUfwYfWpTgSJ&04 z*aa4aKNe|7{Lz4Hkly-rAMRZJyW`c9W5>CPQT+XEz|}`Us)8>-t(o&OCwp6PhEsHO z)&xxgBY;qpm(vE)jotfjnpwTqqwO|G`p`y414e!DF5Er+_`N=Jx97)K@u61uO_KiF ztefJ~;t6>|-h89JqNf9M-;$=p)aE|=ZsE4Q80SAC?t)X|lPQ$+Y>yx|qG0bOaHl>K z*`c8%Kr8E4?KaYWft;lQf>OXNqwnbG=ZcJf_kS6IXBtIyzRbLnUl=yi z@$Dy_LK^=X$!P7zmQpoq_OjZsmBC~T_#ou+F}2q(@EFB%b-tXca{??U_wSt|&r3*1 zKuFy?z|^m}o)(3xhdrxyFCbc|I3NE~NJDkgp9EE6g5Ty^GZ}jIcxBb$g*KMeex>Z| z69R2A*n2c)a|Myo4vghH*oU2MTa`yX#Ku~9#5;hY{WH+E9Vj}p=p8=nJ&l;hvsWf? zH!lzZ)f-9LMEeB-mq5>sZhl50y#_-!bT50ro`x6fr6740L{u@`9Ux0o4d?9L{0qlU z2?{U}D1Pl6+jS)2u{)<>=qGj`cy&GvyZ^~!3iwREL{C6^3m3xQ$n)Ph=viorH82zheJSK_ni3 zeuDR$qHqf19~-8{BEEk9i28M0qcaaLT4___}V5U>8#1wpcDfCPh|?XWG^*P7Mx%#|Fu!DQ@U4`+W_6y< z%|SVh82SG^Fr~;GF_W7Meo#Ja?si9%JDjcnDvva)rzPmwN@<4n@X7BC7j~^qb1R3U zVYq8SnA0k~uVG%UjSYR5#r3Q-{86)ssC$c`z8`R2_6^WokUHY_6oO?R3Aj)grTeh7 zh@A7>+rTc?MRfw`MnyA$)41q;xj}nF1Rk)@k{Ppd{a7h4p69EX?1Or5pvxA$-525O z6399%W36WcVD8%4}SkNADR*XFt-pP6^ zZWe}bXR?o1Ad={$68}*eIbe=c)_6G2KQ!3XKhhW7juiE(b8QpdtJc*RbR{s?*v-gv z?OBCWLfc)=yK~RYB;|rJ8V_tZ`4oo>R>b+fz;`2gjvll@%BuhE9@sr)Hj^H{NB;U= zKyqDaC#53@9+g#Rc6p32v7QEJaT9(r$n{Vom7_6Rql1x45m0vJ{skWt0L5BnMFtRjzo9^Hub&Lq4A(8i z`OGavWtZvq^cJ=t0LsnPmBx~NdIZRo32jMUCX`^Xx)QTyz4G%FX+1fgrOpE@%7U%H z8&S~SA~jQXBX#^M-{${$j(2s$LwX43_qC>a7LFfT)*OW~5N7p8rm_@L%b-+YZ+Fr> zpw1!=xGrcil-cnoKW{#DGkN}(R_aE~|L*!f1%9t9m(B&YEJJaatwh`H3l%qN*e-zG<8iLNCTh*67(!=t-cBXEJbty4&_?PU>X{&yVm zV!(Bg$^t@P)U}|JojV``q?msW2ZKb~%oUxiE_(F*{7Ee(dcqa{4m9prKR4kirE^8w zWIR4F0vnvK%QOGU7h)Xg<%e7$LKXJjlGomc&~i79WH92OeVlEtud8Dq3Hab)#A^yW zwRAdHIRC9%a}E%alCavUkEb2{tP-TP{$38kOmA3ajnUwJ6YBogEcbavUwgGs0 zcmRe&4=FI^PvykSNv(RS|2rrhwu_U3jrQvYe7SzRHzyZ_MEB--JEf0VBP64$CLq$v z&til_=6vsl8SMj8H7UpAZlDOND~BB_DV!hZ=-hVPguzCTxEv?ltEc1YuLVCx@~n2x^1iod5oM@B{6DP1znPXcn56SsaRA97o}c*Q*gYkS*QX zer(|}kY^ZMqdtDL;vSFxY+S!&PBvw-eUDE@ijJNP-_QF7Cy28HZ|BBP#d7s#Hpml7 z7HiDlVc~#Tqxa#cjfl8Gb0=O+f{8jMG^=&1Q!;)pA#E?A#p4*~cj7)bp)O;kK^Y&i zFn^cx4g3(PI1A5AXFdZAiBU7w;%Q$x@F-An0JH(@&gYY>OEC11Lhlg>Lj|Uzdw;zD z$yxb?57?EYm`AW<{Li!bLX#tbpHGy`k4oRA{7+L80W390T}eBH@Y#&GI0&mSX=6I^ zfieby*;>pV&IX3^?nxqNS^M5!ZB?+4&DPN#p3Q5gH9PgiqD~W4@DaZ+XO)&bVCw>+ zGFE=US&rPT@Mj0WXWPHAeoO7XcFnB7{c)>+~%O-*#T+a|FqIX8ZC7eLpRe_8MfYX=-fV z^dO5is&l_T`IZ4%{YgSDGrRt5>;2~IQ(~7rQy~q}@yl!m0?HZvRKs21lmR{HGA_1G z#N!OoeKd>t{7MjqQem%2`3AVftP)Usm&NPGF}(^hF@F+P$;+eB<6*eE!h&Mhy>1mL zhx*pA1V$?>PKhu-eVG`~S`bK{!X4?OynXMTr@vY#3p2rz!FF-mBwg)3LC%L#0xB~EBO6bCB6l9^f8wiTWhl~g**dED|cS&LjrmFV`X&s9dl*cviL6X-fqy@)S z+_UEXnwb9Nn>+-BPOa0Fe}=5VM|@75Cl>+)9AL719H(a|ixGrVL6m5gy#mbin)PM2 zy6Fj4rq9cdz`A6bq!1sabik0TC+&cJ`Yo(oG+eG(;AL!)u8yl!r&l+3qz@qKr#r#d zk9ez8&IBEq%;AgS``D+TX`6;L>%P6mbl5O?EaT<3Zz?v16d-t`;sk;ndwLu{kECwr zTV?vjk3s`wK?cY7#kie4KeVc7tWZmP>sN6&9uRF$^XR|F>G1p&$Y1rMwgD@=m{nF`SC93DHS6 zPmX^ zNWYgVnL3tzuE?kxHF<_G9Ruw>zM0yx43{ff4`2(w2`F*@(-EEUxh)0FW-yl7cX#rc z4q&v#en&Iv$81Q*2Ui{S#nUS4hb`(c0xZy($KXv}4z=JH_={%{h`$W`LNe6q0VPpW z*FVNSoz5!cOJ6jdMu|;Li!$s7wt*Pox1=it9yUg)=!U~Eji@D&+@=Fyz8mQEM z@7(Jw+@ndLJTAo>HD-PMN` zpeQVn?ulZv|8{Zio zVw~MWMaD=+2g1ev-|xi#m$F%yM%l2+sIQloQcYpJ0Lve#tR({8Ky}M^ULXH|Un}c- zs^l%ff=!^aRO#B4wF;r8GZs((zdsQ%7$_Jwl9-vBA`FEVUV|bo4$`m(6&-l2MGbP* zK}-vO!q!mpmD&AM_`Au{*R`j8n-us zX?T!!FgkBqd5R8x%a*cVp2mOtA&w*8G;nxyvC@Fe2K2uVzPwUuK0Ca%*CN`mehE)i zQPeJ<7TsQp1#j%jNDr&4b8|_arui)>8>K^2%I}7qoCGzT_77UP+E0>TLvTteTx1W+ zsM#g@C_pOet5zIK%#=FM?vQ6QM;>u1-vs?YPzEK4_QPn+z{dhrCv3yKoe-%(7xZdX z3$hs1eWob3>gJ{3#{(6DTnl98wU!@P|4fE_XSkiK*XjU~wOc!tsQL`RX+ZJ?B`GLN zb7&$huB0SAj#CJ1LddN${yC678gx9;U8>MS?GJ3NxT9(_uIRMmV}HGx>Bq@ymrwbw zR-;1&MZEmpp9Q+5M?P_E#Li=9Km3V&1r}jQKCbN+2vfn;g3Nnn!t*LGD0V>8q zi;(q%5(5~uVpD>?_r^Oek}s&)lJU=^@zx`rV*i_$_+o#2u~22I*`N^1l-_+1^7EV7%hoA!G;_qWW~pKyvIQ>%TY4o=gTP zU*v`;)bjO)oS?+VEK*5Z;y=Qvi2px)BAqrzO=Ope{@@G?uOVgarX;`9Bvn*5{>~cH zWKeN8KC4Wu;Am>bAwj7X99;}9+MnAs=J+(b-Z514UvHAfKiL`0GNE-|Iv`rTzf+{j0v_jZ0JzaY)B`O(e34CYFn40Rx?*Nfxw`+# z&%tCM)4*zCC)`!fep#!cpYTa?+@-#fwuednHdIc7?+H`P9~ON0_-pFZ(QS@*WRz^O zT$d?Gt*<$0v$Z{@XU=*4T(TvbkL;iSZUuLMlyF01(0(z~MEcH#_AIXbX!t#I)X-fw z2F}%Z%5qN83Y1f70~xcQG+x$!(FP7fM59+X$qJOY=82a7uJRZbzl|R_Q_<}1hjV|p z-&Hy8Wh+-O%qu$PqBS%6fHiK6LAgv7k4078?gf4cls@Q_iQOJ zp@>gDAcj!T_|?lwuV2b%+wS zPSkbLLVS7+awOQRg-8S8v}il-X{3J~$KEUeQylkkAbhJST>bq?nqhZ%!Da2~1w`hb zqn(w0cOY^hc@Pjkwh)%>17z3P*^DskUke|FqcgGefaXQbZ#pU3@+| zLT;gnhb2UY+v1w60+Wa_ns(!?`a4eo8u zFZ|BYBB_Mgg9#09MWh=8E%fnGd8Nj70WbUs#A)|Hhy=lugc6AB&RNz`)`=Nh7a@Ab z<&Owt;=Mw^dkXkK9c&Bq7hVk`5bN-M8ekN4bX2Zeu@$O(%gv?tbYR58@2oIh(>CQy zxqVVknhfOJAX$4!5VExJIg1_2jF-?3lV=If`6bpkU=kBhyf)=tQyYVd0OJhU#KNCJLlkF5hyYbKt+6Bl;?4U00!)#S z7*OZ82_F;geui_zy3JI#j>VU=HE}8y+-YPbR*YiV?g!@6ntDK9gY?i|pZdP+!NKZIwvDyNAte440V zy~wt~){T(f*$z>hL(X*>kz|fNSYT+U8Y5z1nevpz9Ni|RxO=X5vqeCqu)SMIYvMuN z^Yy~G9S;&)r@%5*h;wmbp!tfL&fKFR0zn9i9QjwxoSEZuV;raSPoqBt;^LhHZ+Oyx ziTcDl`d9rJDUESEgBa3APml-x z<7jg0)_4+_49fk)el~)e88Q`RS`diEshV4-S~wV91j_X_DZ>v{9ayjt{%0Enr95nI zbLX;_Ov%537Wm)qnD?%@M8q;P`9vP-@C(~mqj%v~rd%KrJ zV65YUEVh(a-0#@OoV>l17fhCstDUOTV&Tj~uk7>?h+!}mLAj`-b0oW2xDo-t$J5wxbyAW3(;GnT@HckEnZ^b(?#d;YsliyHX3dxh*!`HF6OO!D%O2^N^nIkDSZi3~kV)`p>Hd zw+4i+y}xLuBLvph7h-G*8idPaAh>`ynq~nIw*uUex!^J1QDKi_J&!0}sN+?Y>;>Qs zwi-dFD#-S@E%euDkhsh~3D)YL24PfFZtgivENhlV+szKEamuHdwwTCqntbX zMRxS-yS4=Eok#rG&RnXC7Qi!)7cGn*%|j)hv(&xq8w@oel_u^=s6kUVRC#gbpQpAI|d{ zndF-6(mE5gktz|<@l~5ibCqJ@>0FV>!Nu)NZ0oj0|2Ke+GLIP@%AfF{vS9?k_loTL z%;kszZlBb;X}8+3o1LY>BitA6<@WYp5*;NUf_Xd{4%s+En5L_hI!{r#2?5F5ca@xP zf#wH3*$}Q#s0elJWu64ZMr$~MzkMv`>SCBGH5fm~JYxd}a z39H`J_x3oRqPnPI!MbB$j5&`DmG%w6mcubQF|6Y#*{cqr*=iioDp+^mdRm!Q@eFKM zd&m9J>TWNexXc$P4hBctYqa+0E_!u%DB9D(!fEgG3TwEFkIW*Z;%ZXzwh|OHo_R_} zZq?qSY+W!H(zi}Mctd0OZfP~aDpkM6z=M8uG=uK^&5)@5I+g5r3{@0E;QU?=n53B{ zByQpCHa%DhyJ411&lWBIjvHVoB5FH*)d)UGiqxm2iUygm^jbux-GAmM=s0=&TM=_t z*XgXifPw~WwnQsn3C>jPZQXp6C&2oDv;g@0t>})|aQ;J#uqmzO$~2@Y!ycEy{m>N7 zizL+%ES42mi7&8>f3}1Uf-y_GKo{!r<=?n{ap+!PQp?LXSKFQFJ@L^VuS%Vn!lyyi z1fls)hh&E0@-D$iY*U_?%Ws8syMbga8a3>n9;r};w|+nn%464Cb+{w2e7Pp)1LO{2 zF;2CrXqA|e7jZyjv>VfMZ0I`~h-w(jys=Ml0^4DB~m(a#+nBVe``l#MZ#h+y=*ep0}p*7BFr&v5z>00dV zG1J#&)0!3vNzWK*qgQ{eoLrcMS;?4hg9z1}L05x_?wt;&XXcWVVruCd0h8-(itS+j zNx{=SfX($J-@OkG?jBM^#h{%`J*0kQr7^`z*;34px{tNN@%Oie>Cw$))hQ0eHw`!I zw#LS1DyGHEu@z}?J{4i1i#3o%qnpjT*>BaW+yomVpB;7wbrLyvVvetZCwi)k8%mTv)JOYNpVtzgch({hIJd{#7GU z_%y)4Q=X|FsbpL4zf)|SYVAL*d(>69c?D93Paj?jF_d6zztJqfvb;5(q}WqrLhHT+U$GEN1(#o6M{*Vw#>g7^%GYXIXMY>J&i9@7y9 zmCqxoewv90m>3$lxG~JaAqG#4gkGI(`8Iu!9H=xkWY7X(Lye9>CWO;8>7uvpty9N_ zkoLgH+2eJq-b2 z2Cq6?C6|)%hnMis!>8>ATGspeB2Ug%6n?sFeultbc;nb%qv1^O_}A(uc>@*yYNZQ? z5k1k+9QmI2M8Bq)UWDNb*VRCgfY0WO1rZxj!I~uVf{JY-^#u`f1*b0(qX1Dw^?}R% z#1oKJIAjTp>w_DK3ux@{D zgDQJw?{($2p)-Hg3|o0)=M>caY464dj}Pu(p51pJb$aM<;I}s$zp#*<)?I;0s$xrzE8O9e+W_)DFC-@Y*1`%l~@-Ul(e%PuwJG@yee^iud!NEQZm^)EWeFnd|*zrAk?Sid&A8sx*S!Lf1J z!+l@IfKmY(`&SdU^foITbM(_G|hY*ZFg4|h2+ zdP?lpi!ca<#Fb1s*9LFTLhg|^W;=&XG2wUacQ~b9U8d3{XzZSPoAQ#F-u7% z{1F$D{&?3BkU-elF_=kZqkYZKy{k>DU%WUhmI+Q_TU7nQbOmoH>V#+*H1!#9>?H)U9h&Q|9i zZ7a=Bp;!MCMDI{#;i){LZuGXQTAxKp*r>*#Y?&vQIi)@MM2J4EDe&*hjCwfCzFCj! z6fC~0Gi;}_4M`_TH5yimhw9mA79tll3kVQwGXvh+h5c8EUdd~0vAW-Yafwj*+b*0* z*51Xv&|)bG{RRG2lx4v6|0=PUo*_X<2=3`Pi~-13^t3aVQPqX?)k?iQ{4qqGrIBWZ zNm>SLii)F%WV`vx%5VoCNhFlICPct78GD7l*#oK-*|tTt zgoodUg`&ebEoB_DtZyOH-pEMQiua^3s@pkNdm{W@gBP>CZHX3FvVjzYBSO=w4$`Nd z3y%y>GB)e-I z&nhE%JC!+&Rnerpq??jPxxs-im_eyi@KMZ2VdOY5H#bs~Z;^EE5q!2EGhY{LoYQPa zTNX7AU~~tYldN2vE*FLxga2P5nm7Rb zK}noi6+Gnun}L&ByY1uesCnQ-`I>UDEXik0-2(Xe8QN36YB^wcMscnl_XZD=;}tSB z6%WVXllba$jLsX*x<2Y}CtGz-NgxPy>y^U&vacMQ!%bk;697bg3FqAL5Tp_BcTXI4 z!|zK-54ejd*`+5kZrF%FG+W1$a7DUsZT{39^k4Ese(*IV2q2UKquToux}9;iWosZ6QgDWLLal}%#-SQVgP z1|C3SBv2T&V``FrO6(<~<~X{|QTVkJfxFQW8b8uod+~)N6By{_^noLf5esJpRouiX zdVRelt<}sRB}x@|{*F?Hsv^jjw$v1qY%;@&0M5g%G(ftZvQgLjG?gM_vZJUJ*|5nZ zA9S$j35>T`ILRo+PQ=>JFAXA*E02|g<;V|@4_w4EVMsOjA-H3e`cxtE=?>skfEz%@ zM!$>rcA1CW{fg?fRmnatLS{&g>@4>~+my2u>5YzB$BBjWs1{3UxcCmZj*YfQd4_X- zr`e0W`8fG2!?Nl|LM+?OjVh>(!H3LA__cohwT1q%NBV$`3AFtmB31uoZ%V>65(Qt} zX;FMlAFBp)gaIjKH^AIZn7^YM-8L-<*;Yl%46uhlrvdU~k~~7qTv8_ac-C0l&JH0C z?~Hl>fTWUw=!QU!G%e}jJG5uJbtN|Ey#IKT2FBIT1OiRqf+Ed4T`8LB9>oo4g)EX* zVwYvV#ppqofe%^JxOT{UA%OgOt#zWWTZF6CwVTbK2oNF<`-Q9YO=vkjeEy=MH1K6s04XJWFch(5dMJ;<6pf(hPp=fk~SzoE^Ay1v&0wm3pz^9<*= zT#li>w(GlCP&rrQmpV^^^qucX>2y88o10|$QHL)J+aJ$>=ux@w;A9eHqd!J}iW@j7 z_+sem!H>kfRiA)!@64qQ0+#7;^~nC%9`4Qty`}!(NT4m1f})E8%0mBDSSwIvv)3=i zSx~48K^<7d1)xKrOP4A&W}-)e%EQnswhH4)Pgq*o;`Hp_vF&ah2V;b7;sfALUO+67 zZ(OVagF1P=(~aCpj^dtmI6HP#po+1>8rr^Cvy(wm+?F8;_=C%|LSYOr+sad(2{qHD zC*lxw(wm06{ddmEA$0ecf8RaZ*< zF~}sJeaKAz7a5{4e{DvWsjg>a$(@zAz0}Tpqa}%#tC_GGDp+l|vbt97Rh*FqCCmzX zDn;OO)uMjN0b6#@Tn>&LKPg53hgD0e{j0j*wS+gG^jyh_1GZwKNKa<~rNo!4@f=&s zyLj@z^VQVi5{kxk;JzUc**Lo~=S0_&BOLk&H(}&*`{gDNYJ*2X^oAZ-2j)W^?m5r~ z4gc#kyvEQDGfzqQUyM9}4Wlr<(w^R|n31wlRx{4-gJO({WS!dNpId0*c;A&~=RHLb zI^}|HBzIlD5<9k!v2&9I5KPj;R@5_s6R`NfGl=UHlY2RmF$HNW1-1~Jx?ttaKA?9c z-jgiqrcN+*1RPm;n{gJ{|0vma-a|O2A5}q?^|3rh-z?9M{s+75<@Rwjt=BNRK_(FV z#knugdr;hRHe7(T?8+4*!@D#PWGj>al|<`X8iZQ_Zzm*z01z%jdWIsD2cpv|GVal> zNkR}5TSL_EPF?E4qPo0_>Z0Q1Xm9H`fsxTfV{bUM(a8YQe8nzvc;$<}gNZv5RQVJu zD7;$rScMQ_cLshn3=4#aa`2^Nwl!6*S=}s@DJHR?xSmjY&LgILY>|>lolTLRnMpk( zp-7N4P&`E~y&W2Q7?cO1CU+aj$+JJP6{^$gAHiCUN7~DNj;mYg$3cSFLt*>cu zE>j7nF~Dg+VUM;Gp|IEGNk3T6?zTb>3zHLOMyYaOIQ|H65Kq6%Ggv zOm_;fPW*;Kp|qk)ffGI|>3J@E?}QO3k>881GGFWLqFeRxZ+ES<_Z=yO9cPhBO?xMU zU_=&#cf7H%K*MYqXk{1Z>4Qqjrio$054DBN(?Kf`O0U^Hl`)87gX@DN>VN$o8QS+= zFe)drRM(0|Z+~ zM^k5@cL;L#?uJi4Wc6&H@${V?$sP~*tfv_S{iJS|7i4Dbrq~zhnv5?*AP^gk0;H#M z(YQH@DisI?1a7KoABM6O{O05e(Mtl%Lq_NSD(%bTsm$N^Pg00V6cV9qjbllLWUCmd zq{Y&)D=KB*vSdq|5QQRS852Sg%9bsZvLuYGS+ivihpgZ0IrI5_zVrS2_xv&Abvoxf z%Xu&Necjh}-Ki~F_r!YP@42%2DMMeKaxd{wQtcOc%V>^;`yT8*X=#gozjwgwdsI#M zhF+aJ++RyyLPqsrll`PJUT?Dk2y8gw+F?q4TYFFwCcEj%=^$xTGux0ac)J>D>jcvW z{wNF~yPa)cl~wBh1pPtq?-grr@j;j{1OE3uS_cZVoE#&4&;ge4U#{iZFH}BpJ+SWZ z5}r0XWWWK(F__d-4cCz#HA~Dk6%Wb2-7-<<#$KGY<&4v>yhejR{;>Ma(ep0bYJzuA zXFi@u;J=AJy)L89WV^c# z*qn{oN|Hg84B5gmo597hI==duQ0_d`%q%Dy-iRInpkW#P_vHNgc{Dc5nPcoXZpl95 z^Iq+_?b01beU!KWooq8IX%x^{dtk-|=egAN`QbmS!&-BBH45&F`S=yBE`ibjA2Om| zB|X?Y7I#`00&d~6ky9tv{{eSJI{o!(0i(8&bJY#`Dj`O^BA+jJFRaou;ooN~c0eh~J{7;=^EdP%^M3edwTp`ZiJm!Rs-X z1}(3b$=};WpuH9@fZrp%_}j0vOd!NdDCBR z?Cz8+x{E$qEWdA8%C*vv&{m(@!UxFPv5K9>IH8>S0*>}GOZ^@tde_PrUag*TEmEtw z+FaARd#pX{g!+=au)c(DU?ceyx$)-q_g}`6%$rN+=C?u#=~tbosgchJ+r+TN`nZp} zVZmdI2U{c4_j#!3$g%cLO|j~It9zoziCH?Jj96^M35KAA_**mOLaEK1yvT5k`~`N5X=E~42vFHdYTZ7ZQX!1z|0smkAO>AFoW@9|;^q|Udd4qi z9W3?~2~@5o!htX&5W_s&jEkA6%8ce;J->yAp``Ah?_N4d@y zv$%N0%;lKOL=HLd(w|3_Ed3t!Yz`CrwUhG2n}Kw(mYFPhY1)1lx}+jIw2L1t#QGYq z@lc(^cd&Q0u24ew32#TlpWBbMPpcXR#ASOY_r-q@6Pv>{}EY32Ma zlXYb-(RHoos7f0i@zE(HycX0rMlVVFjs$!u=^|@umRtD%n_-LQ>x*7tOtG)Sp{zZN zkpGR;SfP#iPlpCU+lAr+GAZe~SP`TtPq!E}z5dwjg4AR-%F#7Wgc7lSJ}P;qn`Awj zAN~j!myX(Oj^|mxTb2FdF^6;cyF>YOl8UyMay!N^UE(0|-<37HU-r{#q;U6$AL{rh z*n&J$y#(1Fu<`>;WuO>B>qpcJ#ks&c%O+pI?W?tMLthnt11MyFfNuJm9sL1Q{#(!; z&}#+d-kG8Lp@!GNj$!pw=`PnjqCnFU{@_7@rc}#Ta{nb~B7=uYe zNfRKq5_E`MRqTOCE0QfEyy&jQFx_T-d$FXN{zh2(6BzCm=T9`7c6N09g?8CV>?+0& ztH)4AG{}F*vkDRZVBC6|%KMq?#x}5Lm__(d48UM4*c5E`r1B_hjD!t;t4&_yMmnNE z@uMg^YNcdOSd+nzATEm+CB?MXn8{XwKV+l1;u*(s?Gs0>I(JwNVF?pk3Z#pZq*vicX^mR4KIRD< zU->Zgc*U6L*#kpO4EwmD01Ha#)}BHM_~>X*I4{O>mI(x2GDS#NP`^v-NpiuVAZcJ( zDNOs_vNB>qzfyv?|tEcwNgWoRHevUc6f&V~wV1f+-d=xd;e%AIlXR4z5~rG+2i3}|!^Caiabk>& zUNlU4%Vb(--GZPIJ4DUz@Gq1+ET^VudwQhVGlYjV*pzdMG-d#1> z7c`q(+!eO9OaR*C{Pw~>I~w$m)>$w4GL#+@Rhg}X3|Ht#mTo(DB;3;0NjjOtMqn1= zwXO-GC(UChy)V`ox5XXY7w#69^2t2gq56w%`s0P`e?1$yTO1v#rW#K}2!td)A!LL@ z(wk;}6WI!>^RYoTd7~!Ig)gl-K9j9qn{i?CD$j5F3{nhFRZ^E^mHq0Dl7}y?S6-|q z)%v*w^#OYV^pesfaMw(?y~;}5#qe;+?%Vv&xV6Ac0wMgy5*OAy3B5Xd9^y0+q~C5s z{a7O|-e=KMdszX47|gr-A`U+3W9#_EhEtC3Oi*P8q3nCekQ)KlFJ&7(2eqB;yJ_@b z`6u0)>=T63rQ+GX_Z9X}0acYb}Nbjq1n{nS`We2kh^N8Ax#jJ-=8 zqhPH3-B{#_`t*s8qA(5PU+C_{F_CVZnYnU+@Y((Kb8)@#&Og*voDyqRt|MtsdMCrs zWpjJa2lkuaoH>mOoj$ECjkVI{YIVeR`cGRR5(>^UBYz*8b>Cw$U!KqvA?MZH&=iPh9Edi0JE(PnHd^Sf4=`P&TEx<0X%?1dloYi zcM+7&8O4PjzuAI%PYH*$iml40`BsM2nXfgt05W^^swhNExtwyWNWPddk;kI<3Xu3! zgXtf!lWA=&66y2DDcI~(jDDba3HhM=R*yF?H&^gC9o)Ol0P}TP^~cy!=<}k#v+gI!`I1hpe*EW7K!)%7JBqbZSM1YatF5v+{jkw4%%9 zo+=$K@}|Aix*O~y-%=qB`G(cBm6jjaYB^o!_r%pKVQcrm` zc#TLP#3m&b;^Z`5G2$eT5a0P8TGjMn6Q^CySuP!&05h&NAVqMJd6wZkUMPX>`$R-h z_}@JniMROFB_qnV1@E`bD*5x+5dNKeGwiB*SBd8nNPEEeLzD&85>Z#Y4M;A$+5i4~ z3&=x+@=E_D>~MX((Q*kt%%JsuzO-yc0V!IU=UuZeU0u6@TV`8(^iqx6=P+(g*r}rx zVBF9FTNtO4$~692T;Skw5)HqCe+P{RTYu<136rB&x$g447GR{yBJXOoIp5*e|8fCY0ECe;wk+n@rVc0}eM?4k-@?80t&h z?Qs@*U2Z@<;DxS_$~5t1s$qiAk)a&`uNHaJmUy8WY0K!lwZxfTWt~j~br^>XT}?Ke zb2Nq7L9Wt9vSkg5C^BbYAt0vThNmkI7uNb2v0wt30(lac20lRh7t_B4{NN6$!t_+$ zIc-=N#`v4-XP%{{nOu5LI4rlUNjB`-7tw!Se6n!FiQ7Zj>lDZ$sbU%TN`+ZUh2UM@ zJAEgzxME2X_4l&`>64hV|I8-e16My+c6oy;J_upP3qE0!re9#b^LnfiJ_?8Ou*6jd zTC}w;nRxxODEu`T)6&UJ@(Dj`=jtF?JbQj@r3KDE(FU{?i1tgUFcpnk+XfY{{Thll zam0m~h8Lsmmo?w*+8=zwh6$r==LcxZ-i|1*_A6hSEL~k3ZRupgB|TwShrEv@BxTFL zGTWq;Ss;W5_z65vK94o1Q>g!33`;-6PT%fK}R6XF0v6IYYgdAm-AX^ypsN zbZrN&Ev$5Pv{V@Vx7Nl*ynl)ikDDZ#GY{8>*@(O%e`5qKWh-lteyJ0#QR*-wPly0t z&6w~qai4zAiO<1+!-#gVO`sZZN*c3tVysOo#JBA)x2LCkV6_S)U)t%gbqZ`&qK%{! zOE&q&L_fkwjWA?9fBS9%&R{A9D&XLmYrhf6Pq=7cDpe6al&kxBtEMN1+~Uv=0R)MN zfzu%a({U+S5-)$=4PSrUwZZ%q{3eN8y%#F7*6qd}cprP9yejw2xy5M1YpCsH-owd_ z%Y(Rs`6f7EeLj?jNnbsNayGE7Glb;pdpC@SJ!k*oOCmO$4X@EiVvii0ETD%1X>BeQhZ5;3&C9u(ZeE1N$3EjF-hbSAo?|{UBu-!1x zg=HvwEn$J$(^IyVhc?twJwm|C06YbJ#s)@2Bw^1gmw+GD&HqTxY}Azfrb<*GzIT6) zJ)&;%t6Fckv%rydKY>(7<$1^1CHSyqtx?m7e4CE!66)eyaSU=H7V*@1cL>~{Z+ ze($O~&^V!B8(BngV@)=oYd!b%&(ls88%s9JB#MGS=9`<$G=-*-(REkSF0*X|4h)O( zITy^@0-ARkArWi!emf9hFg#twmy%aJZhgZBV|@EQp3_RBgow<0F~wWoF$nX@U>Vw? z?JI=S+E^*}5ck;z^_I>(D8RN?DN*;+v_ea^lb(|vBbbne#AgytScZ#LQaDIiKiXv* z2HakO3h90X(4Q}N4N&!Lvijh+u#K*+?*uIClM+So^9-$eNUYmELnrxTPF^Ey`^Px+ zPl99~Mcp5DPV&4Euutq4)JTMMx6BjyPFk8EBOTLi6)gQ=fl+j=&i>v`x~FEFiy^6dnsTBlu~7JkRBY?T{hTM{K4t}#{` ztLPB{VlrXJ_0I$8z3NT9Is(6eu}<%*W;{VVHcYpOHS&Siy6Kt$*W`J&3@L#bq!!Wu|KAA@>8OPz zD#Y7os`~8{hYm%Z-cu!*XbFnpLMI%BJI5nHQw<$Ar^M~%>XvD@aW#h`n3NC+7(z4(-*i*QBFPQzAAmbD%h5SLfwoU>mF-F;MWpz+!v} zJX9H(1Qc+*Y#yH!%3jGHlHu8s0yo;#-zC5zQW$VkVw21gmH4xJQawa=vdEmfXWM+a z2$MM@EYKbOg!_ya4oaG$4}g^zEU`S(p!`++1u&KNn!0 zm4EPKKQ@6FG-4kC%T(9NaUF?&eP+qx4-9&~Sg+yZeEN&KURBFs;WLfPXKJ1rhd;~{ zFW6DVcrA5wOB5E6NUZ%dxjY&^c^_QqVNlMWJpw}G_HTx0^OG~BFIOQIz$RCV@B-Hd zgN?oR^cpQCDNVz~3<+rGjJ*Hn`BXB?Z&98~_D6Tu+^N#ysS=VmmJ9tISb?WuvXFjx z5*mB~#Jp$GMt0FsRZSG#*jPjs8(>2tE?`tA2C`ph2U&LjFIztkCMtry0hbg_tqOY{ zXfTEi@5Wzl&&4lyc$sh)TKOrt=8#xr>^avA_+=QvxC@_Of{s8&eiVR3-nIctz4#)$ z$qWjeMj9KfyQV{Kez~Bfs0OYmx&UdL7b9{-YLN*_m0RmZ4lCjN{7fCtDE>B4;%V{T z@q(wpzh9!rH0L7z+D+{9C9`SFopu%Aow*F@zSK=Y$R!)J>V^j+j;D|`qOSBawA_1? z_71z6N!|43WMV^UP(}v$>PoNkn0s;D$ZCJ~%@^l=FG)wKdcEa%*;b{~cH-^eZapLO zP&>n;(PW{fk%1QiIUQAxoeOSDvxS71aa}Rfy{`j!ZNcE&qsQ5PqJeDr?q&8*{kI+K z9plUIsL^!TRSO~E5lgdJno}Tr; zeNWn?Usf~Ruw`y7L;E)|3x-~|ICILDPRh-etcnOI37fUd z;ber->oUMHU#w7GKE7DaS>ofPj9um%yz%zA|NJB}La6=&Z4jxL_k7r8`%HN$NBOVr zm2chRh5PWc`AiiatdNSz^#7x!H^;DlD?>`{CgnWxsn3-Gr~3FAkNsGw%{^cftei)B zpu_#vYOPl#D7=WVms(e2x9z5Pm!#K%A=O=x2pE5KjQu*c5y9*@_4Cb=a12-bML*?* z^@SM4K{gN`z4(L;)P9kW*TOo-yFnB2DK8DqXwp*FBznkb5{--gc`sq}x7`yK21$HN zS$tx&YkgSMvd|x5ANGbim78nLknQJc*s_`4fc2$vKn0>wUHyJOmW``Q4x6VK8BD%U z_g&q$+!wFxtwgoA7qNbvEoE6*%!D*9?3zpr&1q#826LfagIB$TAJ!p`&#iddy<$Tc z{(l3={qGmvSwF*N)`0YRGX5ZS5smOPrpMpS^R{ zvy+Ae_=M+!aY*>hCBgMB^4>i-gB@P{p;vlx4bEyO;3l$m!46ZNKbVjR5ae$xCV$?@ zERMiV=^_shu%Qk^WGWUkSr`sz5gl5Y+~=D0Mp9Ga5+jY>1F&!sCeji{h1yQ+xX7;< z(Lh|~9S2#6J=|-G!JCzHJ?=`SONYNNY+vOD%NNJbRVfDK3+{-bcik@V^t?q3oack< zAz&rNj1I=wCKFMB*K(D25iu5VlySTT_5-F->F||*UbuUYd5Y+iU>fAim(w%?-CxwC zY&e_vD5DZ%%IN@)1JAO4P)8ZbOv-xZv$T)=_dh=5f#T1l8X7EL&pkHhm1df{m#*j7 zNV@+jpL0#ncWW=zo+q!-#qFc~lYi3E|9;Rk^u?)Mb6#~;&S%?h-p^n9$)R+`c)%9l zz`qBSAm7p)8#}}5N8ze%ck*I66_KoeFDY6tQC`&Gvu=Crxb!$%hp>0N6rWi{Xk}We{OCo_1 zIoRo?)fp6jvrKKl4{`oI>3BcPJ=nP%x}+zT=N`5)z16RX(O;zy+|!=>&# zu%ze?2JlETPw@xFvBEL=n@vg=io;owh}uKL(G|Ln5$$498?MoJky$Fpo&r160_Y>| z%;(%owoLuY7a)lF7HHi7I9@1iK(AzINwhou_dSy9IMwxSNvc6}Cz_V(;?mQBN z2EElnMl2PKx1JQ41`ED}`z=c1M-yc*z5aESO_)(+*k*fPo)V#4rQSYNTMe((70O35|MH94+*w^d*?+(^%M*X&t_U>{S}0oMSYFkMD4Wx$m}7qM2Z zp#KN8KGv7TErc?1gUWGkJ7k|I%|uv?EdQE8DnQF5)Vlalt#(<<7-g%UahmRXU$z42 z94(X3`E3WZ#lSO^Woy)-$kjOxA{HSL{O$$0PxJL(&bOd8M)Khwbp(lYBBy;0HrJuNrUjtsN^WQ5%7Qj65zW1?dic^V%n^$(bHo_<9R_kIOUp6UaA)g zI^;5$k0Os{0-{-XrV(vZnh!gOHLz6yYUKF?il<3-wNUz_AIXgGvdIoSRhgQ51NQ)3 zI<=9RXeJ4$HU@Zp+z?EG+0}ju4k)TmwyL(ppZeED;?Gv2uEN)#xO#R)yXxMU>c#l% zc6<5OIF-t#`. This is to safeguard against things like mem::swap-ing the contents of +two mutable references, given that Rust doesn't have information about the size +of the underlying object and couldn't invoke an appropriate C++ move constructor +anyway. + +**Thread safety:** Be aware that CXX does not assume anything about the thread +safety of your extern C++ types. In other words the `MyType` etc bindings which +CXX produces for you in Rust *do not* come with `Send` and `Sync` impls. If you +are sure that your C++ type satisfies the requirements of `Send` and/or `Sync` +and need to leverage that fact from Rust, you must provide your own unsafe +marker trait impls. + +```rust,noplayground +# #[cxx::bridge] +# mod ffi { +# extern "C++" { +# include!("path/to/header.h"); +# +# type MyType; +# } +# } +# +/// The C++ implementation of MyType is thread safe. +unsafe impl Send for ffi::MyType {} +unsafe impl Sync for ffi::MyType {} +``` + +Take care in doing this because thread safety in C++ can be extremely tricky to +assess if you are coming from a Rust background. For example the +`BlobstoreClient` type in the tutorial is *not thread safe* despite doing only +completely innocuous things in its implementation. Concurrent calls to the `tag` +member function trigger a data race on the `blobs` map. + +## Functions and member functions + +This largely follows the same principles as ***[extern +"Rust"](extern-rust.md)*** functions and methods. In particular, any signature +with a `self` parameter is interpreted as a C++ non-static member function and +exposed to Rust as a method. + +The programmer **does not** need to promise that the signatures they have typed +in are accurate; that would be unreasonable. CXX performs static assertions that +the signatures exactly correspond with what is declared in C++. Rather, the +programmer is only on the hook for things that C++'s static information is not +precise enough to capture, i.e. things that would only be represented at most by +comments in the C++ code unintelligible to a static assertion: namely whether +the C++ function is safe or unsafe to be called from Rust. + +**Safety:** the extern "C++" block is responsible for deciding whether to expose +each signature inside as safe-to-call or unsafe-to-call. If an extern block +contains at least one safe-to-call signature, it must be written as an `unsafe +extern` block, which serves as an item level unsafe block to indicate that an +unchecked safety claim is being made about the contents of the block. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + # include!("path/to/header.h"); + # + fn f(); // safe to call + } + + extern "C++" { + unsafe fn g(); // unsafe to call + } +} +``` + +## Lifetimes + +C++ types holding borrowed data may be described naturally in Rust by an extern +type with a generic lifetime parameter. For example in the case of the following +pair of types: + +```cpp +// header.h + +class Resource; + +class TypeContainingBorrow { + TypeContainingBorrow(const Resource &res) : res(res) {} + const Resource &res; +}; + +std::shared_ptr create(const Resource &res); +``` + +we'd want to expose this to Rust as: + +```rust,noplayground +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + # include!("path/to/header.h"); + # + type Resource; + type TypeContainingBorrow<'a>; + + fn create<'a>(res: &'a Resource) -> SharedPtr>; + + // or with lifetime elision: + fn create(res: &Resource) -> SharedPtr; + } +} +``` + +## Reusing existing binding types + +Extern C++ types support a syntax for declaring that a Rust binding of the +correct C++ type already exists outside of the current bridge module. This +avoids generating a fresh new binding which Rust's type system would consider +non-interchangeable with the first. + +```rust,noplayground +#[cxx::bridge(namespace = "path::to")] +mod ffi { + extern "C++" { + type MyType = crate::existing::MyType; + } + + extern "Rust" { + fn f(x: &MyType) -> usize; + } +} +``` + +In this case rather than producing a unique new Rust type `ffi::MyType` for the +Rust binding of C++'s `::path::to::MyType`, CXX will reuse the already existing +binding at `crate::existing::MyType` in expressing the signature of `f` and any +other uses of `MyType` within the bridge module. + +CXX safely validates that `crate::existing::MyType` is in fact a binding for the +right C++ type `::path::to::MyType` by generating a static assertion based on +`crate::existing::MyType`'s implementation of [`ExternType`], which is a trait +automatically implemented by CXX for bindings that it generates but can also be +manually implemented as described below. + +[`ExternType`]: https://docs.rs/cxx/*/cxx/trait.ExternType.html + +`ExternType` serves the following two related use cases. + +#### Safely unifying occurrences of an extern type across bridges + +In the following snippet, two #\[cxx::bridge\] invocations in different files +(possibly different crates) both contain function signatures involving the same +C++ type `example::Demo`. If both were written just containing `type Demo;`, +then both macro expansions would produce their own separate Rust type called +`Demo` and thus the compiler wouldn't allow us to take the `Demo` returned by +`file1::ffi::create_demo` and pass it as the `Demo` argument accepted by +`file2::ffi::take_ref_demo`. Instead, one of the two `Demo`s has been defined as +an extern type alias of the other, making them the same type in Rust. + +```rust,noplayground +// file1.rs +#[cxx::bridge(namespace = "example")] +pub mod ffi { + unsafe extern "C++" { + type Demo; + + fn create_demo() -> UniquePtr; + } +} +``` + +```rust,noplayground +// file2.rs +#[cxx::bridge(namespace = "example")] +pub mod ffi { + unsafe extern "C++" { + type Demo = crate::file1::ffi::Demo; + + fn take_ref_demo(demo: &Demo); + } +} +``` + +#### Integrating with bindgen-generated or handwritten unsafe bindings + +Handwritten `ExternType` impls make it possible to plug in a data structure +emitted by bindgen as the definition of a C++ type emitted by CXX. + +By writing the unsafe `ExternType` impl, the programmer asserts that the C++ +namespace and type name given in the type id refers to a C++ type that is +equivalent to Rust type that is the `Self` type of the impl. + +```rust,noplayground +mod folly_sys; // the bindgen-generated bindings + +use cxx::{type_id, ExternType}; + +unsafe impl ExternType for folly_sys::StringPiece { + type Id = type_id!("folly::StringPiece"); + type Kind = cxx::kind::Opaque; +} + +#[cxx::bridge(namespace = "folly")] +pub mod ffi { + unsafe extern "C++" { + include!("rust_cxx_bindings.h"); + + type StringPiece = crate::folly_sys::StringPiece; + + fn print_string_piece(s: &StringPiece); + } +} + +// Now if we construct a StringPiece or obtain one through one +// of the bindgen-generated signatures, we are able to pass it +// along to ffi::print_string_piece. +``` + +The `ExternType::Id` associated type encodes a type-level representation of the +type's C++ namespace and type name. It will always be defined using the +`type_id!` macro exposed in the cxx crate. + +The `ExternType::Kind` associated type will always be either +[`cxx::kind::Opaque`] or [`cxx::kind::Trivial`] identifying whether a C++ type +is soundly relocatable by Rust's move semantics. A C++ type is only okay to hold +and pass around by value in Rust if its [move constructor is trivial] and it has +no destructor. In CXX, these are called Trivial extern C++ types, while types +with nontrivial move behavior or a destructor must be considered Opaque and +handled by Rust only behind an indirection, such as a reference or UniquePtr. + +[`cxx::kind::Opaque`]: https://docs.rs/cxx/*/cxx/kind/enum.Opaque.html +[`cxx::kind::Trivial`]: https://docs.rs/cxx/*/cxx/kind/enum.Trivial.html +[move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible + +If you believe your C++ type reflected by the ExternType impl is indeed fine to +hold by value and move in Rust, you can specify: + +```rust,noplayground +# unsafe impl cxx::ExternType for TypeName { +# type Id = cxx::type_id!("name::space::of::TypeName"); + type Kind = cxx::kind::Trivial; +# } +``` + +which will enable you to pass it into C++ functions by value, return it by +value, and include it in `struct`s that you have declared to `cxx::bridge`. Your +claim about the triviality of the C++ type will be checked by a `static_assert` +in the generated C++ side of the binding. + +## Explicit shim trait impls + +This is a somewhat niche feature, but important when you need it. + +CXX's support for C++'s std::unique\_ptr and std::vector is built on a set of +internal trait impls connecting the Rust API of UniquePtr and CxxVector to +underlying template instantiations performed by the C++ compiler. + +When reusing a binding type across multiple bridge modules as described in the +previous section, you may find that your code needs some trait impls which CXX +hasn't decided to generate. + +```rust,noplayground +#[cxx::bridge] +mod ffi1 { + extern "C++" { + include!("path/to/header.h"); + + type A; + type B; + + // Okay: CXX sees UniquePtr using a type B defined within the same + // bridge, and automatically emits the right template instantiations + // corresponding to std::unique_ptr. + fn get_b() -> UniquePtr; + } +} + +#[cxx::bridge] +mod ffi2 { + extern "C++" { + type A = crate::ffi1::A; + + // Rust trait error: CXX processing this module has no visibility into + // whether template instantiations corresponding to std::unique_ptr + // have already been emitted by the upstream library, so it does not + // emit them here. If the upstream library does not have any signatures + // involving UniquePtr, an explicit instantiation of the template + // needs to be requested in one module or the other. + fn get_a() -> UniquePtr; + } +} +``` + +You can request a specific template instantiation at a particular location in +the Rust crate hierarchy by writing `impl UniquePtr {}` inside of the bridge +module which defines `A` but does not otherwise contain any use of +`UniquePtr`. + +```rust,noplayground +#[cxx::bridge] +mod ffi1 { + extern "C++" { + include!("path/to/header.h"); + + type A; + type B; + + fn get_b() -> UniquePtr; + } + + impl UniquePtr {} // explicit instantiation +} +``` diff --git a/book/src/extern-rust.md b/book/src/extern-rust.md new file mode 100644 index 0000000..40f2237 --- /dev/null +++ b/book/src/extern-rust.md @@ -0,0 +1,165 @@ +{{#title extern "Rust" — Rust ♡ C++}} +# extern "Rust" + +```rust,noplayground +#[cxx::bridge] +mod ffi { + extern "Rust" { + + } +} +``` + +The `extern "Rust"` section of a CXX bridge declares Rust types and signatures +to be made available to C++. + +The CXX code generator uses your extern "Rust" section(s) to produce a C++ +header file containing the corresponding C++ declarations. The generated header +has the same path as the Rust source file containing the bridge, except with a +`.rs.h` file extension. + +A bridge module may contain zero or more extern "Rust" blocks. + +## Opaque Rust types + +Types defined in Rust that are made available to C++, but only behind an +indirection. + +```rust,noplayground +# #[cxx::bridge] +# mod ffi { + extern "Rust" { + type MyType; + type MyOtherType; + type OneMoreType<'a>; + } +# } +``` + +For example in the ***[Tutorial](tutorial.md)*** we saw `MultiBuf` used in this +way. Rust code created the `MultiBuf`, passed a `&mut MultiBuf` to C++, and C++ +later passed a `&mut MultiBuf` back across the bridge to Rust. + +Another example is the one on the ***[Box\](binding/box.md)*** page, which +exposes the Rust standard library's `std::fs::File` to C++ as an opaque type in +a similar way but with Box as the indirection rather than &mut. + +The types named as opaque types (`MyType` etc) refer to types in the `super` +module, the parent module of the CXX bridge. You can think of an opaque type `T` +as being like a re-export `use super::T` made available to C++ via the generated +header. + +Opaque types are currently required to be [`Sized`] and [`Unpin`]. In +particular, a trait object `dyn MyTrait` or slice `[T]` may not be used for an +opaque Rust type. These restrictions may be lifted in the future. + +[`Sized`]: https://doc.rust-lang.org/std/marker/trait.Sized.html +[`Unpin`]: https://doc.rust-lang.org/std/marker/trait.Unpin.html + +For now, types used as extern Rust types are required to be defined by the same +crate that contains the bridge using them. This restriction may be lifted in the +future. + +The bridge's parent module will contain the appropriate imports or definitions +for these types. + +```rust,noplayground +use path::to::MyType; + +pub struct MyOtherType { + ... +} +# +# #[cxx::bridge] +# mod ffi { +# extern "Rust" { +# type MyType; +# type MyOtherType; +# } +# } +``` + +## Functions + +Rust functions made callable to C++. + +Just like for opaque types, these functions refer implicitly to something in +scope in the `super` module, whether defined there or imported by some `use` +statement. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + extern "Rust" { + type MyType; + fn f() -> Box; + } +} + +struct MyType(i32); + +fn f() -> Box { + return Box::new(MyType(1)); +} +``` + +Extern Rust function signature may consist of types defined in the bridge, +primitives, and [any of these additional bindings](bindings.md). + +## Methods + +Any signature with a `self` parameter is interpreted as a Rust method and +exposed to C++ as a non-static member function. + +```rust,noplayground +# #[cxx::bridge] +# mod ffi { + extern "Rust" { + type MyType; + fn f(&self) -> usize; + } +# } +``` + +The `self` parameter may be a shared reference `&self`, an exclusive reference +`&mut self`, or a pinned reference `self: Pin<&mut Self>`. A by-value `self` is +not currently supported. + +If the surrounding `extern "Rust"` block contains exactly one extern type, that +type is implicitly the receiver for a `&self` or `&mut self` method. If the +surrounding block contains *more than one* extern type, a receiver type must be +provided explicitly for the self parameter, or you can consider splitting into +multiple extern blocks. + +```rust,noplayground +# #[cxx::bridge] +# mod ffi { + extern "Rust" { + type First; + type Second; + fn bar(self: &First); + fn foo(self: &mut Second); + } +# } +``` + +## Functions with explicit lifetimes + +An extern Rust function signature is allowed to contain explicit lifetimes but +in this case the function must be declared unsafe-to-call. This is pretty +meaningless given we're talking about calls from C++, but at least it draws some +extra attention from the caller that they may be responsible for upholding some +atypical lifetime relationship. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + extern "Rust" { + type MyType; + unsafe fn f<'a>(&'a self, s: &str) -> &'a str; + } +} +``` + +Bounds on a lifetime (like `<'a, 'b: 'a>`) are not currently supported. Nor are +type parameters or where-clauses. diff --git a/book/src/index.md b/book/src/index.md new file mode 100644 index 0000000..7add044 --- /dev/null +++ b/book/src/index.md @@ -0,0 +1,83 @@ +
    +githubcrates-iodocs-rs +
    + +# CXX — safe interop between Rust and C++ + +This library provides a safe mechanism for calling C++ code from Rust and Rust +code from C++. It carves out a regime of commonality where Rust and C++ are +semantically very similar and guides the programmer to express their language +boundary effectively within this regime. CXX fills in the low level stuff so +that you get a safe binding, preventing the pitfalls of doing a foreign function +interface over unsafe C-style signatures. + +
    + +
    + +From a high level description of the language boundary, CXX uses static analysis +of the types and function signatures to protect both Rust's and C++'s +invariants. Then it uses a pair of code generators to implement the boundary +efficiently on both sides together with any necessary static assertions for +later in the build process to verify correctness. + +The resulting FFI bridge operates at zero or negligible overhead, i.e. no +copying, no serialization, no memory allocation, no runtime checks needed. + +The FFI signatures are able to use native data structures from whichever side +they please. In addition, CXX provides builtin bindings for key standard library +types like strings, vectors, Box, unique\_ptr, etc to expose an idiomatic API on +those types to the other language. + +## Example + +In this example we are writing a Rust application that calls a C++ client of a +large-file blobstore service. The blobstore supports a `put` operation for a +discontiguous buffer upload. For example we might be uploading snapshots of a +circular buffer which would tend to consist of 2 pieces, or fragments of a file +spread across memory for some other reason (like a rope data structure). + +```rust,noplayground +#[cxx::bridge] +mod ffi { + extern "Rust" { + type MultiBuf; + + fn next_chunk(buf: &mut MultiBuf) -> &[u8]; + } + + unsafe extern "C++" { + include!("example/include/blobstore.h"); + + type BlobstoreClient; + + fn new_blobstore_client() -> UniquePtr; + fn put(self: &BlobstoreClient, buf: &mut MultiBuf) -> Result; + } +} +``` + +Now we simply provide Rust definitions of all the things in the `extern "Rust"` +block and C++ definitions of all the things in the `extern "C++"` block, and get +to call back and forth safely. + +The [***Tutorial***](tutorial.md) chapter walks through a fleshed out version of +this blobstore example in full detail, including all of the Rust code and all of +the C++ code. The code is also provided in runnable form in the *demo* directory +of . To try it out, run `cargo run` from that +directory. + +- [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs) +- [demo/include/blobstore.h](https://github.com/dtolnay/cxx/blob/master/demo/include/blobstore.h) +- [demo/src/blobstore.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/blobstore.cc) + +The key takeaway, which is enabled by the CXX library, is that the Rust code in +main.rs is 100% ordinary safe Rust code working idiomatically with Rust types +while the C++ code in blobstore.cc is 100% ordinary C++ code working +idiomatically with C++ types. The Rust code feels like Rust and the C++ code +feels like C++, not like C-style "FFI glue". + +
    + +***Chapter outline:** See the hamburger menu in the top left if you are on a +small screen and it didn't open with a sidebar by default.* diff --git a/book/src/overview.svg b/book/src/overview.svg new file mode 100644 index 0000000..df4fcf4 --- /dev/null +++ b/book/src/overview.svg @@ -0,0 +1,444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/src/reference.md b/book/src/reference.md new file mode 100644 index 0000000..62ca35c --- /dev/null +++ b/book/src/reference.md @@ -0,0 +1,29 @@ +{{#title The bridge module — Rust ♡ C++}} +# The bridge module reference + +The ***[Core concepts](concepts.md)*** in chapter 2 covered the high level model +that CXX uses to represent a language boundary. This chapter builds on that one +to document an exhaustive reference on the syntax and functionality of +\#\[cxx::bridge\]. + +- ***[extern "Rust"](extern-rust.md)*** — exposing opaque Rust types, Rust + functions, Rust methods to C++; functions with lifetimes. + +- ***[extern "C++"](extern-c++.md)*** — binding opaque C++ types, C++ + functions, C++ member functions; sharing an opaque type definition across + multiple bridge modules or different crates; using bindgen-generated data + structures across a CXX bridge; Rust orphan-rule-compatible way to request + that particular glue code be emitted in a specific bridge module. + +- ***[Shared types](shared.md)*** — shared structs; shared enums; using + Rust as source of truth vs C++ as source of truth. + +- ***[Attributes](attributes.md)*** — working with namespaces; giving + functions a different name in their non-native language. + +- ***[Async functions](async.md)*** — integrating async C++ with async + Rust. + +- ***[Error handling](binding/result.md)*** — representing fallibility on + the language boundary; accessing a Rust error message from C++; customizing + the set of caught exceptions and their conversion to a Rust error message. diff --git a/book/src/shared.md b/book/src/shared.md new file mode 100644 index 0000000..4043db1 --- /dev/null +++ b/book/src/shared.md @@ -0,0 +1,246 @@ +{{#title Shared types — Rust ♡ C++}} +# Shared types + +Shared types enable *both* languages to have visibility into the internals of a +type. This is in contrast to opaque Rust types and opaque C++ types, for which +only one side gets to manipulate the internals. + +Unlike opaque types, the FFI bridge is allowed to pass and return shared types +by value. + +The order in which shared types are written is not important. C++ is order +sensitive but CXX will topologically sort and forward-declare your types as +necessary. + +## Shared structs and enums + +For enums, only C-like a.k.a. unit variants are currently supported. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + struct PlayingCard { + suit: Suit, + value: u8, // A=1, J=11, Q=12, K=13 + } + + enum Suit { + Clubs, + Diamonds, + Hearts, + Spades, + } + + unsafe extern "C++" { + fn deck() -> Vec; + fn sort(cards: &mut Vec); + } +} +``` + +## The generated data structures + +Shared structs compile to an aggregate-initialization compatible C++ struct. + +Shared enums compile to a C++ `enum class` with a sufficiently sized integral +base type decided by CXX. + +```cpp +// generated header + +struct PlayingCard final { + Suit suit; + uint8_t value; +}; + +enum class Suit : uint8_t { + Clubs = 0, + Diamonds = 1, + Hearts = 2, + Spades = 3, +}; +``` + +Because it is not UB in C++ for an `enum class` to hold a value different from +all of the listed variants, we use a Rust representation for shared enums that +is compatible with this. The API you'll get is something like: + +```rust,noplayground +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(transparent)] +pub struct Suit { + pub repr: u8, +} +#[allow(non_upper_case_globals)] +impl Suit { + pub const Clubs: Self = Suit { repr: 0 }; + pub const Diamonds: Self = Suit { repr: 1 }; + pub const Hearts: Self = Suit { repr: 2 }; + pub const Spades: Self = Suit { repr: 3 }; +} +``` + +Notice you're free to treat the enum as an integer in Rust code via the public +`repr` field. + +Pattern matching with `match` still works but will require you to write wildcard +arms to handle the situation of an enum value that is not one of the listed +variants. + +```rust,noplayground +fn main() { + let suit: Suit = /*...*/; + match suit { + Suit::Clubs => ..., + Suit::Diamonds => ..., + Suit::Hearts => ..., + Suit::Spades => ..., + _ => ..., // fallback arm + } +} +``` + +If a shared struct has generic lifetime parameters, the lifetimes are simply not +represented on the C++ side. C++ code will need care when working with borrowed +data (as usual in C++). + +```rust,noplayground +#[cxx::bridge] +mod ffi { + struct Borrowed<'a> { + flags: &'a [&'a str], + } +} +``` + +```cpp +// generated header + +struct Borrowed final { + rust::Slice flags; +}; +``` + +## Enum discriminants + +You may provide explicit discriminants for some or all of the enum variants, in +which case those numbers will be propagated into the generated C++ `enum class`. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + enum SmallPrime { + Two = 2, + Three = 3, + Five = 5, + Seven = 7, + } +} +``` + +Variants without an explicit discriminant are assigned the previous discriminant +plus 1. If the first variant has not been given an explicit discriminant, it is +assigned discriminant 0. + +By default CXX represents your enum using the smallest integer type capable of +fitting all the discriminants (whether explicit or implicit). If you need a +different representation for reasons, provide a `repr` attribute. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + #[repr(i32)] + enum Enum { + Zero, + One, + Five = 5, + Six, + } +} +``` + +```cpp +// generated header + +enum class Enum : int32_t { + Zero = 0, + One = 1, + Five = 5, + Six = 6, +}; +``` + +## Extern enums + +If you need to interoperate with an already existing enum for which an existing +C++ definition is the source of truth, make sure that definition is provided by +some header in the bridge and then declare your enum *additionally* as an extern +C++ type. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + enum Enum { + Yes, + No, + } + + extern "C++" { + include!("path/to/the/header.h"); + type Enum; + } +} +``` + +CXX will recognize this pattern and, instead of generating a C++ definition of +the enum, will instead generate C++ static assertions asserting that the +variants and discriminant values and integer representation written in Rust all +correctly match the existing C++ enum definition. + +Extern enums support all the same features as ordinary shared enums (explicit +discriminants, repr). Again, CXX will static assert that all of those things you +wrote are correct. + +## Derives + +The following standard traits are supported in `derive(...)` within the CXX +bridge module. + +- `Clone` +- `Copy` +- `Debug` +- `Default` +- `Eq` +- `Hash` +- `Ord` +- `PartialEq` +- `PartialOrd` + +Note that shared enums automatically always come with impls of `Copy`, `Clone`, +`Eq`, and `PartialEq`, so you're free to omit those derives on an enum. + +```rust,noplayground +#[cxx::bridge] +mod ffi { + #[derive(Clone, Debug, Hash)] + struct ExampleStruct { + x: u32, + s: String, + } + + #[derive(Hash, Ord, PartialOrd)] + enum ExampleEnum { + Yes, + No, + } +} +``` + +The derives naturally apply to *both* the Rust data type *and* the corresponding +C++ data type: + +- `Hash` gives you a specialization of [`template <> struct std::hash`][hash] in C++ +- `PartialEq` produces `operator==` and `operator!=` +- `PartialOrd` produces `operator<`, `operator<=`, `operator>`, `operator>=` + +[hash]: https://en.cppreference.com/w/cpp/utility/hash diff --git a/book/src/tutorial.md b/book/src/tutorial.md new file mode 100644 index 0000000..1182dc2 --- /dev/null +++ b/book/src/tutorial.md @@ -0,0 +1,692 @@ +{{#title Tutorial — Rust ♡ C++}} +# Tutorial: CXX blobstore client + +This example walks through a Rust application that calls into a C++ client of a +blobstore service. In fact we'll see calls going in both directions: Rust to C++ +as well as C++ to Rust. For your own use case it may be that you need just one +of these directions. + +All of the code involved in the example is shown on this page, but it's also +provided in runnable form in the *demo* directory of +. To try it out directly, run `cargo run` from +that directory. + +This tutorial assumes you've read briefly about **shared structs**, **opaque +types**, and **functions** in the [*Core concepts*](concepts.md) page. + +## Creating the project + +We'll use Cargo, which is the build system commonly used by open source Rust +projects. (CXX works with other build systems too; refer to chapter 5.) + +Create a blank Cargo project: `mkdir cxx-demo`; `cd cxx-demo`; `cargo init`. + +Edit the Cargo.toml to add a dependency on the `cxx` crate: + +```toml,hidelines=... +# Cargo.toml +...[package] +...name = "cxx-demo" +...version = "0.1.0" +...edition = "2021" + +[dependencies] +cxx = "1.0" +``` + +We'll revisit this Cargo.toml later when we get to compiling some C++ code. + +## Defining the language boundary + +CXX relies on a description of the function signatures that will be exposed from +each language to the other. You provide this description using `extern` blocks +in a Rust module annotated with the `#[cxx::bridge]` attribute macro. + +We'll open with just the following at the top of src/main.rs and walk through +each item in detail. + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + +} +# +# fn main() {} +``` + +The contents of this module will be everything that needs to be agreed upon by +both sides of the FFI boundary. + +## Calling a C++ function from Rust + +Let's obtain an instance of the C++ blobstore client, a class `BlobstoreClient` +defined in C++. + +We'll treat `BlobstoreClient` as an *opaque type* in CXX's classification so +that Rust does not need to assume anything about its implementation, not even +its size or alignment. In general, a C++ type might have a move-constructor +which is incompatible with Rust's move semantics, or may hold internal +references which cannot be modeled by Rust's borrowing system. Though there are +alternatives, the easiest way to not care about any such thing on an FFI +boundary is to require no knowledge about a type by treating it as opaque. + +Opaque types may only be manipulated behind an indirection such as a reference +`&`, a Rust `Box`, or a `UniquePtr` (Rust binding of `std::unique_ptr`). We'll +add a function through which C++ can return a `std::unique_ptr` +to Rust. + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("cxx-demo/include/blobstore.h"); + + type BlobstoreClient; + + fn new_blobstore_client() -> UniquePtr; + } +} + +fn main() { + let client = ffi::new_blobstore_client(); +} +``` + +The nature of `unsafe` extern blocks is clarified in more detail in the +[*extern "C++"*](extern-c++.md) chapter. In brief: the programmer is **not** +promising that the signatures they have typed in are accurate; that would be +unreasonable. CXX performs static assertions that the signatures exactly match +what is declared in C++. Rather, the programmer is only on the hook for things +that C++'s semantics are not precise enough to capture, i.e. things that would +only be represented at most by comments in the C++ code. In this case, it's +whether `new_blobstore_client` is safe or unsafe to call. If that function said +something like "must be called at most once or we'll stomp yer memery", Rust +would instead want to expose it as `unsafe fn new_blobstore_client`, this time +inside a safe `extern "C++"` block because the programmer is no longer on the +hook for any safety claim about the signature. + +If you build this file right now with `cargo build`, it won't build because we +haven't written a C++ implementation of `new_blobstore_client` nor instructed +Cargo about how to link it into the resulting binary. You'll see an error from +the linker like this: + +```console +error: linking with `cc` failed: exit code: 1 + | + = /bin/ld: target/debug/deps/cxx-demo-7cb7fddf3d67d880.rcgu.o: in function `cxx_demo::ffi::new_blobstore_client': + src/main.rs:1: undefined reference to `cxxbridge1$new_blobstore_client' + collect2: error: ld returned 1 exit status +``` + +## Adding in the C++ code + +In CXX's integration with Cargo, all #include paths begin with a crate name by +default (when not explicitly selected otherwise by a crate; see +`CFG.include_prefix` in chapter 5). That's why we see +`include!("cxx-demo/include/blobstore.h")` above — we'll be putting the +C++ header at relative path `include/blobstore.h` within the Rust crate. If your +crate is named something other than `cxx-demo` according to the `name` field in +Cargo.toml, you will need to use that name everywhere in place of `cxx-demo` +throughout this tutorial. + +```cpp +// include/blobstore.h + +#pragma once +#include + +class BlobstoreClient { +public: + BlobstoreClient(); +}; + +std::unique_ptr new_blobstore_client(); +``` + +```cpp +// src/blobstore.cc + +#include "cxx-demo/include/blobstore.h" + +BlobstoreClient::BlobstoreClient() {} + +std::unique_ptr new_blobstore_client() { + return std::unique_ptr(new BlobstoreClient()); +} +``` + +Using `std::make_unique` would work too, as long as you pass `std("c++14")` to +the C++ compiler as described later on. + +The placement in *include/* and *src/* is not significant; you can place C++ +code anywhere else in the crate as long as you use the right paths throughout +the tutorial. + +Be aware that *CXX does not look at any of these files.* You're free to put +arbitrary C++ code in here, #include your own libraries, etc. All we do is emit +static assertions against what you provide in the headers. + +## Compiling the C++ code with Cargo + +Cargo has a [build scripts] feature suitable for compiling non-Rust code. + +We need to introduce a new build-time dependency on CXX's C++ code generator in +Cargo.toml: + +```toml,hidelines=... +# Cargo.toml +...[package] +...name = "cxx-demo" +...version = "0.1.0" +...edition = "2021" + +[dependencies] +cxx = "1.0" + +[build-dependencies] +cxx-build = "1.0" +``` + +Then add a build.rs build script adjacent to Cargo.toml to run the cxx-build +code generator and C++ compiler. The relevant arguments are the path to the Rust +source file containing the cxx::bridge language boundary definition, and the +paths to any additional C++ source files to be compiled during the Rust crate's +build. + +```rust,noplayground +// build.rs + +fn main() { + cxx_build::bridge("src/main.rs") + .file("src/blobstore.cc") + .compile("cxx-demo"); + + println!("cargo:rerun-if-changed=src/main.rs"); + println!("cargo:rerun-if-changed=src/blobstore.cc"); + println!("cargo:rerun-if-changed=include/blobstore.h"); +} +``` + +This build.rs would also be where you set up C++ compiler flags, for example if +you'd like to have access to `std::make_unique` from C++14. See the page on +***[Cargo-based builds](build/cargo.md)*** for more details about CXX's Cargo +integration. + +```rust,noplayground +# // build.rs +# +# fn main() { + cxx_build::bridge("src/main.rs") + .file("src/blobstore.cc") + .std("c++14") + .compile("cxx-demo"); +# } +``` + +[build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html + +The project should now build and run successfully, though not do anything useful +yet. + +```console +cxx-demo$ cargo run + Compiling cxx-demo v0.1.0 + Finished dev [unoptimized + debuginfo] target(s) in 0.34s + Running `target/debug/cxx-demo` + +cxx-demo$ +``` + +## Calling a Rust function from C++ + +Our C++ blobstore supports a `put` operation for a discontiguous buffer upload. +For example we might be uploading snapshots of a circular buffer which would +tend to consist of 2 pieces, or fragments of a file spread across memory for +some other reason (like a rope data structure). + +We'll express this by handing off an iterator over contiguous borrowed chunks. +This loosely resembles the API of the widely used `bytes` crate's `Buf` trait. +During a `put`, we'll make C++ call back into Rust to obtain contiguous chunks +of the upload (all with no copying or allocation on the language boundary). In +reality the C++ client might contain some sophisticated batching of chunks +and/or parallel uploading that all of this ties into. + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + extern "Rust" { + type MultiBuf; + + fn next_chunk(buf: &mut MultiBuf) -> &[u8]; + } + + unsafe extern "C++" { + include!("cxx-demo/include/blobstore.h"); + + type BlobstoreClient; + + fn new_blobstore_client() -> UniquePtr; + fn put(&self, parts: &mut MultiBuf) -> u64; + } +} +# +# fn main() { +# let client = ffi::new_blobstore_client(); +# } +``` + +Any signature having a `self` parameter (the Rust name for C++'s `this`) is +considered a method / non-static member function. If there is only one `type` in +the surrounding extern block, it'll be a method of that type. If there is more +than one `type`, you can disambiguate which one a method belongs to by writing +`self: &BlobstoreClient` in the argument list. + +As usual, now we need to provide Rust definitions of everything declared by the +`extern "Rust"` block and a C++ definition of the new signature declared by the +`extern "C++"` block. + +```rust,noplayground +// src/main.rs +# +# #[cxx::bridge] +# mod ffi { +# extern "Rust" { +# type MultiBuf; +# +# fn next_chunk(buf: &mut MultiBuf) -> &[u8]; +# } +# +# unsafe extern "C++" { +# include!("cxx-demo/include/blobstore.h"); +# +# type BlobstoreClient; +# +# fn new_blobstore_client() -> UniquePtr; +# fn put(&self, parts: &mut MultiBuf) -> u64; +# } +# } + +// An iterator over contiguous chunks of a discontiguous file object. Toy +// implementation uses a Vec> but in reality this might be iterating +// over some more complex Rust data structure like a rope, or maybe loading +// chunks lazily from somewhere. +pub struct MultiBuf { + chunks: Vec>, + pos: usize, +} + +pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] { + let next = buf.chunks.get(buf.pos); + buf.pos += 1; + next.map_or(&[], Vec::as_slice) +} +# +# fn main() { +# let client = ffi::new_blobstore_client(); +# } +``` + +```cpp,hidelines=... +// include/blobstore.h + +...#pragma once +...#include +... +struct MultiBuf; + +class BlobstoreClient { +public: + BlobstoreClient(); + uint64_t put(MultiBuf &buf) const; +}; +... +...std::unique_ptr new_blobstore_client(); +``` + +In blobstore.cc we're able to call the Rust `next_chunk` function, exposed to +C++ by a header `main.rs.h` generated by the CXX code generator. In CXX's Cargo +integration this generated header has a path containing the crate name, the +relative path of the Rust source file within the crate, and a `.rs.h` extension. + +```cpp,hidelines=... +// src/blobstore.cc + +#include "cxx-demo/include/blobstore.h" +#include "cxx-demo/src/main.rs.h" +#include +#include +... +...BlobstoreClient::BlobstoreClient() {} +... +...std::unique_ptr new_blobstore_client() { +... return std::make_unique(); +...} + +// Upload a new blob and return a blobid that serves as a handle to the blob. +uint64_t BlobstoreClient::put(MultiBuf &buf) const { + // Traverse the caller's chunk iterator. + std::string contents; + while (true) { + auto chunk = next_chunk(buf); + if (chunk.size() == 0) { + break; + } + contents.append(reinterpret_cast(chunk.data()), chunk.size()); + } + + // Pretend we did something useful to persist the data. + auto blobid = std::hash{}(contents); + return blobid; +} +``` + +This is now ready to use. :) + +```rust,noplayground +// src/main.rs +# +# #[cxx::bridge] +# mod ffi { +# extern "Rust" { +# type MultiBuf; +# +# fn next_chunk(buf: &mut MultiBuf) -> &[u8]; +# } +# +# unsafe extern "C++" { +# include!("cxx-demo/include/blobstore.h"); +# +# type BlobstoreClient; +# +# fn new_blobstore_client() -> UniquePtr; +# fn put(&self, parts: &mut MultiBuf) -> u64; +# } +# } +# +# pub struct MultiBuf { +# chunks: Vec>, +# pos: usize, +# } +# pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] { +# let next = buf.chunks.get(buf.pos); +# buf.pos += 1; +# next.map_or(&[], Vec::as_slice) +# } + +fn main() { + let client = ffi::new_blobstore_client(); + + // Upload a blob. + let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; + let mut buf = MultiBuf { chunks, pos: 0 }; + let blobid = client.put(&mut buf); + println!("blobid = {}", blobid); +} +``` + +```console +cxx-demo$ cargo run + Compiling cxx-demo v0.1.0 + Finished dev [unoptimized + debuginfo] target(s) in 0.41s + Running `target/debug/cxx-demo` + +blobid = 9851996977040795552 +``` + +## Interlude: What gets generated? + +For the curious, it's easy to look behind the scenes at what CXX has done to +make these function calls work. You shouldn't need to do this during normal +usage of CXX, but for the purpose of this tutorial it can be educative. + +CXX comprises *two* code generators: a Rust one (which is the cxx::bridge +attribute procedural macro) and a C++ one. + +### Rust generated code + +It's easiest to view the output of the procedural macro by installing +[cargo-expand]. Then run `cargo expand ::ffi` to macro-expand the `mod ffi` +module. + +[cargo-expand]: https://github.com/dtolnay/cargo-expand + +```console +cxx-demo$ cargo install cargo-expand +cxx-demo$ cargo expand ::ffi +``` + +You'll see some deeply unpleasant code involving `#[repr(C)]`, `#[link_name]`, +and `#[export_name]`. + +### C++ generated code + +For debugging convenience, `cxx_build` links all generated C++ code into Cargo's +target directory under *target/cxxbridge/*. + +```console +cxx-demo$ exa -T target/cxxbridge/ +target/cxxbridge +├── cxx-demo +│ └── src +│ ├── main.rs.cc -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/sources/cxx-demo/src/main.rs.cc +│ └── main.rs.h -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/include/cxx-demo/src/main.rs.h +└── rust + └── cxx.h -> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cxx-1.0.0/include/cxx.h +``` + +In those files you'll see declarations or templates of any CXX Rust types +present in your language boundary (like `rust::Slice` for `&[T]`) and `extern +"C"` signatures corresponding to your extern functions. + +If it fits your workflow better, the CXX C++ code generator is also available as +a standalone executable which outputs generated code to stdout. + +```console +cxx-demo$ cargo install cxxbridge-cmd +cxx-demo$ cxxbridge src/main.rs +``` + +## Shared data structures + +So far the calls in both directions above only used **opaque types**, not +**shared structs**. + +Shared structs are data structures whose complete definition is visible to both +languages, making it possible to pass them by value across the language +boundary. Shared structs translate to a C++ aggregate-initialization compatible +struct exactly matching the layout of the Rust one. + +As the last step of this demo, we'll use a shared struct `BlobMetadata` to pass +metadata about blobs between our Rust application and C++ blobstore client. + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + struct BlobMetadata { + size: usize, + tags: Vec, + } + + extern "Rust" { + // ... +# type MultiBuf; +# +# fn next_chunk(buf: &mut MultiBuf) -> &[u8]; + } + + unsafe extern "C++" { + // ... +# include!("cxx-demo/include/blobstore.h"); +# +# type BlobstoreClient; +# +# fn new_blobstore_client() -> UniquePtr; +# fn put(&self, parts: &mut MultiBuf) -> u64; + fn tag(&self, blobid: u64, tag: &str); + fn metadata(&self, blobid: u64) -> BlobMetadata; + } +} +# +# pub struct MultiBuf { +# chunks: Vec>, +# pos: usize, +# } +# pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] { +# let next = buf.chunks.get(buf.pos); +# buf.pos += 1; +# next.map_or(&[], Vec::as_slice) +# } + +fn main() { + let client = ffi::new_blobstore_client(); + + // Upload a blob. + let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; + let mut buf = MultiBuf { chunks, pos: 0 }; + let blobid = client.put(&mut buf); + println!("blobid = {}", blobid); + + // Add a tag. + client.tag(blobid, "rust"); + + // Read back the tags. + let metadata = client.metadata(blobid); + println!("tags = {:?}", metadata.tags); +} +``` + +```cpp,hidelines=... +// include/blobstore.h + +#pragma once +#include "rust/cxx.h" +...#include + +struct MultiBuf; +struct BlobMetadata; + +class BlobstoreClient { +public: + BlobstoreClient(); + uint64_t put(MultiBuf &buf) const; + void tag(uint64_t blobid, rust::Str tag) const; + BlobMetadata metadata(uint64_t blobid) const; + +private: + class impl; + std::shared_ptr impl; +}; +... +...std::unique_ptr new_blobstore_client(); +``` + +```cpp,hidelines=... +// src/blobstore.cc + +#include "cxx-demo/include/blobstore.h" +#include "cxx-demo/src/main.rs.h" +#include +#include +#include +#include +#include + +// Toy implementation of an in-memory blobstore. +// +// In reality the implementation of BlobstoreClient could be a large +// complex C++ library. +class BlobstoreClient::impl { + friend BlobstoreClient; + using Blob = struct { + std::string data; + std::set tags; + }; + std::unordered_map blobs; +}; + +BlobstoreClient::BlobstoreClient() : impl(new class BlobstoreClient::impl) {} +... +...// Upload a new blob and return a blobid that serves as a handle to the blob. +...uint64_t BlobstoreClient::put(MultiBuf &buf) const { +... // Traverse the caller's chunk iterator. +... std::string contents; +... while (true) { +... auto chunk = next_chunk(buf); +... if (chunk.size() == 0) { +... break; +... } +... contents.append(reinterpret_cast(chunk.data()), chunk.size()); +... } +... +... // Insert into map and provide caller the handle. +... auto blobid = std::hash{}(contents); +... impl->blobs[blobid] = {std::move(contents), {}}; +... return blobid; +...} + +// Add tag to an existing blob. +void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const { + impl->blobs[blobid].tags.emplace(tag); +} + +// Retrieve metadata about a blob. +BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const { + BlobMetadata metadata{}; + auto blob = impl->blobs.find(blobid); + if (blob != impl->blobs.end()) { + metadata.size = blob->second.data.size(); + std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(), + [&](auto &t) { metadata.tags.emplace_back(t); }); + } + return metadata; +} +... +...std::unique_ptr new_blobstore_client() { +... return std::make_unique(); +...} +``` + +```console +cxx-demo$ cargo run + Running `target/debug/cxx-demo` + +blobid = 9851996977040795552 +tags = ["rust"] +``` + +*You've now seen all the code involved in the tutorial. It's available all +together in runnable form in the* demo *directory of +. You can run it directly without stepping +through the steps above by running `cargo run` from that directory.* + +
    + +# Takeaways + +The key contribution of CXX is it gives you Rust–C++ interop in which +*all* of the Rust side of the code you write *really* looks like you are just +writing normal Rust, and the C++ side *really* looks like you are just writing +normal C++. + +You've seen in this tutorial that none of the code involved feels like C or like +the usual perilous "FFI glue" prone to leaks or memory safety flaws. + +An expressive system of opaque types, shared types, and key standard library +type bindings enables API design on the language boundary that captures the +proper ownership and borrowing contracts of the interface. + +CXX plays to the strengths of the Rust type system *and* C++ type system *and* +the programmer's intuitions. An individual working on the C++ side without a +Rust background, or the Rust side without a C++ background, will be able to +apply all their usual intuitions and best practices about development in their +language to maintain a correct FFI. + +

    diff --git a/book/theme/head.hbs b/book/theme/head.hbs new file mode 100644 index 0000000..d6b32cb --- /dev/null +++ b/book/theme/head.hbs @@ -0,0 +1,7 @@ + + diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..2fbb018 --- /dev/null +++ b/build.rs @@ -0,0 +1,75 @@ +#![allow(unknown_lints)] +#![allow(unexpected_cfgs)] + +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + let manifest_dir_opt = env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from); + let manifest_dir = manifest_dir_opt.as_deref().unwrap_or(Path::new("")); + + cc::Build::new() + .file(manifest_dir.join("src/cxx.cc")) + .cpp(true) + .cpp_link_stdlib(None) // linked via link-cplusplus crate + .std(cxxbridge_flags::STD) + .warnings_into_errors(cfg!(deny_warnings)) + .compile("cxxbridge1"); + + println!("cargo:rerun-if-changed=src/cxx.cc"); + println!("cargo:rerun-if-changed=include/cxx.h"); + println!("cargo:rustc-cfg=built_with_cargo"); + + if let Some(manifest_dir) = &manifest_dir_opt { + let cxx_h = manifest_dir.join("include").join("cxx.h"); + println!("cargo:HEADER={}", cxx_h.to_string_lossy()); + } + + if let Some(rustc) = rustc_version() { + if rustc.minor >= 80 { + println!("cargo:rustc-check-cfg=cfg(built_with_cargo)"); + println!("cargo:rustc-check-cfg=cfg(compile_error_if_alloc)"); + println!("cargo:rustc-check-cfg=cfg(compile_error_if_std)"); + println!("cargo:rustc-check-cfg=cfg(cxx_experimental_no_alloc)"); + println!("cargo:rustc-check-cfg=cfg(error_in_core)"); + println!("cargo:rustc-check-cfg=cfg(seek_relative)"); + println!("cargo:rustc-check-cfg=cfg(skip_ui_tests)"); + } + + if rustc.minor < 73 { + println!("cargo:warning=The cxx crate requires a rustc version 1.73.0 or newer."); + println!( + "cargo:warning=You appear to be building with: {}", + rustc.version, + ); + } + + if rustc.minor >= 80 { + // std::io::Seek::seek_relative + println!("cargo:rustc-cfg=seek_relative"); + } + + if rustc.minor >= 81 { + // core::error::Error + println!("cargo:rustc-cfg=error_in_core"); + } + } +} + +struct RustVersion { + version: String, + minor: u32, +} + +fn rustc_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = String::from_utf8(output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + let minor = pieces.next()?.parse().ok()?; + Some(RustVersion { version, minor }) +} diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..c24e3b5 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1 @@ +-std=c++11 diff --git a/include/cxx.h b/include/cxx.h new file mode 100644 index 0000000..4e261a3 --- /dev/null +++ b/include/cxx.h @@ -0,0 +1,1149 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_WIN32) +#include +#else +#include +#endif + +#if __cplusplus >= 201703L +#include +#endif + +#if __cplusplus >= 202002L +#include +#endif + +namespace rust { +inline namespace cxxbridge1 { + +struct unsafe_bitcopy_t; + +namespace { +template +class impl; +} + +#ifndef CXXBRIDGE1_RUST_STRING +#define CXXBRIDGE1_RUST_STRING +// https://cxx.rs/binding/string.html +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + String(const char *, std::size_t); + String(const char16_t *); + String(const char16_t *, std::size_t); +#ifdef __cpp_char8_t + String(const char8_t *s); + String(const char8_t *s, std::size_t len); +#endif + + // Replace invalid Unicode data with the replacement character (U+FFFD). + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, std::size_t) noexcept; + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, std::size_t) noexcept; + + String &operator=(const String &) & noexcept; + String &operator=(String &&) & noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + std::size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; + + // Internal API only intended for the cxxbridge code generator. + String(unsafe_bitcopy_t, const String &) noexcept; + +private: + struct lossy_t; + String(lossy_t, const char *, std::size_t) noexcept; + String(lossy_t, const char16_t *, std::size_t) noexcept; + friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } + + // Size and alignment statically verified by rust_string.rs. + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_STRING + +#ifndef CXXBRIDGE1_RUST_STR +#define CXXBRIDGE1_RUST_STR +// https://cxx.rs/binding/str.html +class Str final { +public: + Str() noexcept; + Str(const String &) noexcept; + Str(const std::string &); + Str(const char *); + Str(const char *, std::size_t); + + Str &operator=(const Str &) & noexcept = default; + + explicit operator std::string() const; +#if __cplusplus >= 201703L + explicit operator std::string_view() const; +#endif + + // Note: no null terminator. + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + // Important in order for System V ABI to pass in registers. + Str(const Str &) noexcept = default; + ~Str() noexcept = default; + + using iterator = const char *; + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; + + void swap(Str &) noexcept; + +private: + class uninit; + Str(uninit) noexcept; + friend impl; + + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_STR + +#ifndef CXXBRIDGE1_RUST_SLICE +namespace detail { +template +struct copy_assignable_if {}; + +template <> +struct copy_assignable_if { + copy_assignable_if() noexcept = default; + copy_assignable_if(const copy_assignable_if &) noexcept = default; + copy_assignable_if &operator=(const copy_assignable_if &) & noexcept = delete; + copy_assignable_if &operator=(copy_assignable_if &&) & noexcept = default; +}; +} // namespace detail + +// https://cxx.rs/binding/slice.html +template +class Slice final + : private detail::copy_assignable_if::value> { +public: + using value_type = T; + + Slice() noexcept; + Slice(T *, std::size_t count) noexcept; + + template + explicit Slice(C &c) : Slice(c.data(), c.size()) {} + + Slice &operator=(const Slice &) & noexcept = default; + Slice &operator=(Slice &&) & noexcept = default; + + T *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + T &operator[](std::size_t n) const noexcept; + T &at(std::size_t n) const; + T &front() const noexcept; + T &back() const noexcept; + + // Important in order for System V ABI to pass in registers. + Slice(const Slice &) noexcept = default; + ~Slice() noexcept = default; + + class iterator; + iterator begin() const noexcept; + iterator end() const noexcept; + + void swap(Slice &) noexcept; + +private: + class uninit; + Slice(uninit) noexcept; + friend impl; + friend void sliceInit(void *, const void *, std::size_t) noexcept; + friend void *slicePtr(const void *) noexcept; + friend std::size_t sliceLen(const void *) noexcept; + + std::array repr; +}; + +#ifdef __cpp_deduction_guides +template +explicit Slice(C &c) + -> Slice().data())>>; +#endif // __cpp_deduction_guides + +template +class Slice::iterator final { +public: +#if __cplusplus >= 202002L + using iterator_category = std::contiguous_iterator_tag; +#else + using iterator_category = std::random_access_iterator_tag; +#endif + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = typename std::add_pointer::type; + using reference = typename std::add_lvalue_reference::type; + + reference operator*() const noexcept; + pointer operator->() const noexcept; + reference operator[](difference_type) const noexcept; + + iterator &operator++() noexcept; + iterator operator++(int) noexcept; + iterator &operator--() noexcept; + iterator operator--(int) noexcept; + + iterator &operator+=(difference_type) noexcept; + iterator &operator-=(difference_type) noexcept; + iterator operator+(difference_type) const noexcept; + friend inline iterator operator+(difference_type lhs, iterator rhs) noexcept { + return rhs + lhs; + } + iterator operator-(difference_type) const noexcept; + difference_type operator-(const iterator &) const noexcept; + + bool operator==(const iterator &) const noexcept; + bool operator!=(const iterator &) const noexcept; + bool operator<(const iterator &) const noexcept; + bool operator<=(const iterator &) const noexcept; + bool operator>(const iterator &) const noexcept; + bool operator>=(const iterator &) const noexcept; + +private: + friend class Slice; + void *pos; + std::size_t stride; +}; + +#if __cplusplus >= 202002L +static_assert(std::ranges::contiguous_range>); +static_assert(std::contiguous_iterator::iterator>); +#endif + +#endif // CXXBRIDGE1_RUST_SLICE + +#ifndef CXXBRIDGE1_RUST_BOX +// https://cxx.rs/binding/box.html +template +class Box final { +public: + using element_type = T; + using const_pointer = + typename std::add_pointer::type>::type; + using pointer = typename std::add_pointer::type; + + Box() = delete; + Box(Box &&) noexcept; + ~Box() noexcept; + + explicit Box(const T &); + explicit Box(T &&); + + Box &operator=(Box &&) & noexcept; + + const T *operator->() const noexcept; + const T &operator*() const noexcept; + T *operator->() noexcept; + T &operator*() noexcept; + + template + static Box in_place(Fields &&...); + + void swap(Box &) noexcept; + + // Important: requires that `raw` came from an into_raw call. Do not pass a + // pointer from `new` or any other source. + static Box from_raw(T *) noexcept; + + T *into_raw() noexcept; + + /* Deprecated */ using value_type = element_type; + +private: + class uninit; + class allocation; + Box(uninit) noexcept; + void drop() noexcept; + + friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); } + + T *ptr; +}; +#endif // CXXBRIDGE1_RUST_BOX + +#ifndef CXXBRIDGE1_RUST_VEC +// https://cxx.rs/binding/vec.html +template +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) & noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + // Internal API only intended for the cxxbridge code generator. + Vec(unsafe_bitcopy_t, const Vec &) noexcept; + +private: + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; + + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + + // Size and alignment statically verified by rust_vec.rs. + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_RUST_FN +// https://cxx.rs/binding/fn.html +template +class Fn; + +template +class Fn final { +public: + Ret operator()(Args... args) const noexcept; + Fn operator*() const noexcept; + +private: + Ret (*trampoline)(Args..., void *fn) noexcept; + void *fn; +}; +#endif // CXXBRIDGE1_RUST_FN + +#ifndef CXXBRIDGE1_RUST_ERROR +#define CXXBRIDGE1_RUST_ERROR +// https://cxx.rs/binding/result.html +class Error final : public std::exception { +public: + Error(const Error &); + Error(Error &&) noexcept; + ~Error() noexcept override; + + Error &operator=(const Error &) &; + Error &operator=(Error &&) & noexcept; + + const char *what() const noexcept override; + +private: + Error() noexcept = default; + friend impl; + const char *msg; + std::size_t len; +}; +#endif // CXXBRIDGE1_RUST_ERROR + +#ifndef CXXBRIDGE1_RUST_ISIZE +#define CXXBRIDGE1_RUST_ISIZE +#if defined(_WIN32) +using isize = SSIZE_T; +#else +using isize = ssize_t; +#endif +#endif // CXXBRIDGE1_RUST_ISIZE + +std::ostream &operator<<(std::ostream &, const String &); +std::ostream &operator<<(std::ostream &, const Str &); + +#ifndef CXXBRIDGE1_RUST_OPAQUE +#define CXXBRIDGE1_RUST_OPAQUE +// Base class of generated opaque Rust types. +class Opaque { +public: + Opaque() = delete; + Opaque(const Opaque &) = delete; + ~Opaque() = delete; +}; +#endif // CXXBRIDGE1_RUST_OPAQUE + +template +std::size_t size_of(); +template +std::size_t align_of(); + +// IsRelocatable is used in assertions that a C++ type passed by value +// between Rust and C++ is soundly relocatable by Rust. +// +// There may be legitimate reasons to opt out of the check for support of types +// that the programmer knows are soundly Rust-movable despite not being +// recognized as such by the C++ type system due to a move constructor or +// destructor. To opt out of the relocatability check, do either of the +// following things in any header used by `include!` in the bridge. +// +// --- if you define the type: +// struct MyType { +// ... +// + using IsRelocatable = std::true_type; +// }; +// +// --- otherwise: +// + template <> +// + struct rust::IsRelocatable : std::true_type {}; +template +struct IsRelocatable; + +using u8 = std::uint8_t; +using u16 = std::uint16_t; +using u32 = std::uint32_t; +using u64 = std::uint64_t; +using usize = std::size_t; // see static asserts in cxx.cc +using i8 = std::int8_t; +using i16 = std::int16_t; +using i32 = std::int32_t; +using i64 = std::int64_t; +using f32 = float; +using f64 = double; + +// Snake case aliases for use in code that uses this style for type names. +using string = String; +using str = Str; +template +using slice = Slice; +template +using box = Box; +template +using vec = Vec; +using error = Error; +template +using fn = Fn; +template +using is_relocatable = IsRelocatable; + + + +//////////////////////////////////////////////////////////////////////////////// +/// end public API, begin implementation details + +#ifndef CXXBRIDGE1_PANIC +#define CXXBRIDGE1_PANIC +template +void panic [[noreturn]] (const char *msg); +#endif // CXXBRIDGE1_PANIC + +#ifndef CXXBRIDGE1_RUST_FN +#define CXXBRIDGE1_RUST_FN +template +Ret Fn::operator()(Args... args) const noexcept { + return (*this->trampoline)(std::forward(args)..., this->fn); +} + +template +Fn Fn::operator*() const noexcept { + return *this; +} +#endif // CXXBRIDGE1_RUST_FN + +#ifndef CXXBRIDGE1_RUST_BITCOPY_T +#define CXXBRIDGE1_RUST_BITCOPY_T +struct unsafe_bitcopy_t final { + explicit unsafe_bitcopy_t() = default; +}; +#endif // CXXBRIDGE1_RUST_BITCOPY_T + +#ifndef CXXBRIDGE1_RUST_BITCOPY +#define CXXBRIDGE1_RUST_BITCOPY +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; +#endif // CXXBRIDGE1_RUST_BITCOPY + +#ifndef CXXBRIDGE1_RUST_SLICE +#define CXXBRIDGE1_RUST_SLICE +template +Slice::Slice() noexcept { + sliceInit(this, reinterpret_cast(align_of()), 0); +} + +template +Slice::Slice(T *s, std::size_t count) noexcept { + assert(s != nullptr || count == 0); + sliceInit(this, + s == nullptr && count == 0 + ? reinterpret_cast(align_of()) + : const_cast::type *>(s), + count); +} + +template +T *Slice::data() const noexcept { + return reinterpret_cast(slicePtr(this)); +} + +template +std::size_t Slice::size() const noexcept { + return sliceLen(this); +} + +template +std::size_t Slice::length() const noexcept { + return this->size(); +} + +template +bool Slice::empty() const noexcept { + return this->size() == 0; +} + +template +T &Slice::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto ptr = static_cast(slicePtr(this)) + size_of() * n; + return *reinterpret_cast(ptr); +} + +template +T &Slice::at(std::size_t n) const { + if (n >= this->size()) { + panic("rust::Slice index out of range"); + } + return (*this)[n]; +} + +template +T &Slice::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template +T &Slice::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template +typename Slice::iterator::reference +Slice::iterator::operator*() const noexcept { + return *static_cast(this->pos); +} + +template +typename Slice::iterator::pointer +Slice::iterator::operator->() const noexcept { + return static_cast(this->pos); +} + +template +typename Slice::iterator::reference Slice::iterator::operator[]( + typename Slice::iterator::difference_type n) const noexcept { + auto ptr = static_cast(this->pos) + this->stride * n; + return *reinterpret_cast(ptr); +} + +template +typename Slice::iterator &Slice::iterator::operator++() noexcept { + this->pos = static_cast(this->pos) + this->stride; + return *this; +} + +template +typename Slice::iterator Slice::iterator::operator++(int) noexcept { + auto ret = iterator(*this); + this->pos = static_cast(this->pos) + this->stride; + return ret; +} + +template +typename Slice::iterator &Slice::iterator::operator--() noexcept { + this->pos = static_cast(this->pos) - this->stride; + return *this; +} + +template +typename Slice::iterator Slice::iterator::operator--(int) noexcept { + auto ret = iterator(*this); + this->pos = static_cast(this->pos) - this->stride; + return ret; +} + +template +typename Slice::iterator &Slice::iterator::operator+=( + typename Slice::iterator::difference_type n) noexcept { + this->pos = static_cast(this->pos) + this->stride * n; + return *this; +} + +template +typename Slice::iterator &Slice::iterator::operator-=( + typename Slice::iterator::difference_type n) noexcept { + this->pos = static_cast(this->pos) - this->stride * n; + return *this; +} + +template +typename Slice::iterator Slice::iterator::operator+( + typename Slice::iterator::difference_type n) const noexcept { + auto ret = iterator(*this); + ret.pos = static_cast(this->pos) + this->stride * n; + return ret; +} + +template +typename Slice::iterator Slice::iterator::operator-( + typename Slice::iterator::difference_type n) const noexcept { + auto ret = iterator(*this); + ret.pos = static_cast(this->pos) - this->stride * n; + return ret; +} + +template +typename Slice::iterator::difference_type +Slice::iterator::operator-(const iterator &other) const noexcept { + auto diff = std::distance(static_cast(other.pos), + static_cast(this->pos)); + return diff / static_cast::iterator::difference_type>( + this->stride); +} + +template +bool Slice::iterator::operator==(const iterator &other) const noexcept { + return this->pos == other.pos; +} + +template +bool Slice::iterator::operator!=(const iterator &other) const noexcept { + return this->pos != other.pos; +} + +template +bool Slice::iterator::operator<(const iterator &other) const noexcept { + return this->pos < other.pos; +} + +template +bool Slice::iterator::operator<=(const iterator &other) const noexcept { + return this->pos <= other.pos; +} + +template +bool Slice::iterator::operator>(const iterator &other) const noexcept { + return this->pos > other.pos; +} + +template +bool Slice::iterator::operator>=(const iterator &other) const noexcept { + return this->pos >= other.pos; +} + +template +typename Slice::iterator Slice::begin() const noexcept { + iterator it; + it.pos = slicePtr(this); + it.stride = size_of(); + return it; +} + +template +typename Slice::iterator Slice::end() const noexcept { + iterator it = this->begin(); + it.pos = static_cast(it.pos) + it.stride * this->size(); + return it; +} + +template +void Slice::swap(Slice &rhs) noexcept { + std::swap(*this, rhs); +} +#endif // CXXBRIDGE1_RUST_SLICE + +#ifndef CXXBRIDGE1_RUST_BOX +#define CXXBRIDGE1_RUST_BOX +template +class Box::uninit {}; + +template +class Box::allocation { + static T *alloc() noexcept; + static void dealloc(T *) noexcept; + +public: + allocation() noexcept : ptr(alloc()) {} + ~allocation() noexcept { + if (this->ptr) { + dealloc(this->ptr); + } + } + T *ptr; +}; + +template +Box::Box(Box &&other) noexcept : ptr(other.ptr) { + other.ptr = nullptr; +} + +template +Box::Box(const T &val) { + allocation alloc; + ::new (alloc.ptr) T(val); + this->ptr = alloc.ptr; + alloc.ptr = nullptr; +} + +template +Box::Box(T &&val) { + allocation alloc; + ::new (alloc.ptr) T(std::move(val)); + this->ptr = alloc.ptr; + alloc.ptr = nullptr; +} + +template +Box::~Box() noexcept { + if (this->ptr) { + this->drop(); + } +} + +template +Box &Box::operator=(Box &&other) & noexcept { + if (this->ptr) { + this->drop(); + } + this->ptr = other.ptr; + other.ptr = nullptr; + return *this; +} + +template +const T *Box::operator->() const noexcept { + return this->ptr; +} + +template +const T &Box::operator*() const noexcept { + return *this->ptr; +} + +template +T *Box::operator->() noexcept { + return this->ptr; +} + +template +T &Box::operator*() noexcept { + return *this->ptr; +} + +template +template +Box Box::in_place(Fields &&...fields) { + allocation alloc; + auto ptr = alloc.ptr; + ::new (ptr) T{std::forward(fields)...}; + alloc.ptr = nullptr; + return from_raw(ptr); +} + +template +void Box::swap(Box &rhs) noexcept { + using std::swap; + swap(this->ptr, rhs.ptr); +} + +template +Box Box::from_raw(T *raw) noexcept { + Box box = uninit{}; + box.ptr = raw; + return box; +} + +template +T *Box::into_raw() noexcept { + T *raw = this->ptr; + this->ptr = nullptr; + return raw; +} + +template +Box::Box(uninit) noexcept {} +#endif // CXXBRIDGE1_RUST_BOX + +#ifndef CXXBRIDGE1_RUST_VEC +#define CXXBRIDGE1_RUST_VEC +template +Vec::Vec(std::initializer_list init) : Vec{} { + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); +} + +template +Vec::Vec(const Vec &other) : Vec() { + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); +} + +template +Vec::Vec(Vec &&other) noexcept : repr(other.repr) { + new (&other) Vec(); +} + +template +Vec::~Vec() noexcept { + this->drop(); +} + +template +Vec &Vec::operator=(Vec &&other) & noexcept { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; +} + +template +Vec &Vec::operator=(const Vec &other) & { + if (this != &other) { + this->drop(); + new (this) Vec(other); + } + return *this; +} + +template +bool Vec::empty() const noexcept { + return this->size() == 0; +} + +template +T *Vec::data() noexcept { + return const_cast(const_cast *>(this)->data()); +} + +template +const T &Vec::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); +} + +template +const T &Vec::at(std::size_t n) const { + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template +const T &Vec::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template +const T &Vec::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template +T &Vec::operator[](std::size_t n) noexcept { + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); +} + +template +T &Vec::at(std::size_t n) { + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template +T &Vec::front() noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template +T &Vec::back() noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template +void Vec::reserve(std::size_t new_cap) { + this->reserve_total(new_cap); +} + +template +void Vec::push_back(const T &value) { + this->emplace_back(value); +} + +template +void Vec::push_back(T &&value) { + this->emplace_back(std::move(value)); +} + +template +template +void Vec::emplace_back(Args &&...args) { + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast(reinterpret_cast(this->data()) + + size * size_of())) + T(std::forward(args)...); + this->set_len(size + 1); +} + +template +void Vec::clear() { + this->truncate(0); +} + +template +typename Vec::iterator Vec::begin() noexcept { + return Slice(this->data(), this->size()).begin(); +} + +template +typename Vec::iterator Vec::end() noexcept { + return Slice(this->data(), this->size()).end(); +} + +template +typename Vec::const_iterator Vec::begin() const noexcept { + return this->cbegin(); +} + +template +typename Vec::const_iterator Vec::end() const noexcept { + return this->cend(); +} + +template +typename Vec::const_iterator Vec::cbegin() const noexcept { + return Slice(this->data(), this->size()).begin(); +} + +template +typename Vec::const_iterator Vec::cend() const noexcept { + return Slice(this->data(), this->size()).end(); +} + +template +void Vec::swap(Vec &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +// Internal API only intended for the cxxbridge code generator. +template +Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_IS_COMPLETE +#define CXXBRIDGE1_IS_COMPLETE +namespace detail { +namespace { +template +struct is_complete : std::false_type {}; +template +struct is_complete : std::true_type {}; +} // namespace +} // namespace detail +#endif // CXXBRIDGE1_IS_COMPLETE + +#ifndef CXXBRIDGE1_LAYOUT +#define CXXBRIDGE1_LAYOUT +class layout { + template + friend std::size_t size_of(); + template + friend std::size_t align_of(); + template + static typename std::enable_if::value, + std::size_t>::type + do_size_of() { + return T::layout::size(); + } + template + static typename std::enable_if::value, + std::size_t>::type + do_size_of() { + return sizeof(T); + } + template + static + typename std::enable_if::value, std::size_t>::type + size_of() { + return do_size_of(); + } + template + static typename std::enable_if::value, + std::size_t>::type + do_align_of() { + return T::layout::align(); + } + template + static typename std::enable_if::value, + std::size_t>::type + do_align_of() { + return alignof(T); + } + template + static + typename std::enable_if::value, std::size_t>::type + align_of() { + return do_align_of(); + } +}; + +template +std::size_t size_of() { + return layout::size_of(); +} + +template +std::size_t align_of() { + return layout::align_of(); +} +#endif // CXXBRIDGE1_LAYOUT + +#ifndef CXXBRIDGE1_RELOCATABLE +#define CXXBRIDGE1_RELOCATABLE +namespace detail { +template +struct make_void { + using type = void; +}; + +template +using void_t = typename make_void::type; + +template class, typename...> +struct detect : std::false_type {}; +template