Skip to content

Commit fb02f45

Browse files
committed
Refactor API and Core to use raw pointers for plugin namespaces and IDs
- Updated `plugin_by_namespace` and `plugin_by_id` methods in `api.rs` to accept raw pointers instead of `&str`, removing the need for `CString`. - Modified `CoreRef` methods to create `CString` from `&str` before passing to API functions. - Enhanced `Node` and `FilterDependency` structs to support lifetimes, ensuring proper ownership semantics. - Updated various methods in `CoreRef`, `Node`, and `Filter` traits to reflect changes in type signatures and improve safety. - Added new methods in `CoreRef` for retrieving standard, resize, and text plugins. - Adjusted tests to use `std::f64::consts::PI` instead of hardcoded values for better accuracy.
1 parent 3a97c0d commit fb02f45

14 files changed

Lines changed: 256 additions & 222 deletions

File tree

example/src/lib.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,24 @@ mod plugin {
1818
const FLAGS: i32 = PluginConfigFlags::NONE.bits();
1919

2020
#[vapoursynth_filter(video)]
21-
struct Invert {
22-
input_node: Node,
21+
#[derive(Clone)]
22+
struct Invert<'core> {
23+
input_node: Node<'core>,
2324
}
2425

2526
// Just implement the trait methods and the macro handles all C FFI
26-
impl Filter for Invert {
27+
impl<'core> Filter<'core> for Invert<'core> {
2728
const NAME: &'static str = "Invert";
2829
const ARGS: &'static str = "clip:vnode;";
2930
const RETURNTYPE: &'static str = "clip:vnode;";
3031
const MODE: FilterMode = FilterMode::Parallel;
3132

32-
fn from_args(args: &Map, _core: &CoreRef) -> Result<Self, String> {
33+
fn from_args(args: &Map<'core>, _core: &CoreRef<'core>) -> Result<Self, String> {
3334
let input_node = args.get_node("clip")?;
3435
Ok(Self { input_node })
3536
}
3637

37-
fn get_dependencies(&self) -> Vec<FilterDependency> {
38+
fn get_dependencies(&self) -> Vec<FilterDependency<'core>> {
3839
vec![FilterDependency {
3940
source: self.input_node.clone(),
4041
request_pattern: RequestPattern::StrictSpatial,
@@ -47,13 +48,13 @@ mod plugin {
4748
.request_frame_filter(n, frame_ctx);
4849
}
4950

50-
fn process_frame<'core>(
51+
fn process_frame<'frame>(
5152
&mut self,
5253
n: i32,
5354
_frame_data: &[u8; 4],
5455
frame_ctx: &FrameContext,
55-
core: CoreRef<'core>,
56-
) -> Result<Frame<'core>, String> {
56+
core: CoreRef<'frame>,
57+
) -> Result<Frame<'frame>, String> {
5758
let src = self.input_node.get_frame_filter(n, frame_ctx).unwrap();
5859
let vf = src.get_video_format().unwrap();
5960
let height = src.get_height(0);

rustsynth-derive/src/lib.rs

Lines changed: 146 additions & 130 deletions
Large diffs are not rendered by default.

rustsynth/src/api.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Module for interacting with the VapourSynth API
22
use rustsynth_sys as ffi;
33
use std::{
4-
ffi::{c_char, c_int, c_void, CString},
4+
ffi::{c_char, c_int, c_void},
55
ptr::{self, NonNull},
66
sync::atomic::{AtomicPtr, Ordering},
77
};
@@ -147,13 +147,12 @@ impl API {
147147

148148
pub(crate) fn plugin_by_namespace<'core>(
149149
&self,
150-
namespace: &str,
150+
namespace: *const c_char,
151151
core: &CoreRef<'core>,
152152
) -> Option<Plugin<'core>> {
153153
unsafe {
154-
let ns = CString::new(namespace).unwrap();
155154
let handle =
156-
self.handle.as_ref().getPluginByNamespace.unwrap()(ns.as_ptr(), core.as_ptr());
155+
self.handle.as_ref().getPluginByNamespace.unwrap()(namespace, core.as_ptr());
157156
if handle.is_null() {
158157
None
159158
} else {
@@ -164,12 +163,11 @@ impl API {
164163

165164
pub(crate) fn plugin_by_id<'core>(
166165
&self,
167-
id: &str,
166+
id: *const c_char,
168167
core: &'core CoreRef<'core>,
169168
) -> Option<Plugin<'core>> {
170169
unsafe {
171-
let id = CString::new(id).unwrap();
172-
let handle = self.handle.as_ref().getPluginByID.unwrap()(id.as_ptr(), core.as_ptr());
170+
let handle = self.handle.as_ref().getPluginByID.unwrap()(id, core.as_ptr());
173171
if handle.is_null() {
174172
None
175173
} else {
@@ -940,7 +938,7 @@ impl API {
940938
pub(crate) fn get_plugin_function_return_type(
941939
&self,
942940
function: *mut ffi::VSPluginFunction,
943-
) -> *const i8 {
941+
) -> *const c_char {
944942
unsafe { self.handle.as_ref().getPluginFunctionReturnType.unwrap()(function) }
945943
}
946944

rustsynth/src/core.rs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use rustsynth_sys as ffi;
1414
use std::{
1515
ffi::{CStr, CString},
1616
marker::PhantomData,
17-
mem,
1817
ptr::NonNull,
1918
};
2019

@@ -36,7 +35,7 @@ bitflags! {
3635
}
3736

3837
/// A reference to a VapourSynth core.
39-
#[derive(Debug, Clone, Copy)]
38+
#[derive(Debug, Clone, PartialEq, Eq)]
4039
pub struct CoreRef<'core> {
4140
handle: NonNull<ffi::VSCore>,
4241
_owner: PhantomData<&'core ()>,
@@ -48,11 +47,6 @@ unsafe impl<'core> Sync for CoreRef<'core> {}
4847
impl<'core> CoreRef<'core> {
4948
/// Creates and returns a new core.
5049
///
51-
/// Note that there's currently no safe way of freeing the returned core, and the lifetime is
52-
/// unbounded, because it can live for an arbitrary long time. You may use the (unsafe)
53-
/// `rustsynth_sys::VSAPI::freeCore()` after ensuring that all frame requests have completed
54-
/// and all objects belonging to the core have been released.
55-
///
5650
/// # Example
5751
///
5852
/// ```
@@ -111,14 +105,34 @@ impl<'core> CoreRef<'core> {
111105
///
112106
/// [None] if no plugin is found
113107
pub fn plugin_by_namespace(&self, namespace: &str) -> Option<Plugin<'core>> {
114-
unsafe { API::get_cached() }.plugin_by_namespace(namespace, self)
108+
let namespace = CString::new(namespace).unwrap();
109+
unsafe { API::get_cached() }.plugin_by_namespace(namespace.as_ptr(), self)
115110
}
116111

117112
/// Returns an instance of [Some]<[Plugin]> if there exists a plugin loaded associated with the id
118113
///
119114
/// [None] if no plugin is found
120115
pub fn plugin_by_id(&self, id: &str) -> Option<Plugin<'_>> {
121-
unsafe { API::get_cached() }.plugin_by_id(id, self)
116+
let id = CString::new(id).unwrap();
117+
unsafe { API::get_cached() }.plugin_by_id(id.as_ptr(), self)
118+
}
119+
120+
pub fn std(&self) -> Option<Plugin<'_>> {
121+
unsafe {
122+
API::get_cached().plugin_by_id(ffi::VSH_STD_PLUGIN_ID.as_ptr() as *const i8, self)
123+
}
124+
}
125+
126+
pub fn resize(&self) -> Option<Plugin<'_>> {
127+
unsafe {
128+
API::get_cached().plugin_by_id(ffi::VSH_RESIZE_PLUGIN_ID.as_ptr() as *const i8, self)
129+
}
130+
}
131+
132+
pub fn text(&self) -> Option<Plugin<'_>> {
133+
unsafe {
134+
API::get_cached().plugin_by_id(ffi::VSH_TEXT_PLUGIN_ID.as_ptr() as *const i8, self)
135+
}
122136
}
123137

124138
/// Returns a iterator over the loaded plugins
@@ -194,9 +208,9 @@ impl<'core> CoreRef<'core> {
194208
}
195209

196210
/// Create a video filter using the Filter trait
197-
pub fn create_video_filter<F>(&self, filter: F) -> Result<OwnedMap<'_>, String>
211+
pub fn create_video_filter<F>(&self, filter: &F) -> Result<OwnedMap<'_>, String>
198212
where
199-
F: Filter + Send + Sync + 'static,
213+
F: Filter<'core>,
200214
{
201215
let out = OwnedMap::new();
202216
// Get video info from the filter
@@ -233,9 +247,9 @@ impl<'core> CoreRef<'core> {
233247
}
234248

235249
/// Create a video filter using the Filter trait (returns node directly)
236-
pub fn create_video_filter2<F>(&self, filter: F) -> Result<crate::node::Node, String>
250+
pub fn create_video_filter2<F>(&self, filter: &F) -> Result<crate::node::Node<'core>, String>
237251
where
238-
F: Filter + Send + Sync + 'static,
252+
F: Filter<'core>,
239253
{
240254
// Get video info from the filter
241255
let video_info = filter.get_video_info()?;
@@ -274,9 +288,9 @@ impl<'core> CoreRef<'core> {
274288
}
275289

276290
/// Create a audio filter using the Filter trait
277-
pub fn create_audio_filter<F>(&self, filter: F) -> Result<OwnedMap<'_>, String>
291+
pub fn create_audio_filter<F>(&self, filter: &F) -> Result<OwnedMap<'_>, String>
278292
where
279-
F: Filter + Send + Sync + 'static,
293+
F: Filter<'core>,
280294
{
281295
let out = OwnedMap::new();
282296
// Get audio info from the filter
@@ -313,9 +327,9 @@ impl<'core> CoreRef<'core> {
313327
}
314328

315329
/// Create an audio filter using the Filter trait (returns node directly)
316-
pub fn create_audio_filter2<F>(&self, filter: F) -> Result<crate::node::Node, String>
330+
pub fn create_audio_filter2<F>(&self, filter: &F) -> Result<crate::node::Node<'core>, String>
317331
where
318-
F: Filter + Send + Sync + 'static,
332+
F: Filter<'core>,
319333
{
320334
// Get audio info from the filter
321335
let audio_info = filter.get_audio_info()?;
@@ -355,7 +369,7 @@ impl<'core> CoreRef<'core> {
355369
}
356370

357371
// Callback functions for Filter trait integration
358-
unsafe extern "C" fn filter_get_frame<F>(
372+
unsafe extern "C" fn filter_get_frame<'core, F>(
359373
n: i32,
360374
activation_reason: i32,
361375
instance_data: *mut std::ffi::c_void,
@@ -365,7 +379,7 @@ unsafe extern "C" fn filter_get_frame<F>(
365379
_vs_api: *const ffi::VSAPI,
366380
) -> *const ffi::VSFrame
367381
where
368-
F: Filter + Send + Sync + 'static,
382+
F: Filter<'core>,
369383
{
370384
if instance_data.is_null() || frame_ctx.is_null() || core.is_null() {
371385
return std::ptr::null();
@@ -398,10 +412,7 @@ where
398412
};
399413

400414
match filter.process_frame(n, &frame_data_array, &frame_context, core_ref) {
401-
Ok(frame) => {
402-
let frame = mem::ManuallyDrop::new(frame);
403-
frame.as_ptr()
404-
}
415+
Ok(frame) => frame.as_ptr(),
405416
Err(error) => {
406417
frame_context.set_filter_error(&error);
407418
std::ptr::null()
@@ -428,12 +439,12 @@ where
428439
}
429440
}
430441

431-
unsafe extern "C" fn filter_free<F>(
442+
unsafe extern "C" fn filter_free<'core, F>(
432443
instance_data: *mut std::ffi::c_void,
433444
_core: *mut ffi::VSCore,
434445
_vs_api: *const ffi::VSAPI,
435446
) where
436-
F: Filter + Send + Sync + 'static,
447+
F: Filter<'core>,
437448
{
438449
if !instance_data.is_null() {
439450
let filter = Box::from_raw(instance_data as *mut F);

rustsynth/src/filter/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ use crate::ffi;
55
use crate::ffi::VSRequestPattern;
66
use crate::node::Node;
77

8-
pub struct FilterDependency {
9-
pub source: Node,
8+
pub struct FilterDependency<'core> {
9+
pub source: Node<'core>,
1010
pub request_pattern: RequestPattern,
1111
}
1212

13-
impl FilterDependency {
13+
impl FilterDependency<'_> {
1414
pub const fn as_ffi(&self) -> ffi::VSFilterDependency {
1515
ffi::VSFilterDependency {
1616
source: self.source.as_ptr(),

rustsynth/src/filter/traits.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ use crate::{
77
};
88

99
/// Trait that filter structs must implement
10-
pub trait Filter {
10+
pub trait Filter<'core>: Send + Sync + Clone + 'core {
1111
const NAME: &'static str;
1212
const ARGS: &'static str;
1313
const RETURNTYPE: &'static str;
1414
const MODE: FilterMode;
1515

1616
/// Create filter instance from input arguments and core
17-
fn from_args(args: &Map, core: &CoreRef) -> Result<Self, String>
17+
fn from_args(args: &Map<'core>, core: &CoreRef<'core>) -> Result<Self, String>
1818
where
1919
Self: Sized;
2020

2121
/// Get filter dependencies
22-
fn get_dependencies(&self) -> Vec<FilterDependency>;
22+
fn get_dependencies(&self) -> Vec<FilterDependency<'core>>;
2323

2424
/// Get video info for video filters - override for source filters
2525
fn get_video_info(&self) -> Result<VideoInfo, String> {
@@ -53,7 +53,7 @@ pub trait Filter {
5353
fn request_input_frames(&self, n: i32, frame_ctx: &FrameContext);
5454

5555
/// Process frame n and return output frame
56-
fn process_frame<'core>(
56+
fn process_frame(
5757
&mut self,
5858
n: i32,
5959
_frame_data: &[u8; 4],

rustsynth/src/frame/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ pub struct Frame<'core> {
2424
unsafe impl<'core> Send for Frame<'core> {}
2525
unsafe impl<'core> Sync for Frame<'core> {}
2626

27-
impl<'core> Drop for Frame<'core> {
28-
fn drop(&mut self) {
29-
unsafe { API::get_cached().free_frame(self.handle.as_ptr()) }
30-
}
31-
}
32-
3327
/// Represents a reference to the obscure object
3428
#[derive(Debug)]
3529
pub struct FrameContext {
@@ -75,6 +69,12 @@ impl<'core> Frame<'core> {
7569
}
7670
}
7771

72+
/// # Safety
73+
/// The frame must be owned (not borrowed) and not passed to vapoursynth core.
74+
pub unsafe fn free(self) {
75+
API::get_cached().free_frame(self.handle.as_ptr());
76+
}
77+
7878
#[inline]
7979
pub const fn as_ptr(&self) -> *const ffi::VSFrame {
8080
self.handle.as_ptr()

rustsynth/src/function.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ impl<'core> Function<'core> {
6363
/// Creates a new function.
6464
///
6565
/// To indicate an error from the callback, set an error on the output map.
66-
#[inline]
6766
pub fn new<F>(core: CoreRef<'core>, callback: F) -> Self
6867
where
6968
F: Fn(CoreRef<'core>, &Map<'core>, &mut Map<'core>) + Send + Sync + 'core,

rustsynth/src/map/iterators.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,12 @@ impl_value_iter!(
116116
get_data_raw_unchecked,
117117
new_data
118118
);
119-
impl_value_iter!(ValueType::Node, Node, get_node_raw_unchecked, new_node);
119+
impl_value_iter!(
120+
ValueType::Node,
121+
Node<'elem>,
122+
get_node_raw_unchecked,
123+
new_node
124+
);
120125
impl_value_iter!(
121126
ValueType::Frame,
122127
Frame<'elem>,

rustsynth/src/map/mod.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -548,14 +548,17 @@ impl<'elem> Map<'elem> {
548548
///
549549
/// This function retrieves the first value associated with the key.
550550
#[inline]
551-
pub fn get_node(&self, key: &str) -> MapResult<Node> {
551+
pub fn get_node(&self, key: &str) -> MapResult<Node<'elem>> {
552552
let key = Map::make_raw_key(key)?;
553553
unsafe { self.get_node_raw_unchecked(&key, 0) }
554554
}
555555

556556
/// Retrieves nodes from a map.
557557
#[inline]
558-
pub fn get_node_iter<'map>(&'map self, key: &str) -> MapResult<ValueIter<'map, 'elem, Node>> {
558+
pub fn get_node_iter<'map>(
559+
&'map self,
560+
key: &str,
561+
) -> MapResult<ValueIter<'map, 'elem, Node<'elem>>> {
559562
let key = Map::make_raw_key(key)?;
560563
unsafe { ValueIter::new_node(self, key) }
561564
}
@@ -674,7 +677,11 @@ impl<'elem> Map<'elem> {
674677
/// # Safety
675678
/// The caller must ensure `key` is valid.
676679
#[inline]
677-
pub(crate) unsafe fn get_node_raw_unchecked(&self, key: &CStr, index: i32) -> MapResult<Node> {
680+
pub(crate) unsafe fn get_node_raw_unchecked(
681+
&self,
682+
key: &CStr,
683+
index: i32,
684+
) -> MapResult<Node<'elem>> {
678685
let mut error = 0;
679686
let value = API::get_cached().map_get_node(self, key.as_ptr(), index, &mut error);
680687
handle_get_prop_error(error)?;

0 commit comments

Comments
 (0)