From 906eae38870aee6cd6cc0734072a39892e8e5e59 Mon Sep 17 00:00:00 2001 From: James Hallowell <5460955+JamesHallowell@users.noreply.github.com> Date: Sun, 20 Oct 2024 20:36:23 +0100 Subject: [PATCH] Add initial support for console endpoint --- src/endpoint/mod.rs | 9 ++++ src/engine/mod.rs | 18 +++++-- src/ffi/performer.rs | 15 ++++++ src/ffi/types.rs | 5 +- src/performer/endpoints/event.rs | 9 ++-- src/performer/mod.rs | 29 +++++++++-- src/value/mod.rs | 3 +- src/value/types.rs | 10 ++++ src/value/values.rs | 17 +++++++ tests/endpoints.rs | 83 +++++++++++++++++++++++++------- 10 files changed, 162 insertions(+), 36 deletions(-) diff --git a/src/endpoint/mod.rs b/src/endpoint/mod.rs index afd1d35..2430cc6 100644 --- a/src/endpoint/mod.rs +++ b/src/endpoint/mod.rs @@ -143,6 +143,15 @@ impl EndpointInfo { } } + /// Get the endpoints type or types. + pub fn types(&self) -> &[Type] { + match self { + Self::Stream(endpoint) => std::slice::from_ref(&endpoint.ty), + Self::Event(endpoint) => &endpoint.ty, + Self::Value(endpoint) => std::slice::from_ref(&endpoint.ty), + } + } + /// Get the endpoint as a value endpoint. pub fn as_stream(&self) -> Option<&StreamEndpoint> { match self { diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 36c58e5..18c7c2e 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -8,7 +8,7 @@ use { crate::{ endpoint::{EndpointHandle, EndpointInfo}, ffi::EnginePtr, - performer::{Endpoint, EndpointError, EndpointType, Performer}, + performer::{Endpoint, EndpointError, EndpointType, OutputEvent, Performer}, program::Program, }, std::{ @@ -135,12 +135,14 @@ pub struct Idle; pub struct Loaded { program_details: ProgramDetails, endpoints: HashMap, + console: Option>, } #[doc(hidden)] #[derive(Debug)] pub struct Linked { endpoints: HashMap, + console: Option>, } impl Engine { @@ -173,13 +175,16 @@ impl Engine { let program_details = serde_json::from_str(program_details.as_ref()) .expect("failed to parse program details"); - Ok(Engine { + let mut loaded = Engine { inner: self.inner, state: Loaded { program_details, endpoints: HashMap::default(), + console: None, }, - }) + }; + loaded.state.console = loaded.endpoint("console").ok(); + Ok(loaded) } Err(error) => Err(Error::FailedToLoad(self, error.to_string().into_owned())), } @@ -223,6 +228,7 @@ impl Engine { Ok(_) => { let linked = Linked { endpoints: self.state.endpoints, + console: self.state.console, }; Ok(Engine { inner: self.inner, @@ -237,7 +243,11 @@ impl Engine { impl Engine { /// Create a performer for the linked program. pub fn performer(&self) -> Performer { - Performer::new(self.inner.create_performer(), self.state.endpoints.clone()) + Performer::new( + self.inner.create_performer(), + self.state.endpoints.clone(), + self.state.console, + ) } } diff --git a/src/ffi/performer.rs b/src/ffi/performer.rs index 694d458..1fed284 100644 --- a/src/ffi/performer.rs +++ b/src/ffi/performer.rs @@ -192,6 +192,21 @@ impl PerformerPtr { pub fn get_latency(&self) -> f64 { unsafe { ((*(*self.performer).vtable).get_latency)(self.performer) } } + + pub fn get_string_for_handle(&self, handle: u32) -> Option<&str> { + let mut length: isize = 0; + let ptr = unsafe { + ((*(*self.performer).vtable).get_string_for_handle)(self.performer, handle, &mut length) + }; + + if ptr.is_null() { + return None; + } + + let slice = unsafe { std::slice::from_raw_parts(ptr.cast::(), length as usize) }; + + std::str::from_utf8(slice).ok() + } } impl Drop for PerformerPtr { diff --git a/src/ffi/types.rs b/src/ffi/types.rs index 0013618..13f4080 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -64,9 +64,6 @@ pub(crate) enum TypeDescriptionError { #[error(transparent)] InvalidJson(#[from] json::Error), - #[error("unsupported type: {0:?}")] - UnsupportedType(String), - #[error("struct has no class")] StructHasNoClass, @@ -124,7 +121,7 @@ impl TryFrom<&TypeDescription> for Type { Ok(Array::new(element_ty, size).into()) } - ty => Err(TypeDescriptionError::UnsupportedType(format!("{:?}", ty))), + TypeTag::String => Ok(Type::String), } } } diff --git a/src/performer/endpoints/event.rs b/src/performer/endpoints/event.rs index 864fd04..399b616 100644 --- a/src/performer/endpoints/event.rs +++ b/src/performer/endpoints/event.rs @@ -87,10 +87,10 @@ pub fn post_event( } pub fn fetch_events( - performer: &mut Performer, + performer: &Performer, Endpoint(endpoint): Endpoint, mut callback: impl FnMut(usize, ValueRef<'_>), -) -> Result { +) -> Result<(), EndpointError> { let types = performer .endpoints .get(&endpoint.handle) @@ -98,8 +98,6 @@ pub fn fetch_events( .map(|endpoint| endpoint.types()) .expect("endpoint should exist and be an event endpoint"); - let mut events = 0; - performer .ptr .iterate_output_events(endpoint.handle, |frame_offset, _, type_index, data| { @@ -108,9 +106,8 @@ pub fn fetch_events( if let Some(ty) = ty { callback(frame_offset, ValueRef::new_from_slice(ty.as_ref(), data)); - events += 1; } }); - Ok(events) + Ok(()) } diff --git a/src/performer/mod.rs b/src/performer/mod.rs index 96173ed..c6015eb 100644 --- a/src/performer/mod.rs +++ b/src/performer/mod.rs @@ -17,7 +17,7 @@ use { stream::{read_stream, write_stream, StreamType}, value::{GetOutputValue, SetInputValue}, }, - value::ValueRef, + value::{StringHandle, ValueRef}, }, sealed::sealed, std::collections::HashMap, @@ -28,17 +28,26 @@ pub struct Performer { ptr: PerformerPtr, endpoints: HashMap, buffer: Vec, + console: Option>, } impl Performer { pub(crate) fn new( performer: PerformerPtr, endpoints: HashMap, + console: Option>, ) -> Self { + let size_of_largest_type = endpoints + .values() + .flat_map(|endpoint| endpoint.types().iter().map(|ty| ty.size()).max()) + .max() + .unwrap_or(0); + Performer { ptr: performer, endpoints, - buffer: vec![0; 512], + buffer: vec![0; size_of_largest_type], + console, } } } @@ -52,6 +61,15 @@ impl Performer { /// Renders the next block of frames. pub fn advance(&mut self) { self.ptr.advance(); + + if let Some(console) = self.console { + let _ = fetch_events(self, console, |_, value| match value { + ValueRef::String(StringHandle(handle)) => { + println!("{}", self.ptr.get_string_for_handle(handle).unwrap_or("?")); + } + value => println!("{value:?}"), + }); + } } /// Returns information about a given endpoint. @@ -92,7 +110,7 @@ impl Performer { &mut self, endpoint: Endpoint, callback: impl FnMut(usize, ValueRef<'_>), - ) -> Result { + ) -> Result<(), EndpointError> { fetch_events(self, endpoint, callback) } @@ -126,6 +144,11 @@ impl Performer { pub fn get_latency(&self) -> f64 { self.ptr.get_latency() } + + /// Returns the string associated with a handle. + pub fn get_string(&self, StringHandle(value): StringHandle) -> Option<&str> { + self.ptr.get_string_for_handle(value) + } } /// An error that can occur when interacting with performer endpoints. diff --git a/src/value/mod.rs b/src/value/mod.rs index d0e8fe6..df07b04 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -4,5 +4,6 @@ pub mod types; mod values; pub use values::{ - ArrayValue, ArrayValueRef, Complex32, Complex64, ObjectValue, ObjectValueRef, Value, ValueRef, + ArrayValue, ArrayValueRef, Complex32, Complex64, ObjectValue, ObjectValueRef, StringHandle, + Value, ValueRef, }; diff --git a/src/value/types.rs b/src/value/types.rs index 9ec7b25..0d839f4 100644 --- a/src/value/types.rs +++ b/src/value/types.rs @@ -14,6 +14,9 @@ pub enum Type { /// A primitive type. Primitive(Primitive), + /// A string type. + String, + /// An array type. Array(Box), @@ -49,6 +52,9 @@ pub enum TypeRef<'a> { /// A primitive type. Primitive(Primitive), + /// A string type. + String, + /// An array type. Array(&'a Array), @@ -88,6 +94,7 @@ impl Type { pub fn as_ref(&self) -> TypeRef<'_> { match self { Type::Primitive(primitive) => TypeRef::Primitive(*primitive), + Type::String => TypeRef::String, Type::Array(array) => TypeRef::Array(array.as_ref()), Type::Object(object) => TypeRef::Object(object.as_ref()), } @@ -149,6 +156,7 @@ impl TypeRef<'_> { TypeRef::Primitive(Primitive::Int64) => 8, TypeRef::Primitive(Primitive::Float32) => 4, TypeRef::Primitive(Primitive::Float64) => 8, + TypeRef::String => 4, TypeRef::Array(array) => array.size(), TypeRef::Object(object) => object.size(), } @@ -158,6 +166,7 @@ impl TypeRef<'_> { pub fn to_owned(&self) -> Type { match *self { TypeRef::Primitive(primitive) => Type::Primitive(primitive), + TypeRef::String => Type::String, TypeRef::Array(array) => Type::Array(Box::new(array.clone())), TypeRef::Object(object) => Type::Object(Box::new(object.clone())), } @@ -171,6 +180,7 @@ impl TypeRef<'_> { TypeRef::Primitive(Primitive::Float32) => vec![3], TypeRef::Primitive(Primitive::Float64) => vec![4], TypeRef::Primitive(Primitive::Bool) => vec![5], + TypeRef::String => todo!("serialising string types is not yet supported"), TypeRef::Array(array) => { let mut buffer = vec![]; buffer.put_u8(7); diff --git a/src/value/values.rs b/src/value/values.rs index b1721da..238b790 100644 --- a/src/value/values.rs +++ b/src/value/values.rs @@ -26,6 +26,9 @@ pub enum Value { /// A 64-bit floating-point value. Float64(f64), + /// A string value. + String(StringHandle), + /// An array value. Array(Box), @@ -54,6 +57,9 @@ pub enum ValueRef<'a> { /// A 64-bit floating-point value. Float64(f64), + /// A string value. + String(StringHandle), + /// An array value. Array(ArrayValueRef<'a>), @@ -61,6 +67,10 @@ pub enum ValueRef<'a> { Object(ObjectValueRef<'a>), } +/// A handle to a string value. +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub struct StringHandle(pub(crate) u32); + /// An array value. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ArrayValue { @@ -99,6 +109,7 @@ impl Value { Self::Int64(_) => TypeRef::Primitive(Primitive::Int64), Self::Float32(_) => TypeRef::Primitive(Primitive::Float32), Self::Float64(_) => TypeRef::Primitive(Primitive::Float64), + Self::String(_) => TypeRef::String, Self::Array(array) => TypeRef::Array(&array.ty), Self::Object(object) => TypeRef::Object(&object.ty), } @@ -113,6 +124,7 @@ impl Value { Self::Int64(value) => ValueRef::Int64(*value), Self::Float32(value) => ValueRef::Float32(*value), Self::Float64(value) => ValueRef::Float64(*value), + Self::String(value) => ValueRef::String(*value), Self::Array(ref array) => ValueRef::Array(array.as_ref().as_ref()), Self::Object(object) => ValueRef::Object(object.as_ref().as_ref()), } @@ -144,6 +156,7 @@ impl<'a> ValueRef<'a> { TypeRef::Primitive(Primitive::Int64) => Self::Int64(data.get_i64_ne()), TypeRef::Primitive(Primitive::Float32) => Self::Float32(data.get_f32_ne()), TypeRef::Primitive(Primitive::Float64) => Self::Float64(data.get_f64_ne()), + TypeRef::String => Self::String(StringHandle(data.get_u32_ne())), TypeRef::Array(array) => Self::Array(ArrayValueRef::new_from_slice(array, data)), TypeRef::Object(object) => Self::Object(ObjectValueRef::new_from_slice(object, data)), } @@ -174,6 +187,7 @@ impl<'a> ValueRef<'a> { Self::Int64(_) => TypeRef::Primitive(Primitive::Int64), Self::Float32(_) => TypeRef::Primitive(Primitive::Float32), Self::Float64(_) => TypeRef::Primitive(Primitive::Float64), + Self::String(_) => TypeRef::String, Self::Array(array) => TypeRef::Array(array.ty), Self::Object(object) => TypeRef::Object(object.ty), } @@ -188,6 +202,7 @@ impl<'a> ValueRef<'a> { Self::Int64(value) => Value::from(value), Self::Float32(value) => Value::from(value), Self::Float64(value) => Value::from(value), + Self::String(value) => Value::String(value), Self::Array(array) => Value::from(array.to_owned()), Self::Object(object) => Value::from(object.to_owned()), } @@ -201,6 +216,7 @@ impl<'a> ValueRef<'a> { Self::Int64(value) => callback(value.to_ne_bytes().as_slice()), Self::Float32(value) => callback(value.to_ne_bytes().as_slice()), Self::Float64(value) => callback(value.to_ne_bytes().as_slice()), + Self::String(StringHandle(value)) => callback(value.to_ne_bytes().as_slice()), Self::Array(array) => callback(array.data), Self::Object(object) => callback(object.data), } @@ -588,6 +604,7 @@ impl<'a> From<&'a Value> for ValueRef<'a> { Value::Int64(value) => Self::Int64(*value), Value::Float32(value) => Self::Float32(*value), Value::Float64(value) => Self::Float64(*value), + Value::String(value) => Self::String(*value), Value::Array(array) => Self::Array(array.as_ref().as_ref()), Value::Object(object) => Self::Object(object.as_ref().as_ref()), } diff --git a/tests/endpoints.rs b/tests/endpoints.rs index d8bdab0..10d2a9c 100644 --- a/tests/endpoints.rs +++ b/tests/endpoints.rs @@ -335,27 +335,28 @@ fn can_read_events() { performer.post(input, 5).unwrap(); performer.advance(); - assert_eq!( - performer - .fetch(output, |frame, data| { - assert_eq!(frame, 0); - assert_eq!(data, ValueRef::Int32(5)); - }) - .unwrap(), - 1 - ); + + let mut events = vec![]; + performer + .fetch(output, |frame, event| { + events.push((frame, event.to_owned())); + }) + .unwrap(); + + assert_eq!(events.len(), 1); + assert_eq!(events[0], (0, Value::Int32(5))); performer.post(input, true).unwrap(); performer.advance(); - assert_eq!( - performer - .fetch(output, |frame, data| { - assert_eq!(frame, 0); - assert_eq!(data, ValueRef::Bool(true)); - }) - .unwrap(), - 1 - ); + + performer + .fetch(output, |frame, event| { + events.push((frame, event.to_owned())); + }) + .unwrap(); + + assert_eq!(events.len(), 2); + assert_eq!(events[1], (0, Value::Bool(true))); } #[test] @@ -733,3 +734,49 @@ fn vector_stream_endpoints() { assert_eq!(output_buffer, [[2., 1.], [2., 1.], [2., 1.], [2., 1.]]); } + +#[test] +fn writing_to_console() { + const PROGRAM: &str = r#" + processor P + { + output value int out; + + void main() { + console <- "Hello, world!" <- 5 <- 3.14 <- "🦀"; + advance(); + } + } + "#; + + let (mut performer, _) = setup(PROGRAM, |_| {}); + + performer.advance(); +} + +#[test] +fn string_endpoints() { + const PROGRAM: &str = r#" + processor P + { + output value string out; + + void main() { + out <- "Cool 🫘!"; + advance(); + } + } + "#; + + let (mut performer, out) = setup(PROGRAM, |engine| engine.endpoint("out").unwrap()); + + performer.advance(); + + let value = if let ValueRef::String(string) = performer.get::(out).unwrap() { + string + } else { + panic!("expected string"); + }; + + assert_eq!(performer.get_string(value), Some("Cool 🫘!")); +}