use crate::ByteStorage;
use std::collections::HashSet;
use std::sync::{LazyLock, Mutex};
static BYTE_STORAGE_HANDLES: LazyLock<Mutex<HashSet<usize>>> =
LazyLock::new(|| Mutex::new(HashSet::new()));
#[cfg(feature = "encryption")]
static ENCRYPTOR_HANDLES: LazyLock<Mutex<HashSet<usize>>> =
LazyLock::new(|| Mutex::new(HashSet::new()));
macro_rules! opaque_handle {
(
$(#[$meta:meta])*
$handle:ident,
$inner:ty,
$registry:ident
) => {
$(#[$meta])*
#[repr(C)]
pub struct $handle {
_private: [u8; 0],
}
#[allow(dead_code)]
impl $handle {
pub(crate) fn into_opaque_ptr(inner: $inner) -> *mut Self {
let ptr = Box::into_raw(Box::new(inner)) as *mut Self;
if let Ok(mut handles) = $registry.lock() {
handles.insert(ptr as usize);
}
ptr
}
pub(crate) unsafe fn from_opaque_ptr(ptr: *mut Self) -> Option<$inner> {
if ptr.is_null() {
return None;
}
let addr = ptr as usize;
let registered = $registry
.lock()
.map(|mut handles| handles.remove(&addr))
.unwrap_or(false);
if !registered {
return None; }
Some(unsafe { *Box::from_raw(ptr as *mut $inner) })
}
pub(crate) fn is_valid(ptr: *const Self) -> bool {
if ptr.is_null() {
return false;
}
$registry
.lock()
.map(|handles| handles.contains(&(ptr as usize)))
.unwrap_or(false)
}
pub(crate) unsafe fn as_ref<'a>(ptr: *const Self) -> Option<&'a $inner> {
if !Self::is_valid(ptr) {
return None;
}
Some(unsafe { &*(ptr as *const $inner) })
}
pub(crate) unsafe fn as_mut<'a>(ptr: *mut Self) -> Option<&'a mut $inner> {
if !Self::is_valid(ptr) {
return None;
}
Some(unsafe { &mut *(ptr as *mut $inner) })
}
}
};
}
opaque_handle!(
CachekitByteStorage,
ByteStorage,
BYTE_STORAGE_HANDLES
);
#[cfg(feature = "encryption")]
opaque_handle!(
CachekitEncryptor,
crate::encryption::ZeroKnowledgeEncryptor,
ENCRYPTOR_HANDLES
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_byte_storage_opaque_roundtrip() {
let storage = ByteStorage::new(None);
let ptr = CachekitByteStorage::into_opaque_ptr(storage);
assert!(!ptr.is_null());
assert!(CachekitByteStorage::is_valid(ptr));
unsafe {
let restored = CachekitByteStorage::from_opaque_ptr(ptr);
assert!(restored.is_some());
}
assert!(!CachekitByteStorage::is_valid(ptr));
}
#[test]
fn test_byte_storage_double_free_detection() {
let storage = ByteStorage::new(None);
let ptr = CachekitByteStorage::into_opaque_ptr(storage);
unsafe {
let first = CachekitByteStorage::from_opaque_ptr(ptr);
assert!(first.is_some());
}
unsafe {
let second = CachekitByteStorage::from_opaque_ptr(ptr);
assert!(second.is_none());
}
}
#[test]
fn test_byte_storage_null_handle() {
let null_ptr: *mut CachekitByteStorage = std::ptr::null_mut();
assert!(!CachekitByteStorage::is_valid(null_ptr));
unsafe {
assert!(CachekitByteStorage::from_opaque_ptr(null_ptr).is_none());
assert!(CachekitByteStorage::as_ref(null_ptr).is_none());
assert!(CachekitByteStorage::as_mut(null_ptr).is_none());
}
}
#[cfg(feature = "encryption")]
#[test]
fn test_encryptor_opaque_roundtrip() {
let encryptor = crate::encryption::ZeroKnowledgeEncryptor::new().unwrap();
let ptr = CachekitEncryptor::into_opaque_ptr(encryptor);
assert!(!ptr.is_null());
assert!(CachekitEncryptor::is_valid(ptr));
unsafe {
let restored = CachekitEncryptor::from_opaque_ptr(ptr);
assert!(restored.is_some());
}
assert!(!CachekitEncryptor::is_valid(ptr));
}
#[cfg(feature = "encryption")]
#[test]
fn test_encryptor_double_free_detection() {
let encryptor = crate::encryption::ZeroKnowledgeEncryptor::new().unwrap();
let ptr = CachekitEncryptor::into_opaque_ptr(encryptor);
unsafe {
let first = CachekitEncryptor::from_opaque_ptr(ptr);
assert!(first.is_some());
}
unsafe {
let second = CachekitEncryptor::from_opaque_ptr(ptr);
assert!(second.is_none());
}
}
}