sqlite3_ext/value/
unsafe_ptr.rs1use crate::{ffi, sqlite3_match_version, types::*, value::*};
2use std::{
3 mem::{size_of, zeroed},
4 ptr::write_unaligned,
5};
6
7#[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 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 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 pub fn get(&self) -> *const T {
105 self.ptr
106 }
107
108 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}