diff --git a/litebox/src/fd/mod.rs b/litebox/src/fd/mod.rs index aeef757dd..d93da35b1 100644 --- a/litebox/src/fd/mod.rs +++ b/litebox/src/fd/mod.rs @@ -477,7 +477,7 @@ impl Descriptors { ) -> Option where Subsystem: FdEnabledSubsystem, - T: core::any::Any + Send + Sync, + T: core::any::Any + Clone + Send + Sync, { self.entries[fd.x.as_usize()?] .as_ref() @@ -506,7 +506,7 @@ impl Descriptors { ) -> Option where Subsystem: FdEnabledSubsystem, - T: core::any::Any + Send + Sync, + T: core::any::Any + Clone + Send + Sync, { self.entries[fd.x.as_usize()?] .as_mut() diff --git a/litebox/src/utilities/anymap.rs b/litebox/src/utilities/anymap.rs index 68af3f8a6..96253e8fb 100644 --- a/litebox/src/utilities/anymap.rs +++ b/litebox/src/utilities/anymap.rs @@ -18,14 +18,30 @@ use alloc::boxed::Box; use core::any::{Any, TypeId}; use hashbrown::HashMap; +/// Type-erased clone function stored alongside each value. +/// +/// We cannot use `Box` because `Clone` is not +/// object-safe (its `clone` method returns `Self`). Instead we store a +/// function pointer that knows the concrete type and can clone through the +/// trait object. +type CloneFn = fn(&(dyn Any + Send + Sync)) -> Box; + /// A safe store of exactly one value of any type `T`. pub(crate) struct AnyMap { // Invariant: the value at a particular typeid is guaranteed to be the correct type boxed up. - storage: HashMap>, + storage: HashMap, CloneFn)>, } const GUARANTEED: &str = "guaranteed correct type by invariant"; +/// Create a clone function for a specific concrete type. +fn make_clone_fn() -> CloneFn { + |val: &(dyn Any + Send + Sync)| -> Box { + let concrete = val.downcast_ref::().expect(GUARANTEED); + Box::new(concrete.clone()) + } +} + impl AnyMap { /// Create a new empty `AnyMap` pub(crate) fn new() -> Self { @@ -35,20 +51,26 @@ impl AnyMap { } /// Insert `v`, replacing and returning the old value if one existed already. - pub(crate) fn insert(&mut self, v: T) -> Option { - let old = self.storage.insert(TypeId::of::(), Box::new(v))?; - Some(*old.downcast().expect(GUARANTEED)) + /// + /// The `Clone` bound is required to capture a type-erased clone function + /// at insertion time. Read-only accessors (`get`, `get_mut`, `remove`) do + /// not require `Clone`. + pub(crate) fn insert(&mut self, v: T) -> Option { + let old = self + .storage + .insert(TypeId::of::(), (Box::new(v), make_clone_fn::()))?; + Some(*old.0.downcast().expect(GUARANTEED)) } /// Get a reference to a value of type `T` if it exists. pub(crate) fn get(&self) -> Option<&T> { - let v = self.storage.get(&TypeId::of::())?; + let v = &self.storage.get(&TypeId::of::())?.0; Some(v.downcast_ref().expect(GUARANTEED)) } /// Get a mutable reference to a value of type `T` if it exists. pub(crate) fn get_mut(&mut self) -> Option<&mut T> { - let v = self.storage.get_mut(&TypeId::of::())?; + let v = &mut self.storage.get_mut(&TypeId::of::())?.0; Some(v.downcast_mut().expect(GUARANTEED)) } @@ -58,7 +80,67 @@ impl AnyMap { )] /// Remove and return the value of type `T` if it exists. pub(crate) fn remove(&mut self) -> Option { - let v = self.storage.remove(&TypeId::of::())?; + let v = self.storage.remove(&TypeId::of::())?.0; Some(*v.downcast().expect(GUARANTEED)) } } + +impl Clone for AnyMap { + fn clone(&self) -> Self { + Self { + storage: self + .storage + .iter() + .map(|(&type_id, (val, clone_fn))| (type_id, (clone_fn(val.as_ref()), *clone_fn))) + .collect(), + } + } +} + +#[cfg(test)] +mod tests { + use alloc::string::String; + + use super::AnyMap; + + #[test] + fn insert_and_get() { + let mut map = AnyMap::new(); + assert!(map.insert(42u32).is_none()); + assert_eq!(map.get::(), Some(&42)); + } + + #[test] + fn clone_produces_independent_copy() { + let mut original = AnyMap::new(); + original.insert(10u32); + original.insert(String::from("hello")); + + let mut cloned = original.clone(); + + // Cloned values match. + assert_eq!(cloned.get::(), Some(&10)); + assert_eq!(cloned.get::().map(String::as_str), Some("hello")); + + // Mutating the clone does not affect the original. + *cloned.get_mut::().unwrap() = 99; + assert_eq!(original.get::(), Some(&10)); + assert_eq!(cloned.get::(), Some(&99)); + } + + #[test] + fn cloned_map_can_be_cloned_again() { + let mut map = AnyMap::new(); + map.insert(7u64); + let clone1 = map.clone(); + let clone2 = clone1.clone(); + assert_eq!(clone2.get::(), Some(&7)); + } + + #[test] + fn clone_empty_map() { + let map = AnyMap::new(); + let cloned = map.clone(); + assert_eq!(cloned.get::(), None); + } +} diff --git a/litebox_shim_linux/src/lib.rs b/litebox_shim_linux/src/lib.rs index 2834f7b72..2026a0f15 100644 --- a/litebox_shim_linux/src/lib.rs +++ b/litebox_shim_linux/src/lib.rs @@ -343,9 +343,11 @@ fn default_fs( } // Special override so that `GETFL` can return stdio-specific flags +#[derive(Clone)] pub(crate) struct StdioStatusFlags(litebox::fs::OFlags); /// Status flags for pipes +#[derive(Clone)] pub(crate) struct PipeStatusFlags(pub litebox::fs::OFlags); impl syscalls::file::FilesState { diff --git a/litebox_shim_linux/src/syscalls/net.rs b/litebox_shim_linux/src/syscalls/net.rs index d1b04e894..9192971c1 100644 --- a/litebox_shim_linux/src/syscalls/net.rs +++ b/litebox_shim_linux/src/syscalls/net.rs @@ -155,7 +155,7 @@ impl SocketAddress { } } -#[derive(Default)] +#[derive(Default, Clone)] pub(super) struct SocketOptions { pub(super) reuse_address: bool, pub(super) keep_alive: bool, @@ -171,7 +171,9 @@ pub(super) struct SocketOptions { pub(super) linger_timeout: Option, } +#[derive(Clone)] pub(crate) struct SocketOFlags(pub OFlags); +#[derive(Clone)] pub(crate) struct SocketProxy(pub Arc>); pub(super) enum SocketOptionValue {