diff --git a/Cargo.toml b/Cargo.toml index bbe9ed5..5a6120a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsl" -version = "0.1.1" +version = "0.1.3" authors = ["Intheon ", "Christian Kothe "] edition = "2018" description = "Lab streaming layer (liblsl) bindings for rust." diff --git a/src/lib.rs b/src/lib.rs index 81d02b1..5906d9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ use lsl_sys::*; use std::convert::{From, TryFrom}; use std::ffi; use std::fmt; -use std::rc; +use std::sync::Arc; use std::vec; /// Constant to indicate that a stream has variable sampling rate. @@ -186,7 +186,6 @@ pub fn local_clock() -> f64 { unsafe { lsl_local_clock() } } - // ========================== // === Stream Declaration === // ========================== @@ -217,7 +216,7 @@ github repository). You can find various uses of the `StreamInfo` object in most #[derive(Debug)] pub struct StreamInfo { // internal fields - handle: rc::Rc, + handle: Arc, } impl StreamInfo { @@ -270,7 +269,9 @@ impl StreamInfo { source_id.as_ptr(), ); match handle.is_null() { - false => Ok(StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle }) }), + false => Ok(StreamInfo { + handle: Arc::new(StreamInfoHandle { handle }), + }), true => Err(Error::ResourceCreation), } } @@ -290,7 +291,7 @@ impl StreamInfo { experimenter). */ pub fn stream_name(&self) -> String { - unsafe { make_string(lsl_get_name(self.handle.handle )) } + unsafe { make_string(lsl_get_name(self.handle.handle)) } } /** @@ -303,7 +304,7 @@ impl StreamInfo { search for: XDF meta-data). */ pub fn stream_type(&self) -> String { - unsafe { make_string(lsl_get_type(self.handle.handle )) } + unsafe { make_string(lsl_get_type(self.handle.handle)) } } /** @@ -311,7 +312,7 @@ impl StreamInfo { A stream has at least one channel; the channel count stays constant for all samples. */ pub fn channel_count(&self) -> i32 { - unsafe { lsl_get_channel_count(self.handle.handle ) } + unsafe { lsl_get_channel_count(self.handle.handle) } } /** @@ -418,7 +419,7 @@ impl StreamInfo { cursor: lsl_get_desc(self.handle.handle), // keep a shared ref of the underlying native handle since the xml element or // elements obtained from it may outlive the StreamInfo object - doc: self.handle.clone() + doc: self.handle.clone(), } } } @@ -487,7 +488,9 @@ impl StreamInfo { unsafe { let handle = lsl_streaminfo_from_xml(xml.as_ptr()); match handle.is_null() { - false => Ok(StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle }) }), + false => Ok(StreamInfo { + handle: Arc::new(StreamInfoHandle { handle }), + }), true => Err(Error::ResourceCreation), } } @@ -506,7 +509,9 @@ impl StreamInfo { !handle.is_null(), "Attempted to create a StreamInfo from a NULL handle." ); - StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle } ) } + StreamInfo { + handle: Arc::new(StreamInfoHandle { handle }), + } } // Get the native implementation handle. @@ -523,7 +528,9 @@ impl Clone for StreamInfo { !handle.is_null(), "Failed to clone native lsl_streaminfo object." ); - StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle }) } + StreamInfo { + handle: Arc::new(StreamInfoHandle { handle }), + } } } } @@ -662,23 +669,29 @@ impl StreamOutlet { Arguments: * `func`: the native FFI function to call to push a sample - * `data`: A vector of values to push (one for each channel). + * `data`: A collection of values, treatable as a slice, to push (one for each channel). * `timestamp`: Optionally the capture time of the sample, in agreement with `local_clock()`; if passed as 0.0, the current time is used. * `pushthrough`: Whether to push the sample through to the receivers instead of buffering it with subsequent samples. Typically this would be `true`. Note that the `chunk_size`, if specified at outlet construction, takes precedence over the pushthrough flag. */ - fn safe_push_numeric( + fn safe_push_numeric, V>( &self, - func: NativePushFunction, - data: &vec::Vec, + func: NativePushFunction, + data: &T, timestamp: f64, pushthrough: bool, ) -> Result<()> { + let data = data.as_ref(); self.assert_len(data.len()); unsafe { - errcode_to_result(func(self.handle, data.as_ptr(), timestamp, pushthrough as i32))?; + errcode_to_result(func( + self.handle, + data.as_ptr(), + timestamp, + pushthrough as i32, + ))?; } Ok(()) } @@ -688,19 +701,20 @@ impl StreamOutlet { byte slices via `.as_ref()`. Arguments: - * `data`: A vector of values to push (one for each channel). + * `data`: A collection of values, treatable as a slice, to push (one for each channel). * `timestamp`: Optionally the capture time of the sample, in agreement with `local_clock()`; if passed as 0.0, the current time is used. * `pushthrough`: Whether to push the sample through to the receivers instead of buffering it with subsequent samples. Typically this would be `true`. Note that the `chunk_size`, if specified at outlet construction, takes precedence over the pushthrough flag. */ - fn safe_push_blob>( + fn safe_push_blob, T: AsRef<[u8]>>( &self, - data: &vec::Vec, + data: &U, timestamp: f64, pushthrough: bool, ) -> Result<()> { + let data = data.as_ref(); self.assert_len(data.len()); let ptrs: Vec<_> = data.iter().map(|x| x.as_ref().as_ptr()).collect(); let lens: Vec<_> = data @@ -725,15 +739,16 @@ A trait that enables the methods `push_sample()` and `push_chunk()`. Imple StreamOutlet. See also the `ExPushable` trait for the extended-argument versions of these methods, -`push_sample_ex()` and `push_chunk_ex()`. +`push_sample_ex()` and `push_chunk_ex()`. **Note:** If you push in data that as the wrong size (array length not matching the declared number of channels), these functions will trigger an assertion and panic. */ -pub trait Pushable { +pub trait Pushable, V> { /** - Push a vector of values of some type as a sample into the outlet. Each entry in the vector - corresponds to one channel. The function handles type checking & conversion. + Push a collection, treatable as a slice, of values of some type as a sample into the outlet. + Each entry in the collection corresponds to one channel. The function handles type + checking & conversion. The data are time-stamped with the current time (using `local_clock()`), and immediately transmitted (unless a `chunk_size` was provided at outlet construction, which overrides in what @@ -745,7 +760,8 @@ pub trait Pushable { /** Push a chunk of samples (batched into a `Vec`) into the outlet. Each element of the given - vector must itself be in a format accepted by `push_sample()` (e.g., `Vec`). + vector must itself be in a format accepted by `push_sample()` (e.g. all types that can be + treated as a slice). The data are time-stamped with the current time (using `local_clock()`), and immediately transmitted (unless a `chunk_size` was provided at outlet construction, which causes the data @@ -760,7 +776,8 @@ pub trait Pushable { sample (for irregular-rate streams) into the outlet. Arguments: - * `samples`: A `Vec` of samples, each in a format accepted by `push_sample()` (e.g., `Vec`). + * `samples`: A `Vec` of samples, each in a format accepted by `push_sample()` (e.g. all types + that can be treated as a slice). * `timestamps`: A `Vec` of capture times for each sample, in agreement with `local_clock()`. The data are immediately transmitted (unless a `chunk_size` was provided at outlet @@ -772,7 +789,11 @@ pub trait Pushable { } // Pushable is basically a convenience layer on top of ExPushable -impl> Pushable for U { +impl Pushable for U +where + T: AsRef<[V]>, + U: ExPushable, +{ fn push_sample(&self, data: &T) -> Result<()> { self.push_sample_ex(data, 0.0, true) } @@ -787,7 +808,7 @@ impl> Pushable for U { } /** -A trait that enables the methods `push_sample_ex()` and `push_chunk_ex()`. +A trait that enables the methods `push_sample_ex()` and `push_chunk_ex()`. Implemented by StreamOutlet. See also the `Pushable` trait for the simpler methods `push_sample()` and `push_chunk()`. @@ -795,14 +816,14 @@ See also the `Pushable` trait for the simpler methods `push_sample()` and `pu **Note:** If you push in data that as the wrong size (array length not matching the declared number of channels), these functions will trigger an assertion and panic. */ -pub trait ExPushable: HasNominalRate { +pub trait ExPushable, V>: HasNominalRate { /** Push a vector of values of some type as a sample into the outlet. Each entry in the vector corresponds to one channel. The function handles type checking & conversion. Arguments: - * `data`: A vector of values to push (one for each channel). + * `data`: A collection of values, treatable as a slice, to push (one for each channel). * `timestamp`: Optionally the capture time of the sample, in agreement with `local_clock()`; if passed as 0.0, the current time is used. * `pushthrough`: Whether to push the sample through to the receivers instead of buffering it @@ -818,7 +839,8 @@ pub trait ExPushable: HasNominalRate { Push a chunk of samples (batched into a `Vec`) into the outlet. Arguments: - * `samples`: A `Vec` of samples, each in a format accepted by `push_sample()` (e.g., `Vec`). + * `samples`: A `Vec` of samples, each in a format accepted by `push_sample()` (e.g. all types + that can be treated as a slice). * `timestamp`: Optionally the capture time of the most recent sample, in agreement with `local_clock()`; if specified as 0.0, the current time is used. The time stamps of other samples are automatically derived according to the sampling rate of the stream. @@ -861,7 +883,8 @@ pub trait ExPushable: HasNominalRate { Allows for specifying a separate time stamp for each sample (for irregular-rate streams). Arguments: - * `samples`: A `Vec` of samples, each in a format accepted by `push_sample()` (e.g., `Vec`). + * `samples`: A `Vec` of samples, each in a format accepted by `push_sample()` (e.g. all types + that can be treated as a slice). * `timestamps`: A `Vec` of capture times for each sample, in agreement with `local_clock()`. * `pushthrough`: Whether to push the chunk through to the receivers instead of buffering it with subsequent samples. Typically this would be `true`. Note that the `chunk_size`, if @@ -887,57 +910,83 @@ pub trait ExPushable: HasNominalRate { } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec, timestamp: f64, pushthrough: bool) -> Result<()> { +impl ExPushable for StreamOutlet +where + T: AsRef<[f32]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_numeric(lsl_push_sample_ftp, data, timestamp, pushthrough) } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec, timestamp: f64, pushthrough: bool) -> Result<()> { +impl ExPushable for StreamOutlet +where + T: AsRef<[f64]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_numeric(lsl_push_sample_dtp, data, timestamp, pushthrough) } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec, timestamp: f64, pushthrough: bool) -> Result<()> { +impl ExPushable for StreamOutlet +where + T: AsRef<[i8]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_numeric(lsl_push_sample_ctp, data, timestamp, pushthrough) } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec, timestamp: f64, pushthrough: bool) -> Result<()> { +impl ExPushable for StreamOutlet +where + T: AsRef<[i16]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_numeric(lsl_push_sample_stp, data, timestamp, pushthrough) } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec, timestamp: f64, pushthrough: bool) -> Result<()> { +impl ExPushable for StreamOutlet +where + T: AsRef<[i32]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_numeric(lsl_push_sample_itp, data, timestamp, pushthrough) } } -#[cfg(not(windows))] // TODO: once we upgrade to liblsl 1.14, we can drop this platform restriction -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec, timestamp: f64, pushthrough: bool) -> Result<()> { +impl ExPushable for StreamOutlet +where + T: AsRef<[i64]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_numeric(lsl_push_sample_ltp, data, timestamp, pushthrough) } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec, timestamp: f64, pushthrough: bool) -> Result<()> { +impl ExPushable for StreamOutlet +where + T: AsRef<[String]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_blob(data, timestamp, pushthrough) } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec<&str>, timestamp: f64, pushthrough: bool) -> Result<()> { +impl<'a, T> ExPushable for StreamOutlet +where + T: AsRef<[&'a str]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_blob(data, timestamp, pushthrough) } } -impl ExPushable> for StreamOutlet { - fn push_sample_ex(&self, data: &vec::Vec<&[u8]>, timestamp: f64, pushthrough: bool) -> Result<()> { +impl<'a, T> ExPushable for StreamOutlet +where + T: AsRef<[&'a [u8]]>, +{ + fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()> { self.safe_push_blob(data, timestamp, pushthrough) } } @@ -1705,7 +1754,7 @@ intermittent zero bytes (otherwise this will trigger an assertion). pub struct XMLElement { // internal fields cursor: lsl_xml_ptr, - doc: rc::Rc, + doc: Arc, } impl XMLElement { @@ -2110,7 +2159,9 @@ impl Drop for ContinuousResolver { // wrapper around a native streaminfo handle #[derive(Debug)] -struct StreamInfoHandle { handle: lsl_streaminfo } +struct StreamInfoHandle { + handle: lsl_streaminfo, +} impl Drop for StreamInfoHandle { fn drop(&mut self) { @@ -2213,9 +2264,7 @@ impl std::error::Error for Error {} fn make_cstring(s: &str) -> ffi::CString { // If you're getting this, you passed a string containing 0 bytes to the library. In the // context where it happened, this is a fatal error. - ffi::CString::new(s).expect( - "Embedded zero bytes are invalid in strings passed to liblsl.", - ) + ffi::CString::new(s).expect("Embedded zero bytes are invalid in strings passed to liblsl.") } // Internal function that creates a String from a const char* returned by a trusted C routine.