This commit is contained in:
neuecc 2023-03-08 18:41:46 +09:00
parent e1b69be5b7
commit 5b1de23200
6 changed files with 129 additions and 27 deletions

View File

@ -224,7 +224,7 @@ csbindgen::Builder::default()
.input_bindgen_file("src/lz4.rs") // required .input_bindgen_file("src/lz4.rs") // required
.method_filter(|x| { x.starts_with("LZ4") } ) // optional, default: |x| !x.starts_with('_') .method_filter(|x| { x.starts_with("LZ4") } ) // optional, default: |x| !x.starts_with('_')
.rust_method_prefix("csbindgen_") // optional, default: "csbindgen_" .rust_method_prefix("csbindgen_") // optional, default: "csbindgen_"
.rust_file_header("use super::lz4;") // optional, default: "" .rust_file_header("use super::lz4::*;") // optional, default: ""
.rust_method_type_path("lz4") // optional, default: "" .rust_method_type_path("lz4") // optional, default: ""
.csharp_dll_name("lz4") // required .csharp_dll_name("lz4") // required
.csharp_class_name("NativeMethods") // optional, default: NativeMethods .csharp_class_name("NativeMethods") // optional, default: NativeMethods
@ -369,6 +369,8 @@ Rust types will map these C# types.
| `*const T` | `T*` | | `*const T` | `T*` |
| `*mut *mut T` | `T**` | | `*mut *mut T` | `T**` |
| `*const *const T` | `T**` | | `*const *const T` | `T**` |
| `*mut *const T` | `T**` |
| `*const *mut T` | `T**` |
csbindgen is designed to return primitives that do not cause marshalling. It is better to convert from pointers to Span yourself than to do the conversion implicitly and in a black box. This is a recent trend, such as the addition of [DisableRuntimeMarshalling](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.disableruntimemarshallingattribute) from .NET 7. csbindgen is designed to return primitives that do not cause marshalling. It is better to convert from pointers to Span yourself than to do the conversion implicitly and in a black box. This is a recent trend, such as the addition of [DisableRuntimeMarshalling](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.disableruntimemarshallingattribute) from .NET 7.
@ -814,7 +816,53 @@ finally
} }
``` ```
C# to Rust would be a bit simpler to send, just pass byte* and length. In Rust, use `std::slice::from_raw_parts` to create slice. Again, the important thing is that memory allocated in Rust must release in Rust and memory allocated in C# must release in C#. C# to Rust would be a bit simpler to send, just pass byte* and length. In Rust, use `std::slice::from_raw_parts` to create slice.
```rust
#[no_mangle]
pub unsafe extern "C" fn csharp_to_rust_string(utf16_str: *const u16, utf16_len: i32) {
let slice = std::slice::from_raw_parts(utf16_str, utf16_len as usize);
let str = String::from_utf16(slice).unwrap();
println!("{}", str);
}
#[no_mangle]
pub unsafe extern "C" fn csharp_to_rust_utf8(utf8_str: *const u8, utf8_len: i32) {
let slice = std::slice::from_raw_parts(utf8_str, utf8_len as usize);
let str = String::from_utf8_unchecked(slice.to_vec());
println!("{}", str);
}
#[no_mangle]
pub unsafe extern "C" fn csharp_to_rust_bytes(bytes: *const u8, len: i32) {
let slice = std::slice::from_raw_parts(bytes, len as usize);
let vec = slice.to_vec();
println!("{:?}", vec);
}
```
```csharp
var str = "foobarbaz:あいうえお"; // JPN(Unicode)
fixed (char* p = str)
{
NativeMethods.csharp_to_rust_string((ushort*)p, str.Length);
}
var str2 = Encoding.UTF8.GetBytes("あいうえお:foobarbaz");
fixed (byte* p = str2)
{
NativeMethods.csharp_to_rust_utf8(p, str2.Length);
}
var bytes = new byte[] { 1, 10, 100, 255 };
fixed (byte* p = bytes)
{
NativeMethods.csharp_to_rust_bytes(p, bytes.Length);
}
```
Again, the important thing is that memory allocated in Rust must release in Rust and memory allocated in C# must release in C#.
Build Tracing Build Tracing
--- ---

View File

@ -61,10 +61,10 @@ fn main() -> Result<(), Box<dyn Error>> {
csbindgen::Builder::default() csbindgen::Builder::default()
.input_extern_file("src/lib.rs") .input_extern_file("src/lib.rs")
.csharp_class_name("LibRust") .csharp_class_name("NativeMethods")
.csharp_dll_name("csbindgen_tests") .csharp_dll_name("csbindgen_tests")
.csharp_use_function_pointer(true) .csharp_use_function_pointer(true)
.generate_csharp_file("../dotnet-sandbox/method_call.cs") .generate_csharp_file("../dotnet-sandbox/NativeMethods.cs")
.unwrap(); .unwrap();
// csbindgen::Builder::new() // csbindgen::Builder::new()

View File

@ -25,7 +25,6 @@ mod lz4_ffi;
// #[allow(non_camel_case_types)] // #[allow(non_camel_case_types)]
// mod bullet3_ffi; // mod bullet3_ffi;
// #[allow(dead_code)] // #[allow(dead_code)]
// #[allow(non_snake_case)] // #[allow(non_snake_case)]
// #[allow(non_camel_case_types)] // #[allow(non_camel_case_types)]
@ -37,7 +36,6 @@ mod lz4_ffi;
// #[allow(non_camel_case_types)] // #[allow(non_camel_case_types)]
// mod quiche_ffi; // mod quiche_ffi;
// #[allow(dead_code)] // #[allow(dead_code)]
// #[allow(non_snake_case)] // #[allow(non_snake_case)]
// #[allow(non_camel_case_types)] // #[allow(non_camel_case_types)]
@ -49,10 +47,6 @@ mod lz4_ffi;
// #[allow(non_camel_case_types)] // #[allow(non_camel_case_types)]
// mod zstd_ffi; // mod zstd_ffi;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type LONG_PTR = ::std::os::raw::c_longlong; pub type LONG_PTR = ::std::os::raw::c_longlong;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -73,6 +67,33 @@ pub unsafe extern "C" fn nullpointer_test(p: *const u8) {
}; };
} }
#[no_mangle]
pub unsafe extern "C" fn csharp_to_rust_string(utf16_str: *const u16, utf16_len: i32) {
let slice = std::slice::from_raw_parts(utf16_str, utf16_len as usize);
let str = String::from_utf16(slice).unwrap();
println!("{}", str);
}
#[no_mangle]
pub unsafe extern "C" fn csharp_to_rust_utf8(utf8_str: *const u8, utf8_len: i32) {
let slice = std::slice::from_raw_parts(utf8_str, utf8_len as usize);
let str = String::from_utf8_unchecked(slice.to_vec());
println!("{}", str);
}
#[no_mangle]
pub unsafe extern "C" fn csharp_to_rust_bytes(bytes: *const u8, len: i32) {
let slice = std::slice::from_raw_parts(bytes, len as usize);
let vec = slice.to_vec();
println!("{:?}", vec);
}
#[no_mangle] #[no_mangle]
pub extern "C" fn callback_test(cb: extern "C" fn(a: i32) -> i32) -> i32 { pub extern "C" fn callback_test(cb: extern "C" fn(a: i32) -> i32) -> i32 {
cb(100) cb(100)
@ -85,7 +106,7 @@ pub extern "C" fn csharp_to_rust(cb: extern "C" fn(x: i32, y: i32) -> i32) {
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_to_csharp() -> extern fn(x: i32, y: i32) -> i32 { pub extern "C" fn rust_to_csharp() -> extern "C" fn(x: i32, y: i32) -> i32 {
sum // return rust method sum // return rust method
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "csbindgen" name = "csbindgen"
version = "1.0.0" version = "1.1.0"
edition = "2021" edition = "2021"
authors = [ authors = [
"Yoshifumi Kawai <ils@neue.cc>", "Yoshifumi Kawai <ils@neue.cc>",

View File

@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
namespace CsBindgen namespace CsBindgen
{ {
internal static unsafe partial class LibRust internal static unsafe partial class NativeMethods
{ {
const string __DllName = "csbindgen_tests"; const string __DllName = "csbindgen_tests";
@ -21,6 +21,15 @@ namespace CsBindgen
[DllImport(__DllName, EntryPoint = "nullpointer_test", CallingConvention = CallingConvention.Cdecl)] [DllImport(__DllName, EntryPoint = "nullpointer_test", CallingConvention = CallingConvention.Cdecl)]
public static extern void nullpointer_test(byte* p); public static extern void nullpointer_test(byte* p);
[DllImport(__DllName, EntryPoint = "csharp_to_rust_string", CallingConvention = CallingConvention.Cdecl)]
public static extern void csharp_to_rust_string(ushort* utf16_str, int utf16_len);
[DllImport(__DllName, EntryPoint = "csharp_to_rust_utf8", CallingConvention = CallingConvention.Cdecl)]
public static extern void csharp_to_rust_utf8(byte* utf8_str, int utf8_len);
[DllImport(__DllName, EntryPoint = "csharp_to_rust_bytes", CallingConvention = CallingConvention.Cdecl)]
public static extern void csharp_to_rust_bytes(byte* bytes, int len);
[DllImport(__DllName, EntryPoint = "callback_test", CallingConvention = CallingConvention.Cdecl)] [DllImport(__DllName, EntryPoint = "callback_test", CallingConvention = CallingConvention.Cdecl)]
public static extern int callback_test(delegate* unmanaged[Cdecl]<int, int> cb); public static extern int callback_test(delegate* unmanaged[Cdecl]<int, int> cb);

View File

@ -14,9 +14,33 @@ using System.Text;
unsafe unsafe
{ {
LibRust.call_bindgen_lz4();
LibRust.alias_test1(null);
//NativeMethods.call_bindgen_lz4();
var str = "foobarbaz:あいうえお"; // JPN(Unicode)
fixed (char* p = str)
{
NativeMethods.csharp_to_rust_string((ushort*)p, str.Length);
}
var str2 = Encoding.UTF8.GetBytes("あいうえお:foobarbaz");
fixed (byte* p = str2)
{
NativeMethods.csharp_to_rust_utf8(p, str2.Length);
}
var bytes = new byte[] { 1, 10, 100, 255 };
fixed (byte* p = bytes)
{
NativeMethods.csharp_to_rust_bytes(p, bytes.Length);
}
//NativeMethods.csharp_to_rust_utf8
//NativeMethods.alias_test1(null);
@ -25,10 +49,10 @@ unsafe
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
static int Sum(int x, int y) => x + y; static int Sum(int x, int y) => x + y;
LibRust.csharp_to_rust(&Sum); NativeMethods.csharp_to_rust(&Sum);
// Rust -> C#, get typed delegate* // Rust -> C#, get typed delegate*
var f = LibRust.rust_to_csharp(); var f = NativeMethods.rust_to_csharp();
var v = f(20, 30); var v = f(20, 30);
Console.WriteLine(v); // 50 Console.WriteLine(v); // 50
@ -50,18 +74,18 @@ unsafe
//Console.WriteLine(cc); //Console.WriteLine(cc);
var context = LibRust.create_context(); // var context = LibRust.create_context();
// do anything... // // do anything...
LibRust.delete_context(context); // LibRust.delete_context(context);
var ctx = LibRust.create_counter_context(); // ctx = void* //var ctx = LibRust.create_counter_context(); // ctx = void*
LibRust.insert_counter_context(ctx, 10); //LibRust.insert_counter_context(ctx, 10);
LibRust.insert_counter_context(ctx, 20); //LibRust.insert_counter_context(ctx, 20);
LibRust.delete_counter_context(ctx); //LibRust.delete_counter_context(ctx);
//LibRust.insert_counter_context(ctx, 20); //LibRust.insert_counter_context(ctx, 20);
//LibRust.insert_counter_context(ctx, 30); //LibRust.insert_counter_context(ctx, 30);