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
//! Callback handling.

// This file is part of the PulseAudio Rust language binding.
//
// Copyright (c) 2018 Lyndon Brown
//
// This library is free software; you can redistribute it and/or modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation; either version
// 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with this library;
// if not, see <http://www.gnu.org/licenses/>.

use std;
use std::os::raw::c_void;
use std::ptr::null_mut;

/// List result instance. Fetching a list can result in a callback being fired for each list item,
/// and then once to signal that the end of the list having been reached. This is used to
/// distinguish such state to a closure callback.
pub enum ListResult<T> {
    /// List item
    Item(T),
    /// End of list reached
    End,
    /// Failure, an error occurred
    Error,
}

/// Unwraps optional callback function + data pointer tuple, wrapping the function pointer in an
/// option wrapper. Used internally in passing such parameters to an underlying C function.
///
/// Example:
///
/// ```rust,ignore
/// fn foo(cb: Option<(SuccessCb, *mut c_void)>) {
///     let (cb_f, cb_d) = ::util::unwrap_optional_callback::<SuccessCb>(cb);
///     //do something, i.e. passing cb_f and cb_d to C function
/// }
/// ```
#[inline]
pub(crate) fn unwrap_optional_callback<T>(cb: Option<(T, *mut c_void)>) -> (Option<T>, *mut c_void) {
    match cb {
        Some((f, d)) => (Some(f), d),
        None => (None, null_mut::<c_void>()),
    }
}

/// A saved multi-use callback. Closures of multi-use callbacks (those that may be called multiple
/// times) need saving, and releasing later at an appropriate time (on change of registered
/// callback, or on destruction of associated object). This is used for saving the pointer to it
/// for such deferred destruction.
pub(crate) struct MultiUseCallback<ClosureProto: ?Sized, ProxyProto> {
    saved: Option<*mut Box<ClosureProto>>,
    proxy: std::marker::PhantomData<*const ProxyProto>,
}

impl<ClosureProto: ?Sized, ProxyProto> Default for MultiUseCallback<ClosureProto, ProxyProto> {
    fn default() -> Self {
        MultiUseCallback::<ClosureProto, ProxyProto> { saved: None, proxy: std::marker::PhantomData }
    }
}

impl<ClosureProto: ?Sized, ProxyProto> MultiUseCallback<ClosureProto, ProxyProto> {
    /// Create a new instance. **Note**, an existing instance should always be overwritten with a
    /// new one, to ensure the old one is correctly freed.
    pub fn new(cb: Option<Box<ClosureProto>>) -> Self {
        match cb {
            Some(f) => MultiUseCallback::<ClosureProto, ProxyProto> {
                saved: Some(Box::into_raw(Box::new(f))),
                proxy: std::marker::PhantomData,
            },
            None => Default::default(),
        }
    }

    /// Returns callback params to give to the C API (a tuple of function pointer and data pointer).
    pub fn get_capi_params(&self, proxy: ProxyProto) -> (Option<ProxyProto>, *mut c_void) {
        match self.saved {
            Some(ref f) => (Some(proxy), *f as *mut c_void),
            None => (None, std::ptr::null_mut::<c_void>()),
        }
    }

    /// Convert void closure pointer back to real type. For use in callback proxies. Only a
    /// reference is returned, in order to deliberately avoid reclaiming ownership and thus
    /// triggering of destruction.
    ///
    /// Panics if `ptr` is null.
    pub fn get_callback<'a>(ptr: *mut c_void) -> &'a mut Box<ClosureProto> {
        assert!(!ptr.is_null());
        // Note, does NOT destroy closure callback after use - only handles pointer
        unsafe { &mut *(ptr as *mut Box<ClosureProto>) }
    }
}

impl<ClosureProto: ?Sized, ProxyProto> Drop for MultiUseCallback<ClosureProto, ProxyProto> {
    fn drop(&mut self) {
        if self.saved.is_some() {
            let _to_drop = unsafe { Box::from_raw(self.saved.unwrap()) };
        }
    }
}

/// Convert single-use-callback closure to pointer for C API.
///
/// It can be restored in an `extern "C"` callback proxy with `get_su_callback`.
///
/// Note: The closure itself needs to exist on the heap, and here we take it as such (wrapped in a
/// `Box`); from this you may assume that a pointer is already available. However, this is a pointer
/// to a trait object, and these are special in Rust, they are twice the size of a normal pointer
/// because in actual fact it is implemented as a pair of pointers (one to a type instance and one
/// to a 'vtable'). We can only pass a normal sized pointer through the C API, so we must further
/// box it, producing `Box<Box<Closure>>` which we convert to `*mut Box<Closure>` and then further
/// to simply `*mut c_void`.
pub(crate) fn box_closure_get_capi_ptr<ClosureProto: ?Sized>(callback: Box<ClosureProto>
    ) -> *mut c_void
{
    Box::into_raw(Box::new(callback)) as *mut c_void
}

/// Get the C API callback params (function pointer and data pointer pair), for an optional
/// single-use callback closure. The proxy function must be specified. If `callback` is `None` then
/// a pair of null pointers will be returned. Otherwise, a pair consisting of the given proxy and
/// a pointer for the given closure will be returned. The data pointer can be restored to the actual
/// (boxed) closure in the `extern "C"` callback proxy with `get_su_callback`.
pub(crate) fn get_su_capi_params<ClosureProto: ?Sized, ProxyProto>(
    callback: Option<Box<ClosureProto>>, proxy: ProxyProto) -> (Option<ProxyProto>, *mut c_void)
{
    match callback {
        Some(f) => (Some(proxy), box_closure_get_capi_ptr::<ClosureProto>(f)),
        None => (None, std::ptr::null_mut::<c_void>()),
    }
}

/// Convert void single-use-callback closure pointer back to real type. For use in callback proxies.
/// Returns ownership of the closure, thus it can be destroyed after use.
///
/// Panics if `ptr` is null.
pub(crate) fn get_su_callback<ClosureProto: ?Sized>(ptr: *mut c_void) -> Box<Box<ClosureProto>> {
    assert!(!ptr.is_null());
    unsafe { Box::from_raw(ptr as *mut Box<ClosureProto>) }
}

pub(crate) enum ListInstanceCallback<'a, ClosureProto: 'a + ?Sized> {
    /// An entry instance. Contains reference to closure callback.
    Entry(&'a mut Box<ClosureProto>),
    /// End-of-list instance. Contains owned closure callback, for destruction after use.
    End(Box<Box<ClosureProto>>),
    /// Error instance. Contains owned closure callback, for destruction after use.
    Error(Box<Box<ClosureProto>>),
}

/// Used by multi-use-list style callback proxies. Provide this with the `eol` parameter, and the
/// userdata (closure) pointer parameter, and it will return either a reference to the closure or
/// the owned closure, depending upon whether or not `eol` signals end-of-list/error.
pub(crate) fn callback_for_list_instance<'a, ClosureProto: ?Sized>(eol: i32, ptr: *mut c_void
    ) -> ListInstanceCallback<'a, ClosureProto>
{
    assert!(!ptr.is_null());
    match eol {
        0 => { // NOT end-of-list or error. Return reference to avoid destruction.
            let callback = unsafe { &mut *(ptr as *mut Box<ClosureProto>) };
            ListInstanceCallback::Entry(callback)
        },
        i if i > 0 => { // End-of-list. Return owned, so it can be destroyed after use.
            let mut callback = unsafe { Box::from_raw(ptr as *mut Box<ClosureProto>) };
            ListInstanceCallback::End(callback)
        },
        _ => { // Error. Return owned, so it can be destroyed after use.
            let mut callback = unsafe { Box::from_raw(ptr as *mut Box<ClosureProto>) };
            ListInstanceCallback::Error(callback)
        },
    }
}