Skip to main content

livi/features/
urid_map.rs

1use 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
15/// # Safety
16/// Dereference to `uri_ptr` may be unsafe.
17extern "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}