Rust библиотеки
Интеграция Rust-библиотек будет описана на основе интеграции хеш-функции BLAKE3.
Первый шаг интеграции — добавить библиотеку в папку /rust. Для этого необходимо создать пустой проект Rust и включить требуемую библиотеку в Cargo.toml. Также необходимо настроить компиляцию новой библиотеки как статической, добавив crate-type = ["staticlib"]
в Cargo.toml.
Далее необходимо связать библиотеку с CMake, используя библиотеку Corrosion. Первый шаг — добавить папку с библиотекой в CMakeLists.txt внутри папки /rust. После этого следует добавить файл CMakeLists.txt в директорию библиотеки. В нем необходимо вызвать функцию импорта Corrosion. Эти строки были использованы для импорта BLAKE3:
Таким образом, мы создадим корректную цель CMake с использованием Corrosion, а затем переименуем ее в более удобное имя. Обратите внимание, что имя _ch_rust_blake3
взято из Cargo.toml, где оно используется как имя проекта (name = "_ch_rust_blake3"
).
Поскольку типы данных Rust несовместимы с типами данных C/C++, мы будем использовать наш пустой проект библиотеки для создания методов-оберток для преобразования данных, полученных из C/C++, вызова методов библиотеки и обратного преобразования для выходных данных. Например, этот метод был написан для BLAKE3:
Этот метод получает строку в формате, совместимом с C, ее размер и указатель на выходную строку в качестве входных параметров. Затем он преобразует входные данные, совместимые с C, в типы, используемые реальными методами библиотеки, и вызывает их. После этого он должен преобразовать выходные данные методов библиотеки обратно в тип, совместимый с C. В данном случае библиотека поддерживала прямую запись в указатель методом fill(), так что преобразование не требовалось. Главный совет здесь — создавать меньше методов, чтобы вам потребовалось меньше преобразований при каждом вызове метода и не создавать значительных накладных расходов.
Стоит отметить, что атрибут #[no_mangle]
и extern "C"
обязательны для всех таких методов. Без них не будет возможности правильно скомпилировать код, совместимый с C/C++. Более того, они необходимы для следующего шага интеграции.
После написания кода для методов-оберток нам необходимо подготовить заголовочный файл для библиотеки. Это можно сделать вручную или использовать библиотеку cbindgen для автогенерации. В случае использования cbindgen вам нужно будет написать скрипт сборки build.rs и включить cbindgen как зависимость для сборки.
Пример скрипта сборки, который может авто-сгенерировать заголовочный файл:
Также следует использовать атрибуты #[no_mangle] и extern "C"
для каждого атрибута, совместимого с C. Без этого библиотека может скомпилироваться некорректно, и cbindgen не запустит автогенерацию заголовка.
После всех этих шагов вы можете протестировать свою библиотеку в небольшом проекте, чтобы найти все проблемы с совместимостью или генерацией заголовков. Если возникнут проблемы во время генерации заголовка, вы можете попытаться настроить его с помощью файла cbindgen.toml (шаблон можно найти здесь: https://github.com/eqrion/cbindgen/blob/master/template.toml).
Стоит отметить проблему, которая возникла при интеграции BLAKE3: MemorySanitizer может вызывать ложные срабатывания, поскольку не может определить, инициализированы ли некоторые переменные в Rust или нет. Это было решено путем написания метода с более явным определением для некоторых переменных, хотя эта реализация метода медленнее и используется только для исправления сборок MemorySanitizer.