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    // SAFETY:
230    // Any caller must ensure that the provided data buffer is valid and not modified until the database connection is closed
231    // Sqlite's documentation states:
232    // Applications must not modify the buffer P or invalidate it before the database connection D is closed.
233    pub(super) unsafe fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
234        let db_size = data
235            .len()
236            .try_into()
237            .map_err(|e| Error::DeserializationError(Box::new(e)))?;
238        // the cast for `ffi::SQLITE_DESERIALIZE_READONLY` is required for old libsqlite3-sys versions
239        #[allow(clippy::unnecessary_cast)]
240        unsafe {
241            let result = ffi::sqlite3_deserialize(
242                self.internal_connection.as_ptr(),
243                std::ptr::null(),
244                data.as_ptr() as *mut u8,
245                db_size,
246                db_size,
247                ffi::SQLITE_DESERIALIZE_READONLY as u32,
248            );
249
250            ensure_sqlite_ok(result, self.internal_connection.as_ptr())
251        }
252    }
253
254    fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
255        CString::new(fn_name)
256    }
257
258    fn get_flags(deterministic: bool) -> i32 {
259        let mut flags = ffi::SQLITE_UTF8;
260        if deterministic {
261            flags |= ffi::SQLITE_DETERMINISTIC;
262        }
263        flags
264    }
265
266    fn process_sql_function_result(result: i32) -> Result<(), Error> {
267        if result == ffi::SQLITE_OK {
268            Ok(())
269        } else {
270            let error_message = super::error_message(result);
271            Err(DatabaseError(
272                DatabaseErrorKind::Unknown,
273                Box::new(error_message.to_string()),
274            ))
275        }
276    }
277}
278
279impl Drop for RawConnection {
280    fn drop(&mut self) {
281        use std::thread::panicking;
282
283        let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
284        if close_result != ffi::SQLITE_OK {
285            let error_message = super::error_message(close_result);
286            if panicking() {
287                stderr().write_fmt(format_args!("Error closing SQLite connection: {0}",
        error_message))write!(stderr(), "Error closing SQLite connection: {error_message}")
288                    .expect("Error writing to `stderr`");
289            } else {
290                {
    ::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
            error_message));
};panic!("Error closing SQLite connection: {error_message}");
291            }
292        }
293    }
294}
295
296enum SqliteCallbackError {
297    Abort(&'static str),
298    DieselError(crate::result::Error),
299    Panic(String),
300}
301
302impl SqliteCallbackError {
303    fn emit(&self, ctx: *mut ffi::sqlite3_context) {
304        let s;
305        let msg = match self {
306            SqliteCallbackError::Abort(msg) => *msg,
307            SqliteCallbackError::DieselError(e) => {
308                s = e.to_string();
309                &s
310            }
311            SqliteCallbackError::Panic(msg) => msg,
312        };
313        unsafe {
314            context_error_str(ctx, msg);
315        }
316    }
317}
318
319impl From<crate::result::Error> for SqliteCallbackError {
320    fn from(e: crate::result::Error) -> Self {
321        Self::DieselError(e)
322    }
323}
324
325struct CustomFunctionUserPtr<F> {
326    callback: F,
327    function_name: String,
328}
329
330#[allow(warnings)]
331extern "C" fn run_custom_function<F, Ret, RetSqlType>(
332    ctx: *mut ffi::sqlite3_context,
333    num_args: libc::c_int,
334    value_ptr: *mut *mut ffi::sqlite3_value,
335) where
336    F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
337        + std::panic::UnwindSafe
338        + Send
339        + 'static,
340    Ret: ToSql<RetSqlType, Sqlite>,
341    Sqlite: HasSqlType<RetSqlType>,
342{
343    use std::ops::Deref;
344    static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
345    static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
346
347    let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
348        // We use `ManuallyDrop` here because we do not want to run the
349        // Drop impl of `RawConnection` as this would close the connection
350        Some(conn) => mem::ManuallyDrop::new(RawConnection {
351            internal_connection: conn,
352        }),
353        None => {
354            unsafe { context_error_str(ctx, NULL_CONN_ERR) };
355            return;
356        }
357    };
358
359    let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
360
361    let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
362        None => unsafe {
363            context_error_str(ctx, NULL_DATA_ERR);
364            return;
365        },
366        Some(mut f) => f,
367    };
368    let data_ptr = unsafe { data_ptr.as_mut() };
369
370    // We need this to move the reference into the catch_unwind part
371    // this is sound as `F` itself and the stored string is `UnwindSafe`
372    let callback = std::panic::AssertUnwindSafe(&mut data_ptr.callback);
373
374    let result = std::panic::catch_unwind(move || {
375        let _ = &callback;
376        let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
377        let res = (callback.0)(&*conn, args)?;
378        let value = process_sql_function_result(&res)?;
379        // We've checked already that ctx is not null
380        unsafe {
381            value.result_of(&mut *ctx);
382        }
383        Ok(())
384    })
385    .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
386    if let Err(e) = result {
387        e.emit(ctx);
388    }
389}
390
391#[allow(warnings)]
392extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
393    ctx: *mut ffi::sqlite3_context,
394    num_args: libc::c_int,
395    value_ptr: *mut *mut ffi::sqlite3_value,
396) where
397    A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + std::panic::UnwindSafe,
398    Args: FromSqlRow<ArgsSqlType, Sqlite>,
399    Ret: ToSql<RetSqlType, Sqlite>,
400    Sqlite: HasSqlType<RetSqlType>,
401{
402    let result = std::panic::catch_unwind(move || {
403        let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
404        run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
405    })
406    .unwrap_or_else(|e| {
407        Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::step() panicked",
                std::any::type_name::<A>()))
    })format!(
408            "{}::step() panicked",
409            std::any::type_name::<A>()
410        )))
411    });
412
413    match result {
414        Ok(()) => {}
415        Err(e) => e.emit(ctx),
416    }
417}
418
419fn run_aggregator_step<A, Args, ArgsSqlType>(
420    ctx: *mut ffi::sqlite3_context,
421    args: &mut [*mut ffi::sqlite3_value],
422) -> Result<(), SqliteCallbackError>
423where
424    A: SqliteAggregateFunction<Args>,
425    Args: FromSqlRow<ArgsSqlType, Sqlite>,
426{
427    let aggregator = unsafe {
428        const {
429            if core::mem::size_of::<*mut A>() == 0 {
430                {
    ::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!(
431                    "The pointer size is zero, that's unexpected.\
432                        If you ever see this error message open a issue\
433                        describing your environment"
434                );
435            }
436        }
437        // sqlite3_aggregate_context will return a memory allocation of the requested
438        // size. For the first call this will be zeroed, for any future call in the same execution
439        // this will contain the value we wrote into it.
440        //
441        // We write just a pointer to rust allocated memory in there to
442        // have the rust side deal with layout and alignment of our aggregator
443        let ctx = ffi::sqlite3_aggregate_context(
444            ctx,
445            core::mem::size_of::<*mut A>()
446                .try_into()
447                .expect("Memory size of a pointer is smaller than i32::MAX"),
448        )
449        // we cast the returned memory here to be a pointer to the aggregate instance
450        .cast::<*mut A>();
451        // we are interested in the inner pointer
452        let inner = &mut *ctx;
453        // if the inner pointer is null we the aggregate_step
454        // function is executed the first time and we need to create the actual
455        // aggregator
456        if inner.is_null() {
457            // for that we allocate a box and turn it into a raw pointer
458            // by leaking the memory
459            let obj = Box::into_raw(Box::new(A::default()));
460            *inner = obj;
461        }
462        // at this point the inner value is never null
463        // as we initialised in in the null branch above,
464        // therefore it's sound to dereference the pointer here
465        &mut **inner
466    };
467
468    let args = build_sql_function_args::<ArgsSqlType, Args>(args)?;
469
470    aggregator.step(args);
471    Ok(())
472}
473
474extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
475    ctx: *mut ffi::sqlite3_context,
476) where
477    A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
478    Args: FromSqlRow<ArgsSqlType, Sqlite>,
479    Ret: ToSql<RetSqlType, Sqlite>,
480    Sqlite: HasSqlType<RetSqlType>,
481{
482    let result = std::panic::catch_unwind(|| {
483        let aggregator = unsafe {
484            // Get back the aggregated context
485            // This might be null
486            let ctx = ffi::sqlite3_aggregate_context(
487                ctx,
488                // use zero sized allocation here to not allocate if this is the first call to `sqlite3_aggregate_context`
489                0,
490            )
491            // the allocation contains a pointer to the actual aggregator
492            .cast::<*mut A>();
493            // if the context was not allocated yet
494            // we get back a null pointer here due to
495            // the requested zero sized allocation
496            if ctx.is_null() {
497                None
498            } else {
499                // from this point we are interested in the inner pointer
500                // we checked above that this pointer is not null
501                // so it's sound to dereference it
502                let inner = &mut *ctx;
503                if inner.is_null() {
504                    // if the inner pointer is null the aggregator has not been initialized
505                    None
506                } else {
507                    // if it's not null
508                    // we need to construct back the box and move out the
509                    // value to correctly deallocate the allocation
510                    let value = Box::from_raw(*inner);
511                    let value = Some(*value);
512                    // we also want to write a null pointer back to the
513                    // context to make sure that there is no dangling pointer left
514                    *inner = core::ptr::null_mut();
515                    value
516                }
517            }
518        };
519
520        let res = A::finalize(aggregator);
521        let value = process_sql_function_result(&res)?;
522        // We've checked already that ctx is not null
523        let r = unsafe { value.result_of(&mut *ctx) };
524        r.map_err(|e| {
525            SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
526        })?;
527        Ok(())
528    })
529    .unwrap_or_else(|_e| {
530        Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::finalize() panicked",
                std::any::type_name::<A>()))
    })format!(
531            "{}::finalize() panicked",
532            std::any::type_name::<A>()
533        )))
534    });
535    if let Err(e) = result {
536        e.emit(ctx);
537    }
538}
539
540unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
541    let len: i32 = error.len().try_into().unwrap_or(i32::MAX);
542    unsafe {
543        ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
544    }
545}
546
547struct CollationUserPtr<F> {
548    callback: F,
549    collation_name: String,
550}
551
552#[allow(warnings)]
553extern "C" fn run_collation_function<F>(
554    user_ptr: *mut libc::c_void,
555    lhs_len: libc::c_int,
556    lhs_ptr: *const libc::c_void,
557    rhs_len: libc::c_int,
558    rhs_ptr: *const libc::c_void,
559) -> libc::c_int
560where
561    F: Fn(&str, &str) -> std::cmp::Ordering + Send + std::panic::UnwindSafe + 'static,
562{
563    let user_ptr = user_ptr as *const CollationUserPtr<F>;
564    let user_ptr = std::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
565
566    let result = std::panic::catch_unwind(|| {
567        let user_ptr = user_ptr.ok_or_else(|| {
568            SqliteCallbackError::Abort(
569                "Got a null pointer as data pointer. This should never happen",
570            )
571        })?;
572        for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
573            if *len < 0 {
574                {
    ::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", 574u32));
};
std::process::abort();assert_fail!(
575                    "An unknown error occurred. {}_len is negative. This should never happen.",
576                    side
577                );
578            }
579            if ptr.is_null() {
580                {
    ::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", 580u32));
};
std::process::abort();assert_fail!(
581                "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
582                side
583            );
584            }
585        }
586
587        let (rhs, lhs) = unsafe {
588            // Depending on the eTextRep-parameter to sqlite3_create_collation_v2() the strings can
589            // have various encodings. register_collation_function() always selects SQLITE_UTF8, so the
590            // pointers point to valid UTF-8 strings (assuming correct behavior of libsqlite3).
591            (
592                str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
593                str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
594            )
595        };
596
597        let rhs =
598            rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
599        let lhs =
600            lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
601
602        Ok((user_ptr.callback)(rhs, lhs))
603    })
604    .unwrap_or_else(|p| {
605        Err(SqliteCallbackError::Panic(
606            user_ptr
607                .map(|u| u.collation_name.clone())
608                .unwrap_or_default(),
609        ))
610    });
611
612    match result {
613        Ok(std::cmp::Ordering::Less) => -1,
614        Ok(std::cmp::Ordering::Equal) => 0,
615        Ok(std::cmp::Ordering::Greater) => 1,
616        Err(SqliteCallbackError::Abort(a)) => {
617            {
    ::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!(
618                "Collation function {} failed with: {}",
619                user_ptr
620                    .map(|c| &c.collation_name as &str)
621                    .unwrap_or_default(),
622                a
623            );
624            std::process::abort()
625        }
626        Err(SqliteCallbackError::DieselError(e)) => {
627            {
    ::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!(
628                "Collation function {} failed with: {}",
629                user_ptr
630                    .map(|c| &c.collation_name as &str)
631                    .unwrap_or_default(),
632                e
633            );
634            std::process::abort()
635        }
636        Err(SqliteCallbackError::Panic(msg)) => {
637            {
    ::std::io::_eprint(format_args!("Collation function {0} panicked\n",
            msg));
};eprintln!("Collation function {} panicked", msg);
638            std::process::abort()
639        }
640    }
641}
642
643extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
644    let ptr = data as *mut F;
645    unsafe { std::mem::drop(Box::from_raw(ptr)) };
646}