diff --git a/.github/workflows/develop.yaml b/.github/workflows/develop.yaml index 87af4d5..adf61a5 100644 --- a/.github/workflows/develop.yaml +++ b/.github/workflows/develop.yaml @@ -63,11 +63,11 @@ jobs: run: cargo test --workspace --all-features ############ Examples ############ + - name: Run 'hello world add' example + # python export_model.py + run: cargo run + working-directory: examples/hello_world_add - name: Run 'hello world add no_std' example # python export_model.py run: cargo run working-directory: examples/hello_world_add_no_std - # - name: Run 'hello world add' example - # # python export_model.py - # run: cargo run - # working-directory: examples/hello_world_add diff --git a/examples/hello_world_add/src/main.rs b/examples/hello_world_add/src/main.rs index 1b4b2e3..535e054 100644 --- a/examples/hello_world_add/src/main.rs +++ b/examples/hello_world_add/src/main.rs @@ -1,5 +1,7 @@ #![deny(warnings)] +use std::path::PathBuf; + use executorch::evalue::{EValue, Tag}; use executorch::module::Module; use executorch::tensor::{Array, Tensor}; @@ -12,8 +14,10 @@ fn main() { executorch::platform::pal_init(); - let mut module = Module::new("model.pte", None); - + let mut module = Module::new( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("model.pte"), + None, + ); let input_array1 = Array::new(array![1.0_f32]); let input_tensor1 = input_array1.to_tensor_impl(); let input_evalue1 = EValue::from_tensor(Tensor::new(&input_tensor1)); diff --git a/examples/hello_world_add_no_std/src/main.rs b/examples/hello_world_add_no_std/src/main.rs index 05472b7..efcd131 100644 --- a/examples/hello_world_add_no_std/src/main.rs +++ b/examples/hello_world_add_no_std/src/main.rs @@ -17,10 +17,11 @@ fn main() { executorch::platform::pal_init(); - let memory_allocator = - MemoryAllocator::new(unsafe { &mut *core::ptr::addr_of_mut!(MEMORY_ALLOCATOR_BUF) }); + // Safety: We are the main function, no other function access the buffer + let buffer = unsafe { &mut *core::ptr::addr_of_mut!(MEMORY_ALLOCATOR_BUF) }; + let memory_allocator = MemoryAllocator::new(buffer); - let file_data_loader = FileDataLoader::from_cstr(cstr::cstr!(b"model.pte"), None).unwrap(); + let file_data_loader = FileDataLoader::from_path_cstr(cstr::cstr!(b"model.pte"), None).unwrap(); let program = Program::load( &file_data_loader, diff --git a/executorch-sys/cpp/executorch_rs_ext/api_utils.cpp b/executorch-sys/cpp/executorch_rs_ext/api_utils.cpp index bf400b9..335ed51 100644 --- a/executorch-sys/cpp/executorch_rs_ext/api_utils.cpp +++ b/executorch-sys/cpp/executorch_rs_ext/api_utils.cpp @@ -6,57 +6,57 @@ namespace executorch_rs { - namespace - { - template - struct ManuallyDrop - { - union - { - T value; - }; - ManuallyDrop(T &&value) : value(std::move(value)) {} - ~ManuallyDrop() {} +#if defined(EXECUTORCH_RS_STD) + template + Vec crate_Vec(std::vector &&vec) + { + size_t len = vec.size(); + T *arr = new T[len]; + std::move(vec.begin(), vec.end(), arr); + return Vec{ + .data = arr, + .len = len, + .cap = len, }; + } - template - RawVec crate_RawVec(std::vector &&vec) - { - auto vec2 = ManuallyDrop>(std::move(vec)); - return RawVec{ - .data = vec2.value.data(), - .len = vec2.value.size(), - .cap = vec2.value.capacity(), - }; - } +#define VEC_DESTRUCTOR_IMPL(T, name) \ + void Vec_##name##_destructor(Vec *vec) \ + { \ + delete[] vec->data; \ + } - static_assert(sizeof(Result_i64) == sizeof(torch::executor::Result), "Result_i64 size mismatch"); - // static_assert(offsetof(Result_i64, value_) == offsetof(torch::executor::Result, value_), "Result_i64 value_ offset mismatch"); - // static_assert(offsetof(Result_i64, error_) == offsetof(torch::executor::Result, error_), "Result_i64 error_ offset mismatch"); - // static_assert(offsetof(Result_i64, hasValue_) == offsetof(torch::executor::Result, hasValue_), "Result_i64 hasValue_ offset mismatch"); - Result_i64 crate_Result_i64(const torch::executor::Result &result) - { - Result_i64 result2{ - .error_ = torch::executor::Error::Ok, - .hasValue_ = false, - }; - memcpy(&result2, &result, sizeof(Result_i64)); - return result2; - } + VEC_DESTRUCTOR_IMPL(char, char) + VEC_DESTRUCTOR_IMPL(Vec, Vec_char) + VEC_DESTRUCTOR_IMPL(torch::executor::EValue, EValue) +#endif - static_assert(sizeof(Result_MethodMeta) == sizeof(torch::executor::Result), "Result_MethodMeta size mismatch"); - // static_assert(offsetof(Result_MethodMeta, value_) == offsetof(torch::executor::Result, value_), "Result_MethodMeta value_ offset mismatch"); - // static_assert(offsetof(Result_MethodMeta, error_) == offsetof(torch::executor::Result, error_), "Result_MethodMeta error_ offset mismatch"); - // static_assert(offsetof(Result_MethodMeta, hasValue_) == offsetof(torch::executor::Result, hasValue_), "Result_MethodMeta hasValue_ offset mismatch"); - Result_MethodMeta crate_Result_MethodMeta(const torch::executor::Result &result) - { - Result_MethodMeta result2{ - .error_ = torch::executor::Error::Ok, - .hasValue_ = false, - }; - memcpy(&result2, &result, sizeof(Result_MethodMeta)); - return result2; - } + static_assert(sizeof(Result_i64) == sizeof(torch::executor::Result), "Result_i64 size mismatch"); + // static_assert(offsetof(Result_i64, value_) == offsetof(torch::executor::Result, value_), "Result_i64 value_ offset mismatch"); + // static_assert(offsetof(Result_i64, error_) == offsetof(torch::executor::Result, error_), "Result_i64 error_ offset mismatch"); + // static_assert(offsetof(Result_i64, hasValue_) == offsetof(torch::executor::Result, hasValue_), "Result_i64 hasValue_ offset mismatch"); + Result_i64 crate_Result_i64(const torch::executor::Result &result) + { + Result_i64 result2{ + .error_ = torch::executor::Error::Ok, + .hasValue_ = false, + }; + memcpy(&result2, &result, sizeof(Result_i64)); + return result2; + } + + static_assert(sizeof(Result_MethodMeta) == sizeof(torch::executor::Result), "Result_MethodMeta size mismatch"); + // static_assert(offsetof(Result_MethodMeta, value_) == offsetof(torch::executor::Result, value_), "Result_MethodMeta value_ offset mismatch"); + // static_assert(offsetof(Result_MethodMeta, error_) == offsetof(torch::executor::Result, error_), "Result_MethodMeta error_ offset mismatch"); + // static_assert(offsetof(Result_MethodMeta, hasValue_) == offsetof(torch::executor::Result, hasValue_), "Result_MethodMeta hasValue_ offset mismatch"); + Result_MethodMeta crate_Result_MethodMeta(const torch::executor::Result &result) + { + Result_MethodMeta result2{ + .error_ = torch::executor::Error::Ok, + .hasValue_ = false, + }; + memcpy(&result2, &result, sizeof(Result_MethodMeta)); + return result2; } Result_MethodMeta Program_method_meta(const torch::executor::Program *program, const char *method_name) @@ -151,6 +151,10 @@ namespace executorch_rs tensor->~Tensor(); } + torch::executor::EValue EValue_shallow_clone(torch::executor::EValue *evalue) + { + return *evalue; + } void EValue_destructor(torch::executor::EValue *evalue) { evalue->~EValue(); @@ -170,48 +174,48 @@ namespace executorch_rs } #if defined(EXECUTORCH_RS_MODULE) - torch::executor::Module Module_new(torch::executor::ArrayRef file_path, torch::executor::Module::MlockConfig mlock_config, torch::executor::EventTracer *event_tracer) + torch::executor::Module *Module_new(torch::executor::ArrayRef file_path, torch::executor::Module::MlockConfig mlock_config, torch::executor::EventTracer *event_tracer) { std::string file_path_str(file_path.begin(), file_path.end()); std::unique_ptr event_tracer2(event_tracer); - return torch::executor::Module(file_path_str, mlock_config, std::move(event_tracer2)); + return new torch::executor::Module(file_path_str, mlock_config, std::move(event_tracer2)); } - void Module_destructor(torch::executor::Module *module) + void Module_destructor(torch::executor::Module *module_) { - module->~Module(); + module_->~Module(); } - torch::executor::Result>> Module_method_names(torch::executor::Module *module) + torch::executor::Result>> Module_method_names(torch::executor::Module *module_) { - std::unordered_set method_names = ET_UNWRAP(module->method_names()); - std::vector> method_names_vec; + std::unordered_set method_names = ET_UNWRAP(module_->method_names()); + std::vector> method_names_vec; for (const std::string &method_name : method_names) { std::vector method_name_vec(method_name.begin(), method_name.end()); - method_names_vec.push_back(crate_RawVec(std::move(method_name_vec))); + method_names_vec.push_back(crate_Vec(std::move(method_name_vec))); } - return crate_RawVec(std::move(method_names_vec)); + return crate_Vec(std::move(method_names_vec)); } - torch::executor::Error Module_load_method(torch::executor::Module *module, torch::executor::ArrayRef method_name) + torch::executor::Error Module_load_method(torch::executor::Module *module_, torch::executor::ArrayRef method_name) { std::string method_name_str(method_name.begin(), method_name.end()); - return module->load_method(method_name_str); + return module_->load_method(method_name_str); } - bool Module_is_method_loaded(const torch::executor::Module *module, torch::executor::ArrayRef method_name) + bool Module_is_method_loaded(const torch::executor::Module *module_, torch::executor::ArrayRef method_name) { std::string method_name_str(method_name.begin(), method_name.end()); - return module->is_method_loaded(method_name_str); + return module_->is_method_loaded(method_name_str); } - Result_MethodMeta Module_method_meta(torch::executor::Module *module, torch::executor::ArrayRef method_name) + Result_MethodMeta Module_method_meta(torch::executor::Module *module_, torch::executor::ArrayRef method_name) { std::string method_name_str(method_name.begin(), method_name.end()); - return crate_Result_MethodMeta(module->method_meta(method_name_str)); + return crate_Result_MethodMeta(module_->method_meta(method_name_str)); } - torch::executor::Result> Module_execute(torch::executor::Module *module, torch::executor::ArrayRef method_name, torch::executor::ArrayRef inputs) + torch::executor::Result> Module_execute(torch::executor::Module *module_, torch::executor::ArrayRef method_name, torch::executor::ArrayRef inputs) { std::string method_name_str(method_name.begin(), method_name.end()); std::vector inputs_vec(inputs.begin(), inputs.end()); - std::vector outputs = ET_UNWRAP(module->execute(method_name_str, inputs_vec)); - return crate_RawVec(std::move(outputs)); + std::vector outputs = ET_UNWRAP(module_->execute(method_name_str, inputs_vec)); + return crate_Vec(std::move(outputs)); } #endif } diff --git a/executorch-sys/cpp/executorch_rs_ext/api_utils.hpp b/executorch-sys/cpp/executorch_rs_ext/api_utils.hpp index e443253..c9fd266 100644 --- a/executorch-sys/cpp/executorch_rs_ext/api_utils.hpp +++ b/executorch-sys/cpp/executorch_rs_ext/api_utils.hpp @@ -25,14 +25,24 @@ namespace executorch_rs { +#if defined(EXECUTORCH_RS_STD) template - struct RawVec + struct Vec { T *data; size_t len; size_t cap; }; +#define VEC_DESTRUCTOR_DEC(T, name) \ + void Vec_##name##_destructor(Vec *vec); + + VEC_DESTRUCTOR_DEC(char, char) + VEC_DESTRUCTOR_DEC(Vec, Vec_char) + VEC_DESTRUCTOR_DEC(torch::executor::EValue, EValue) + +#endif + struct Result_i64 { @@ -84,6 +94,7 @@ namespace executorch_rs void *Tensor_mutable_data_ptr(const exec_aten::Tensor *tensor); void Tensor_destructor(exec_aten::Tensor *tensor); + torch::executor::EValue EValue_shallow_clone(torch::executor::EValue *evalue); void EValue_destructor(torch::executor::EValue *evalue); const exec_aten::ArrayRef BoxedEvalueList_i64_get(const torch::executor::BoxedEvalueList *list); const exec_aten::ArrayRef BoxedEvalueList_Tensor_get(const torch::executor::BoxedEvalueList *list); @@ -91,13 +102,13 @@ namespace executorch_rs torch::executor::util::BufferDataLoader BufferDataLoader_new(const void *data, size_t size); #if defined(EXECUTORCH_RS_MODULE) - torch::executor::Module Module_new(torch::executor::ArrayRef file_path, torch::executor::Module::MlockConfig mlock_config, torch::executor::EventTracer *event_tracer); + torch::executor::Module *Module_new(torch::executor::ArrayRef file_path, torch::executor::Module::MlockConfig mlock_config, torch::executor::EventTracer *event_tracer); void Module_destructor(torch::executor::Module *module); - torch::executor::Result>> Module_method_names(torch::executor::Module *module); + torch::executor::Result>> Module_method_names(torch::executor::Module *module); torch::executor::Error Module_load_method(torch::executor::Module *module, torch::executor::ArrayRef method_name); bool Module_is_method_loaded(const torch::executor::Module *module, torch::executor::ArrayRef method_name); - Result_MethodMeta Module_method_meta(const torch::executor::Module *module, torch::executor::ArrayRef method_name); - torch::executor::Result> Module_execute(torch::executor::Module *module, torch::executor::ArrayRef method_name, torch::executor::ArrayRef inputs); + Result_MethodMeta Module_method_meta(torch::executor::Module *module, torch::executor::ArrayRef method_name); + torch::executor::Result> Module_execute(torch::executor::Module *module, torch::executor::ArrayRef method_name, torch::executor::ArrayRef inputs); #endif } diff --git a/executorch-sys/src/lib.rs b/executorch-sys/src/lib.rs index 3be2508..a7a0141 100644 --- a/executorch-sys/src/lib.rs +++ b/executorch-sys/src/lib.rs @@ -109,3 +109,14 @@ impl Drop for et_c::Tensor { unsafe { et_rs_c::Tensor_destructor(self) } } } + +#[cfg(feature = "std")] +impl et_rs_c::Vec { + pub fn as_slice(&self) -> &[T] { + unsafe { std::slice::from_raw_parts(self.data, self.len) } + } + + pub fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.data, self.len) } + } +} diff --git a/src/data_loader.rs b/src/data_loader.rs index 246e3d3..c15d193 100644 --- a/src/data_loader.rs +++ b/src/data_loader.rs @@ -13,6 +13,12 @@ use crate::{et_c, et_rs_c}; /// This struct is like a base class for data loaders. All other data loaders implement `AsRef` and other /// structs, such as `Program`, take a reference to `DataLoader` instead of the concrete data loader type. pub struct DataLoader(pub(crate) UnsafeCell); +impl DataLoader { + pub(crate) fn from_inner_ref(loader: &et_c::DataLoader) -> &Self { + // Safety: Self has a single field of (UnsafeCell of) et_c::DataLoader + unsafe { std::mem::transmute(loader) } + } +} /// A DataLoader that wraps a pre-allocated buffer. The FreeableBuffers /// that it returns do not actually free any data. @@ -27,16 +33,18 @@ pub struct BufferDataLoader<'a>( impl<'a> BufferDataLoader<'a> { /// Creates a new BufferDataLoader that wraps the given data. pub fn new(data: &'a [u8]) -> Self { - let loader = - unsafe { et_rs_c::BufferDataLoader_new(data.as_ptr() as *const _, data.len()) }; + // Safety: the returned Self has a lifetime guaranteeing it will not outlive the buffer + let loader = unsafe { et_rs_c::BufferDataLoader_new(data.as_ptr().cast(), data.len()) }; Self(UnsafeCell::new(loader), PhantomData) } } impl AsRef for BufferDataLoader<'_> { fn as_ref(&self) -> &DataLoader { - // SAFETY: BufferDataLoader has a single field of (UnsafeCell of) et_c::util::BufferDataLoader, which is a - // subclass of et_c::DataLoader, and DataLoaders has a single field of (UnsafeCell of) et_c::DataLoader. - unsafe { std::mem::transmute::<&BufferDataLoader, &DataLoader>(self) } + // Safely: et_c::util::BufferDataLoader is a subclass of et_c::DataLoader + let loader = unsafe { + std::mem::transmute::<&et_c::util::BufferDataLoader, &et_c::DataLoader>(&*self.0.get()) + }; + DataLoader::from_inner_ref(loader) } } @@ -89,7 +97,7 @@ mod file_data_loader { ) -> Result { let file_name = file_name.as_ref().to_str().expect("Invalid file name"); let file_name = std::ffi::CString::new(file_name).unwrap(); - Self::from_cstr(&file_name, alignment) + Self::from_path_cstr(&file_name, alignment) } /// Creates a new FileDataLoader given a `CStr`. @@ -115,7 +123,7 @@ mod file_data_loader { /// # Safety /// /// The `file_name` should be a valid UTF-8 string and not contains a null byte other than the one at the end. - pub fn from_cstr(file_name: &CStr, alignment: Option) -> Result { + pub fn from_path_cstr(file_name: &CStr, alignment: Option) -> Result { let alignment = alignment.unwrap_or(16); let loader = unsafe { et_c::util::FileDataLoader::from(file_name.as_ptr(), alignment) }.rs()?; diff --git a/src/evalue.rs b/src/evalue.rs index 5f86941..ecb7277 100644 --- a/src/evalue.rs +++ b/src/evalue.rs @@ -469,6 +469,12 @@ impl Debug for EValue<'_> { st.finish() } } +impl<'a> Clone for EValue<'a> { + fn clone(&self) -> Self { + let value = unsafe { et_rs_c::EValue_shallow_clone(&self.0 as *const _ as *mut _) }; + unsafe { EValue::new(value) } + } +} /// Helper class used to correlate EValues in the executor table, with the /// unwrapped list of the proper type. Because values in the runtime's values diff --git a/src/module.rs b/src/module.rs index 3974938..54de535 100644 --- a/src/module.rs +++ b/src/module.rs @@ -8,6 +8,7 @@ //! //! See the `hello_world_add` example for how to load and execute a module. +use core::ptr::NonNull; use std::collections::HashSet; use std::path::Path; use std::ptr; @@ -21,7 +22,7 @@ use crate::{et_c, et_rs_c}; /// A facade class for loading programs and executing methods within them. /// /// See the `hello_world_add` example for how to load and execute a module. -pub struct Module(et_c::Module); +pub struct Module(NonNull); impl Module { /// Constructs an instance by loading a program from a file with specified /// memory locking behavior. @@ -43,7 +44,8 @@ impl Module { let file_path = ArrayRef::from_slice(util::str2chars(file_path).unwrap()); let mlock_config = mlock_config.unwrap_or(MlockConfig::UseMlock); let event_tracer = ptr::null_mut(); // TODO: support event tracer - Self(unsafe { et_rs_c::Module_new(file_path.0, mlock_config, event_tracer) }) + let module = unsafe { et_rs_c::Module_new(file_path.0, mlock_config, event_tracer) }; + Self(unsafe { NonNull::new_unchecked(module) }) } /// Loads the program using the specified data loader and memory allocator. @@ -58,7 +60,7 @@ impl Module { /// An Error to indicate success or failure of the loading process. pub fn load(&mut self, verification: Option) -> Result<()> { let verification = verification.unwrap_or(ProgramVerification::Minimal); - unsafe { et_c::Module_load(&mut self.0, verification) }.rs() + unsafe { et_c::Module_load(self.0.as_mut(), verification) }.rs() } /// Checks if the program is loaded. @@ -67,7 +69,7 @@ impl Module { /// /// true if the program is loaded, false otherwise. pub fn is_loaded(&self) -> bool { - unsafe { et_c::Module_is_loaded(&self.0) } + unsafe { et_c::Module_is_loaded(self.0.as_ref()) } } /// Get a list of method names available in the loaded program. @@ -77,11 +79,13 @@ impl Module { /// /// A set of strings containing the names of the methods, or an error if the program or method failed to load. pub fn method_names(&mut self) -> Result> { - let names = unsafe { et_rs_c::Module_method_names(&mut self.0) }.rs()?; + let names = unsafe { et_rs_c::Module_method_names(self.0.as_mut()) } + .rs()? + .rs(); Ok(names - .rs() - .into_iter() - .map(|s| util::chars2string(s.rs())) + .as_slice() + .iter() + .map(|s| util::chars2string(s.to_vec())) .collect()) } @@ -101,7 +105,7 @@ impl Module { /// If the method name is not a valid UTF-8 string or contains a null character. pub fn load_method(&mut self, method_name: &str) -> Result<()> { let method_name = ArrayRef::from_slice(util::str2chars(method_name).unwrap()); - unsafe { et_rs_c::Module_load_method(&mut self.0, method_name.0) }.rs() + unsafe { et_rs_c::Module_load_method(self.0.as_mut(), method_name.0) }.rs() } /// Checks if a specific method is loaded. @@ -119,7 +123,7 @@ impl Module { /// If the method name is not a valid UTF-8 string or contains a null character. pub fn is_method_loaded(&self, method_name: &str) -> bool { let method_name = ArrayRef::from_slice(util::str2chars(method_name).unwrap()); - unsafe { et_rs_c::Module_is_method_loaded(&self.0, method_name.0) } + unsafe { et_rs_c::Module_is_method_loaded(self.0.as_ref(), method_name.0) } } /// Get a method metadata struct by method name. @@ -138,7 +142,7 @@ impl Module { /// If the method name is not a valid UTF-8 string or contains a null character. pub fn method_meta<'a>(&'a self, method_name: &str) -> Result> { let method_name = ArrayRef::from_slice(util::str2chars(method_name).unwrap()); - let meta = unsafe { et_rs_c::Module_method_meta(&self.0, method_name.0) }.rs()?; + let meta = unsafe { et_rs_c::Module_method_meta(self.0.as_ptr(), method_name.0) }.rs()?; Ok(unsafe { MethodMeta::new(meta) }) } @@ -167,11 +171,12 @@ impl Module { let inputs = unsafe { std::mem::transmute::<&[EValue], &[et_c::EValue]>(inputs) }; let inputs = ArrayRef::from_slice(inputs); let outputs = - unsafe { et_rs_c::Module_execute(&mut self.0, method_name.0, inputs.0) }.rs()?; - let outputs = outputs.rs(); + unsafe { et_rs_c::Module_execute(self.0.as_mut(), method_name.0, inputs.0) }.rs()?; // Safety: The transmute is safe because the memory layout of EValue and et_c::EValue is the same. - let outputs = unsafe { std::mem::transmute::, Vec>>(outputs) }; - Ok(outputs) + let outputs = unsafe { + std::mem::transmute::, et_rs_c::Vec>>(outputs) + }; + Ok(outputs.rs().to_vec()) } /// Executes the 'forward' method with the given input and retrieves the output. @@ -190,7 +195,7 @@ impl Module { } impl Drop for Module { fn drop(&mut self) { - unsafe { et_rs_c::Module_destructor(&mut self.0) }; + unsafe { et_rs_c::Module_destructor(self.0.as_mut()) }; } } diff --git a/src/util.rs b/src/util.rs index 6f23ef6..ecd4b9f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -211,10 +211,76 @@ pub(crate) fn chars2string(chars: Vec) -> String { } #[cfg(feature = "std")] -impl IntoRust for crate::et_rs_c::RawVec { - type RsType = Vec; - fn rs(self) -> Self::RsType { - unsafe { Vec::from_raw_parts(self.data, self.len, self.cap) } +#[allow(dead_code)] +mod cpp_vec { + use super::IntoRust; + use crate::{et_c, et_rs_c, evalue::EValue}; + + pub(crate) struct CppVec(et_rs_c::Vec); + impl CppVec { + pub fn as_slice(&self) -> &[T] { + self.0.as_slice() + } + + pub fn to_vec(&self) -> Vec + where + T: Clone, + { + self.as_slice().to_vec() + } + } + impl IntoRust for et_rs_c::Vec { + type RsType = CppVec; + fn rs(self) -> Self::RsType { + CppVec(self) + } + } + impl IntoRust for et_rs_c::Vec> { + type RsType = CppVec>; + fn rs(self) -> Self::RsType { + // Safety: et_rs_c::Vec has the same memory layout as CppVec. + unsafe { + std::mem::transmute::< + et_rs_c::Vec>, + CppVec>, + >(self) + } + } + } + impl Drop for CppVec { + fn drop(&mut self) { + T::drop_vec(self); + } + } + pub(crate) trait CppVecElm: Sized { + fn drop_vec(vec: &mut CppVec); + } + impl CppVecElm for core::ffi::c_char { + fn drop_vec(vec: &mut CppVec) { + unsafe { et_rs_c::Vec_char_destructor(&mut vec.0) } + } + } + impl CppVecElm for CppVec { + fn drop_vec(vec: &mut CppVec) { + // Safety: CppVec has the same memory layout as et_rs_c::Vec. + let vec = unsafe { + std::mem::transmute::< + &mut CppVec>, + &mut et_rs_c::Vec>, + >(vec) + }; + unsafe { et_rs_c::Vec_Vec_char_destructor(vec) } + } + } + impl<'a> CppVecElm for EValue<'a> { + fn drop_vec(vec: &mut CppVec) { + let vec = unsafe { + std::mem::transmute::<&mut et_rs_c::Vec>, &mut et_rs_c::Vec>( + &mut vec.0, + ) + }; + unsafe { et_rs_c::Vec_EValue_destructor(vec) } + } } }