diff --git a/src/runtime/ffi.rs b/src/runtime/ffi.rs index de43f4181242967f4393013ef30171d267bfcef8..45ca85619c84dace3b62f3944c6dc506d237fce7 100644 --- a/src/runtime/ffi.rs +++ b/src/runtime/ffi.rs @@ -1,7 +1,9 @@ use super::*; use core::cell::RefCell; -use std::os::raw::{c_char, c_int, c_uchar, c_uint}; +use core::convert::TryFrom; +use std::slice::from_raw_parts as slice_from_parts; +// use std::os::raw::{c_char, c_int, c_uchar, c_uint}; #[derive(Default)] struct StoredError { @@ -15,24 +17,35 @@ impl StoredError { // no null terminator either! self.buf.clear(); } - fn store(&mut self, error: &E) { - write!(&mut self.buf, "{:?}", error); + fn debug_store(&mut self, error: &E) { + let _ = write!(&mut self.buf, "{:?}", error); self.buf.push(Self::NULL_TERMINATOR); } - fn tl_store(error: &E) { + fn tl_debug_store(error: &E) { STORED_ERROR.with(|stored_error| { let mut stored_error = stored_error.borrow_mut(); stored_error.clear(); - stored_error.store(error); + stored_error.debug_store(error); }) } + // fn bytes_store(&mut self, bytes: &[u8]) { + // let _ = self.buf.write_all(bytes); + // self.buf.push(Self::NULL_TERMINATOR); + // } + // fn tl_bytes_store(bytes: &[u8]) { + // STORED_ERROR.with(|stored_error| { + // let mut stored_error = stored_error.borrow_mut(); + // stored_error.clear(); + // stored_error.bytes_store(bytes); + // }) + // } fn tl_clear() { STORED_ERROR.with(|stored_error| { let mut stored_error = stored_error.borrow_mut(); stored_error.clear(); }) } - fn tl_raw_peek() -> (*const u8, usize) { + fn tl_bytes_peek() -> (*const u8, usize) { STORED_ERROR.with(|stored_error| { let stored_error = stored_error.borrow(); match stored_error.buf.len() { @@ -51,7 +64,7 @@ thread_local! { type ErrorCode = i32; -////////////////////////////////////// +///////////////////// REOWOLF ////////////////////////// /// Returns length (via out pointer) and pointer (via return value) of the last Reowolf error. /// - pointer is NULL iff there was no last error @@ -59,51 +72,331 @@ type ErrorCode = i32; /// - len does NOT include the length of the null-delimiter #[no_mangle] pub unsafe extern "C" fn reowolf_error_peek(len: *mut usize) -> *const u8 { - let (err_ptr, err_len) = StoredError::tl_raw_peek(); + let (err_ptr, err_len) = StoredError::tl_bytes_peek(); len.write(err_len); err_ptr } +///////////////////// PROTOCOL DESCRIPTION ////////////////////////// + +/// Parses the utf8-encoded string slice to initialize a new protocol description object. +/// - On success, initializes `out` and returns 0 +/// - On failure, stores an error string (see `reowolf_error_peek`) and returns -1 #[no_mangle] pub unsafe extern "C" fn protocol_description_parse( pdl: *const u8, pdl_len: usize, - pd: *mut Arc, + out: *mut Arc, ) -> ErrorCode { StoredError::tl_clear(); - let slice: *const [u8] = std::slice::from_raw_parts(pdl, pdl_len); - let slice: &[u8] = &*slice; - match ProtocolDescription::parse(slice) { + match ProtocolDescription::parse(&*slice_from_parts(pdl, pdl_len)) { Ok(new) => { - pd.write(Arc::new(new)); + out.write(Arc::new(new)); 0 } Err(err) => { - StoredError::tl_store(&err); + StoredError::tl_debug_store(&err); -1 } } } +/// Destroys the given initialized protocol description and frees its resources. #[no_mangle] -pub unsafe extern "C" fn protocol_description_destroy(pd: Arc) { - drop(pd) +pub unsafe extern "C" fn protocol_description_destroy(pd: *mut Arc) { + drop(Box::from_raw(pd)) } +/// Given an initialized protocol description, initializes `out` with a clone which can be independently created or destroyed. #[no_mangle] pub unsafe extern "C" fn protocol_description_clone( pd: &Arc, -) -> Arc { - pd.clone() + out: *mut Arc, +) { + out.write(pd.clone()); +} + +///////////////////// CONNECTOR ////////////////////////// + +/// Initializes `out` with a new connector using the given protocol description as its configuration. +/// The connector is assigned a random (internal) connector ID. +#[no_mangle] +pub unsafe extern "C" fn connector_new(pd: &Arc, out: *mut Connector) { + connector_new_with_id(pd, random_connector_id(), out) +} + +/// Initializes `out` with a new connector using the given protocol description as its configuration. +/// The connector uses the given (internal) connector ID. +#[no_mangle] +pub unsafe extern "C" fn connector_new_with_id( + pd: &Arc, + cid: ConnectorId, + out: *mut Connector, +) { + out.write(Connector::new(Box::new(DummyLogger), pd.clone(), cid, 8)) +} + +/// Destroys the given initialized connector and frees its resources. +/// Usable in {setup, communication} states. +#[no_mangle] +pub unsafe extern "C" fn connector_destroy(connector: Connector) { + drop(connector) +} + +/// Given an initialized connector in setup or connecting state, +/// - Creates a new directed port pair with logical channel putter->getter, +/// - adds the ports to the native component's interface, +/// - and returns them using the given out pointers. +/// Usable in {setup, communication} states. +#[no_mangle] +pub unsafe extern "C" fn connector_add_port_pair( + connector: &mut Connector, + out_putter: *mut PortId, + out_getter: *mut PortId, +) { + let [o, i] = connector.new_port_pair(); + out_putter.write(o); + out_getter.write(i); +} + +/// Given +/// - an initialized connector in setup or connecting state, +/// - a string slice for the component's identifier in the connector's configured protocol description, +/// - a set of ports (represented as a slice; duplicates are ignored) in the native component's interface, +/// the connector creates a new (internal) protocol component C, such that the set of native ports are moved to C. +/// Usable in {setup, communication} states. +#[no_mangle] +pub unsafe extern "C" fn connector_add_component( + connector: &mut Connector, + ident_ptr: *const u8, + ident_len: usize, + ports_ptr: *const PortId, + ports_len: usize, +) -> ErrorCode { + StoredError::tl_clear(); + match connector.add_component( + &*slice_from_parts(ident_ptr, ident_len), + &*slice_from_parts(ports_ptr, ports_len), + ) { + Ok(()) => 0, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } +} + +/// Given +/// - an initialized connector in setup or connecting state, +/// - a utf-8 encoded socket address, +/// - the logical polarity of P, +/// - the "physical" polarity in {Active, Passive} of the endpoint through which P's peer will be discovered, +/// returns P, a port newly added to the native interface. +#[no_mangle] +pub unsafe extern "C" fn connector_add_net_port( + connector: &mut Connector, + addr_str_ptr: *const u8, + addr_str_len: usize, + port_polarity: Polarity, + endpoint_polarity: EndpointPolarity, + port: *mut PortId, +) -> ErrorCode { + StoredError::tl_clear(); + let addr_bytes = &*slice_from_parts(addr_str_ptr, addr_str_len); + let addr_str = match std::str::from_utf8(addr_bytes) { + Ok(addr_str) => addr_str, + Err(err) => { + StoredError::tl_debug_store(&err); + return -1; + } + }; + let sock_address: SocketAddr = match addr_str.parse() { + Ok(addr) => addr, + Err(err) => { + StoredError::tl_debug_store(&err); + return -2; + } + }; + match connector.new_net_port(port_polarity, sock_address, endpoint_polarity) { + Ok(p) => { + port.write(p); + 0 + } + Err(err) => { + StoredError::tl_debug_store(&err); + -3 + } + } +} + +/// Connects this connector to the distributed system of connectors reachable through endpoints, +/// Usable in setup state, and changes the state to communication. +#[no_mangle] +pub unsafe extern "C" fn connector_connect( + connector: &mut Connector, + timeout_millis: i64, +) -> ErrorCode { + StoredError::tl_clear(); + let option_timeout_millis: Option = TryFrom::try_from(timeout_millis).ok(); + let timeout = option_timeout_millis.map(Duration::from_millis); + match connector.connect(timeout) { + Ok(()) => 0, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } } -// #[no_mangle] -// pub extern "C" fn connector_new(pd: *const Arc) -> *mut Connector { -// Box::into_raw(Box::new(Connector::default())) -// } +#[no_mangle] +pub unsafe extern "C" fn connector_put_payload( + connector: &mut Connector, + port: PortId, + payload: *mut Payload, +) -> ErrorCode { + match connector.put(port, payload.read()) { + Ok(()) => 0, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } +} -// /// Creates and returns Reowolf Connector structure allocated on the heap. -// #[no_mangle] -// pub extern "C" fn connector_with_controller_id(controller_id: ControllerId) -> *mut Connector { -// Box::into_raw(Box::new(Connector::Unconfigured(Unconfigured { controller_id }))) -// } +#[no_mangle] +pub unsafe extern "C" fn connector_put_payload_cloning( + connector: &mut Connector, + port: PortId, + payload: &Payload, +) -> ErrorCode { + match connector.put(port, payload.clone()) { + Ok(()) => 0, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } +} + +/// Convenience function combining the functionalities of +/// "payload_new" with "connector_put_payload". +#[no_mangle] +pub unsafe extern "C" fn connector_put_bytes( + connector: &mut Connector, + port: PortId, + bytes_ptr: *const u8, + bytes_len: usize, +) -> ErrorCode { + StoredError::tl_clear(); + let payload: Payload = { + let payload: *mut Payload = std::ptr::null_mut(); // uninitialized + payload_new(bytes_ptr, bytes_len, payload); // initializes; + payload.read() + }; + match connector.put(port, payload) { + Ok(()) => 0, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn connector_get(connector: &mut Connector, port: PortId) -> ErrorCode { + match connector.get(port) { + Ok(()) => 0, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn connector_next_batch(connector: &mut Connector) -> isize { + match connector.next_batch() { + Ok(n) => n as isize, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn connector_sync(connector: &mut Connector, timeout_millis: i64) -> isize { + StoredError::tl_clear(); + let option_timeout_millis: Option = TryFrom::try_from(timeout_millis).ok(); + let timeout = option_timeout_millis.map(Duration::from_millis); + match connector.sync(timeout) { + Ok(n) => n as isize, + Err(err) => { + StoredError::tl_debug_store(&err); + -1 + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn connector_gotten_bytes( + connector: &mut Connector, + port: PortId, + len: *mut usize, +) -> *const u8 { + StoredError::tl_clear(); + match connector.gotten(port) { + Ok(payload_borrow) => { + let slice = payload_borrow.as_slice(); + len.write(slice.len()); + slice.as_ptr() + } + Err(err) => { + StoredError::tl_debug_store(&err); + std::ptr::null() + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn connector_gotten_payload( + connector: &mut Connector, + port: PortId, +) -> *const Payload { + StoredError::tl_clear(); + match connector.gotten(port) { + Ok(payload_borrow) => payload_borrow, + Err(err) => { + StoredError::tl_debug_store(&err); + std::ptr::null() + } + } +} + +///////////////////// PAYLOAD ////////////////////////// +#[no_mangle] +pub unsafe extern "C" fn payload_new( + bytes_ptr: *const u8, + bytes_len: usize, + out_payload: *mut Payload, +) { + let bytes: &[u8] = &*slice_from_parts(bytes_ptr, bytes_len); + out_payload.write(Payload::from(bytes)); +} + +#[no_mangle] +pub unsafe extern "C" fn payload_destroy(payload: *mut Payload) { + drop(Box::from_raw(payload)) +} + +#[no_mangle] +pub unsafe extern "C" fn payload_clone(payload: &Payload, out_payload: *mut Payload) { + out_payload.write(payload.clone()) +} + +#[no_mangle] +pub unsafe extern "C" fn payload_peek_bytes(payload: &Payload, bytes_len: *mut usize) -> *const u8 { + let slice = payload.as_slice(); + bytes_len.write(slice.len()); + slice.as_ptr() +}