From 23d9546b5a38623110f328d94a2a70856c21d073 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 13 Oct 2025 14:39:37 -0500 Subject: [PATCH 1/2] test GitHub Actions workflow --- .github/workflows/webrtc-builds.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/webrtc-builds.yml b/.github/workflows/webrtc-builds.yml index b6c97b1f6..dce371b17 100644 --- a/.github/workflows/webrtc-builds.yml +++ b/.github/workflows/webrtc-builds.yml @@ -15,8 +15,8 @@ name: WebRTC builds on: push: - tags: - - "webrtc-*" +# tags: +# - "webrtc-*" workflow_dispatch: jobs: From 020f52acc371b43081783854cffe13219a9f42e0 Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 8 Oct 2025 16:54:31 -0500 Subject: [PATCH 2/2] build_linux.sh: add option to choose toolchain This adds a --toolchain CLI option to the script that can be either * gnu * llvm * chromium-llvm The gnu and llvm options use the system's toolchain. chromium-llvm uses Chromium's prebuilt LLVM toolchain. Previously, the script tried to combine the system's gcc with libstdc++ from Chromium's Debian sysroot, which failed to find glibc headers: [1/4184] CXX obj/third_party/abseil-cpp/absl/base/base/cycleclock.o FAILED: obj/third_party/abseil-cpp/absl/base/base/cycleclock.o g++ -MMD -MF obj/third_party/abseil-cpp/absl/base/base/cycleclock.o.d -DUSE_UDEV -DUSE_AURA=1 -DUSE_GLIB=1 -DUSE_OZONE=1 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -D_GLIBCXX_ASSERTIONS=1 -DCR_SYSROOT_KEY=20250129T203412Z-1 -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -DABSL_ALLOCATOR_NOTHROW=1 -I.. -Igen -I../third_party/abseil-cpp -fno-strict-overflow -fno-ident -fno-strict-aliasing -fstack-protector -funwind-tables -fPIC -pipe -pthread -m64 -msse3 -Wno-builtin-macro-redefined -D__DATE__= -D__TIME__= -D__TIMESTAMP__= -O2 -fdata-sections -ffunction-sections -fno-math-errno -fno-omit-frame-pointer -g0 -fvisibility=hidden -Wno-unused-local-typedefs -Wno-maybe-uninitialized -Wno-deprecated-declarations -Wno-comments -Wno-packed-not-aligned -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi -std=gnu++2a -Wno-changes-meaning -fno-exceptions --sysroot=../build/linux/debian_bullseye_amd64-sysroot -fvisibility-inlines-hidden -Wno-narrowing -Wno-class-memaccess -Wno-invalid-offsetof -c ../third_party/abseil-cpp/absl/base/internal/cycleclock.cc -o obj/third_party/abseil-cpp/absl/base/base/cycleclock.o In file included from /usr/include/c++/15/bits/version.h:51, from /usr/include/c++/15/atomic:50, from ../third_party/abseil-cpp/absl/base/internal/cycleclock.h:45, from ../third_party/abseil-cpp/absl/base/internal/cycleclock.cc:23: /usr/include/c++/15/x86_64-redhat-linux/bits/c++config.h:3:10: fatal error: bits/wordsize.h: No such file or directory 3 | #include | ^~~~~~~~~~~~~~~~~ --- .github/workflows/webrtc-builds.yml | 34 +- webrtc-sys/Cargo.toml | 7 +- webrtc-sys/build.rs | 17 + webrtc-sys/libwebrtc/build_linux.sh | 104 +++- webrtc-sys/src/cxx.cc | 870 ++++++++++++++++++++++++++++ 5 files changed, 973 insertions(+), 59 deletions(-) create mode 100644 webrtc-sys/src/cxx.cc diff --git a/.github/workflows/webrtc-builds.yml b/.github/workflows/webrtc-builds.yml index dce371b17..bb1db314c 100644 --- a/.github/workflows/webrtc-builds.yml +++ b/.github/workflows/webrtc-builds.yml @@ -47,12 +47,12 @@ jobs: - name: linux os: ubuntu-latest - cmd: ./build_linux.sh + cmd: ./build_linux.sh --toolchain chromium-llvm arch: x64 - name: linux os: ubuntu-latest - cmd: ./build_linux.sh + cmd: ./build_linux.sh --toolchain chromium-llvm arch: arm64 - name: android @@ -110,38 +110,18 @@ jobs: - name: install setuptools (none-macOS) if: ${{ matrix.target.os != 'macos-latest' }} run: | - pip3 install setuptools # pkg_resources is sometimes not found? + pip3 install setuptools # pkg_resources is sometimes not found? - - name: Add GCC PPA and install GCC 14 - if: ${{ matrix.target.os == 'ubuntu-latest' }} - run: | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt install gcc-14 g++-14 g++-14-aarch64-linux-gnu -y - - - name: Verify GCC 14 installation - if: ${{ matrix.target.os == 'ubuntu-latest' }} - run: | - gcc-14 --version - g++-14 --version - aarch64-linux-gnu-g++-14 --version - - - name: Set GCC 14 as default (optional) - if: ${{ matrix.target.os == 'ubuntu-latest' }} - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 140 --slave /usr/bin/g++ g++ /usr/bin/g++-14 - sudo update-alternatives --config gcc - gcc --version - sudo update-alternatives --install /usr/bin/aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc /usr/bin/aarch64-linux-gnu-gcc-14 140 --slave /usr/bin/aarch64-linux-gnu-g++ aarch64-linux-gnu-g++ /usr/bin/aarch64-linux-gnu-g++-14 - sudo update-alternatives --config aarch64-linux-gnu-gcc - aarch64-linux-gnu-gcc --version - - name: Install linux dependencies if: ${{ matrix.target.os == 'ubuntu-latest' }} run: | sudo apt update -y sudo apt install -y ninja-build pkg-config openjdk-11-jdk + - name: Disable __GLIBC_USE_ISOC2X macro + if: ${{ matrix.target.name == 'linux' && matrix.target.arch == 'arm64' }} + run: sudo sed -i 's/__GLIBC_USE_ISOC2X[[:space:]]*1/__GLIBC_USE_ISOC2X\t0/' /usr/include/features.h + - name: Install macos dependencies if: ${{ matrix.target.os == 'macos-latest' }} run: brew install ninja diff --git a/webrtc-sys/Cargo.toml b/webrtc-sys/Cargo.toml index f34c155ab..1d23d99a7 100644 --- a/webrtc-sys/Cargo.toml +++ b/webrtc-sys/Cargo.toml @@ -13,12 +13,15 @@ use_vaapi = [] use_nvidia = [] [dependencies] -cxx = "1.0" +# Copy an updated version of the cxx.cc file from cxx's repository +# into the src directory when updating to a new version of cxx +# and change the include path for cxx.h to "rust/cxx.h" +cxx = "=1.0.186" log = "0.4" [build-dependencies] webrtc-sys-build = { workspace = true } -cxx-build = "1.0" +cxx-build = "=1.0.186" glob = "0.3" cc = "1.0" diff --git a/webrtc-sys/build.rs b/webrtc-sys/build.rs index 84fa8588d..1ef2bce8b 100644 --- a/webrtc-sys/build.rs +++ b/webrtc-sys/build.rs @@ -148,6 +148,23 @@ fn main() { .flag("/EHsc"); } "linux" => { + // If libwebrtc was built with Chromium's libc++, the C++ in this crate needs to be built with it too. + let buildtools = webrtc_include.join("buildtools/third_party/libc++"); + if buildtools.exists() { + // Chromium's libc++ doesn't build with GCC + if env::var("CC").is_err() { + builder.compiler("clang++"); + } + builder.include(buildtools); + builder.flag("-nostdinc++"); + let webrtc_include = webrtc_include.to_string_lossy(); + builder.flag(format!("-isystem{webrtc_include}/third_party/libc++/src/include")); + builder.flag(format!("-isystem{webrtc_include}/third_party/libc++abi/src/include")); + // The cxx crate builds this C++ file. However, this crate needs to rebuild it when using a + // different C++ standard library or linking will fail with unresolved symbol errors. + builder.file("src/cxx.cc"); + } + println!("cargo:rustc-link-lib=dylib=rt"); println!("cargo:rustc-link-lib=dylib=dl"); println!("cargo:rustc-link-lib=dylib=pthread"); diff --git a/webrtc-sys/libwebrtc/build_linux.sh b/webrtc-sys/libwebrtc/build_linux.sh index a1d84fe1f..92f5262ed 100755 --- a/webrtc-sys/libwebrtc/build_linux.sh +++ b/webrtc-sys/libwebrtc/build_linux.sh @@ -16,6 +16,7 @@ arch="" profile="release" +toolchain="gnu" while [ "$#" -gt 0 ]; do case "$1" in @@ -35,6 +36,14 @@ while [ "$#" -gt 0 ]; do fi shift 2 ;; + --toolchain) + toolchain="$2" + if [ "$toolchain" != "gnu" ] && [ "$toolchain" != "llvm" ] && [ "$toolchain" != "chromium-llvm" ]; then + echo "Error: Invalid value for --toolchain. Must be 'gnu', 'llvm', or 'chromium-llvm' (Chromium's bundled Clang with Debian sysroot)" + exit 1 + fi + shift 2 + ;; *) echo "Error: Unknown argument '$1'" exit 1 @@ -50,46 +59,70 @@ fi echo "Building LiveKit WebRTC - Linux" echo "Arch: $arch" echo "Profile: $profile" +echo "Toolchain: $toolchain" + +export COMMAND_DIR=$(cd $(dirname $0); pwd) +export OUTPUT_DIR="$(pwd)/build-$arch-$profile" +export ARTIFACTS_DIR="$(pwd)/linux-$arch-$profile" + +if [ "$toolchain" == "gnu" ]; then + [ -n "$CC" ] || export CC="$(which gcc)" + [ -n "$CXX" ] || export CXX="$(which g++)" + [ -n "$AR" ] || export AR="$(which ar)" + [ -n "$NM" ] || export NM="$(which nm)" + export CXXFLAGS="${CXXFLAGS} -Wno-changes-meaning -Wno-unknown-pragmas -D_DEFAULT_SOURCE" + OBJCOPY="$(which objcopy)" + chromium_libcxx=false + toolchain_gn_args="is_clang=false \ + custom_toolchain=\"//build/toolchain/linux/unbundle:default\" \ + host_toolchain=\"//build/toolchain/linux/unbundle:default\"" +elif [ "$toolchain" == "llvm" ]; then + [ -n "$CC" ] || export CC="$(which clang)" + [ -n "$CXX" ] || export CXX="$(which clang++)" + [ -n "$AR" ] || export AR="$(which llvm-ar)" + [ -n "$NM" ] || export NM="$(which llvm-nm)" + OBJCOPY="$(which llvm-objcopy)" + # Using system libc++ stumbles over + # https://github.com/llvm/llvm-project/issues/50248 + # so use Chromium's libc++ + chromium_libcxx=true + toolchain_gn_args="is_clang=true \ + clang_use_chrome_plugins=false \ + custom_toolchain=\"//build/toolchain/linux/unbundle:default\" \ + host_toolchain=\"//build/toolchain/linux/unbundle:default\"" +elif [ "$toolchain" == "chromium-llvm" ]; then + AR="$COMMAND_DIR/src/third_party/llvm-build/Release+Asserts/bin/llvm-ar" + OBJCOPY="$COMMAND_DIR/src/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy" + chromium_libcxx=true + toolchain_gn_args="is_clang=true use_custom_libcxx=true" +fi + +set -x if [ ! -e "$(pwd)/depot_tools" ] then git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git fi -export COMMAND_DIR=$(cd $(dirname $0); pwd) +# must be done after runing `which` to find toolchain's executables above export PATH="$(pwd)/depot_tools:$PATH" -export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile" -export ARTIFACTS_DIR="$(pwd)/linux-$arch-$profile" -if [ ! -e "$(pwd)/src" ] -then - gclient sync -D --no-history +if [ ! -e "$(pwd)/src" ]; then + # use --nohooks to avoid the download_from_google_storage hook that takes > 6 minutes + # then manually run the other hooks + gclient sync -D --no-history --nohooks + python3 src/tools/rust/update_rust.py + if [ "$toolchain" == "chromium-llvm" ] || [ "$toolchain" == "llvm" ]; then + python3 src/tools/clang/scripts/update.py + fi fi cd src git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn - -cd build - -git apply "$COMMAND_DIR/patches/force_gcc.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn - -cd .. - -cd third_party - git apply "$COMMAND_DIR/patches/david_disable_gun_source_macro.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn - -cd ../.. - -mkdir -p "$ARTIFACTS_DIR/lib" - -python3 "./src/build/linux/sysroot_scripts/install-sysroot.py" --arch="$arch" - -if [ "$arch" = "arm64" ]; then - sudo sed -i 's/__GLIBC_USE_ISOC2X[[:space:]]*1/__GLIBC_USE_ISOC2X\t0/' /usr/aarch64-linux-gnu/include/features.h -fi +cd .. debug="false" if [ "$profile" = "debug" ]; then @@ -101,10 +134,11 @@ args="is_debug=$debug \ target_cpu=\"$arch\" \ rtc_enable_protobuf=false \ treat_warnings_as_errors=false \ - use_custom_libcxx=false \ + use_custom_libcxx=${chromium_libcxx} use_llvm_libatomic=false \ use_libcxx_modules=false \ use_custom_libcxx_for_host=false \ + use_sysroot=false \ rtc_include_tests=false \ rtc_build_tools=false \ rtc_build_examples=false \ @@ -119,8 +153,10 @@ args="is_debug=$debug \ symbol_level=0 \ enable_iterator_debugging=false \ use_rtti=true \ - is_clang=false \ - rtc_use_x11=false" + rtc_use_x11=false \ + $toolchain_gn_args" + +set -e # generate ninja files gn gen "$OUTPUT_DIR" --root="src" --args="${args}" @@ -128,10 +164,12 @@ gn gen "$OUTPUT_DIR" --root="src" --args="${args}" # build static library ninja -C "$OUTPUT_DIR" :default +mkdir -p "$ARTIFACTS_DIR/lib" + # make libwebrtc.a # don't include nasm -ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` -objcopy --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a" +"$AR" -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` +"$OBJCOPY" --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a" python3 "./src/tools_webrtc/libs/generate_licenses.py" \ --target :default "$OUTPUT_DIR" "$OUTPUT_DIR" @@ -141,5 +179,11 @@ cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" cd src +if [ $chromium_libcxx == "true" ]; then + mkdir -p "$ARTIFACTS_DIR/include/buildtools/third_party" + cp -R buildtools/third_party/libc++ "$ARTIFACTS_DIR/include/buildtools/third_party" + mkdir -p "$ARTIFACTS_DIR/include/third_party/libc++/src" + cp -R third_party/libc++/src/include "$ARTIFACTS_DIR/include/third_party/libc++/src" +fi find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include" find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include" diff --git a/webrtc-sys/src/cxx.cc b/webrtc-sys/src/cxx.cc new file mode 100644 index 000000000..951c91078 --- /dev/null +++ b/webrtc-sys/src/cxx.cc @@ -0,0 +1,870 @@ +// This file was copied from src/cxx.cc in cxx's repository, which is licensed MIT OR Apache-2.0. +// Only the include path for cxx.h has been modified. +#include "rust/cxx.h" +#include +#include +#include +#include + +#ifdef __cpp_lib_bit_cast +#include +#endif + +// Most compilers set __cpp_attributes on C++11 and up, and set __cpp_exceptions +// if the flag `-fno-exceptions` is not set. On these compilers we detect +// `-fno-exceptions` this way. +// +// Some compilers never set either one. On these, rely on the user to do +// `-DRUST_CXX_NO_EXCEPTIONS` if they are not using exceptions. +// +// On MSVC, it is possible for exception throwing and catching to be enabled +// without __cpp_exceptions being defined, so do not try to detect anything. +#if !defined(RUST_CXX_NO_EXCEPTIONS) && defined(__cpp_attributes) && \ + !defined(__cpp_exceptions) && (!defined(_MSC_VER) || defined(__llvm__)) +#define RUST_CXX_NO_EXCEPTIONS +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#endif + +extern "C" { +void cxxbridge1$cxx_string$init(std::string *s, const std::uint8_t *ptr, + std::size_t len) noexcept { + new (s) std::string(reinterpret_cast(ptr), len); +} + +void cxxbridge1$cxx_string$destroy(std::string *s) noexcept { + using std::string; + s->~string(); +} + +const char *cxxbridge1$cxx_string$data(const std::string &s) noexcept { + return s.data(); +} + +std::size_t cxxbridge1$cxx_string$length(const std::string &s) noexcept { + return s.length(); +} + +void cxxbridge1$cxx_string$clear(std::string &s) noexcept { s.clear(); } + +void cxxbridge1$cxx_string$reserve_total(std::string &s, + size_t new_cap) noexcept { + s.reserve(new_cap); +} + +void cxxbridge1$cxx_string$push(std::string &s, const std::uint8_t *ptr, + std::size_t len) noexcept { + s.append(reinterpret_cast(ptr), len); +} + +// rust::String +void cxxbridge1$string$new(rust::String *self) noexcept; +void cxxbridge1$string$clone(rust::String *self, + const rust::String &other) noexcept; +bool cxxbridge1$string$from_utf8(rust::String *self, const char *ptr, + std::size_t len) noexcept; +void cxxbridge1$string$from_utf8_lossy(rust::String *self, const char *ptr, + std::size_t len) noexcept; +bool cxxbridge1$string$from_utf16(rust::String *self, const char16_t *ptr, + std::size_t len) noexcept; +void cxxbridge1$string$from_utf16_lossy(rust::String *self, const char16_t *ptr, + std::size_t len) noexcept; +void cxxbridge1$string$drop(rust::String *self) noexcept; +const char *cxxbridge1$string$ptr(const rust::String *self) noexcept; +std::size_t cxxbridge1$string$len(const rust::String *self) noexcept; +std::size_t cxxbridge1$string$capacity(const rust::String *self) noexcept; +void cxxbridge1$string$reserve_additional(rust::String *self, + size_t additional) noexcept; +void cxxbridge1$string$reserve_total(rust::String *self, + size_t new_cap) noexcept; + +// rust::Str +void cxxbridge1$str$new(rust::Str *self) noexcept; +void cxxbridge1$str$ref(rust::Str *self, const rust::String *string) noexcept; +bool cxxbridge1$str$from(rust::Str *self, const char *ptr, + std::size_t len) noexcept; +const char *cxxbridge1$str$ptr(const rust::Str *self) noexcept; +std::size_t cxxbridge1$str$len(const rust::Str *self) noexcept; + +// rust::Slice +void cxxbridge1$slice$new(void *self, const void *ptr, + std::size_t len) noexcept; +void *cxxbridge1$slice$ptr(const void *self) noexcept; +std::size_t cxxbridge1$slice$len(const void *self) noexcept; +} // extern "C" + +namespace rust { +inline namespace cxxbridge1 { + +template +void panic [[noreturn]] (const char *msg) { +#if defined(RUST_CXX_NO_EXCEPTIONS) + std::fprintf(stderr, "Error: %s. Aborting.\n", msg); + std::abort(); +#else + throw Exception(msg); +#endif +} + +template void panic [[noreturn]] (const char *msg); + +template +static bool is_aligned(const void *ptr) noexcept { + auto iptr = reinterpret_cast(ptr); + return !(iptr % alignof(T)); +} + +String::String() noexcept { cxxbridge1$string$new(this); } + +String::String(const String &other) noexcept { + cxxbridge1$string$clone(this, other); +} + +String::String(String &&other) noexcept : repr(other.repr) { + cxxbridge1$string$new(&other); +} + +String::~String() noexcept { cxxbridge1$string$drop(this); } + +static void initString(String *self, const char *s, std::size_t len) { + if (!cxxbridge1$string$from_utf8(self, s, len)) { + panic("data for rust::String is not utf-8"); + } +} + +static void initString(String *self, const char16_t *s, std::size_t len) { + if (!cxxbridge1$string$from_utf16(self, s, len)) { + panic("data for rust::String is not utf-16"); + } +} + +String::String(const std::string &s) { initString(this, s.data(), s.length()); } + +String::String(const char *s) { + assert(s != nullptr); + initString(this, s, std::strlen(s)); +} + +String::String(const char *s, std::size_t len) { + assert(s != nullptr || len == 0); + initString(this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); +} + +#ifdef __cpp_char8_t +String::String(const char8_t *s) : String(reinterpret_cast(s)) {} + +String::String(const char8_t *s, std::size_t len) + : String(reinterpret_cast(s), len) {} +#endif + +String::String(const char16_t *s) { + assert(s != nullptr); + assert(is_aligned(s)); + initString(this, s, std::char_traits::length(s)); +} + +String::String(const char16_t *s, std::size_t len) { + assert(s != nullptr || len == 0); + assert(is_aligned(s)); + initString(this, + s == nullptr && len == 0 ? reinterpret_cast(2) + : s, + len); +} + +struct String::lossy_t {}; + +String::String(lossy_t, const char *s, std::size_t len) noexcept { + cxxbridge1$string$from_utf8_lossy( + this, s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); +} + +String::String(lossy_t, const char16_t *s, std::size_t len) noexcept { + cxxbridge1$string$from_utf16_lossy( + this, + s == nullptr && len == 0 ? reinterpret_cast(2) : s, + len); +} + +String String::lossy(const std::string &s) noexcept { + return String::lossy(s.data(), s.length()); +} + +String String::lossy(const char *s) noexcept { + assert(s != nullptr); + return String::lossy(s, std::strlen(s)); +} + +String String::lossy(const char *s, std::size_t len) noexcept { + assert(s != nullptr || len == 0); + return String(lossy_t{}, s, len); +} + +String String::lossy(const char16_t *s) noexcept { + assert(s != nullptr); + assert(is_aligned(s)); + return String(lossy_t{}, s, std::char_traits::length(s)); +} + +String String::lossy(const char16_t *s, std::size_t len) noexcept { + assert(s != nullptr || len == 0); + assert(is_aligned(s)); + return String(lossy_t{}, s, len); +} + +String &String::operator=(const String &other) & noexcept { + if (this != &other) { + cxxbridge1$string$drop(this); + cxxbridge1$string$clone(this, other); + } + return *this; +} + +String &String::operator=(String &&other) & noexcept { + cxxbridge1$string$drop(this); + this->repr = other.repr; + cxxbridge1$string$new(&other); + return *this; +} + +String::operator std::string() const { + return std::string(this->data(), this->size()); +} + +const char *String::data() const noexcept { + return cxxbridge1$string$ptr(this); +} + +std::size_t String::size() const noexcept { + return cxxbridge1$string$len(this); +} + +std::size_t String::length() const noexcept { + return cxxbridge1$string$len(this); +} + +bool String::empty() const noexcept { return this->size() == 0; } + +const char *String::c_str() noexcept { + auto len = this->length(); + cxxbridge1$string$reserve_additional(this, 1); + auto ptr = this->data(); + const_cast(ptr)[len] = '\0'; + return ptr; +} + +std::size_t String::capacity() const noexcept { + return cxxbridge1$string$capacity(this); +} + +void String::reserve(std::size_t new_cap) noexcept { + cxxbridge1$string$reserve_total(this, new_cap); +} + +String::iterator String::begin() noexcept { + return const_cast(this->data()); +} + +String::iterator String::end() noexcept { + return const_cast(this->data()) + this->size(); +} + +String::const_iterator String::begin() const noexcept { return this->cbegin(); } + +String::const_iterator String::end() const noexcept { return this->cend(); } + +String::const_iterator String::cbegin() const noexcept { return this->data(); } + +String::const_iterator String::cend() const noexcept { + return this->data() + this->size(); +} + +bool String::operator==(const String &rhs) const noexcept { + return rust::Str(*this) == rust::Str(rhs); +} + +bool String::operator!=(const String &rhs) const noexcept { + return rust::Str(*this) != rust::Str(rhs); +} + +bool String::operator<(const String &rhs) const noexcept { + return rust::Str(*this) < rust::Str(rhs); +} + +bool String::operator<=(const String &rhs) const noexcept { + return rust::Str(*this) <= rust::Str(rhs); +} + +bool String::operator>(const String &rhs) const noexcept { + return rust::Str(*this) > rust::Str(rhs); +} + +bool String::operator>=(const String &rhs) const noexcept { + return rust::Str(*this) >= rust::Str(rhs); +} + +void String::swap(String &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +String::String(unsafe_bitcopy_t, const String &bits) noexcept + : repr(bits.repr) {} + +std::ostream &operator<<(std::ostream &os, const String &s) { + os.write(s.data(), static_cast(s.size())); + return os; +} + +Str::Str() noexcept { cxxbridge1$str$new(this); } + +Str::Str(const String &s) noexcept { cxxbridge1$str$ref(this, &s); } + +static void initStr(Str *self, const char *ptr, std::size_t len) { + if (!cxxbridge1$str$from(self, ptr, len)) { + panic("data for rust::Str is not utf-8"); + } +} + +Str::Str(const std::string &s) { initStr(this, s.data(), s.length()); } + +Str::Str(const char *s) { + assert(s != nullptr); + initStr(this, s, std::strlen(s)); +} + +Str::Str(const char *s, std::size_t len) { + assert(s != nullptr || len == 0); + initStr(this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); +} + +Str::operator std::string() const { + return std::string(this->data(), this->size()); +} + +#if __cplusplus >= 201703L +Str::operator std::string_view() const { + return std::string_view(this->data(), this->size()); +} +#endif + +const char *Str::data() const noexcept { return cxxbridge1$str$ptr(this); } + +std::size_t Str::size() const noexcept { return cxxbridge1$str$len(this); } + +std::size_t Str::length() const noexcept { return this->size(); } + +bool Str::empty() const noexcept { return this->size() == 0; } + +Str::const_iterator Str::begin() const noexcept { return this->cbegin(); } + +Str::const_iterator Str::end() const noexcept { return this->cend(); } + +Str::const_iterator Str::cbegin() const noexcept { return this->data(); } + +Str::const_iterator Str::cend() const noexcept { + return this->data() + this->size(); +} + +bool Str::operator==(const Str &rhs) const noexcept { + return this->size() == rhs.size() && + std::equal(this->begin(), this->end(), rhs.begin()); +} + +bool Str::operator!=(const Str &rhs) const noexcept { return !(*this == rhs); } + +bool Str::operator<(const Str &rhs) const noexcept { + return std::lexicographical_compare(this->begin(), this->end(), rhs.begin(), + rhs.end()); +} + +bool Str::operator<=(const Str &rhs) const noexcept { + // std::mismatch(this->begin(), this->end(), rhs.begin(), rhs.end()), except + // without Undefined Behavior on C++11 if rhs is shorter than *this. + const_iterator liter = this->begin(), lend = this->end(), riter = rhs.begin(), + rend = rhs.end(); + while (liter != lend && riter != rend && *liter == *riter) { + ++liter; + ++riter; + } + if (liter == lend) { + return true; // equal or *this is a prefix of rhs + } else if (riter == rend) { + return false; // rhs is a prefix of *this + } else { + return *liter <= *riter; + } +} + +bool Str::operator>(const Str &rhs) const noexcept { return rhs < *this; } + +bool Str::operator>=(const Str &rhs) const noexcept { return rhs <= *this; } + +void Str::swap(Str &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +std::ostream &operator<<(std::ostream &os, const Str &s) { + os.write(s.data(), static_cast(s.size())); + return os; +} + +void sliceInit(void *self, const void *ptr, std::size_t len) noexcept { + cxxbridge1$slice$new(self, ptr, len); +} + +void *slicePtr(const void *self) noexcept { return cxxbridge1$slice$ptr(self); } + +std::size_t sliceLen(const void *self) noexcept { + return cxxbridge1$slice$len(self); +} + +// Rust specifies that usize is ABI compatible with C's uintptr_t. +// https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize +// However there is no direct Rust equivalent for size_t. C does not guarantee +// that size_t and uintptr_t are compatible. In practice though, on all +// platforms supported by Rust, they are identical for ABI purposes. See the +// libc crate which unconditionally defines libc::size_t = usize. We expect the +// same here and these assertions are just here to explicitly document that. +// *Note that no assumption is made about C++ name mangling of signatures +// containing these types, not here nor anywhere in CXX.* +static_assert(sizeof(std::size_t) == sizeof(std::uintptr_t), + "unsupported size_t size"); +static_assert(alignof(std::size_t) == alignof(std::uintptr_t), + "unsupported size_t alignment"); +static_assert(sizeof(rust::isize) == sizeof(std::intptr_t), + "unsupported ssize_t size"); +static_assert(alignof(rust::isize) == alignof(std::intptr_t), + "unsupported ssize_t alignment"); + +// The C++ standard does not guarantee a particular size, alignment, or bit +// pattern for bool. In practice on all platforms supported by Rust, it is +// compatible with Rust's bool. The libc crate freely uses Rust bool in +// foreign function signatures. +static_assert(sizeof(bool) == 1, "unsupported bool size"); +static_assert(alignof(bool) == 1, "unsupported bool alignment"); +#ifdef __cpp_lib_bit_cast +static_assert(std::bit_cast(false) == 0, + "unsupported bit representation of false"); +static_assert(std::bit_cast(true) == 1, + "unsupported bit representation of true"); +#endif + +static_assert(std::is_trivially_copy_constructible::value, + "trivial Str(const Str &)"); +static_assert(std::is_trivially_copy_assignable::value, + "trivial operator=(const Str &)"); +static_assert(std::is_trivially_destructible::value, "trivial ~Str()"); + +static_assert( + std::is_trivially_copy_constructible>::value, + "trivial Slice(const Slice &)"); +static_assert( + std::is_trivially_move_constructible>::value, + "trivial Slice(Slice &&)"); +static_assert( + std::is_trivially_copy_assignable>::value, + "trivial Slice::operator=(const Slice &) for const slices"); +static_assert( + std::is_trivially_move_assignable>::value, + "trivial Slice::operator=(Slice &&)"); +static_assert(std::is_trivially_destructible>::value, + "trivial ~Slice()"); + +static_assert(std::is_trivially_copy_constructible>::value, + "trivial Slice(const Slice &)"); +static_assert(std::is_trivially_move_constructible>::value, + "trivial Slice(Slice &&)"); +static_assert(!std::is_copy_assignable>::value, + "delete Slice::operator=(const Slice &) for mut slices"); +static_assert(std::is_trivially_move_assignable>::value, + "trivial Slice::operator=(Slice &&)"); +static_assert(std::is_trivially_destructible>::value, + "trivial ~Slice()"); + +static_assert(std::is_same::const_iterator, + Vec::iterator>::value, + "Vec::const_iterator == Vec::iterator"); +static_assert(std::is_same::const_iterator, + Vec::iterator>::value, + "Vec::const_iterator == Vec::iterator"); +static_assert(!std::is_same::const_iterator, + Vec::iterator>::value, + "Vec::const_iterator != Vec::iterator"); + +static const char *errorCopy(const char *ptr, std::size_t len) { + char *copy = new char[len + 1]; + std::memcpy(copy, ptr, len); + copy[len] = '\0'; + return copy; +} + +extern "C" { +const char *cxxbridge1$error(const char *ptr, std::size_t len) noexcept { + return errorCopy(ptr, len); +} +} // extern "C" + +Error::Error(const Error &other) + : std::exception(other), + msg(other.msg ? errorCopy(other.msg, other.len) : nullptr), + len(other.len) {} + +Error::Error(Error &&other) noexcept + : std::exception(std::move(other)), msg(other.msg), len(other.len) { + other.msg = nullptr; + other.len = 0; +} + +Error::~Error() noexcept { delete[] this->msg; } + +Error &Error::operator=(const Error &other) & { + if (this != &other) { + std::exception::operator=(other); + delete[] this->msg; + this->msg = nullptr; + if (other.msg) { + this->msg = errorCopy(other.msg, other.len); + this->len = other.len; + } + } + return *this; +} + +Error &Error::operator=(Error &&other) & noexcept { + std::exception::operator=(std::move(other)); + delete[] this->msg; + this->msg = other.msg; + this->len = other.len; + other.msg = nullptr; + other.len = 0; + return *this; +} + +const char *Error::what() const noexcept { return this->msg; } + +namespace { +template +union MaybeUninit { + T value; + MaybeUninit() {} + ~MaybeUninit() {} +}; +} // namespace + +namespace repr { +struct PtrLen final { + void *ptr; + std::size_t len; +}; +} // namespace repr + +extern "C" { +repr::PtrLen cxxbridge1$exception(const char *, std::size_t len) noexcept; +} + +namespace detail { +// On some platforms size_t is the same C++ type as one of the sized integer +// types; on others it is a distinct type. Only in the latter case do we need to +// define a specialized impl of rust::Vec, because in the former case it +// would collide with one of the other specializations. +using usize_if_unique = + typename std::conditional::value || + std::is_same::value, + struct usize_ignore, size_t>::type; +using isize_if_unique = + typename std::conditional::value || + std::is_same::value, + struct isize_ignore, rust::isize>::type; +// Similarly, on some platforms char may just be an alias for [u]int8_t. +using char_if_unique = + typename std::conditional::value || + std::is_same::value, + struct char_ignore, char>::type; + +class Fail final { + repr::PtrLen &throw$; + +public: + Fail(repr::PtrLen &throw$) noexcept : throw$(throw$) {} + void operator()(const char *) noexcept; + void operator()(const std::string &) noexcept; +}; + +void Fail::operator()(const char *catch$) noexcept { + throw$ = cxxbridge1$exception(catch$, std::strlen(catch$)); +} + +void Fail::operator()(const std::string &catch$) noexcept { + throw$ = cxxbridge1$exception(catch$.data(), catch$.length()); +} +} // namespace detail + +} // namespace cxxbridge1 +} // namespace rust + +namespace { +template +void destroy(T *ptr) { + ptr->~T(); +} +} // namespace + +extern "C" { +void cxxbridge1$unique_ptr$std$string$null( + std::unique_ptr *ptr) noexcept { + new (ptr) std::unique_ptr(); +} +void cxxbridge1$unique_ptr$std$string$raw(std::unique_ptr *ptr, + std::string *raw) noexcept { + new (ptr) std::unique_ptr(raw); +} +const std::string *cxxbridge1$unique_ptr$std$string$get( + const std::unique_ptr &ptr) noexcept { + return ptr.get(); +} +std::string *cxxbridge1$unique_ptr$std$string$release( + std::unique_ptr &ptr) noexcept { + return ptr.release(); +} +void cxxbridge1$unique_ptr$std$string$drop( + std::unique_ptr *ptr) noexcept { + ptr->~unique_ptr(); +} +} // extern "C" + +namespace { +const std::size_t kMaxExpectedWordsInString = 8; +static_assert(alignof(std::string) <= alignof(void *), + "unexpectedly large std::string alignment"); +static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), + "unexpectedly large std::string size"); +} // namespace + +#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ + std::vector *cxxbridge1$std$vector$##RUST_TYPE##$new() noexcept { \ + return new std::vector(); \ + } \ + std::size_t cxxbridge1$std$vector$##RUST_TYPE##$size( \ + const std::vector &s) noexcept { \ + return s.size(); \ + } \ + std::size_t cxxbridge1$std$vector$##RUST_TYPE##$capacity( \ + const std::vector &s) noexcept { \ + return s.capacity(); \ + } \ + CXX_TYPE *cxxbridge1$std$vector$##RUST_TYPE##$get_unchecked( \ + std::vector *s, std::size_t pos) noexcept { \ + return &(*s)[pos]; \ + } \ + void cxxbridge1$std$vector$##RUST_TYPE##$reserve( \ + std::vector *s, std::size_t new_cap) noexcept { \ + s->reserve(new_cap); \ + } \ + void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$null( \ + std::unique_ptr> *ptr) noexcept { \ + new (ptr) std::unique_ptr>(); \ + } \ + void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$raw( \ + std::unique_ptr> *ptr, \ + std::vector *raw) noexcept { \ + new (ptr) std::unique_ptr>(raw); \ + } \ + const std::vector \ + *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$get( \ + const std::unique_ptr> &ptr) noexcept { \ + return ptr.get(); \ + } \ + std::vector \ + *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$release( \ + std::unique_ptr> &ptr) noexcept { \ + return ptr.release(); \ + } \ + void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$drop( \ + std::unique_ptr> *ptr) noexcept { \ + ptr->~unique_ptr(); \ + } + +#define STD_VECTOR_TRIVIAL_OPS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$std$vector$##RUST_TYPE##$push_back( \ + std::vector *v, CXX_TYPE *value) noexcept { \ + v->push_back(std::move(*value)); \ + destroy(value); \ + } \ + void cxxbridge1$std$vector$##RUST_TYPE##$pop_back(std::vector *v, \ + CXX_TYPE *out) noexcept { \ + new (out) CXX_TYPE(std::move(v->back())); \ + v->pop_back(); \ + } + +#define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$rust_vec$##RUST_TYPE##$new( \ + rust::Vec *ptr) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$drop( \ + rust::Vec *ptr) noexcept; \ + std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$len( \ + const rust::Vec *ptr) noexcept; \ + std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$capacity( \ + const rust::Vec *ptr) noexcept; \ + const CXX_TYPE *cxxbridge1$rust_vec$##RUST_TYPE##$data( \ + const rust::Vec *ptr) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total( \ + rust::Vec *ptr, std::size_t new_cap) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec *ptr, \ + std::size_t len) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$truncate(rust::Vec *ptr, \ + std::size_t len) noexcept; + +#define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ + template <> \ + Vec::Vec() noexcept { \ + cxxbridge1$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Vec::drop() noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + std::size_t Vec::size() const noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$len(this); \ + } \ + template <> \ + std::size_t Vec::capacity() const noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$capacity(this); \ + } \ + template <> \ + const CXX_TYPE *Vec::data() const noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$data(this); \ + } \ + template <> \ + void Vec::reserve_total(std::size_t new_cap) noexcept { \ + cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(this, new_cap); \ + } \ + template <> \ + void Vec::set_len(std::size_t len) noexcept { \ + cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len); \ + } \ + template <> \ + void Vec::truncate(std::size_t len) { \ + cxxbridge1$rust_vec$##RUST_TYPE##$truncate(this, len); \ + } + +#define SHARED_PTR_OPS(RUST_TYPE, CXX_TYPE) \ + static_assert(sizeof(std::shared_ptr) == 2 * sizeof(void *), ""); \ + static_assert(alignof(std::shared_ptr) == alignof(void *), ""); \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$null( \ + std::shared_ptr *ptr) noexcept { \ + new (ptr) std::shared_ptr(); \ + } \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$raw( \ + std::shared_ptr *ptr, CXX_TYPE *raw) noexcept { \ + new (ptr) std::shared_ptr(raw); \ + } \ + CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$uninit( \ + std::shared_ptr *ptr) noexcept { \ + CXX_TYPE *uninit = \ + reinterpret_cast(new rust::MaybeUninit); \ + new (ptr) std::shared_ptr(uninit); \ + return uninit; \ + } \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$clone( \ + const std::shared_ptr &self, \ + std::shared_ptr *ptr) noexcept { \ + new (ptr) std::shared_ptr(self); \ + } \ + const CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$get( \ + const std::shared_ptr &self) noexcept { \ + return self.get(); \ + } \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$drop( \ + const std::shared_ptr *self) noexcept { \ + self->~shared_ptr(); \ + } \ + static_assert(sizeof(std::weak_ptr) == 2 * sizeof(void *), ""); \ + static_assert(alignof(std::weak_ptr) == alignof(void *), ""); \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$null( \ + std::weak_ptr *ptr) noexcept { \ + new (ptr) std::weak_ptr(); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$clone( \ + const std::weak_ptr &self, \ + std::weak_ptr *ptr) noexcept { \ + new (ptr) std::weak_ptr(self); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$downgrade( \ + const std::shared_ptr &shared, \ + std::weak_ptr *weak) noexcept { \ + new (weak) std::weak_ptr(shared); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$upgrade( \ + const std::weak_ptr &weak, \ + std::shared_ptr *shared) noexcept { \ + new (shared) std::shared_ptr(weak.lock()); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$drop( \ + const std::weak_ptr *self) noexcept { \ + self->~weak_ptr(); \ + } + +// Usize and isize are the same type as one of the below. +#define FOR_EACH_NUMERIC(MACRO) \ + MACRO(u8, std::uint8_t) \ + MACRO(u16, std::uint16_t) \ + MACRO(u32, std::uint32_t) \ + MACRO(u64, std::uint64_t) \ + MACRO(i8, std::int8_t) \ + MACRO(i16, std::int16_t) \ + MACRO(i32, std::int32_t) \ + MACRO(i64, std::int64_t) \ + MACRO(f32, float) \ + MACRO(f64, double) + +#define FOR_EACH_TRIVIAL_STD_VECTOR(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(usize, std::size_t) \ + MACRO(isize, rust::isize) + +#define FOR_EACH_STD_VECTOR(MACRO) \ + FOR_EACH_TRIVIAL_STD_VECTOR(MACRO) \ + MACRO(string, std::string) + +#define FOR_EACH_RUST_VEC(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(bool, bool) \ + MACRO(char, rust::detail::char_if_unique) \ + MACRO(usize, rust::detail::usize_if_unique) \ + MACRO(isize, rust::detail::isize_if_unique) \ + MACRO(string, rust::String) \ + MACRO(str, rust::Str) + +#define FOR_EACH_SHARED_PTR(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(bool, bool) \ + MACRO(usize, std::size_t) \ + MACRO(isize, rust::isize) \ + MACRO(string, std::string) + +extern "C" { +FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) +FOR_EACH_TRIVIAL_STD_VECTOR(STD_VECTOR_TRIVIAL_OPS) +FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) +FOR_EACH_SHARED_PTR(SHARED_PTR_OPS) +} // extern "C" + +namespace rust { +inline namespace cxxbridge1 { +FOR_EACH_RUST_VEC(RUST_VEC_OPS) +} // namespace cxxbridge1 +} // namespace rust