This commit is contained in:
Maurice 2023-10-17 19:00:03 +02:00
commit 828e08c6ce
8 changed files with 564 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
bin/
obj/

345
Cargo.lock generated Normal file
View File

@ -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"

15
Cargo.toml Normal file
View File

@ -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"

43
FfiChannels/Ffi.cs Normal file
View File

@ -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<byte[]> ReceiveAsync() {
return Task.Run(() => {
Console.WriteLine("Receiving..");
var ptr = receive(); //blocking call
Console.WriteLine("Received!");
return ptr.ToByteArray();
});
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<NativeOutputPath>../target/debug</NativeOutputPath>
<LibraryName>ffi_channels</LibraryName>
</PropertyGroup>
<ItemGroup>
<None Condition="$([MSBuild]::IsOsPlatform('MacOS'))" Include="$(NativeOutputPath)/lib$(LibraryName).dylib" CopyToOutputDirectory="PreserveNewest" />
<None Condition="$([MSBuild]::IsOsPlatform('Linux'))" Include="$(NativeOutputPath)/lib$(LibraryName).so" CopyToOutputDirectory="PreserveNewest" />
<None Condition="$([MSBuild]::IsOsPlatform('Windows'))" Include="$(NativeOutputPath)/$(LibraryName).dll" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

25
FfiChannels/Program.cs Normal file
View File

@ -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)}");

25
ffichannels.sln Normal file
View File

@ -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

87
src/lib.rs Normal file
View File

@ -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<Vec<u8>>, Receiver<Vec<u8>>)> = OnceCell::new();
static CALLBACK: RwLock<Option<Callback>> = RwLock::new(None);
fn get_channel() -> &'static (Sender<Vec<u8>>, Receiver<Vec<u8>>) {
CHANNEL.get_or_init_blocking(|| {
println!("Initialized channel");
unbounded()
})
}
fn get_sender() -> &'static Sender<Vec<u8>> {
&get_channel().0
}
fn get_receiver() -> &'static Receiver<Vec<u8>> {
&get_channel().1
}
#[repr(C)]
pub struct ByteArray {
data: *const u8,
length: usize
}
impl From<Vec<u8>> for ByteArray {
fn from(value: Vec<u8>) -> 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)
}