Skip to main content

couchbase_lite_core_sys/
helpers.rs

1//! Code to help deal with C API
2
3use crate::{
4    C4CollectionSpec, C4String, FLHeapSlice, FLSlice, FLSliceResult, FLSliceResult_Release,
5    FLString,
6};
7use std::{borrow::Cow, os::raw::c_void, ptr, slice, str};
8
9impl Default for FLSlice {
10    #[inline]
11    fn default() -> Self {
12        Self {
13            buf: ptr::null(),
14            size: 0,
15        }
16    }
17}
18
19impl<'a> From<&'a str> for FLSlice {
20    #[inline]
21    fn from(s: &'a str) -> Self {
22        Self {
23            buf: if !s.is_empty() {
24                s.as_ptr() as *const c_void
25            } else {
26                ptr::null()
27            },
28            size: s.len(),
29        }
30    }
31}
32
33impl<'a> From<&'a [u8]> for FLSlice {
34    #[inline]
35    fn from(ba: &'a [u8]) -> Self {
36        Self {
37            buf: if !ba.is_empty() {
38                ba.as_ptr() as *const c_void
39            } else {
40                ptr::null()
41            },
42            size: ba.len(),
43        }
44    }
45}
46
47impl From<FLSlice> for &[u8] {
48    #[inline]
49    fn from(s: FLSlice) -> Self {
50        if s.size != 0 {
51            unsafe { slice::from_raw_parts(s.buf as *const u8, s.size) }
52        } else {
53            // pointer should not be null, even in zero case
54            // but pointer from FLSlice can be null in zero case, so:
55            &[]
56        }
57    }
58}
59
60impl Drop for FLSliceResult {
61    #[inline]
62    fn drop(&mut self) {
63        unsafe {
64            FLSliceResult_Release(FLSliceResult {
65                buf: self.buf,
66                size: self.size,
67            })
68        };
69    }
70}
71
72impl Default for FLSliceResult {
73    #[inline]
74    fn default() -> Self {
75        Self {
76            buf: ptr::null(),
77            size: 0,
78        }
79    }
80}
81
82impl AsRef<[u8]> for FLSliceResult {
83    #[inline]
84    fn as_ref(&self) -> &[u8] {
85        self.as_bytes()
86    }
87}
88
89/// looks like according C++ code it is safe from other thread
90unsafe impl Send for FLSliceResult {}
91
92impl FLSliceResult {
93    #[inline]
94    pub fn as_bytes(&self) -> &[u8] {
95        if self.size != 0 {
96            unsafe { slice::from_raw_parts(self.buf as *const u8, self.size) }
97        } else {
98            // pointer should not be null, even in zero case
99            // but pointer from FLSlice can be null in zero case, so:
100            &[]
101        }
102    }
103    #[inline]
104    pub fn as_utf8_lossy(&self) -> Cow<'_, str> {
105        String::from_utf8_lossy(self.as_bytes())
106    }
107    #[inline]
108    pub fn is_empty(&self) -> bool {
109        self.size == 0
110    }
111    #[inline]
112    pub fn as_fl_slice(&self) -> FLSlice {
113        FLSlice {
114            buf: self.buf,
115            size: self.size,
116        }
117    }
118}
119
120impl<'a> TryFrom<FLString> for &'a str {
121    type Error = str::Utf8Error;
122    #[inline]
123    fn try_from(value: FLString) -> Result<Self, Self::Error> {
124        let bytes: &'a [u8] = value.into();
125        str::from_utf8(bytes)
126    }
127}
128
129impl FLHeapSlice {
130    #[inline]
131    pub fn as_fl_slice(&self) -> FLSlice {
132        *self
133    }
134}
135
136// bindgen can not handle these constants properly, so redefine them
137// see https://github.com/rust-lang/rust-bindgen/issues/316
138macro_rules! flstr {
139    ($str:expr) => {
140        C4String {
141            buf: $str.as_bytes().as_ptr() as *const c_void,
142            size: $str.as_bytes().len() - 1,
143        }
144    };
145}
146
147/// #define kC4DefaultScopeID FLSTR("_default")
148#[allow(non_upper_case_globals)]
149pub const kC4DefaultScopeID: C4String = flstr!("_default\0");
150
151/// #define kC4DefaultCollectionName FLSTR("_default")
152#[allow(non_upper_case_globals)]
153pub const kC4DefaultCollectionName: C4String = flstr!("_default\0");
154
155#[allow(non_upper_case_globals)]
156pub const kC4DefaultCollectionSpec: C4CollectionSpec = C4CollectionSpec {
157    name: kC4DefaultCollectionName,
158    scope: kC4DefaultScopeID,
159};
160
161#[test]
162fn test_null_slice_handling() {
163    let ffi_null_slice = FLSlice {
164        buf: ptr::null(),
165        size: 0,
166    };
167    let slice: &[u8] = ffi_null_slice.into();
168    assert!(slice.is_empty());
169
170    let ffi_null_slice: FLSliceResult = unsafe { crate::FLSliceResult_New(0) };
171    let slice: &[u8] = ffi_null_slice.as_bytes();
172    assert!(slice.is_empty());
173
174    let ffi_null_slice = FLSliceResult {
175        buf: ptr::null(),
176        size: 0,
177    };
178    let slice: &[u8] = ffi_null_slice.as_bytes();
179    assert!(slice.is_empty());
180}