1// Copyright 2020 Tyler Neely
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
1415use std::ffi::CString;
16use std::slice;
1718use libc::{c_char, c_uchar, c_void, size_t};
1920use crate::{ffi, ffi_util::CStrLike};
2122/// A `SliceTransform` is a generic pluggable way of transforming one string
23/// to another. Its primary use-case is in configuring rocksdb
24/// to store prefix blooms by setting prefix_extractor in
25/// ColumnFamilyOptions.
26pub struct SliceTransform {
27pub inner: *mut ffi::rocksdb_slicetransform_t,
28}
2930// NB we intentionally don't implement a Drop that passes
31// through to rocksdb_slicetransform_destroy because
32// this is currently only used (to my knowledge)
33// by people passing it as a prefix extractor when
34// opening a DB.
3536impl SliceTransform {
37pub fn create(
38 name: impl CStrLike,
39 transform_fn: TransformFn,
40 in_domain_fn: Option<InDomainFn>,
41 ) -> SliceTransform {
42let cb = Box::into_raw(Box::new(TransformCallback {
43 name: name.into_c_string().unwrap(),
44 transform_fn,
45 in_domain_fn,
46 }));
4748let st = unsafe {
49 ffi::rocksdb_slicetransform_create(
50 cb as *mut c_void,
51Some(slice_transform_destructor_callback),
52Some(transform_callback),
53Some(in_domain_callback),
54// this None points to the deprecated InRange callback
55None,
56Some(slice_transform_name_callback),
57 )
58 };
5960 SliceTransform { inner: st }
61 }
6263pub fn create_fixed_prefix(len: size_t) -> SliceTransform {
64 SliceTransform {
65 inner: unsafe { ffi::rocksdb_slicetransform_create_fixed_prefix(len) },
66 }
67 }
6869pub fn create_noop() -> SliceTransform {
70 SliceTransform {
71 inner: unsafe { ffi::rocksdb_slicetransform_create_noop() },
72 }
73 }
74}
7576pub type TransformFn<'a> = fn(&'a [u8]) -> &'a [u8];
77pub type InDomainFn = fn(&[u8]) -> bool;
7879pub struct TransformCallback<'a> {
80pub name: CString,
81pub transform_fn: TransformFn<'a>,
82pub in_domain_fn: Option<InDomainFn>,
83}
8485pub unsafe extern "C" fn slice_transform_destructor_callback(raw_cb: *mut c_void) {
86 drop(Box::from_raw(raw_cb as *mut TransformCallback));
87}
8889pub unsafe extern "C" fn slice_transform_name_callback(raw_cb: *mut c_void) -> *const c_char {
90let cb = &mut *(raw_cb as *mut TransformCallback);
91 cb.name.as_ptr()
92}
9394pub unsafe extern "C" fn transform_callback(
95 raw_cb: *mut c_void,
96 raw_key: *const c_char,
97 key_len: size_t,
98 dst_length: *mut size_t,
99) -> *mut c_char {
100let cb = &mut *(raw_cb as *mut TransformCallback);
101let key = slice::from_raw_parts(raw_key as *const u8, key_len);
102let prefix = (cb.transform_fn)(key);
103*dst_length = prefix.len() as size_t;
104 prefix.as_ptr() as *mut c_char
105}
106107pub unsafe extern "C" fn in_domain_callback(
108 raw_cb: *mut c_void,
109 raw_key: *const c_char,
110 key_len: size_t,
111) -> c_uchar {
112let cb = &mut *(raw_cb as *mut TransformCallback);
113let key = slice::from_raw_parts(raw_key as *const u8, key_len);
114 c_uchar::from(cb.in_domain_fn.map_or(true, |in_domain| in_domain(key)))
115}