Skip to main content

rust_rocksdb/
slice_transform.rs

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.
14
15use std::ffi::CString;
16use std::slice;
17
18use libc::{c_char, c_uchar, c_void, size_t};
19
20use crate::{ffi, ffi_util::CStrLike};
21
22/// 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 {
27    pub inner: *mut ffi::rocksdb_slicetransform_t,
28}
29
30// 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.
35
36impl SliceTransform {
37    pub fn create(
38        name: impl CStrLike,
39        transform_fn: TransformFn,
40        in_domain_fn: Option<InDomainFn>,
41    ) -> SliceTransform {
42        let cb = Box::into_raw(Box::new(TransformCallback {
43            name: name.into_c_string().unwrap(),
44            transform_fn,
45            in_domain_fn,
46        }));
47
48        let st = unsafe {
49            ffi::rocksdb_slicetransform_create(
50                cb as *mut c_void,
51                Some(slice_transform_destructor_callback),
52                Some(transform_callback),
53                Some(in_domain_callback),
54                Some(slice_transform_name_callback),
55            )
56        };
57
58        SliceTransform { inner: st }
59    }
60
61    pub fn create_fixed_prefix(len: size_t) -> SliceTransform {
62        SliceTransform {
63            inner: unsafe { ffi::rocksdb_slicetransform_create_fixed_prefix(len) },
64        }
65    }
66
67    pub fn create_noop() -> SliceTransform {
68        SliceTransform {
69            inner: unsafe { ffi::rocksdb_slicetransform_create_noop() },
70        }
71    }
72}
73
74pub type TransformFn<'a> = fn(&'a [u8]) -> &'a [u8];
75pub type InDomainFn = fn(&[u8]) -> bool;
76
77pub struct TransformCallback<'a> {
78    pub name: CString,
79    pub transform_fn: TransformFn<'a>,
80    pub in_domain_fn: Option<InDomainFn>,
81}
82
83pub unsafe extern "C" fn slice_transform_destructor_callback(raw_cb: *mut c_void) {
84    unsafe {
85        drop(Box::from_raw(raw_cb as *mut TransformCallback));
86    }
87}
88
89pub unsafe extern "C" fn slice_transform_name_callback(raw_cb: *mut c_void) -> *const c_char {
90    unsafe {
91        let cb = &mut *(raw_cb as *mut TransformCallback);
92        cb.name.as_ptr()
93    }
94}
95
96pub unsafe extern "C" fn transform_callback(
97    raw_cb: *mut c_void,
98    raw_key: *const c_char,
99    key_len: size_t,
100    dst_length: *mut size_t,
101) -> *mut c_char {
102    unsafe {
103        let cb = &mut *(raw_cb as *mut TransformCallback);
104        let key = slice::from_raw_parts(raw_key as *const u8, key_len);
105        let prefix = (cb.transform_fn)(key);
106        *dst_length = prefix.len() as size_t;
107        prefix.as_ptr() as *mut c_char
108    }
109}
110
111pub unsafe extern "C" fn in_domain_callback(
112    raw_cb: *mut c_void,
113    raw_key: *const c_char,
114    key_len: size_t,
115) -> c_uchar {
116    unsafe {
117        let cb = &mut *(raw_cb as *mut TransformCallback);
118        let key = slice::from_raw_parts(raw_key as *const u8, key_len);
119        c_uchar::from(cb.in_domain_fn.is_none_or(|in_domain| in_domain(key)))
120    }
121}