lv2rs_urid/
debug.rs

1//! Placeholder URID mapper for debugging purposes.
2//!
3//! Many LV2 libraries depend on URID mapping and therefore, a mapper is needed when debugging and
4//! testing. However, testing can (and should) not be done within a running host. This is where
5//! these utilities come in hand: They map URIs to unique URIDs and backwards without needing an
6//! external host.
7use crate::{MapHandle, URID};
8use std::collections::HashMap;
9use std::ffi::{CStr, CString};
10use std::marker::PhantomPinned;
11use std::os::raw::*;
12
13/// Container holding both the mapping feature and a storage for URID mappings.
14///
15/// Since the mapping feature contains a raw pointer to the storage, this struct must be pinned.
16/// This means that it cannot be moved.
17pub struct DebugMap {
18    storage: HashMap<CString, URID>,
19    feature: crate::Map,
20    _pin: PhantomPinned,
21}
22
23extern "C" fn mapping_fn(handle: MapHandle, uri: *const c_char) -> URID {
24    let map = unsafe { (handle as *mut HashMap<CString, URID>).as_mut() }.unwrap();
25    let uri = unsafe { CStr::from_ptr(uri) }.to_owned();
26
27    if !map.contains_key(&uri) {
28        let biggest_urid = map.values().map(|urid: &URID| -> URID { *urid }).max();
29        let new_urid = match biggest_urid {
30            Some(urid) => urid + 1,
31            None => 0,
32        };
33        map.insert(uri.clone(), new_urid);
34    }
35
36    map[&uri]
37}
38
39impl DebugMap {
40    /// Create a new debug map in a box.
41    pub fn new() -> Box<Self> {
42        let mut debug_map = Box::new(Self {
43            storage: HashMap::new(),
44            feature: crate::Map {
45                handle: std::ptr::null_mut(),
46                map: mapping_fn,
47            },
48            _pin: PhantomPinned,
49        });
50        debug_map.feature.handle =
51            &mut debug_map.storage as *mut HashMap<CString, URID> as *mut c_void;
52        debug_map
53    }
54
55    /// Return a reference to the mapping feature.
56    pub fn get_map_ref(&self) -> &crate::Map {
57        &self.feature
58    }
59
60    /// Return a mutable reference to the mapping feature.
61    pub fn get_map_mut(&mut self) -> &mut crate::Map {
62        &mut self.feature
63    }
64
65    /// Create a cached map.
66    ///
67    /// Technically, this is useless since this debug map already contains the URIDs in a `HashMap`,
68    /// but many LV2 libraries use the `CachedMap` and therefore need such an object.
69    ///
70    /// This method is unsafe since it has to fake the lifetime of the mapping feature. In reality,
71    /// this mapping feature only lives as long as the `DebugMap` exists, but the `CachedMap`
72    /// expects the mapping feature to come from a host. This implies that the mapping feature will
73    /// live for the whole lifetime of the plugin and therefore is static.
74    pub unsafe fn create_cached_map(&mut self) -> crate::CachedMap {
75        let faked_map: &'static mut crate::Map =
76            (self.get_map_mut() as *mut crate::Map).as_mut().unwrap();
77        crate::CachedMap::new(faked_map)
78    }
79}
80#[cfg(test)]
81mod test {
82    use crate::debug::*;
83
84    const GITHUB_URI: &[u8] = b"https://github.com\0";
85    const GITLAB_URI: &[u8] = b"https://gitlab.com\0";
86
87    #[test]
88    fn test_mapping() {
89        let mut debug_map = DebugMap::new();
90        let map = debug_map.get_map_mut();
91
92        let github_urid = map.map(CStr::from_bytes_with_nul(GITHUB_URI).unwrap());
93        let gitlab_urid = map.map(CStr::from_bytes_with_nul(GITLAB_URI).unwrap());
94
95        assert_ne!(github_urid, gitlab_urid);
96    }
97
98    #[test]
99    fn test_cached_mapping() {
100        let mut debug_map = DebugMap::new();
101        let mut cached_map = unsafe { debug_map.create_cached_map() };
102
103        let github_urid = cached_map.map(CStr::from_bytes_with_nul(GITHUB_URI).unwrap());
104        let gitlab_urid = cached_map.map(CStr::from_bytes_with_nul(GITLAB_URI).unwrap());
105
106        assert_ne!(github_urid, gitlab_urid);
107    }
108}