commit 828e08c6ceedbcfaae548d78e99fd997a6145241 Author: Maurice Date: Tue Oct 17 19:00:03 2023 +0200 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4c48f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +bin/ +obj/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e248be1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,345 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "concurrent-queue" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "ffichannels" +version = "0.1.0" +dependencies = [ + "async-channel", + "async-io", + "async-lock", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "parking" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.37.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..88af973 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ffichannels" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "ffi_channels" +crate-type = ["cdylib"] + +[dependencies] +async-channel = "1.9.0" +async-io = "1.13.0" +async-lock = "2.8.0" diff --git a/FfiChannels/Ffi.cs b/FfiChannels/Ffi.cs new file mode 100644 index 0000000..ac92402 --- /dev/null +++ b/FfiChannels/Ffi.cs @@ -0,0 +1,43 @@ +using System.Runtime.InteropServices; + +namespace FfiChannels +{ + [StructLayout(LayoutKind.Sequential)] + public struct ByteArray + { + public IntPtr data; + public int length; + + public byte[] ToByteArray() { + var array = new byte[length]; + Marshal.Copy(data, array, 0, length); + return array; + } + } + + public delegate void Callback(ByteArray byteArray); + + public static class Bindings { + + [DllImport("ffi_channels")] + public static extern void send(byte[] data, int length); + + [DllImport("ffi_channels")] + public static extern ByteArray receive(); + + [DllImport("ffi_channels")] + public static extern void register_callback(Callback cb); + + [DllImport("ffi_channels")] + public static extern void start_listening(); + + public static Task ReceiveAsync() { + return Task.Run(() => { + Console.WriteLine("Receiving.."); + var ptr = receive(); //blocking call + Console.WriteLine("Received!"); + return ptr.ToByteArray(); + }); + } + } +} \ No newline at end of file diff --git a/FfiChannels/FfiChannels.csproj b/FfiChannels/FfiChannels.csproj new file mode 100644 index 0000000..c02b145 --- /dev/null +++ b/FfiChannels/FfiChannels.csproj @@ -0,0 +1,21 @@ + + + + Exe + net7.0 + enable + enable + + + + ../target/debug + ffi_channels + + + + + + + + + diff --git a/FfiChannels/Program.cs b/FfiChannels/Program.cs new file mode 100644 index 0000000..040527d --- /dev/null +++ b/FfiChannels/Program.cs @@ -0,0 +1,25 @@ +using FfiChannels; + +Bindings.register_callback((bytes) => { + var data = bytes.ToByteArray(); + Console.WriteLine($"Callback called, bytes: {string.Join(',',data)}"); +}); +Bindings.start_listening(); + +Console.WriteLine("Press key to send"); +Console.ReadKey(true); + +Bindings.send(new byte[]{ 1,2,3,4,5,6,7,8,9,10 }, 10); + +Console.WriteLine("Press key to exit"); +Console.ReadKey(true); + +// var receiving = Bindings.ReceiveAsync(); +// Console.WriteLine("Press key to send"); +// Console.ReadKey(true); +// Console.WriteLine("Hello world, sending data to Rust:"); +// Bindings.send(new byte[]{ 1,2,3,4,5,6,7,8,9,10 }, 10); +// Console.WriteLine("Press a key to receive"); +// Console.ReadKey(true); +// var data = await receiving; +// Console.WriteLine($"Received: {string.Join(',',data)}"); \ No newline at end of file diff --git a/ffichannels.sln b/ffichannels.sln new file mode 100644 index 0000000..e45881f --- /dev/null +++ b/ffichannels.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FfiChannels", "FfiChannels\FfiChannels.csproj", "{BD86F9D2-3857-47AA-B9E8-842FB91131CB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BD86F9D2-3857-47AA-B9E8-842FB91131CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD86F9D2-3857-47AA-B9E8-842FB91131CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD86F9D2-3857-47AA-B9E8-842FB91131CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD86F9D2-3857-47AA-B9E8-842FB91131CB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AFBE47FA-0D1A-4EBF-B552-B050F83A917C} + EndGlobalSection +EndGlobal diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3a8417d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,87 @@ +use std::{slice, thread}; + +use async_channel::{Sender, Receiver, unbounded}; +use async_io::block_on; +use async_lock::{OnceCell, RwLock}; + +type Callback = extern "C" fn (ByteArray); + +static CHANNEL: OnceCell<(Sender>, Receiver>)> = OnceCell::new(); +static CALLBACK: RwLock> = RwLock::new(None); + +fn get_channel() -> &'static (Sender>, Receiver>) { + CHANNEL.get_or_init_blocking(|| { + println!("Initialized channel"); + unbounded() + }) +} + +fn get_sender() -> &'static Sender> { + &get_channel().0 +} + +fn get_receiver() -> &'static Receiver> { + &get_channel().1 +} + +#[repr(C)] +pub struct ByteArray { + data: *const u8, + length: usize +} + +impl From> for ByteArray { + fn from(value: Vec) -> Self { + ByteArray { data: value.as_ptr(), length: value.len() } + } +} + +#[no_mangle] +pub extern "C" fn register_callback(cb: Callback) { + block_on(async { + let mut writer = CALLBACK.write().await; + *writer = Some(cb); + }); +} + +#[no_mangle] +pub extern "C" fn start_listening() { + let receiver = get_receiver().clone(); + thread::spawn(move|| { + block_on(async { + println!("Start listening for received events"); + loop { + let next = receiver.recv().await; + if let Ok(data) = next { + println!("Received data: {:?}", data); + let reader = CALLBACK.read().await; + if reader.is_some() { + println!("Calling callback"); + let cb = reader.unwrap(); + let bytes = ByteArray::from(data); + cb(bytes); + } else { + println!("No callback"); + } + } + } + }) + }); +} + +#[no_mangle] +pub extern "C" fn send(data: *const u8, length: usize) { + let byte_slice = unsafe { + assert!(!data.is_null()); + slice::from_raw_parts(data, length) + }; + + get_sender().send_blocking(byte_slice.to_vec()).unwrap(); + println!("Received {} bytes, array: {:?}", length, byte_slice); +} + +#[no_mangle] +pub extern "C" fn receive() -> ByteArray { + let data = get_receiver().recv_blocking().unwrap(); + ByteArray::from(data) +} \ No newline at end of file