livi/features/
urid_map.rs1use log::error;
2use lv2_raw::LV2Feature;
3use std::collections::HashMap;
4use std::convert::TryFrom;
5use std::ffi::{CStr, CString};
6use std::pin::Pin;
7use std::ptr::NonNull;
8use std::sync::Mutex;
9
10static URID_MAP: &[u8] = b"http://lv2plug.in/ns/ext/urid#map\0";
11static URID_UNMAP: &[u8] = b"http://lv2plug.in/ns/ext/urid#unmap\0";
12
13type MapImpl = Mutex<HashMap<CString, u32>>;
14
15extern "C" fn do_map(handle: lv2_raw::LV2UridMapHandle, uri_ptr: *const i8) -> lv2_raw::LV2Urid {
18 let handle: *const MapImpl = handle as *const _;
19 let map_mutex = unsafe { &*handle };
20 let mut map = map_mutex.lock().unwrap();
21 let uri = unsafe { CStr::from_ptr(uri_ptr) };
22
23 if let Some(id) = map.get(uri) {
24 return *id;
25 }
26 let id = u32::try_from(map.len()).expect("URID space has exceeded capacity for u32.") + 1;
27 map.insert(uri.to_owned(), id);
28 id
29}
30
31extern "C" fn do_unmap(handle: lv2_sys::LV2_URID_Map_Handle, urid: lv2_raw::LV2Urid) -> *const i8 {
32 let handle: *const MapImpl = handle as *const _;
33 let map_mutex = unsafe { &*handle };
34 let map = map_mutex.lock().unwrap();
35 for (uri, id) in map.iter() {
36 if *id == urid {
37 return uri.as_ptr();
38 }
39 }
40 std::ptr::null()
41}
42
43pub struct UridMap {
44 map: MapImpl,
45 map_data: lv2_raw::LV2UridMap,
46 unmap_data: lv2_sys::LV2_URID_Unmap,
47 urid_map_feature: LV2Feature,
48 urid_unmap_feature: LV2Feature,
49 _pin: std::marker::PhantomPinned,
50}
51
52unsafe impl Send for UridMap {}
53
54impl UridMap {
55 pub fn new() -> Pin<Box<UridMap>> {
56 let mut urid_map = Box::pin(UridMap {
57 map: Mutex::default(),
58 map_data: lv2_raw::LV2UridMap {
59 handle: std::ptr::null_mut(),
60 map: do_map,
61 },
62 unmap_data: lv2_sys::LV2_URID_Unmap {
63 handle: std::ptr::null_mut(),
64 unmap: Some(do_unmap),
65 },
66 urid_map_feature: LV2Feature {
67 uri: URID_MAP.as_ptr().cast(),
68 data: std::ptr::null_mut(),
69 },
70 urid_unmap_feature: LV2Feature {
71 uri: URID_UNMAP.as_ptr().cast(),
72 data: std::ptr::null_mut(),
73 },
74 _pin: std::marker::PhantomPinned,
75 });
76 let map_impl_ptr = NonNull::from(&urid_map.map);
77 let map_data_ptr = NonNull::from(&urid_map.map_data);
78 let unmap_data_ptr = NonNull::from(&urid_map.unmap_data);
79 unsafe {
80 let mut_ref_pin: Pin<&mut UridMap> = Pin::as_mut(&mut urid_map);
81 let mut_ref = Pin::get_unchecked_mut(mut_ref_pin);
82 mut_ref.map_data.handle = map_impl_ptr.as_ptr().cast();
83 mut_ref.unmap_data.handle = map_impl_ptr.as_ptr().cast();
84 mut_ref.urid_map_feature.data = map_data_ptr.as_ptr().cast();
85 mut_ref.urid_unmap_feature.data = unmap_data_ptr.as_ptr().cast();
86 }
87 urid_map
88 }
89
90 pub fn map(&self, uri: &CStr) -> lv2_raw::LV2Urid {
91 do_map(self.map_data.handle, uri.as_ptr())
92 }
93
94 pub fn unmap(&self, urid: lv2_raw::LV2Urid) -> Option<&str> {
95 let ptr = do_unmap(self.unmap_data.handle, urid);
96 let non_null_ptr = std::ptr::NonNull::new(ptr as *mut _)?;
97 let cstr = unsafe { CStr::from_ptr(non_null_ptr.as_ptr()) };
98 match cstr.to_str() {
99 Ok(s) => Some(s),
100 Err(e) => {
101 error!("Could not convert cstr{:?} to str: {:?}", cstr, e);
102 None
103 }
104 }
105 }
106
107 pub fn as_urid_map_feature(&self) -> &LV2Feature {
108 &self.urid_map_feature
109 }
110
111 pub fn as_urid_unmap_feature(&self) -> &LV2Feature {
112 &self.urid_unmap_feature
113 }
114}
115
116impl std::fmt::Debug for UridMap {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 f.debug_struct("UridMap").field("map", &self.map).finish()
119 }
120}