Skip to main content

sqlite3_ext/value/
unsafe_ptr.rs

1use crate::{ffi, sqlite3_match_version, types::*, value::*};
2use std::{
3    mem::{size_of, zeroed},
4    ptr::write_unaligned,
5};
6
7/// Pass arbitrary pointers through SQLite as BLOBs.
8///
9/// Using this technique to pass pointers through SQLite is insecure and error-prone. A much
10/// better solution is available via [PassedRef]. Pointers passed through this
11/// interface require manual memory management, for example using [Box::into_raw] or
12/// [std::mem::forget].
13///
14/// # Examples
15///
16/// This example uses static memory to avoid memory management.
17///
18/// ```no_run
19/// use sqlite3_ext::{UnsafePtr, function::Context, Result, ValueRef};
20///
21/// const VAL: &str = "static memory";
22/// const SUBTYPE: u8 = 'S' as _;
23///
24/// fn produce_ptr(ctx: &Context, args: &mut [&mut ValueRef]) -> UnsafePtr<str> {
25///     UnsafePtr::new(VAL, SUBTYPE)
26/// }
27///
28/// fn consume_ptr(ctx: &Context, args: &mut [&mut ValueRef]) -> Result<()> {
29///     let val: &str = unsafe { &*UnsafePtr::from_value_ref(args[0], SUBTYPE)?.get() };
30///     assert_eq!(val, "static memory");
31///     Ok(())
32/// }
33/// ```
34///
35/// This example uses a boxed value to manage memory:
36///
37/// ```no_run
38/// use sqlite3_ext::{UnsafePtr, function::Context, Result, ValueRef};
39///
40/// const SUBTYPE: u8 = 'S' as _;
41///
42/// fn produce_ptr(ctx: &Context, args: &mut [&mut ValueRef]) -> UnsafePtr<u64> {
43///     let val = Box::new(100u64);
44///     UnsafePtr::new(Box::into_raw(val), SUBTYPE)
45/// }
46///
47/// fn consume_ptr(ctx: &Context, args: &mut [&mut ValueRef]) -> Result<()> {
48///     let val: Box<u64> =
49///         unsafe { Box::from_raw(UnsafePtr::from_value_ref(args[0], SUBTYPE)?.get_mut()) };
50///     assert_eq!(*val, 100);
51///     Ok(())
52/// }
53/// ```
54#[derive(Debug)]
55pub struct UnsafePtr<T: ?Sized> {
56    #[cfg_attr(not(modern_sqlite), allow(unused))]
57    pub(crate) subtype: u8,
58    ptr: *const T,
59}
60
61impl<T: ?Sized> UnsafePtr<T> {
62    /// Create a new UnsafePtr with the given subtype.
63    ///
64    /// Subtype verification requires SQLite 3.9.0. On earlier versions of SQLite, the
65    /// subtype field is ignored.
66    pub fn new(ptr: *const T, subtype: u8) -> Self {
67        assert!(subtype != 0, "subtype must not be 0");
68        Self { subtype, ptr }
69    }
70
71    /// Retrieve an UnsafePtr from a ValueRef.
72    ///
73    /// The subtype provided to this method must match the subtype originally provided to
74    /// UnsafeRef.
75    ///
76    /// This method will fail if the value cannot be interpreted as a pointer. It will
77    /// create a null pointer if the value is SQL NULL.
78    ///
79    /// Subtype verification requires SQLite 3.9.0. On earlier versions of SQLite, the
80    /// subtype field is ignored.
81    pub fn from_value_ref(val: &mut ValueRef, subtype: u8) -> Result<Self> {
82        unsafe {
83            let len = ffi::sqlite3_value_bytes(val.as_ptr()) as usize;
84            let subtype_match = sqlite3_match_version! {
85                3_009_000 => ffi::sqlite3_value_subtype(val.as_ptr()) as u8 == subtype,
86                _ => true,
87            };
88            if len == 0 {
89                Ok(UnsafePtr {
90                    ptr: zeroed(),
91                    subtype,
92                })
93            } else if len != size_of::<&T>() || !subtype_match {
94                Err(SQLITE_MISMATCH)
95            } else {
96                let bits = ffi::sqlite3_value_blob(val.as_ptr()) as *const *const T;
97                let ret = ptr::read_unaligned::<*const T>(bits);
98                Ok(UnsafePtr { ptr: ret, subtype })
99            }
100        }
101    }
102
103    /// Get the stored pointer.
104    pub fn get(&self) -> *const T {
105        self.ptr
106    }
107
108    /// Get the stored pointer, mutably.
109    pub fn get_mut(&mut self) -> *mut T {
110        self.ptr as _
111    }
112
113    pub(crate) fn into_bytes(self) -> Vec<u8> {
114        let len = size_of::<&T>();
115        let mut vec: Vec<u8> = Vec::with_capacity(len);
116        unsafe {
117            let ret_bytes = vec.as_mut_ptr() as *mut *const T;
118            write_unaligned(ret_bytes, self.ptr);
119            vec.set_len(len);
120        }
121        vec
122    }
123}
124
125#[cfg(all(test, feature = "static"))]
126mod test {
127    use crate::test_helpers::prelude::*;
128    use std::mem::{size_of, size_of_val};
129
130    const SUBTYPE: u8 = 't' as _;
131
132    #[test]
133    fn get_ptr() {
134        let h = TestHelpers::new();
135        let owned_string = "input string".to_owned();
136        let ptr = Box::into_raw(Box::new(owned_string));
137        let ptr = UnsafePtr::new(ptr, SUBTYPE);
138        h.with_value(ptr, |val| {
139            assert_eq!(val.value_type(), ValueType::Blob);
140            let borrowed_string: Box<String> =
141                unsafe { Box::from_raw(UnsafePtr::from_value_ref(val, SUBTYPE)?.get_mut()) };
142            assert_eq!(*borrowed_string, "input string");
143            Ok(())
144        });
145    }
146
147    #[test]
148    fn get_ptr_wide() {
149        let h = TestHelpers::new();
150        let val: &str = "static string";
151        let ptr = UnsafePtr::new(val, SUBTYPE);
152        assert_ne!(
153            size_of_val(&ptr),
154            size_of::<UnsafePtr<()>>(),
155            "this isn't a wide pointer"
156        );
157        h.with_value(ptr, |val| {
158            assert_eq!(val.value_type(), ValueType::Blob);
159            let borrowed_slice: &str = unsafe { &*UnsafePtr::from_value_ref(val, SUBTYPE)?.get() };
160            assert_eq!(borrowed_slice, "static string");
161            Ok(())
162        });
163    }
164
165    #[test]
166    fn get_ptr_null() {
167        let h = TestHelpers::new();
168        let null: Option<i64> = None;
169        h.with_value(null, |val| {
170            assert_eq!(val.value_type(), ValueType::Null);
171            let ptr: *const () = UnsafePtr::from_value_ref(val, SUBTYPE)?.get();
172            assert!(ptr.is_null(), "ptr should be null");
173            Ok(())
174        });
175    }
176
177    #[test]
178    fn get_ptr_invalid() {
179        let h = TestHelpers::new();
180        h.with_value(&[1, 2, 3], |val| {
181            assert_eq!(val.value_type(), ValueType::Blob);
182            UnsafePtr::<()>::from_value_ref(val, SUBTYPE).expect_err("incorrect length");
183            Ok(())
184        });
185    }
186
187    #[test]
188    #[cfg(modern_sqlite)]
189    fn get_ptr_invalid_subtype() {
190        let h = TestHelpers::new();
191        let owned_string = "input string".to_owned();
192        let ptr = Box::into_raw(Box::new(owned_string));
193        let ptr = UnsafePtr::new(ptr, SUBTYPE);
194        h.with_value(ptr, |val| {
195            assert_eq!(val.value_type(), ValueType::Blob);
196            UnsafePtr::<String>::from_value_ref(val, !SUBTYPE).expect_err("invalid subtype");
197            Ok(())
198        });
199    }
200}