Handle buffered CLAP stream reads and writes
`clap-validator` now tests this.
This commit is contained in:
parent
5e6e920418
commit
ed880f5297
|
@ -1,4 +1,7 @@
|
||||||
|
use clap_sys::stream::{clap_istream, clap_ostream};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
/// Early exit out of a function with the specified return value when one of the passed pointers is
|
/// Early exit out of a function with the specified return value when one of the passed pointers is
|
||||||
/// null.
|
/// null.
|
||||||
|
@ -47,3 +50,85 @@ impl<T> ClapPtr<T> {
|
||||||
Self { inner: ptr }
|
Self { inner: ptr }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A buffer a stream can be read into. This is needed to allow reading into uninitialized vectors
|
||||||
|
/// using slices without invoking UB.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This may only be implemented by slices of `u8` and types with the same representation as `u8`.
|
||||||
|
pub unsafe trait ByteReadBuffer {
|
||||||
|
/// The length of the slice, in bytes.
|
||||||
|
fn len(&self) -> usize;
|
||||||
|
|
||||||
|
/// Get a pointer to the start of the stream.
|
||||||
|
fn as_mut_ptr(&mut self) -> *mut u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl ByteReadBuffer for &mut [u8] {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
// Bit of a fun one since we reuse the names of the original functions
|
||||||
|
<[u8]>::len(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||||
|
// Bit of a fun one since we reuse the names of the original functions
|
||||||
|
<[u8]>::as_mut_ptr(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl ByteReadBuffer for &mut [MaybeUninit<u8>] {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
<[MaybeUninit<u8>]>::len(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||||
|
<[MaybeUninit<u8>]>::as_mut_ptr(self) as *mut u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from a stream until either the byte slice as been filled, or the stream doesn't contain any
|
||||||
|
/// data anymore. This correctly handles streams that only allow smaller, buffered reads. If the
|
||||||
|
/// stream ended before the entire slice has been filled, then this will return `false`.
|
||||||
|
pub fn read_stream(stream: &clap_istream, mut slice: impl ByteReadBuffer) -> bool {
|
||||||
|
let mut read_pos = 0;
|
||||||
|
while read_pos < slice.len() {
|
||||||
|
let bytes_read = unsafe {
|
||||||
|
(stream.read)(
|
||||||
|
stream,
|
||||||
|
slice.as_mut_ptr().add(read_pos) as *mut c_void,
|
||||||
|
(slice.len() - read_pos) as u64,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if bytes_read <= 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_pos += bytes_read as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the data from a slice to a stream until either all data has been written, or the stream
|
||||||
|
/// returns an error. This correctly handles streams that only allow smaller, buffered writes. This
|
||||||
|
/// returns `false` if the stream returns an error or doesn't allow any writes anymore.
|
||||||
|
pub fn write_stream(stream: &clap_ostream, slice: &[u8]) -> bool {
|
||||||
|
let mut write_pos = 0;
|
||||||
|
while write_pos < slice.len() {
|
||||||
|
let bytes_written = unsafe {
|
||||||
|
(stream.write)(
|
||||||
|
stream,
|
||||||
|
slice.as_ptr().add(write_pos) as *const c_void,
|
||||||
|
(slice.len() - write_pos) as u64,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if bytes_written <= 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_pos += bytes_written as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ use crate::plugin::{
|
||||||
ProcessStatus,
|
ProcessStatus,
|
||||||
};
|
};
|
||||||
use crate::util::permit_alloc;
|
use crate::util::permit_alloc;
|
||||||
|
use crate::wrapper::clap::util::{read_stream, write_stream};
|
||||||
use crate::wrapper::state::{self, PluginState};
|
use crate::wrapper::state::{self, PluginState};
|
||||||
use crate::wrapper::util::{hash_param_id, process_wrapper, strlcpy};
|
use crate::wrapper::util::{hash_param_id, process_wrapper, strlcpy};
|
||||||
|
|
||||||
|
@ -2872,19 +2873,19 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
// CLAP does not provide a way to tell how much data there is left in a stream, so
|
// CLAP does not provide a way to tell how much data there is left in a stream, so
|
||||||
// we need to prepend it to our actual state data.
|
// we need to prepend it to our actual state data.
|
||||||
let length_bytes = (serialized.len() as u64).to_le_bytes();
|
let length_bytes = (serialized.len() as u64).to_le_bytes();
|
||||||
let num_length_bytes_written = ((*stream).write)(
|
if !write_stream(&*stream, &length_bytes) {
|
||||||
stream,
|
nih_debug_assert_failure!(
|
||||||
length_bytes.as_ptr() as *const c_void,
|
"Error or end of stream while writing the state length to the stream."
|
||||||
length_bytes.len() as u64,
|
|
||||||
);
|
);
|
||||||
let num_bytes_written = ((*stream).write)(
|
return false;
|
||||||
stream,
|
}
|
||||||
serialized.as_ptr() as *const c_void,
|
if !write_stream(&*stream, &serialized) {
|
||||||
serialized.len() as u64,
|
nih_debug_assert_failure!(
|
||||||
|
"Error or end of stream while writing the state buffer to the stream."
|
||||||
);
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
nih_debug_assert_eq!(num_length_bytes_written as usize, length_bytes.len());
|
|
||||||
nih_debug_assert_eq!(num_bytes_written as usize, serialized.len());
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -2903,22 +2904,22 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
|
|
||||||
// CLAP does not have a way to tell how much data there is left in a stream, so we've
|
// CLAP does not have a way to tell how much data there is left in a stream, so we've
|
||||||
// prepended the size in front of our JSON state
|
// prepended the size in front of our JSON state
|
||||||
let mut length_bytes = [0; 8];
|
let mut length_bytes = [0u8; 8];
|
||||||
let num_length_bytes_read = ((*stream).read)(
|
if !read_stream(&*stream, length_bytes.as_mut_slice()) {
|
||||||
stream,
|
nih_debug_assert_failure!(
|
||||||
length_bytes.as_mut_ptr() as *mut c_void,
|
"Error or end of stream while reading the state length from the stream."
|
||||||
length_bytes.len() as u64,
|
|
||||||
);
|
);
|
||||||
nih_debug_assert_eq!(num_length_bytes_read as usize, length_bytes.len());
|
return false;
|
||||||
|
}
|
||||||
let length = u64::from_le_bytes(length_bytes);
|
let length = u64::from_le_bytes(length_bytes);
|
||||||
|
|
||||||
let mut read_buffer: Vec<u8> = Vec::with_capacity(length as usize);
|
let mut read_buffer: Vec<u8> = Vec::with_capacity(length as usize);
|
||||||
let num_bytes_read = ((*stream).read)(
|
if !read_stream(&*stream, read_buffer.spare_capacity_mut()) {
|
||||||
stream,
|
nih_debug_assert_failure!(
|
||||||
read_buffer.as_mut_ptr() as *mut c_void,
|
"Error or end of stream while reading the state buffer from the stream."
|
||||||
length as u64,
|
|
||||||
);
|
);
|
||||||
nih_debug_assert_eq!(num_bytes_read as u64, length);
|
return false;
|
||||||
|
}
|
||||||
read_buffer.set_len(length as usize);
|
read_buffer.set_len(length as usize);
|
||||||
|
|
||||||
let success = state::deserialize_json(
|
let success = state::deserialize_json(
|
||||||
|
|
Loading…
Reference in a new issue