use super::types::{CompiledHandle, MeshHandle, SdfHandle};
use crate::compiled::CompiledSdf;
use crate::mesh::Mesh;
use crate::types::SdfNode;
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc, RwLock,
};
static NODE_REGISTRY: std::sync::LazyLock<RwLock<HashMap<u64, Arc<SdfNode>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static NODE_COUNTER: AtomicU64 = AtomicU64::new(1);
#[inline]
pub fn register_node(node: SdfNode) -> SdfHandle {
let id = NODE_COUNTER.fetch_add(1, Ordering::Relaxed);
let arc_node = Arc::new(node);
{
let mut registry = NODE_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.insert(id, arc_node);
}
id as SdfHandle
}
#[inline]
pub fn get_node(handle: SdfHandle) -> Option<Arc<SdfNode>> {
if handle.is_null() {
return None;
}
let id = handle as u64;
let registry = NODE_REGISTRY
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.get(&id).cloned()
}
#[inline]
pub fn remove_node(handle: SdfHandle) {
if handle.is_null() {
return;
}
let id = handle as u64;
let mut registry = NODE_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.remove(&id);
}
static COMPILED_REGISTRY: std::sync::LazyLock<RwLock<HashMap<u64, Arc<CompiledSdf>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static COMPILED_COUNTER: AtomicU64 = AtomicU64::new(0x8000_0000_0000_0001);
#[inline]
pub fn register_compiled(compiled: CompiledSdf) -> CompiledHandle {
let id = COMPILED_COUNTER.fetch_add(1, Ordering::Relaxed);
let arc_compiled = Arc::new(compiled);
{
let mut registry = COMPILED_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.insert(id, arc_compiled);
}
id as CompiledHandle
}
#[inline]
pub fn get_compiled(handle: CompiledHandle) -> Option<Arc<CompiledSdf>> {
if handle.is_null() {
return None;
}
let id = handle as u64;
let registry = COMPILED_REGISTRY
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.get(&id).cloned()
}
#[inline]
pub fn remove_compiled(handle: CompiledHandle) {
if handle.is_null() {
return;
}
let id = handle as u64;
let mut registry = COMPILED_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.remove(&id);
}
static MESH_REGISTRY: std::sync::LazyLock<RwLock<HashMap<u64, Arc<Mesh>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static MESH_COUNTER: AtomicU64 = AtomicU64::new(0x4000_0000_0000_0001);
#[inline]
pub fn register_mesh(mesh: Mesh) -> MeshHandle {
let id = MESH_COUNTER.fetch_add(1, Ordering::Relaxed);
let arc_mesh = Arc::new(mesh);
{
let mut registry = MESH_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.insert(id, arc_mesh);
}
id as MeshHandle
}
#[inline]
pub fn get_mesh(handle: MeshHandle) -> Option<Arc<Mesh>> {
if handle.is_null() {
return None;
}
let id = handle as u64;
let registry = MESH_REGISTRY
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.get(&id).cloned()
}
#[inline]
pub fn remove_mesh(handle: MeshHandle) {
if handle.is_null() {
return;
}
let id = handle as u64;
let mut registry = MESH_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.remove(&id);
}
#[allow(dead_code)] pub fn node_count() -> usize {
let registry = NODE_REGISTRY
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.len()
}
#[allow(dead_code)] pub fn compiled_count() -> usize {
let registry = COMPILED_REGISTRY
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.len()
}
#[allow(dead_code)] pub fn clear_all() {
{
let mut registry = NODE_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.clear();
}
{
let mut registry = COMPILED_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.clear();
}
{
let mut registry = MESH_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
registry.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_and_get() {
let node = SdfNode::Sphere { radius: 1.0 };
let handle = register_node(node);
assert!(!handle.is_null());
let retrieved = get_node(handle);
assert!(retrieved.is_some());
if let SdfNode::Sphere { radius } = &*retrieved.unwrap() {
assert_eq!(*radius, 1.0);
} else {
panic!("Expected Sphere node");
}
remove_node(handle);
}
#[test]
fn test_remove() {
let node = SdfNode::Sphere { radius: 2.0 };
let handle = register_node(node);
assert!(get_node(handle).is_some());
remove_node(handle);
assert!(get_node(handle).is_none());
}
#[test]
fn test_null_handle() {
let null_handle: SdfHandle = std::ptr::null_mut();
assert!(get_node(null_handle).is_none());
}
#[test]
fn test_compiled_registry() {
let node = SdfNode::Sphere { radius: 1.0 };
let compiled = CompiledSdf::compile(&node);
let handle = register_compiled(compiled);
assert!(!handle.is_null());
let retrieved = get_compiled(handle);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().node_count, 1);
remove_compiled(handle);
assert!(get_compiled(handle).is_none());
}
#[test]
fn poisoned_lock_is_tolerated() {
let node = SdfNode::Sphere { radius: 2.0 };
let handle = register_node(node);
let poison = std::panic::catch_unwind(|| {
let _guard = NODE_REGISTRY
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
panic!("simulated panic while holding the registry lock");
});
assert!(poison.is_err());
assert!(get_node(handle).is_some(), "registry unusable after poison");
let other = register_node(SdfNode::Sphere { radius: 3.0 });
assert!(get_node(other).is_some());
remove_node(handle);
remove_node(other);
}
}