1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//! A Rust re-implementation of the LV2 URID library.
//!
//! This LV2 feature enables you to map URIs to numbers and reverse.
//! 
//! This is a frozen prototype and therefore, development of this crate will not continue here. Further
//! development continues as [rust-lv2](https://github.com/rust-dsp/rust-lv2).
//!
//! ## Use
//!
//! URID mapping is only possible in the `instantiate` function of a plugin since there is no
//! guarantee that the required pointers live longer than the `instantiate` function call. Here is
//! an example:
//!
//!     // import the required crates.
//!     extern crate lv2rs_core as core;
//!     extern crate lv2rs_urid as urid;
//!     use std::ffi::CStr;
//!     
//!     // A dummy plugin that doesn't actually do anything.
//!     struct UridPlugin {}
//!
//!     impl core::Plugin for UridPlugin {
//!         fn instantiate(
//!             descriptor: &core::Descriptor,
//!             rate: f64,
//!             bundle_path: &CStr,
//!             features: Option<&core::FeaturesList>
//!         ) -> Option<Self> where Self: Sized {
//!
//!             // Return `None` if there are no features.
//!             let features = features?;
//!
//!             // Try to get the mapper and the un-mapper from the features list.
//!             let map = urid::Map::try_from_features(features)?;
//!             let unmap = urid::Unmap::try_from_features(features)?;
//!
//!             // Create a URI, map it, and un-map it.
//!             let github_uri = CStr::from_bytes_with_nul(b"https://github.com\0").unwrap();
//!             let github_urid = map.map(github_uri);
//!             let github_uri = unmap.unmap(github_urid);
//!
//!             Some(Self {})
//!         }
//!
//!         // Blank implementations to keep the compiler quiet.
//!         fn connect_port(&mut self, _port: u32, _data: *mut ()) {}
//!         fn run(&mut self, _n_samples: u32) {}
//!     }
extern crate lv2rs_core as core;

pub mod debug;
pub mod uris;

use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::os::raw::*;

/// Type to describe pointers to map handles.
pub type MapHandle = *mut c_void;

/// Type to describe pointers to unmap handles.
pub type UnmapHandle = *mut c_void;

/// Type for describing URIDs.
pub type URID = u32;

/// Struct for mapping URIs to URIDs.
#[repr(C)]
pub struct Map {
    /// Pointer to a host-specific handle to map URIs to URIDs.
    pub handle: MapHandle,
    /// Function that maps a URI to a URID.
    pub map: extern "C" fn(handle: MapHandle, uri: *const c_char) -> URID,
}

impl Map {
    /// Try to find the mapping feature in the features map.
    ///
    /// If this function returns None if the host does not support mapping.
    pub fn try_from_features<'a>(features: &core::FeaturesList) -> Option<&'static mut Self> {
        unsafe {
            core::Feature::get_feature::<Self>(
                features,
                CStr::from_bytes_with_nul(uris::MAP_URI).unwrap(),
            )
        }
    }

    /// Map a URI to a URID.
    ///
    /// If the host is properly implemented, this should be an injective function: Every URI should
    /// be mapped to a unique URID.
    pub fn map<'a, S>(&mut self, uri: S) -> URID
    where
        &'a CStr: From<S>,
    {
        let uri: &CStr = uri.into();
        (self.map)(self.handle, uri.as_ptr())
    }
}

/// Struct for mapping URIDs to URIs.
#[repr(C)]
pub struct Unmap {
    /// Pointer to a host-specific handle to map URIDs to URIs.
    pub handle: MapHandle,
    /// Function that maps a URID to a URI.
    pub unmap: extern "C" fn(handle: UnmapHandle, urid: URID) -> *const c_char,
}

impl Unmap {
    /// Try to find the unmapping feature in the features map.
    ///
    /// If this function returns None if the host does not support unmapping.
    pub fn try_from_features(features: &core::FeaturesList) -> Option<&'static mut Self> {
        unsafe {
            core::Feature::get_feature::<Self>(
                features,
                CStr::from_bytes_with_nul(uris::UNMAP_URI).unwrap(),
            )
        }
    }

    /// Try to unmap a URID to a URI.
    ///
    /// Since mapping URIs to URIDs may not be a surjective function, unmapping may be a partial
    /// function: Not every URID is necessarily mapped to a URI. Therefore, this function returns
    /// `None` if the given URID is not mapped.
    pub fn unmap(&mut self, urid: URID) -> Option<&CStr> {
        let uri = (self.unmap)(self.handle, urid);
        if uri.is_null() {
            None
        } else {
            Some(unsafe { CStr::from_ptr(uri) })
        }
    }
}

/// Cached version of [Map](struct.Map.html)
pub struct CachedMap {
    raw: &'static mut Map,
    cache: HashMap<&'static CStr, URID>,
}

impl CachedMap {
    /// Create a new cached map from a mutable map reference.
    pub fn new(raw: &'static mut Map) -> CachedMap {
        Self {
            raw: raw,
            cache: HashMap::new(),
        }
    }

    /// Try to find the mapping feature in the features map.
    ///
    /// If this function returns `None` if the host does not support mapping.
    pub fn try_from_features(features: &core::FeaturesList) -> Option<Self> {
        let raw_map = Map::try_from_features(features)?;
        Some(Self::new(raw_map))
    }

    /// Return a reference to the cache.
    pub fn cache(&self) -> &HashMap<&'static CStr, URID> {
        &self.cache
    }

    /// Map a URI to a URID.
    ///
    /// The same rules from [Map.map](struct.Map.html#method.map) apply. Additionally, this function
    /// will cache the mappings and short-cut if a requested mapping is already cached.
    pub fn map(&mut self, uri: &'static CStr) -> URID {
        if !self.cache.contains_key(&uri) {
            let urid = self.raw.map(uri);
            self.cache.insert(uri.clone(), urid);
        }
        *(self.cache.get(&uri).unwrap())
    }
}
/// Cached version of [Unmap](struct.Unmap.html)
pub struct CachedUnmap {
    raw: &'static mut Unmap,
    cache: HashMap<URID, CString>,
}

impl CachedUnmap {
    /// Create a new cached unmap from a mutable unmap reference.
    pub fn new(raw_map: &'static mut Unmap) -> Self {
        Self {
            raw: raw_map,
            cache: HashMap::new(),
        }
    }

    /// Try to find the unmapping feature in the features map.
    ///
    /// If this function returns `None` if the host does not support unmapping.
    pub fn try_from_features(features: &core::FeaturesList) -> Option<Self> {
        let raw_unmap = Unmap::try_from_features(features)?;
        Some(Self::new(raw_unmap))
    }

    /// Return a reference to the cache.
    pub fn cache(&self) -> &HashMap<URID, CString> {
        &self.cache
    }

    /// Try to map a URID to a URI.
    ///
    /// The same rules from [Unmap.unmap](struct.Unmap.html#method.unmap) apply. Additionally, this
    /// function will cache the mappings and short-cut if a requested mapping is already cached.
    pub fn unmap(&mut self, urid: URID) -> Option<&CString> {
        if !self.cache.contains_key(&urid) {
            let uri = self.raw.unmap(urid);
            match uri {
                Some(uri) => {
                    let uri = CString::from(uri);
                    self.cache.insert(urid, uri);
                }
                None => return None,
            }
        }
        Some(self.cache.get(&urid).unwrap())
    }
}