/* Generated with cbindgen:0.16.0 */ /* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ #include #include #include #include /** * Return value upon success */ #define OK 0 /** * NULL pointer argument */ #define ERR_NULLPTR 1 /** * Invalid coordinator URL */ #define ERR_INVALID_URL 2 /** * Invalid settings: coordinator URL is not set */ #define ERR_SETTINGS_URL 3 /** * Invalid settings: signing keys are not set */ #define ERR_SETTINGS_KEYS 4 /** * Failed to set the local model: invalid model */ #define ERR_SETMODEL_MODEL 5 /** * Failed to set the local model: invalid data type */ #define ERR_SETMODEL_DATATYPE 6 /** * Failed to initialized the crypto library */ #define ERR_CRYPTO_INIT 7 /** * Invalid secret signing key */ #define ERR_CRYPTO_SECRET_KEY 8 /** * Invalid public signing key */ #define ERR_CRYPTO_PUBLIC_KEY 9 /** * No global model is currently available */ #define GLOBALMODEL_NONE 10 /** * Failed to get the global model: communication with the coordinator failed */ #define ERR_GLOBALMODEL_IO 11 /** * Failed to get the global model: invalid data type */ #define ERR_GLOBALMODEL_DATATYPE 12 /** * Failed to get the global model: invalid buffer length */ #define ERR_GLOBALMODEL_LEN 13 /** * Failed to get the global model: invalid model */ #define ERR_GLOBALMODEL_CONVERT 14 /** * The participant is not taking part in the sum or update task */ #define PARTICIPANT_TASK_NONE 1 /** * The participant is not taking part in the sum task */ #define PARTICIPANT_TASK_SUM (1 << 1) /** * The participant is not taking part in the update task */ #define PARTICIPANT_TASK_UPDATE (1 << 2) /** * The participant is expected to set the model it trained */ #define PARTICIPANT_SHOULD_SET_MODEL (1 << 3) /** * The participant is expected to set the model it trained */ #define PARTICIPANT_MADE_PROGRESS (1 << 4) /** * A new global model is available */ #define PARTICIPANT_NEW_GLOBALMODEL (1 << 5) /** * The original primitive data type of the numerical values to be masked. */ enum ModelDataType { /** * Numbers of type f32. */ MODEL_DATA_TYPE_F32 = 0, /** * Numbers of type f64. */ MODEL_DATA_TYPE_F64 = 1, /** * Numbers of type i32. */ MODEL_DATA_TYPE_I32 = 2, /** * Numbers of type i64. */ MODEL_DATA_TYPE_I64 = 3, }; typedef uint8_t ModelDataType; /** * A signing key pair */ typedef struct KeyPair KeyPair; /** * A participant. It embeds an internal state machine that executes the PET * protocol. However, it is the caller's responsibility to drive this state machine by * calling [`Participant::tick()`], and to take action when the participant state * changes. */ typedef struct Participant Participant; /** * A participant settings */ typedef struct Settings Settings; /** * ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries. * There are several cases when you might want to use this, but the primary one for us * is for returning protobuf-encoded data to Swift and Java. The type is currently rather * limited (implementing almost no functionality), however in the future it may be * more expanded. * * ## Caveats * * Note that the order of the fields is `len` (an i64) then `data` (a `*mut u8`), getting * this wrong on the other side of the FFI will cause memory corruption and crashes. * `i64` is used for the length instead of `u64` and `usize` because JNA has interop * issues with both these types. * * ### `Drop` is not implemented * * ByteBuffer does not implement Drop. This is intentional. Memory passed into it will * be leaked if it is not explicitly destroyed by calling [`ByteBuffer::destroy`], or * [`ByteBuffer::destroy_into_vec`]. This is for two reasons: * * 1. In the future, we may allow it to be used for data that is not managed by * the Rust allocator\*, and `ByteBuffer` assuming it's okay to automatically * deallocate this data with the Rust allocator. * * 2. Automatically running destructors in unsafe code is a * [frequent footgun](https://without.boats/blog/two-memory-bugs-from-ringbahn/) * (among many similar issues across many crates). * * Note that calling `destroy` manually is often not needed, as usually you should * be passing these to the function defined by [`define_bytebuffer_destructor!`] from * the other side of the FFI. * * Because this type is essentially *only* useful in unsafe or FFI code (and because * the most common usage pattern does not require manually managing the memory), it * does not implement `Drop`. * * \* Note: in the case of multiple Rust shared libraries loaded at the same time, * there may be multiple instances of "the Rust allocator" (one per shared library), * in which case we're referring to whichever instance is active for the code using * the `ByteBuffer`. Note that this doesn't occur on all platforms or build * configurations, but treating allocators in different shared libraries as fully * independent is always safe. * * ## Layout/fields * * This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so * that we can verify rust users are constructing them appropriately), the fields, their types, and * their order are *very much* a part of the public API of this type. Consumers on the other side * of the FFI will need to know its layout. * * If this were a C struct, it would look like * * ```c,no_run * struct ByteBuffer { * // Note: This should never be negative, but values above * // INT64_MAX / i64::MAX are not allowed. * int64_t len; * // Note: nullable! * uint8_t *data; * }; * ``` * * In rust, there are two fields, in this order: `len: i64`, and `data: *mut u8`. * * For clarity, the fact that the data pointer is nullable means that `Option` is not * the same size as ByteBuffer, and additionally is not FFI-safe (the latter point is not * currently guaranteed anyway as of the time of writing this comment). * * ### Description of fields * * `data` is a pointer to an array of `len` bytes. Note that data can be a null pointer and therefore * should be checked. * * The bytes array is allocated on the heap and must be freed on it as well. Critically, if there * are multiple rust shared libraries using being used in the same application, it *must be freed * on the same heap that allocated it*, or you will corrupt both heaps. * * Typically, this object is managed on the other side of the FFI (on the "FFI consumer"), which * means you must expose a function to release the resources of `data` which can be done easily * using the [`define_bytebuffer_destructor!`] macro provided by this crate. */ typedef struct ByteBuffer { int64_t len; uint8_t *data; } ByteBuffer; /** * `FfiStr<'a>` is a safe (`#[repr(transparent)]`) wrapper around a * nul-terminated `*const c_char` (e.g. a C string). Conceptually, it is * similar to [`std::ffi::CStr`], except that it may be used in the signatures * of extern "C" functions. * * Functions accepting strings should use this instead of accepting a C string * directly. This allows us to write those functions using safe code without * allowing safe Rust to cause memory unsafety. * * A single function for constructing these from Rust ([`FfiStr::from_raw`]) * has been provided. Most of the time, this should not be necessary, and users * should accept `FfiStr` in the parameter list directly. * * ## Caveats * * An effort has been made to make this struct hard to misuse, however it is * still possible, if the `'static` lifetime is manually specified in the * struct. E.g. * * ```rust,no_run * # use ffi_support::FfiStr; * // NEVER DO THIS * #[no_mangle] * extern "C" fn never_do_this(s: FfiStr<'static>) { * // save `s` somewhere, and access it after this * // function returns. * } * ``` * * Instead, one of the following patterns should be used: * * ``` * # use ffi_support::FfiStr; * #[no_mangle] * extern "C" fn valid_use_1(s: FfiStr<'_>) { * // Use of `s` after this function returns is impossible * } * // Alternative: * #[no_mangle] * extern "C" fn valid_use_2(s: FfiStr) { * // Use of `s` after this function returns is impossible * } * ``` */ typedef const char *FfiStr; /** * The model configuration of the model that is expected in [`xaynet_ffi_participant_set_model()`]. * * [`xaynet_ffi_participant_set_model()`]: crate::ffi::xaynet_ffi_participant_set_model */ typedef struct LocalModelConfig { /** * The expected data type of the model. */ ModelDataType data_type; /** * the expected length of the model. */ uint64_t len; } LocalModelConfig; /** * Destroy the given `ByteBuffer` and free its memory. This function must only be * called on `ByteBuffer`s that have been created on the Rust side of the FFI. If you * have created a `ByteBuffer` on the other side of the FFI, do not use this function, * use `free()` instead. * * # Return value * * - [`OK`] on success * - [`ERR_NULLPTR`] if `buf` is NULL * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * 2. After destroying the `ByteBuffer` the pointer becomes invalid and must not be * used. * 3. Calling this function on a `ByteBuffer` that has not been created on the Rust * side of the FFI is UB. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_byte_buffer_destroy(const struct ByteBuffer *buf); /** * Initialize the crypto library. This method must be called before instantiating a * participant with [`xaynet_ffi_participant_new()`] or before generating new keys with * [`xaynet_ffi_generate_key_pair()`]. * * # Return value * * - [`OK`] if the initialization succeeded * - -[`ERR_CRYPTO_INIT`] if the initialization failed * * # Safety * * This function is safe to call */ int xaynet_ffi_crypto_init(void); /** * Destroy the participant created by [`xaynet_ffi_participant_new()`] or * [`xaynet_ffi_participant_restore()`]. * * # Return value * * - [`OK`] on success * - [`ERR_NULLPTR`] if `participant` is NULL * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * 2. After destroying the `Participant`, the pointer becomes invalid and must not be * used. * 3. This function should only be called on a pointer that has been created by * [`xaynet_ffi_participant_new()`] or [`xaynet_ffi_participant_restore()`] * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_participant_destroy(struct Participant *participant); /** * Instantiate a new participant with the given settings. The participant must be * destroyed with [`xaynet_ffi_participant_destroy`]. * * # Return value * * - a NULL pointer if `settings` is NULL or if the participant creation failed * - a valid pointer to a [`Participant`] otherwise * * # Safety * * When calling this method, you have to ensure that *either* the pointer is NULL *or* * all of the following is true: * * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * After destroying the participant with [`xaynet_ffi_participant_destroy`] becomes * invalid and must not be used. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ struct Participant *xaynet_ffi_participant_new(const struct Settings *settings); /** * Drive the participant internal state machine. Every tick, the state machine * attempts to perform a small work unit. * * # Return value * * - [`ERR_NULLPTR`] is `participant` is NULL * - a bitflag otherwise, with the following flags: * - [`PARTICIPANT_MADE_PROGRESS`]: if set, this flag indicates that the participant * internal state machine was able to make some progress, and that the participant * state changed. This information can be used as an indication for saving the * participant state for instance. If the flag is not set, the state machine was * not able to make progress. There are many potential causes for this, including: * - the participant is not taking part to the current training round and is just * waiting for a new one to start * - the Xaynet coordinator is not reachable or has not published some * information the participant is waiting for * - the state machine is waiting for the model to be set (see * [`xaynet_ffi_participant_set_model()`]) * - [`PARTICIPANT_TASK_NONE`], [`PARTICIPANT_TASK_SUM`] and * [`PARTICIPANT_TASK_UPDATE`]: these flags are mutually exclusive, and indicate * which task the participant has been selected for, for the current round. If * [`PARTICIPANT_TASK_NONE`] is set, then the participant will just wait for a new * round to start. If [`PARTICIPANT_TASK_UPDATE`] is set, then the participant has * been selected to update the global model, and should prepare to provide a new * model once the [`PARTICIPANT_SHOULD_SET_MODEL`] flag is set. * - [`PARTICIPANT_SHOULD_SET_MODEL`]: if set, then the participant should set its * model, by calling [`xaynet_ffi_participant_set_model()`] * - [`PARTICIPANT_NEW_GLOBALMODEL`]: if set, the participant can fetch the new global * model, by calling [`xaynet_ffi_participant_global_model()`] * * # Safety * * When calling this method, you have to ensure that *either* the pointer is NULL *or* * all of the following is true: * * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * After destroying the participant with [`xaynet_ffi_participant_destroy`] becomes * invalid and must not be used. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_participant_tick(struct Participant *participant); /** * Serialize the participant state and return a buffer that contains the serialized * participant. * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * 2. the `ByteBuffer` created by this function must be destroyed with * [`xaynet_ffi_participant_destroy`]. Attempting to free the memory from the other * side of the FFI is UB. * 3. This function destroys the participant. Therefore, **the pointer becomes invalid * and must not be used anymore**. Instead, a new participant should be created, * either with [`xaynet_ffi_participant_new()`] or * [`xaynet_ffi_participant_restore()`] * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment * * # Example * * To save the participant into a file: * * ```c * const ByteBuffer *save_buf = xaynet_ffi_participant_save(participant); * assert(save_buf); * * char *path = "./participant.bin"; * FILE *f = fopen(path, "w"); * fwrite(save_buf->data, 1, save_buf->len, f); * fclose(f); * ``` */ const struct ByteBuffer *xaynet_ffi_participant_save(struct Participant *participant); /** * Restore the participant from a buffer that contained its serialized state. * * # Return value * * - a NULL pointer on failure * - a pointer to the restored participant on success * * # Safety * * When calling this method, you have to ensure that *either* the pointers are NULL * *or* all of the following is true: * - The pointers must be properly [aligned]. * - They must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment * * # Example * * To restore a participant from a file: * * ```c * f = fopen("./participant.bin", "r"); * fseek(f, 0L, SEEK_END); * int fsize = ftell(f); * fseek(f, 0L, SEEK_SET); * ByteBuffer buf = { * .len = fsize, * .data = (uint8_t *)malloc(fsize), * }; * int n_read = fread(buf.data, 1, fsize, f); * assert(n_read == fsize); * fclose(f); * Participant *restored = * xaynet_ffi_participant_restore("http://localhost:8081", &buf); * free(buf.data); * ``` */ struct Participant *xaynet_ffi_participant_restore(FfiStr url, const struct ByteBuffer *buffer); /** * Set the participant's model. Usually this should be called when the value returned * by [`xaynet_ffi_participant_tick()`] contains the [`PARTICIPANT_SHOULD_SET_MODEL`] * flag, but it can be called anytime. The model just won't be sent to the coordinator * until it's time. * * - `buffer` should be a pointer to a buffer that contains the model * - `data_type` specifies the type of the model weights (see [`DataType`]). The C header * file generated by this crate provides an enum corresponding to the parameters: `DataType`. * - `len` is the number of weights the model has * * # Return value * * - [`OK`] if the model is set successfully * - [`ERR_NULLPTR`] if `participant` is NULL * - [`ERR_SETMODEL_DATATYPE`] if the datatype is invalid * - [`ERR_SETMODEL_MODEL`] if the model is invalid * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * 2. If `len` or `data_type` do not match the model in `buffer`, this method will * result in a buffer over-read. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_participant_set_model(struct Participant *participant, const void *buffer, unsigned char data_type, unsigned int len); /** * Return the latest global model from the coordinator. * * - `buffer` is the array in which the global model should be copied. * - `data_type` specifies the type of the model weights (see [`DataType`]). The C header * file generated by this crate provides an enum corresponding to the parameters: `DataType`. * - `len` is the number of weights the model has * * # Return Value * * - [`OK`] if the model is set successfully * - [`ERR_NULLPTR`] if `participant` or the `buffer` is NULL * - [`GLOBALMODEL_NONE`] if no model exists * - [`ERR_GLOBALMODEL_IO`] if the communication with the coordinator failed * - [`ERR_GLOBALMODEL_DATATYPE`] if the datatype is invalid * - [`ERR_GLOBALMODEL_LEN`] if the length of the buffer does not match the length of the model * - [`ERR_GLOBALMODEL_CONVERT`] if the conversion of the model failed * * # Note * * It is **not** guaranteed, that the model configuration returned by * [`xaynet_ffi_participant_local_model_config`] corresponds to the configuration of * the global model. This means that the global model can have a different length / data type * than it is defined in model configuration. That both model configurations are the same is * only guaranteed if the model config **never** changes on the coordinator side. * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * 2. If `len` or `data_type` do not match the model in `buffer`, this method will * result in a buffer over-read. * * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_participant_global_model(struct Participant *participant, void *buffer, unsigned char data_type, unsigned int len); /** * Return the local model configuration of the model that is expected in the * [`xaynet_ffi_participant_set_model()`] function. * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ struct LocalModelConfig *xaynet_ffi_participant_local_model_config(const struct Participant *participant); /** * Destroy the settings created by [`xaynet_ffi_settings_new()`]. * * # Return value * * - [`OK`] on success * - [`ERR_NULLPTR`] if `buf` is NULL * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * 2. After destroying the `Settings`, the pointer becomes invalid and must not be * used. * 3. This function should only be called on a pointer that has been created by * [`xaynet_ffi_settings_new`]. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_settings_destroy(struct Settings *settings); /** * Create new [`Settings`] and return a pointer to it. * * # Safety * * The `Settings` created by this function must be destroyed with * [`xaynet_ffi_settings_destroy()`]. Attempting to free the memory from the other side * of the FFI is UB. */ struct Settings *xaynet_ffi_settings_new(void); /** * Set scalar setting. * * # Return value * * - [`OK`] if successful * - [`ERR_NULLPTR`] if `settings` is `NULL` * * # Safety * * When calling this method, you have to ensure that *either* the pointer is NULL *or* * all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_settings_set_scalar(struct Settings *settings, double scalar); /** * Set coordinator URL. * * # Return value * * - [`OK`] if successful * - [`ERR_INVALID_URL`] if `url` is not a valid string * - [`ERR_NULLPTR`] if `settings` is `NULL` * * # Safety * * When calling this method, you have to ensure that *either* the pointers are NULL * *or* all of the following is true: * - The pointers must be properly [aligned]. * - They must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_settings_set_url(struct Settings *settings, FfiStr url); /** * Generate a new signing key pair that can be used in the [`Settings`]. **Before * calling this function you must initialize the crypto library with * [`xaynet_ffi_crypto_init()`]**. * * The returned value contains a pointer to the secret key. For security reasons, you * must make sure that this buffer life is a short as possible, and call * [`xaynet_ffi_forget_key_pair`] to destroy it. * * [`xaynet_ffi_crypto_init()`]: crate::ffi::xaynet_ffi_crypto_init * * # Safety * * This function is safe to call */ const struct KeyPair *xaynet_ffi_generate_key_pair(void); /** * De-allocate the buffers that contain the signing keys, and zero out the content of * the buffer that contains the secret key. * * # Return value * * - [`ERR_NULLPTR`] is `key_pair` is NULL * - [`OK`] otherwise * * # Safety * * When calling this method, you have to ensure that *either* the pointer is NULL *or* * all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_forget_key_pair(const struct KeyPair *key_pair); /** * Set participant signing keys. * * # Return value * * - [`OK`] if successful * - [`ERR_NULLPTR`] if `settings` or `key_pair` is `NULL` * - [`ERR_CRYPTO_PUBLIC_KEY`] if the given `key_pair` contains an invalid public key * - [`ERR_CRYPTO_SECRET_KEY`] if the given `key_pair` contains an invalid secret key * * # Safety * * When calling this method, you have to ensure that *either* the pointers are NULL * *or* all of the following is true: * - The pointers must be properly [aligned]. * - They must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_settings_set_keys(struct Settings *settings, const struct KeyPair *key_pair); /** * Check whether the given settings are valid and can be used to instantiate a * participant (see [`xaynet_ffi_participant_new()`]). * * # Return value * * - [`OK`] on success * - [`ERR_SETTINGS_URL`] if the URL has not been set * - [`ERR_SETTINGS_KEYS`] if the signing keys have not been set * * # Safety * * When calling this method, you have to ensure that *either* the pointer is NULL *or* * all of the following is true: * * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * * [`xaynet_ffi_participant_new()`]: crate::ffi::xaynet_ffi_participant_new * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment */ int xaynet_ffi_check_settings(const struct Settings *settings); /** * Destroy the model configuration created by [`xaynet_ffi_participant_local_model_config()`]. * * # Return value * * - [`OK`] on success * - [`ERR_NULLPTR`] if `local_model_config` is NULL * * # Safety * * 1. When calling this method, you have to ensure that *either* the pointer is NULL * *or* all of the following is true: * - The pointer must be properly [aligned]. * - It must be "dereferencable" in the sense defined in the [`std::ptr`] module * documentation. * 2. After destroying the `LocalModelConfig`, the pointer becomes invalid and must not be * used. * 3. This function should only be called on a pointer that has been created by * [`xaynet_ffi_participant_local_model_config()`]. * * [`std::ptr`]: https://doc.rust-lang.org/std/ptr/index.html#safety * [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment * [`xaynet_ffi_participant_local_model_config()`]: crate::ffi::xaynet_ffi_participant_local_model_config */ int xaynet_ffi_local_model_config_destroy(struct LocalModelConfig *local_model_config);