use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::path::Path;
use std::process;
use std::ptr::{self, NonNull};
use std::slice;
use std::sync::Arc;
use libc::{c_char, c_int, c_uchar, c_void};
use crate::compaction_filter::{self, CompactionFilterCallback, CompactionFilterFn};
use crate::compaction_filter_factory::{self, CompactionFilterFactory};
use crate::comparator::Comparator;
use crate::db_options::{OptionsMustOutliveDB, OwnedCompactionFilter};
use crate::event_listener::DBCompactionReason;
use crate::ffi_util::{CStrLike, convert_rocksdb_error, raw_data_and_free, to_cpath};
use crate::file_checksum::FileChecksumGenFactory;
use crate::merge_operator::{
self, MergeFn, MergeOperatorCallback, full_merge_callback, partial_merge_callback,
};
use crate::slice_transform::SliceTransform;
use crate::sst_partitioner::SstPartitionerFactory;
use crate::{BlockBasedOptions, CuckooTableOptions, Env, Error, InfoLogger, Options, ffi};
const JOB_STATUS_SUCCESS: c_int = ffi::rocksdb_compactionservice_jobstatus_success as c_int;
const JOB_STATUS_FAILURE: c_int = ffi::rocksdb_compactionservice_jobstatus_failure as c_int;
const JOB_STATUS_ABORTED: c_int = ffi::rocksdb_compactionservice_jobstatus_aborted as c_int;
const JOB_STATUS_USE_LOCAL: c_int = ffi::rocksdb_compactionservice_jobstatus_use_local as c_int;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CompactionServiceJobStatus {
Success,
Failure,
Aborted,
UseLocal,
}
impl CompactionServiceJobStatus {
fn as_raw(self) -> c_int {
match self {
CompactionServiceJobStatus::Success => JOB_STATUS_SUCCESS,
CompactionServiceJobStatus::Failure => JOB_STATUS_FAILURE,
CompactionServiceJobStatus::Aborted => JOB_STATUS_ABORTED,
CompactionServiceJobStatus::UseLocal => JOB_STATUS_USE_LOCAL,
}
}
fn try_from_raw(raw: c_int) -> Option<Self> {
match raw {
JOB_STATUS_SUCCESS => Some(CompactionServiceJobStatus::Success),
JOB_STATUS_FAILURE => Some(CompactionServiceJobStatus::Failure),
JOB_STATUS_ABORTED => Some(CompactionServiceJobStatus::Aborted),
JOB_STATUS_USE_LOCAL => Some(CompactionServiceJobStatus::UseLocal),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum EnvPriority {
Bottom,
Low,
High,
User,
}
impl EnvPriority {
fn try_from_raw(raw: c_int) -> Option<Self> {
[
EnvPriority::Bottom,
EnvPriority::Low,
EnvPriority::High,
EnvPriority::User,
]
.into_iter()
.find(|&candidate| raw == candidate as c_int)
}
}
fn compaction_reason_from_raw(raw: c_int) -> Option<DBCompactionReason> {
if !(DBCompactionReason::KUnknown as c_int..DBCompactionReason::KNumOfReasons as c_int)
.contains(&raw)
{
return None;
}
Some(DBCompactionReason::from(raw as u32))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_triggered_is_a_named_compaction_reason() {
assert_eq!(
compaction_reason_from_raw(DBCompactionReason::KReadTriggered as c_int),
Some(DBCompactionReason::KReadTriggered)
);
assert_eq!(
compaction_reason_from_raw(DBCompactionReason::KNumOfReasons as c_int),
None
);
}
}
unsafe fn borrowed_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
if len == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(ptr, len) }
}
}
fn malloc_copy(bytes: &[u8]) -> Option<*mut c_char> {
debug_assert!(!bytes.is_empty(), "malloc_copy is not for empty results");
let buffer = unsafe { libc::malloc(bytes.len()) };
if buffer.is_null() {
return None;
}
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buffer.cast::<u8>(), bytes.len());
}
Some(buffer.cast::<c_char>())
}
pub struct CompactionServiceJobInfo<'a> {
inner: *const ffi::rocksdb_compactionservice_jobinfo_t,
_marker: PhantomData<&'a ()>,
}
impl CompactionServiceJobInfo<'_> {
unsafe fn from_ptr(inner: *const ffi::rocksdb_compactionservice_jobinfo_t) -> Self {
Self {
inner,
_marker: PhantomData,
}
}
pub fn db_name(&self) -> &[u8] {
unsafe { self.string_field(ffi::rocksdb_compactionservice_jobinfo_t_get_db_name) }
}
pub fn db_id(&self) -> &[u8] {
unsafe { self.string_field(ffi::rocksdb_compactionservice_jobinfo_t_get_db_id) }
}
pub fn db_session_id(&self) -> &[u8] {
unsafe { self.string_field(ffi::rocksdb_compactionservice_jobinfo_t_get_db_session_id) }
}
pub fn cf_name(&self) -> &[u8] {
unsafe { self.string_field(ffi::rocksdb_compactionservice_jobinfo_t_get_cf_name) }
}
pub fn cf_id(&self) -> u32 {
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_get_cf_id(self.inner) }
}
pub fn job_id(&self) -> u64 {
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_get_job_id(self.inner) }
}
pub fn priority(&self) -> Option<EnvPriority> {
let raw = unsafe { ffi::rocksdb_compactionservice_jobinfo_t_get_priority(self.inner) };
EnvPriority::try_from_raw(raw)
}
pub fn compaction_reason(&self) -> Option<DBCompactionReason> {
let raw =
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_get_compaction_reason(self.inner) };
compaction_reason_from_raw(raw)
}
pub fn base_input_level(&self) -> i32 {
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_get_base_input_level(self.inner) }
}
pub fn output_level(&self) -> i32 {
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_get_output_level(self.inner) }
}
pub fn is_full_compaction(&self) -> bool {
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_is_full_compaction(self.inner) != 0 }
}
pub fn is_manual_compaction(&self) -> bool {
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_is_manual_compaction(self.inner) != 0 }
}
pub fn is_bottommost_level(&self) -> bool {
unsafe { ffi::rocksdb_compactionservice_jobinfo_t_is_bottommost_level(self.inner) != 0 }
}
unsafe fn string_field(
&self,
getter: unsafe extern "C" fn(
*const ffi::rocksdb_compactionservice_jobinfo_t,
*mut usize,
) -> *const c_char,
) -> &[u8] {
let mut len: usize = 0;
unsafe {
let ptr = getter(self.inner, &raw mut len);
borrowed_slice(ptr.cast::<u8>(), len)
}
}
}
pub struct ScheduleResponse {
inner: NonNull<ffi::rocksdb_compactionservice_scheduleresponse_t>,
}
impl ScheduleResponse {
pub fn scheduled(
scheduled_job_id: impl CStrLike,
status: CompactionServiceJobStatus,
) -> Result<Self, Error> {
let job_id = scheduled_job_id
.bake()
.map_err(|e| Error::new(format!("scheduled job id must not contain NUL: {e}")))?;
let inner = unsafe {
ffi_try!(ffi::rocksdb_compactionservice_scheduleresponse_create(
job_id.as_ptr(),
status.as_raw(),
))
};
Ok(Self {
inner: NonNull::new(inner)
.expect("rocksdb_compactionservice_scheduleresponse_create returned null"),
})
}
pub fn from_status(status: CompactionServiceJobStatus) -> Self {
let mut err: *mut c_char = ptr::null_mut();
let inner = unsafe {
ffi::rocksdb_compactionservice_scheduleresponse_create_with_status(
status.as_raw(),
&raw mut err,
)
};
assert!(
err.is_null(),
"rocksdb_compactionservice_scheduleresponse_create_with_status rejected a status \
this crate produced: {}",
convert_rocksdb_error(err)
);
Self {
inner: NonNull::new(inner).expect(
"rocksdb_compactionservice_scheduleresponse_create_with_status returned null",
),
}
}
pub fn status(&self) -> Option<CompactionServiceJobStatus> {
let raw = unsafe {
ffi::rocksdb_compactionservice_scheduleresponse_getstatus(self.inner.as_ptr())
};
CompactionServiceJobStatus::try_from_raw(raw)
}
pub fn scheduled_job_id(&self) -> &[u8] {
let mut len: usize = 0;
unsafe {
let ptr = ffi::rocksdb_compactionservice_scheduleresponse_get_scheduled_job_id(
self.inner.as_ptr(),
&raw mut len,
);
borrowed_slice(ptr.cast::<u8>(), len)
}
}
fn into_raw(self) -> *mut ffi::rocksdb_compactionservice_scheduleresponse_t {
ManuallyDrop::new(self).inner.as_ptr()
}
}
impl Drop for ScheduleResponse {
fn drop(&mut self) {
unsafe {
ffi::rocksdb_compactionservice_scheduleresponse_t_destroy(self.inner.as_ptr());
}
}
}
unsafe impl Send for ScheduleResponse {}
unsafe impl Sync for ScheduleResponse {}
pub trait CompactionService: Send + Sync {
fn name(&self) -> &CStr;
fn schedule(&self, info: &CompactionServiceJobInfo<'_>, input: &[u8]) -> ScheduleResponse;
fn wait(&self, scheduled_job_id: &CStr, result: &mut Vec<u8>) -> CompactionServiceJobStatus;
fn cancel_awaiting_jobs(&self) {}
fn on_installation(
&self,
_scheduled_job_id: &CStr,
_status: Option<CompactionServiceJobStatus>,
) {
}
}
unsafe extern "C" fn destructor_callback<S: CompactionService>(state: *mut c_void) {
unsafe {
drop(Box::from_raw(state.cast::<S>()));
}
}
unsafe extern "C" fn schedule_callback<S: CompactionService>(
state: *mut c_void,
info: *const ffi::rocksdb_compactionservice_jobinfo_t,
compaction_service_input: *const c_char,
input_len: usize,
) -> *mut ffi::rocksdb_compactionservice_scheduleresponse_t {
let service = unsafe { &*state.cast::<S>() };
let job_info = unsafe { CompactionServiceJobInfo::from_ptr(info) };
let input = unsafe { borrowed_slice(compaction_service_input.cast::<u8>(), input_len) };
let response = catch_unwind(AssertUnwindSafe(|| service.schedule(&job_info, input)));
let Ok(response) = response else {
process::abort()
};
response.into_raw()
}
unsafe extern "C" fn wait_callback<S: CompactionService>(
state: *mut c_void,
scheduled_job_id: *const c_char,
result: *mut *mut c_char,
result_len: *mut usize,
) -> c_int {
let service = unsafe { &*state.cast::<S>() };
let job_id = unsafe { CStr::from_ptr(scheduled_job_id) };
let mut buffer = Vec::new();
let status = catch_unwind(AssertUnwindSafe(|| service.wait(job_id, &mut buffer)));
let Ok(status) = status else { process::abort() };
if buffer.is_empty() {
return status.as_raw();
}
let Some(copied) = malloc_copy(&buffer) else {
return CompactionServiceJobStatus::Failure.as_raw();
};
unsafe {
*result = copied;
*result_len = buffer.len();
}
status.as_raw()
}
unsafe extern "C" fn cancel_awaiting_jobs_callback<S: CompactionService>(state: *mut c_void) {
let service = unsafe { &*state.cast::<S>() };
if catch_unwind(AssertUnwindSafe(|| service.cancel_awaiting_jobs())).is_err() {
process::abort();
}
}
unsafe extern "C" fn on_installation_callback<S: CompactionService>(
state: *mut c_void,
scheduled_job_id: *const c_char,
status: c_int,
) {
let service = unsafe { &*state.cast::<S>() };
let job_id = unsafe { CStr::from_ptr(scheduled_job_id) };
let status = CompactionServiceJobStatus::try_from_raw(status);
if catch_unwind(AssertUnwindSafe(|| service.on_installation(job_id, status))).is_err() {
process::abort();
}
}
#[must_use]
pub(crate) struct OwnedCompactionService {
inner: NonNull<ffi::rocksdb_compactionservice_t>,
}
impl OwnedCompactionService {
pub(crate) fn into_ptr(self) -> *mut ffi::rocksdb_compactionservice_t {
self.inner.as_ptr()
}
}
pub(crate) fn new_compaction_service<S: CompactionService + 'static>(
service: S,
) -> OwnedCompactionService {
let state = Box::into_raw(Box::new(service));
let name = unsafe { (*state).name().as_ptr() };
let inner = unsafe {
ffi::rocksdb_compactionservice_create(
state.cast::<c_void>(),
Some(destructor_callback::<S>),
Some(schedule_callback::<S>),
name,
Some(wait_callback::<S>),
Some(cancel_awaiting_jobs_callback::<S>),
Some(on_installation_callback::<S>),
)
};
OwnedCompactionService {
inner: NonNull::new(inner).expect("rocksdb_compactionservice_create returned null"),
}
}
#[derive(Default)]
struct OverrideOutlive {
env: Option<Env>,
comparator: Option<Arc<Comparator>>,
compaction_filter: Option<OwnedCompactionFilter>,
info_log: Option<InfoLogger>,
_from_options: Option<OptionsMustOutliveDB>,
}
pub struct CompactionServiceOptionsOverride {
inner: NonNull<ffi::rocksdb_compaction_service_options_override_t>,
outlive: OverrideOutlive,
}
unsafe impl Send for CompactionServiceOptionsOverride {}
unsafe impl Sync for CompactionServiceOptionsOverride {}
impl Default for CompactionServiceOptionsOverride {
fn default() -> Self {
Self::create()
}
}
impl CompactionServiceOptionsOverride {
pub fn create() -> Self {
let inner = unsafe { ffi::rocksdb_compaction_service_options_override_create() };
let mut override_options = Self {
inner: NonNull::new(inner)
.expect("rocksdb_compaction_service_options_override_create returned null"),
outlive: OverrideOutlive::default(),
};
override_options.set_block_based_table_factory(&BlockBasedOptions::default());
override_options
}
pub fn from_options(options: &Options) -> Self {
let inner = unsafe {
ffi::rocksdb_compaction_service_options_override_create_from_options(options.inner)
};
Self {
inner: NonNull::new(inner).expect(
"rocksdb_compaction_service_options_override_create_from_options returned null",
),
outlive: OverrideOutlive {
_from_options: Some(options.outlive.clone()),
..OverrideOutlive::default()
},
}
}
pub fn set_env(&mut self, env: &Env) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_env(
self.inner.as_ptr(),
env.0.inner,
);
}
self.outlive.env = Some(env.clone());
}
pub fn set_comparator(&mut self, comparator: Arc<Comparator>) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_comparator(
self.inner.as_ptr(),
comparator.inner.as_ptr(),
);
}
self.outlive.comparator = Some(comparator);
}
pub fn set_merge_operator<F: MergeFn, PF: MergeFn>(
&mut self,
name: impl CStrLike,
full_merge_fn: F,
partial_merge_fn: PF,
) -> Result<(), Error> {
let name = name
.into_c_string()
.map_err(|e| Error::new(format!("merge operator name must not contain NUL: {e}")))?;
let callback = Box::new(MergeOperatorCallback {
name,
full_merge_fn,
partial_merge_fn,
});
unsafe {
let operator = ffi::rocksdb_mergeoperator_create(
Box::into_raw(callback).cast::<c_void>(),
Some(merge_operator::destructor_callback::<F, PF>),
Some(full_merge_callback::<F, PF>),
Some(partial_merge_callback::<F, PF>),
Some(merge_operator::delete_callback),
Some(merge_operator::name_callback::<F, PF>),
);
ffi::rocksdb_compaction_service_options_override_set_merge_operator(
self.inner.as_ptr(),
operator,
);
}
Ok(())
}
pub fn set_compaction_filter<F>(
&mut self,
name: impl CStrLike,
filter_fn: F,
) -> Result<(), Error>
where
F: CompactionFilterFn + Send + 'static,
{
let name = name
.into_c_string()
.map_err(|e| Error::new(format!("compaction filter name must not contain NUL: {e}")))?;
let callback = Box::new(CompactionFilterCallback { name, filter_fn });
let raw = unsafe {
ffi::rocksdb_compactionfilter_create(
Box::into_raw(callback).cast::<c_void>(),
Some(compaction_filter::destructor_callback::<CompactionFilterCallback<F>>),
Some(compaction_filter::filter_callback::<CompactionFilterCallback<F>>),
Some(compaction_filter::name_callback::<CompactionFilterCallback<F>>),
)
};
let filter = OwnedCompactionFilter::new(
NonNull::new(raw).expect("rocksdb_compactionfilter_create returned null"),
);
unsafe {
ffi::rocksdb_compaction_service_options_override_set_compaction_filter(
self.inner.as_ptr(),
raw,
);
}
self.outlive.compaction_filter = Some(filter);
Ok(())
}
pub fn set_compaction_filter_factory<F>(&mut self, factory: F)
where
F: CompactionFilterFactory + 'static,
{
let factory = Box::new(factory);
unsafe {
let raw = ffi::rocksdb_compactionfilterfactory_create(
Box::into_raw(factory).cast::<c_void>(),
Some(compaction_filter_factory::destructor_callback::<F>),
Some(compaction_filter_factory::create_compaction_filter_callback::<F>),
Some(compaction_filter_factory::name_callback::<F>),
);
ffi::rocksdb_compaction_service_options_override_set_compaction_filter_factory(
self.inner.as_ptr(),
raw,
);
}
}
pub fn set_prefix_extractor(&mut self, prefix_extractor: SliceTransform) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_prefix_extractor(
self.inner.as_ptr(),
prefix_extractor.inner,
);
}
}
pub fn set_block_based_table_factory(&mut self, table_options: &BlockBasedOptions) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_block_based_table_factory(
self.inner.as_ptr(),
table_options.inner,
);
}
}
pub fn set_cuckoo_table_factory(&mut self, table_options: &CuckooTableOptions) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_cuckoo_table_factory(
self.inner.as_ptr(),
table_options.inner,
);
}
}
pub fn set_sst_partitioner_factory(&mut self, factory: &SstPartitionerFactory) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_sst_partitioner_factory(
self.inner.as_ptr(),
factory.as_ptr(),
);
}
}
pub fn set_file_checksum_gen_factory(&mut self, factory: &FileChecksumGenFactory) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_file_checksum_gen_factory(
self.inner.as_ptr(),
factory.as_ptr(),
);
}
}
pub fn set_statistics(&mut self, options: &Options) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_statistics(
self.inner.as_ptr(),
options.inner,
);
}
}
pub fn set_info_log(&mut self, logger: InfoLogger) {
unsafe {
ffi::rocksdb_compaction_service_options_override_set_info_log(
self.inner.as_ptr(),
logger.inner,
);
}
self.outlive.info_log = Some(logger);
}
pub fn set_option(&mut self, name: impl CStrLike, value: impl CStrLike) -> Result<(), Error> {
let name = name
.bake()
.map_err(|e| Error::new(format!("option name must not contain NUL: {e}")))?;
let value = value
.bake()
.map_err(|e| Error::new(format!("option value must not contain NUL: {e}")))?;
unsafe {
ffi::rocksdb_compaction_service_options_override_set_option(
self.inner.as_ptr(),
name.as_ptr(),
value.as_ptr(),
);
}
Ok(())
}
fn as_ptr(&self) -> *const ffi::rocksdb_compaction_service_options_override_t {
self.inner.as_ptr().cast_const()
}
}
impl Drop for CompactionServiceOptionsOverride {
fn drop(&mut self) {
unsafe {
ffi::rocksdb_compaction_service_options_override_destroy(self.inner.as_ptr());
}
}
}
pub struct OpenAndCompactCancellationToken {
inner: *mut c_uchar,
}
unsafe impl Send for OpenAndCompactCancellationToken {}
unsafe impl Sync for OpenAndCompactCancellationToken {}
impl Default for OpenAndCompactCancellationToken {
fn default() -> Self {
Self::new()
}
}
impl OpenAndCompactCancellationToken {
pub fn new() -> Self {
let inner = unsafe { ffi::rocksdb_open_and_compact_canceled_create() };
assert!(
!inner.is_null(),
"Could not create RocksDB open and compact cancellation token"
);
Self { inner }
}
pub fn cancel(&self) {
unsafe {
ffi::rocksdb_open_and_compact_canceled_set(self.inner, 1);
}
}
fn as_ptr(&self) -> *mut c_uchar {
self.inner
}
}
impl Drop for OpenAndCompactCancellationToken {
fn drop(&mut self) {
unsafe {
ffi::rocksdb_open_and_compact_canceled_destroy(self.inner);
}
}
}
pub struct OpenAndCompactOptions {
inner: *mut ffi::rocksdb_open_and_compact_options_t,
canceled: Option<Arc<OpenAndCompactCancellationToken>>,
}
unsafe impl Send for OpenAndCompactOptions {}
unsafe impl Sync for OpenAndCompactOptions {}
impl Default for OpenAndCompactOptions {
fn default() -> Self {
let inner = unsafe { ffi::rocksdb_open_and_compact_options_create() };
assert!(
!inner.is_null(),
"Could not create RocksDB open and compact options"
);
Self {
inner,
canceled: None,
}
}
}
impl OpenAndCompactOptions {
pub fn set_allow_resumption(&mut self, allow_resumption: bool) {
unsafe {
ffi::rocksdb_open_and_compact_options_set_allow_resumption(
self.inner,
c_uchar::from(allow_resumption),
);
}
}
pub fn allow_resumption(&self) -> bool {
unsafe { ffi::rocksdb_open_and_compact_options_get_allow_resumption(self.inner) != 0 }
}
pub fn set_canceled(&mut self, token: Arc<OpenAndCompactCancellationToken>) {
let ptr = token.as_ptr();
unsafe {
ffi::rocksdb_open_and_compact_options_set_canceled(self.inner, ptr);
}
self.canceled = Some(token);
}
pub fn canceled(&self) -> Option<&Arc<OpenAndCompactCancellationToken>> {
self.canceled.as_ref()
}
}
impl Drop for OpenAndCompactOptions {
fn drop(&mut self) {
unsafe {
ffi::rocksdb_open_and_compact_options_destroy(self.inner);
}
}
}
pub fn open_and_compact<P: AsRef<Path>, Q: AsRef<Path>>(
db_path: P,
output_directory: Q,
input: &[u8],
override_options: &CompactionServiceOptionsOverride,
) -> Result<Vec<u8>, Error> {
let db_path = to_cpath(db_path)?;
let output_directory = to_cpath(output_directory)?;
let mut output_len: usize = 0;
let output = unsafe {
ffi_try!(ffi::rocksdb_open_and_compact(
db_path.as_ptr(),
output_directory.as_ptr(),
input.as_ptr().cast::<c_char>(),
input.len(),
&raw mut output_len,
override_options.as_ptr(),
))
};
take_compaction_output(output, output_len)
}
pub fn open_and_compact_with_options<P: AsRef<Path>, Q: AsRef<Path>>(
options: &OpenAndCompactOptions,
db_path: P,
output_directory: Q,
input: &[u8],
override_options: &CompactionServiceOptionsOverride,
) -> Result<Vec<u8>, Error> {
let db_path = to_cpath(db_path)?;
let output_directory = to_cpath(output_directory)?;
let mut output_len: usize = 0;
let output = unsafe {
ffi_try!(ffi::rocksdb_open_and_compact_with_options(
options.inner.cast_const(),
db_path.as_ptr(),
output_directory.as_ptr(),
input.as_ptr().cast::<c_char>(),
input.len(),
&raw mut output_len,
override_options.as_ptr(),
))
};
take_compaction_output(output, output_len)
}
fn take_compaction_output(output: *mut c_char, output_len: usize) -> Result<Vec<u8>, Error> {
unsafe { raw_data_and_free(output, output_len) }.ok_or_else(|| {
Error::new("rocksdb_open_and_compact returned no result and no error".to_owned())
})
}