Skip to main content

sqlite3_ext/value/
blob.rs

1use std::{
2    alloc::{alloc, dealloc, realloc, Layout},
3    ffi::c_void,
4    mem::{align_of, forget, size_of},
5    ptr::{copy_nonoverlapping, read_unaligned, write_unaligned, NonNull},
6    slice,
7};
8
9const SIZEI: isize = size_of::<usize>() as _;
10const SIZEU: usize = size_of::<usize>();
11
12const _: () = {
13    assert!(align_of::<u8>() == 1);
14};
15
16fn blob_layout(len: usize) -> Layout {
17    // Safe because align is 1.
18    unsafe { Layout::from_size_align_unchecked(len + SIZEU, 1) }
19}
20
21/// Represents an owned BLOB object.
22///
23/// This container allows BLOB data to be passed to SQLite without copying.
24#[repr(transparent)]
25pub struct Blob {
26    data: NonNull<u8>,
27}
28
29impl Blob {
30    fn alloc(len: usize) -> Blob {
31        let data = unsafe { NonNull::new_unchecked(alloc(blob_layout(len))) };
32        let mut ret = Blob { data };
33        ret.set_len(len);
34        ret
35    }
36
37    fn realloc(&mut self, new_len: usize) {
38        let layout = blob_layout(self.len());
39        self.set_len(new_len);
40        self.data = unsafe { NonNull::new_unchecked(realloc(self.data.as_ptr(), layout, new_len)) };
41    }
42
43    /// Shorten the BLOB, keeping the first len elements and dropping the rest.
44    ///
45    /// If len is greater than the BLOB's current length, this has no effect.
46    pub fn truncate(&mut self, len: usize) {
47        if len < self.len() {
48            self.realloc(len);
49        }
50    }
51
52    /// Consumes the BLOB, returning a pointer to the data.
53    ///
54    /// After calling this function, the caller is responsible for freeing the memory
55    /// previously managed by Blob. The easiest way to do this is by passing
56    /// [ffi::drop_blob](crate::ffi::drop_blob) to SQLite when this value is consumed.
57    pub fn into_raw(self) -> *mut c_void {
58        let ret = unsafe { self.data.as_ptr().offset(SIZEI).cast() };
59        forget(self);
60        ret
61    }
62
63    /// Construct a BLOB from a raw pointer.
64    ///
65    /// After calling this function, the raw pointer is owned by the resulting Blob.
66    ///
67    /// # Safety
68    ///
69    /// It is undefined behavior to call this method on anything other than a pointer that
70    /// was returned by [Blob::into_raw].
71    pub unsafe fn from_raw(ptr: *mut c_void) -> Blob {
72        Blob {
73            data: NonNull::new_unchecked(ptr.cast::<u8>().offset(-SIZEI)),
74        }
75    }
76
77    fn set_len(&mut self, len: usize) {
78        unsafe { write_unaligned(self.data.cast().as_ptr(), len) };
79    }
80
81    /// Return the length of the BLOB.
82    pub fn len(&self) -> usize {
83        unsafe { read_unaligned(self.data.cast::<usize>().as_ptr()) }
84    }
85
86    /// Get the underlying BLOB data.
87    pub fn as_slice(&self) -> &[u8] {
88        unsafe { slice::from_raw_parts(self.data.as_ptr().offset(SIZEI), self.len()) }
89    }
90
91    /// Mutably get the underlying BLOB data.
92    pub fn as_mut_slice(&mut self) -> &mut [u8] {
93        unsafe { slice::from_raw_parts_mut(self.data.as_ptr().offset(SIZEI), self.len()) }
94    }
95}
96
97impl Clone for Blob {
98    fn clone(&self) -> Self {
99        let mut ret = Blob::alloc(self.len());
100        ret.as_mut_slice().copy_from_slice(self.as_slice());
101        ret
102    }
103}
104
105impl PartialEq for Blob {
106    fn eq(&self, other: &Blob) -> bool {
107        self.as_slice() == other.as_slice()
108    }
109}
110
111impl Drop for Blob {
112    fn drop(&mut self) {
113        unsafe { dealloc(self.data.as_ptr(), blob_layout(self.len())) }
114    }
115}
116
117impl From<&[u8]> for Blob {
118    fn from(val: &[u8]) -> Self {
119        let mut ret = Self::alloc(val.len());
120        ret.as_mut_slice()[..val.len()].copy_from_slice(val);
121        ret
122    }
123}
124
125impl<const N: usize> From<[u8; N]> for Blob {
126    fn from(val: [u8; N]) -> Self {
127        Self::from(&val[..])
128    }
129}
130
131impl<const N: usize> From<&[u8; N]> for Blob {
132    fn from(val: &[u8; N]) -> Self {
133        let ret = Self::alloc(N);
134        unsafe { copy_nonoverlapping(val.as_ptr(), ret.data.as_ptr().offset(SIZEI), N) };
135        ret
136    }
137}
138
139impl std::fmt::Debug for Blob {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
141        f.debug_tuple("Blob").field(&self.as_slice()).finish()
142    }
143}
144
145#[cfg(test)]
146mod test {
147    use super::Blob;
148
149    #[test]
150    fn debug() {
151        let blob = Blob::from([1, 2, 3, 4]);
152        assert_eq!(format!("{blob:?}"), "Blob([1, 2, 3, 4])");
153    }
154
155    #[test]
156    fn truncate() {
157        let mut blob = Blob::from([1, 2, 3, 4]);
158        assert_eq!(blob.as_slice(), [1, 2, 3, 4]);
159        blob.truncate(2);
160        assert_eq!(blob.as_slice(), [1, 2]);
161    }
162
163    #[test]
164    fn into_raw() {
165        let ptr;
166        {
167            let blob = Blob::from([1, 2, 3, 4]);
168            assert_eq!(blob.as_slice(), [1, 2, 3, 4]);
169            ptr = blob.into_raw();
170        }
171        let blob = unsafe { Blob::from_raw(ptr) };
172        assert_eq!(blob.as_slice(), [1, 2, 3, 4]);
173    }
174}