Skip to main content

diesel/sqlite/connection/
raw.rs

1#![allow(unsafe_code)] // ffi calls
2#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3extern crate libsqlite3_sys as ffi;
4
5#[cfg(all(target_family = "wasm", target_os = "unknown"))]
6use sqlite_wasm_rs as ffi;
7
8use std::ffi::{CString, NulError};
9use std::io::{stderr, Write};
10use std::os::raw as libc;
11use std::ptr::NonNull;
12use std::{mem, ptr, slice, str};
13
14use super::functions::{build_sql_function_args, process_sql_function_result};
15use super::serialized_database::SerializedDatabase;
16use super::stmt::ensure_sqlite_ok;
17use super::{Sqlite, SqliteAggregateFunction};
18use crate::deserialize::FromSqlRow;
19use crate::result::Error::DatabaseError;
20use crate::result::*;
21use crate::serialize::ToSql;
22use crate::sql_types::HasSqlType;
23
24/// For use in FFI function, which cannot unwind.
25/// Print the message, ask to open an issue at Github and [`abort`](std::process::abort).
26macro_rules! assert_fail {
27    ($fmt:expr $(,$args:tt)*) => {
28        eprint!(concat!(
29            $fmt,
30            "If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\n",
31            "Source location: {}:{}\n",
32        ), $($args,)* file!(), line!());
33        std::process::abort()
34    };
35}
36
37#[allow(missing_debug_implementations, missing_copy_implementations)]
38pub(super) struct RawConnection {
39    pub(super) internal_connection: NonNull<ffi::sqlite3>,
40}
41
42impl RawConnection {
43    pub(super) fn establish(database_url: &str) -> ConnectionResult<Self> {
44        let mut conn_pointer = ptr::null_mut();
45
46        let database_url = if database_url.starts_with("sqlite://") {
47            CString::new(database_url.replacen("sqlite://", "file:", 1))?
48        } else {
49            CString::new(database_url)?
50        };
51        let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI;
52        let connection_status = unsafe {
53            ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null())
54        };
55
56        match connection_status {
57            ffi::SQLITE_OK => {
58                let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
59                Ok(RawConnection {
60                    internal_connection: conn_pointer,
61                })
62            }
63            err_code => {
64                let message = super::error_message(err_code);
65                // sqlite3_open_v2() may allocate a database connection handle
66                // even on failure. To avoid a resource leak, it must be released
67                // with sqlite3_close(). Passing a null pointer to sqlite3_close()
68                // is a harmless no-op, so no null check is needed.
69                // See: https://www.sqlite.org/c3ref/open.html
70                unsafe { ffi::sqlite3_close(conn_pointer) };
71                Err(ConnectionError::BadConnection(message.into()))
72            }
73        }
74    }
75
76    pub(super) fn exec(&self, query: &str) -> QueryResult<()> {
77        let query = CString::new(query)?;
78        let callback_fn = None;
79        let callback_arg = ptr::null_mut();
80        let result = unsafe {
81            ffi::sqlite3_exec(
82                self.internal_connection.as_ptr(),
83                query.as_ptr(),
84                callback_fn,
85                callback_arg,
86                ptr::null_mut(),
87            )
88        };
89
90        ensure_sqlite_ok(result, self.internal_connection.as_ptr())
91    }
92
93    pub(super) fn rows_affected_by_last_query(
94        &self,
95    ) -> Result<usize, Box<dyn std::error::Error + Send + Sync>> {
96        let r = unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) };
97
98        Ok(r.try_into()?)
99    }
100
101    pub(super) fn register_sql_function<F, Ret, RetSqlType>(
102        &self,
103        fn_name: &str,
104        num_args: usize,
105        deterministic: bool,
106        f: F,
107    ) -> QueryResult<()>
108    where
109        F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
110            + std::panic::UnwindSafe
111            + Send
112            + 'static,
113        Ret: ToSql<RetSqlType, Sqlite>,
114        Sqlite: HasSqlType<RetSqlType>,
115    {
116        let c_fn_name = Self::get_fn_name(fn_name)?;
117        let flags = Self::get_flags(deterministic);
118        let num_args = num_args
119            .try_into()
120            .map_err(|e| Error::SerializationError(Box::new(e)))?;
121        // only create the pointer as last step here
122        // as we can otherwise leak memory
123        let callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr {
124            callback: f,
125            function_name: fn_name.to_owned(),
126        }));
127
128        let result = unsafe {
129            ffi::sqlite3_create_function_v2(
130                self.internal_connection.as_ptr(),
131                c_fn_name.as_ptr(),
132                num_args,
133                flags,
134                callback_fn as *mut _,
135                Some(run_custom_function::<F, Ret, RetSqlType>),
136                None,
137                None,
138                Some(destroy_boxed::<CustomFunctionUserPtr<F>>),
139            )
140        };
141
142        Self::process_sql_function_result(result)
143    }
144
145    pub(super) fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
146        &self,
147        fn_name: &str,
148        num_args: usize,
149    ) -> QueryResult<()>
150    where
151        A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + std::panic::UnwindSafe,
152        Args: FromSqlRow<ArgsSqlType, Sqlite>,
153        Ret: ToSql<RetSqlType, Sqlite>,
154        Sqlite: HasSqlType<RetSqlType>,
155    {
156        let fn_name = Self::get_fn_name(fn_name)?;
157        let flags = Self::get_flags(false);
158        let num_args = num_args
159            .try_into()
160            .map_err(|e| Error::SerializationError(Box::new(e)))?;
161
162        let result = unsafe {
163            ffi::sqlite3_create_function_v2(
164                self.internal_connection.as_ptr(),
165                fn_name.as_ptr(),
166                num_args,
167                flags,
168                core::ptr::null_mut(),
169                None,
170                Some(run_aggregator_step_function::<_, _, _, _, A>),
171                Some(run_aggregator_final_function::<_, _, _, _, A>),
172                None,
173            )
174        };
175
176        Self::process_sql_function_result(result)
177    }
178
179    pub(super) fn register_collation_function<F>(
180        &self,
181        collation_name: &str,
182        collation: F,
183    ) -> QueryResult<()>
184    where
185        F: Fn(&str, &str) -> std::cmp::Ordering + std::panic::UnwindSafe + Send + 'static,
186    {
187        let c_collation_name = Self::get_fn_name(collation_name)?;
188        // only create the pointer as last step here as we otherwise could leak memory
189        let callback_fn = Box::into_raw(Box::new(CollationUserPtr {
190            callback: collation,
191            collation_name: collation_name.to_owned(),
192        }));
193
194        let result = unsafe {
195            ffi::sqlite3_create_collation_v2(
196                self.internal_connection.as_ptr(),
197                c_collation_name.as_ptr(),
198                ffi::SQLITE_UTF8,
199                callback_fn as *mut _,
200                Some(run_collation_function::<F>),
201                Some(destroy_boxed::<CollationUserPtr<F>>),
202            )
203        };
204
205        let result = Self::process_sql_function_result(result);
206        if result.is_err() {
207            destroy_boxed::<CollationUserPtr<F>>(callback_fn as *mut _);
208        }
209        result
210    }
211
212    pub(super) fn serialize(&mut self) -> SerializedDatabase {
213        unsafe {
214            let mut size: ffi::sqlite3_int64 = 0;
215            let data_ptr = ffi::sqlite3_serialize(
216                self.internal_connection.as_ptr(),
217                std::ptr::null(),
218                &mut size as *mut _,
219                0,
220            );
221            SerializedDatabase::new(
222                data_ptr,
223                size.try_into()
224                    .expect("Cannot fit the serialized database into memory"),
225            )
226        }
227    }
228
229    pub(super) fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
230        let db_size = data
231            .len()
232            .try_into()
233            .map_err(|e| Error::DeserializationError(Box::new(e)))?;
234        // the cast for `ffi::SQLITE_DESERIALIZE_READONLY` is required for old libsqlite3-sys versions
235        #[allow(clippy::unnecessary_cast)]
236        unsafe {
237            let result = ffi::sqlite3_deserialize(
238                self.internal_connection.as_ptr(),
239                std::ptr::null(),
240                data.as_ptr() as *mut u8,
241                db_size,
242                db_size,
243                ffi::SQLITE_DESERIALIZE_READONLY as u32,
244            );
245
246            ensure_sqlite_ok(result, self.internal_connection.as_ptr())
247        }
248    }
249
250    fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
251        CString::new(fn_name)
252    }
253
254    fn get_flags(deterministic: bool) -> i32 {
255        let mut flags = ffi::SQLITE_UTF8;
256        if deterministic {
257            flags |= ffi::SQLITE_DETERMINISTIC;
258        }
259        flags
260    }
261
262    fn process_sql_function_result(result: i32) -> Result<(), Error> {
263        if result == ffi::SQLITE_OK {
264            Ok(())
265        } else {
266            let error_message = super::error_message(result);
267            Err(DatabaseError(
268                DatabaseErrorKind::Unknown,
269                Box::new(error_message.to_string()),
270            ))
271        }
272    }
273}
274
275impl Drop for RawConnection {
276    fn drop(&mut self) {
277        use std::thread::panicking;
278
279        let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
280        if close_result != ffi::SQLITE_OK {
281            let error_message = super::error_message(close_result);
282            if panicking() {
283                stderr().write_fmt(format_args!("Error closing SQLite connection: {0}",
        error_message))write!(stderr(), "Error closing SQLite connection: {error_message}")
284                    .expect("Error writing to `stderr`");
285            } else {
286                {
    ::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
            error_message));
};panic!("Error closing SQLite connection: {error_message}");
287            }
288        }
289    }
290}
291
292enum SqliteCallbackError {
293    Abort(&'static str),
294    DieselError(crate::result::Error),
295    Panic(String),
296}
297
298impl SqliteCallbackError {
299    fn emit(&self, ctx: *mut ffi::sqlite3_context) {
300        let s;
301        let msg = match self {
302            SqliteCallbackError::Abort(msg) => *msg,
303            SqliteCallbackError::DieselError(e) => {
304                s = e.to_string();
305                &s
306            }
307            SqliteCallbackError::Panic(msg) => msg,
308        };
309        unsafe {
310            context_error_str(ctx, msg);
311        }
312    }
313}
314
315impl From<crate::result::Error> for SqliteCallbackError {
316    fn from(e: crate::result::Error) -> Self {
317        Self::DieselError(e)
318    }
319}
320
321struct CustomFunctionUserPtr<F> {
322    callback: F,
323    function_name: String,
324}
325
326#[allow(warnings)]
327extern "C" fn run_custom_function<F, Ret, RetSqlType>(
328    ctx: *mut ffi::sqlite3_context,
329    num_args: libc::c_int,
330    value_ptr: *mut *mut ffi::sqlite3_value,
331) where
332    F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
333        + std::panic::UnwindSafe
334        + Send
335        + 'static,
336    Ret: ToSql<RetSqlType, Sqlite>,
337    Sqlite: HasSqlType<RetSqlType>,
338{
339    use std::ops::Deref;
340    static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
341    static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
342
343    let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
344        // We use `ManuallyDrop` here because we do not want to run the
345        // Drop impl of `RawConnection` as this would close the connection
346        Some(conn) => mem::ManuallyDrop::new(RawConnection {
347            internal_connection: conn,
348        }),
349        None => {
350            unsafe { context_error_str(ctx, NULL_CONN_ERR) };
351            return;
352        }
353    };
354
355    let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
356
357    let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
358        None => unsafe {
359            context_error_str(ctx, NULL_DATA_ERR);
360            return;
361        },
362        Some(mut f) => f,
363    };
364    let data_ptr = unsafe { data_ptr.as_mut() };
365
366    // We need this to move the reference into the catch_unwind part
367    // this is sound as `F` itself and the stored string is `UnwindSafe`
368    let callback = std::panic::AssertUnwindSafe(&mut data_ptr.callback);
369
370    let result = std::panic::catch_unwind(move || {
371        let _ = &callback;
372        let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
373        let res = (callback.0)(&*conn, args)?;
374        let value = process_sql_function_result(&res)?;
375        // We've checked already that ctx is not null
376        unsafe {
377            value.result_of(&mut *ctx);
378        }
379        Ok(())
380    })
381    .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
382    if let Err(e) = result {
383        e.emit(ctx);
384    }
385}
386
387#[allow(warnings)]
388extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
389    ctx: *mut ffi::sqlite3_context,
390    num_args: libc::c_int,
391    value_ptr: *mut *mut ffi::sqlite3_value,
392) where
393    A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + std::panic::UnwindSafe,
394    Args: FromSqlRow<ArgsSqlType, Sqlite>,
395    Ret: ToSql<RetSqlType, Sqlite>,
396    Sqlite: HasSqlType<RetSqlType>,
397{
398    let result = std::panic::catch_unwind(move || {
399        let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
400        run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
401    })
402    .unwrap_or_else(|e| {
403        Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::step() panicked",
                std::any::type_name::<A>()))
    })format!(
404            "{}::step() panicked",
405            std::any::type_name::<A>()
406        )))
407    });
408
409    match result {
410        Ok(()) => {}
411        Err(e) => e.emit(ctx),
412    }
413}
414
415fn run_aggregator_step<A, Args, ArgsSqlType>(
416    ctx: *mut ffi::sqlite3_context,
417    args: &mut [*mut ffi::sqlite3_value],
418) -> Result<(), SqliteCallbackError>
419where
420    A: SqliteAggregateFunction<Args>,
421    Args: FromSqlRow<ArgsSqlType, Sqlite>,
422{
423    let aggregator = unsafe {
424        const {
425            if core::mem::size_of::<*mut A>() == 0 {
426                {
    ::core::panicking::panic_fmt(format_args!("The pointer size is zero, that\'s unexpected.If you ever see this error message open a issuedescribing your environment"));
};panic!(
427                    "The pointer size is zero, that's unexpected.\
428                        If you ever see this error message open a issue\
429                        describing your environment"
430                );
431            }
432        }
433        // sqlite3_aggregate_context will return a memory allocation of the requested
434        // size. For the first call this will be zeroed, for any future call in the same execution
435        // this will contain the value we wrote into it.
436        //
437        // We write just a pointer to rust allocated memory in there to
438        // have the rust side deal with layout and alignment of our aggregator
439        let ctx = ffi::sqlite3_aggregate_context(
440            ctx,
441            core::mem::size_of::<*mut A>()
442                .try_into()
443                .expect("Memory size of a pointer is smaller than i32::MAX"),
444        )
445        // we cast the returned memory here to be a pointer to the aggregate instance
446        .cast::<*mut A>();
447        // we are interested in the inner pointer
448        let inner = &mut *ctx;
449        // if the inner pointer is null we the aggregate_step
450        // function is executed the first time and we need to create the actual
451        // aggregator
452        if inner.is_null() {
453            // for that we allocate a box and turn it into a raw pointer
454            // by leaking the memory
455            let obj = Box::into_raw(Box::new(A::default()));
456            *inner = obj;
457        }
458        // at this point the inner value is never null
459        // as we initialised in in the null branch above,
460        // therefore it's sound to dereference the pointer here
461        &mut **inner
462    };
463
464    let args = build_sql_function_args::<ArgsSqlType, Args>(args)?;
465
466    aggregator.step(args);
467    Ok(())
468}
469
470extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
471    ctx: *mut ffi::sqlite3_context,
472) where
473    A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
474    Args: FromSqlRow<ArgsSqlType, Sqlite>,
475    Ret: ToSql<RetSqlType, Sqlite>,
476    Sqlite: HasSqlType<RetSqlType>,
477{
478    let result = std::panic::catch_unwind(|| {
479        let aggregator = unsafe {
480            // Get back the aggregated context
481            // This might be null
482            let ctx = ffi::sqlite3_aggregate_context(
483                ctx,
484                // use zero sized allocation here to not allocate if this is the first call to `sqlite3_aggregate_context`
485                0,
486            )
487            // the allocation contains a pointer to the actual aggregator
488            .cast::<*mut A>();
489            // if the context was not allocated yet
490            // we get back a null pointer here due to
491            // the requested zero sized allocation
492            if ctx.is_null() {
493                None
494            } else {
495                // from this point we are interested in the inner pointer
496                // we checked above that this pointer is not null
497                // so it's sound to dereference it
498                let inner = &mut *ctx;
499                if inner.is_null() {
500                    // if the inner pointer is null the aggregator has not been initialized
501                    None
502                } else {
503                    // if it's not null
504                    // we need to construct back the box and move out the
505                    // value to correctly deallocate the allocation
506                    let value = Box::from_raw(*inner);
507                    let value = Some(*value);
508                    // we also want to write a null pointer back to the
509                    // context to make sure that there is no dangling pointer left
510                    *inner = core::ptr::null_mut();
511                    value
512                }
513            }
514        };
515
516        let res = A::finalize(aggregator);
517        let value = process_sql_function_result(&res)?;
518        // We've checked already that ctx is not null
519        let r = unsafe { value.result_of(&mut *ctx) };
520        r.map_err(|e| {
521            SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
522        })?;
523        Ok(())
524    })
525    .unwrap_or_else(|_e| {
526        Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::finalize() panicked",
                std::any::type_name::<A>()))
    })format!(
527            "{}::finalize() panicked",
528            std::any::type_name::<A>()
529        )))
530    });
531    if let Err(e) = result {
532        e.emit(ctx);
533    }
534}
535
536unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
537    let len: i32 = error.len().try_into().unwrap_or(i32::MAX);
538    unsafe {
539        ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
540    }
541}
542
543struct CollationUserPtr<F> {
544    callback: F,
545    collation_name: String,
546}
547
548#[allow(warnings)]
549extern "C" fn run_collation_function<F>(
550    user_ptr: *mut libc::c_void,
551    lhs_len: libc::c_int,
552    lhs_ptr: *const libc::c_void,
553    rhs_len: libc::c_int,
554    rhs_ptr: *const libc::c_void,
555) -> libc::c_int
556where
557    F: Fn(&str, &str) -> std::cmp::Ordering + Send + std::panic::UnwindSafe + 'static,
558{
559    let user_ptr = user_ptr as *const CollationUserPtr<F>;
560    let user_ptr = std::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
561
562    let result = std::panic::catch_unwind(|| {
563        let user_ptr = user_ptr.ok_or_else(|| {
564            SqliteCallbackError::Abort(
565                "Got a null pointer as data pointer. This should never happen",
566            )
567        })?;
568        for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
569            if *len < 0 {
570                {
    ::std::io::_eprint(format_args!("An unknown error occurred. {0}_len is negative. This should never happen.If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\nSource location: {1}:{2}\n",
            side, "diesel/src/sqlite/connection/raw.rs", 570u32));
};
std::process::abort();assert_fail!(
571                    "An unknown error occurred. {}_len is negative. This should never happen.",
572                    side
573                );
574            }
575            if ptr.is_null() {
576                {
    ::std::io::_eprint(format_args!("An unknown error occurred. {0}_ptr is a null pointer. This should never happen.If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\nSource location: {1}:{2}\n",
            side, "diesel/src/sqlite/connection/raw.rs", 576u32));
};
std::process::abort();assert_fail!(
577                "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
578                side
579            );
580            }
581        }
582
583        let (rhs, lhs) = unsafe {
584            // Depending on the eTextRep-parameter to sqlite3_create_collation_v2() the strings can
585            // have various encodings. register_collation_function() always selects SQLITE_UTF8, so the
586            // pointers point to valid UTF-8 strings (assuming correct behavior of libsqlite3).
587            (
588                str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
589                str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
590            )
591        };
592
593        let rhs =
594            rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
595        let lhs =
596            lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
597
598        Ok((user_ptr.callback)(rhs, lhs))
599    })
600    .unwrap_or_else(|p| {
601        Err(SqliteCallbackError::Panic(
602            user_ptr
603                .map(|u| u.collation_name.clone())
604                .unwrap_or_default(),
605        ))
606    });
607
608    match result {
609        Ok(std::cmp::Ordering::Less) => -1,
610        Ok(std::cmp::Ordering::Equal) => 0,
611        Ok(std::cmp::Ordering::Greater) => 1,
612        Err(SqliteCallbackError::Abort(a)) => {
613            {
    ::std::io::_eprint(format_args!("Collation function {0} failed with: {1}\n",
            user_ptr.map(|c| &c.collation_name as &str).unwrap_or_default(),
            a));
};eprintln!(
614                "Collation function {} failed with: {}",
615                user_ptr
616                    .map(|c| &c.collation_name as &str)
617                    .unwrap_or_default(),
618                a
619            );
620            std::process::abort()
621        }
622        Err(SqliteCallbackError::DieselError(e)) => {
623            {
    ::std::io::_eprint(format_args!("Collation function {0} failed with: {1}\n",
            user_ptr.map(|c| &c.collation_name as &str).unwrap_or_default(),
            e));
};eprintln!(
624                "Collation function {} failed with: {}",
625                user_ptr
626                    .map(|c| &c.collation_name as &str)
627                    .unwrap_or_default(),
628                e
629            );
630            std::process::abort()
631        }
632        Err(SqliteCallbackError::Panic(msg)) => {
633            {
    ::std::io::_eprint(format_args!("Collation function {0} panicked\n",
            msg));
};eprintln!("Collation function {} panicked", msg);
634            std::process::abort()
635        }
636    }
637}
638
639extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
640    let ptr = data as *mut F;
641    unsafe { std::mem::drop(Box::from_raw(ptr)) };
642}