use ::alloc::*;
use core::{num::NonZeroU32, ptr::NonNull};
use crate::types::ObjectId;
pub struct ObjectManager<T: Copy + PartialEq> {
objects: NonNull<Option<T>>,
objects_cap: u32,
next: u32,
server_objects: NonNull<Option<T>>,
server_objects_cap: u32,
server_next: u32,
}
impl<T: Copy + PartialEq> ObjectManager<T> {
pub(crate) fn new(display: T) -> Self {
let objects = allocate::<Option<T>, 1>(Some(display));
let server_objects = allocate::<Option<T>, 1>(None);
Self {
objects,
objects_cap: 1,
next: 1,
server_objects,
server_objects_cap: 1,
server_next: 0,
}
}
pub fn create_from_server(&mut self, object: T) {
if self.server_next == self.server_objects_cap {
grow(&mut self.server_objects, &mut self.server_objects_cap, None);
}
unsafe {
self.server_objects
.add(self.server_next as usize)
.write(Some(object))
}
self.server_next += 1;
while self.server_next < self.server_objects_cap {
if unsafe {
self.server_objects
.add(self.server_next as usize)
.read()
.is_none()
} {
break;
}
self.server_next += 1;
}
}
#[must_use]
pub fn get_first(&self, variant: T) -> Option<ObjectId> {
for i in 0..self.objects_cap as usize {
if unsafe { self.objects.add(i).read() }.is_some_and(|obj| obj == variant) {
return Some(ObjectId::new(NonZeroU32::new(i as u32 + 1).unwrap()));
}
}
None
}
pub fn get_all(&self, variant: T) -> impl Iterator<Item = ObjectId> {
let objects = unsafe {
core::slice::from_raw_parts(self.objects.as_ptr(), self.objects_cap as usize)
};
objects.iter().enumerate().filter_map(move |(i, obj)| {
if obj.is_some_and(|obj| obj == variant) {
Some(ObjectId::new(NonZeroU32::new(i as u32 + 1).unwrap()))
} else {
None
}
})
}
#[must_use]
pub fn get(&self, object_id: ObjectId) -> Option<T> {
if object_id.created_by_server() {
let pos = object_id.get().get() as usize - 0xFF000000;
if pos < self.server_objects_cap as usize {
return unsafe { self.server_objects.add(pos).read() };
}
} else {
let pos = object_id.get().get() as usize - 1;
if pos < self.objects_cap as usize {
return unsafe { self.objects.add(pos).read() };
}
}
None
}
#[must_use]
pub fn create(&mut self, object: T) -> ObjectId {
if self.next == self.objects_cap {
grow(&mut self.objects, &mut self.objects_cap, None);
}
let i = self.next as usize;
unsafe { self.objects.add(self.next as usize).write(Some(object)) }
self.next += 1;
while self.next < self.objects_cap {
if unsafe { self.objects.add(self.next as usize).read().is_none() } {
break;
}
self.next += 1;
}
ObjectId::new(unsafe { NonZeroU32::new_unchecked(i as u32 + 1) })
}
pub fn remove(&mut self, object_id: u32) -> Option<T> {
if let Ok(object_id) = ObjectId::try_new(object_id) {
if object_id.created_by_server() {
let pos = object_id.get().get() - 0xFF000000;
if pos < self.server_objects_cap {
let ret = unsafe { self.server_objects.add(pos as usize).read() };
unsafe { self.server_objects.add(pos as usize).write(None) };
if pos < self.server_next {
self.server_next = pos;
}
return ret;
}
} else {
let pos = object_id.get().get() - 1;
if pos < self.objects_cap {
let ret = unsafe { self.objects.add(pos as usize).read() };
unsafe { self.objects.add(pos as usize).write(None) };
if pos < self.next {
self.next = pos;
}
return ret;
}
}
}
None
}
}
fn allocate<T: Copy, const INITIAL_SIZE: usize>(fill: T) -> NonNull<T> {
const { assert!(INITIAL_SIZE > 0) }
let layout = alloc::Layout::array::<T>(INITIAL_SIZE).unwrap();
let ptr = unsafe { alloc::alloc(layout) }.cast::<T>();
match NonNull::new(ptr) {
Some(ptr) => {
for i in 0..INITIAL_SIZE {
unsafe { ptr.add(i).write(fill) }
}
ptr
}
None => alloc::handle_alloc_error(layout),
}
}
#[cold]
fn grow<T: Copy>(ptr: &mut NonNull<T>, cap: &mut u32, fill: T) {
let old_cap = *cap;
let layout = alloc::Layout::array::<T>(*cap as usize).unwrap();
let new_cap = *cap * 2;
let new_layout = alloc::Layout::array::<T>(new_cap as usize).unwrap();
let old_ptr = ptr.as_ptr() as *mut u8;
let new_ptr = unsafe { alloc::realloc(old_ptr, layout, new_layout.size()) }.cast::<T>();
match NonNull::new(new_ptr) {
Some(new_ptr) => {
for i in old_cap..new_cap {
unsafe { new_ptr.add(i as usize).write(fill) }
}
*cap = new_cap;
*ptr = new_ptr
}
None => alloc::handle_alloc_error(new_layout),
}
}
fn deallocate<T>(ptr: NonNull<T>, cap: u32) {
let layout = alloc::Layout::array::<T>(cap as usize).unwrap();
let ptr = ptr.as_ptr() as *mut u8;
unsafe { alloc::dealloc(ptr, layout) }
}
impl<T: Copy + PartialEq> Drop for ObjectManager<T> {
fn drop(&mut self) {
deallocate(self.objects, self.objects_cap);
deallocate(self.server_objects, self.server_objects_cap);
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::vec::Vec;
use super::*;
#[derive(Clone, Copy, Debug, PartialEq)]
enum DummyProtocol {
Display,
Region,
Surface,
}
fn obj_from_u32(u: u32) -> ObjectId {
ObjectId::new(NonZeroU32::new(u).unwrap())
}
fn server_objects<T: Copy + PartialEq>(objman: &ObjectManager<T>) -> &[Option<T>] {
unsafe {
core::slice::from_raw_parts(
objman.server_objects.as_ptr(),
objman.server_objects_cap as usize,
)
}
}
#[test]
fn creating_object_ids() {
let mut manager = ObjectManager::new(DummyProtocol::Display);
let id1 = manager.create(DummyProtocol::Region);
assert_eq!(id1, obj_from_u32(2));
let id2 = manager.create(DummyProtocol::Region);
assert_eq!(id2, obj_from_u32(3));
let id3 = manager.create(DummyProtocol::Region);
assert_eq!(id3, obj_from_u32(4));
manager.remove(id2.get().get());
let id4 = manager.create(DummyProtocol::Region);
assert_eq!(id4, id2);
manager.remove(id1.get().get());
let id5 = manager.create(DummyProtocol::Region);
assert_eq!(id5, id1);
manager.remove(id2.get().get());
manager.remove(id1.get().get());
let id6 = manager.create(DummyProtocol::Region);
assert_eq!(id6, id1);
let id7 = manager.create(DummyProtocol::Region);
assert_eq!(id7, id2);
}
#[test]
fn get_all() {
let mut manager = ObjectManager::new(DummyProtocol::Display);
let id1 = manager.create(DummyProtocol::Region);
let id2 = manager.create(DummyProtocol::Surface);
let id3 = manager.create(DummyProtocol::Region);
let id4 = manager.create(DummyProtocol::Region);
let id5 = manager.create(DummyProtocol::Surface);
let id6 = manager.create(DummyProtocol::Surface);
let id7 = manager.create(DummyProtocol::Region);
let id8 = manager.create(DummyProtocol::Region);
let id9 = manager.create(DummyProtocol::Region);
let id10 = manager.create(DummyProtocol::Surface);
let regions: Vec<ObjectId> = manager.get_all(DummyProtocol::Region).collect();
let surfaces: Vec<ObjectId> = manager.get_all(DummyProtocol::Surface).collect();
assert_eq!(®ions, &[id1, id3, id4, id7, id8, id9]);
assert_eq!(&surfaces, &[id2, id5, id6, id10]);
}
#[test]
fn creating_object_ids_from_server() {
let mut manager = ObjectManager::new(DummyProtocol::Display);
manager.create_from_server(DummyProtocol::Region);
manager.create_from_server(DummyProtocol::Region);
manager.create_from_server(DummyProtocol::Region);
assert_eq!(
&server_objects(&manager)[..3],
&[Some(DummyProtocol::Region); 3]
);
assert_eq!(manager.server_next, 3);
manager.remove(0xFF000002);
assert_eq!(manager.server_next, 2);
manager.create_from_server(DummyProtocol::Region);
assert_eq!(
&server_objects(&manager)[..3],
&[Some(DummyProtocol::Region); 3]
);
assert_eq!(manager.server_next, 3);
manager.remove(0xFF000001);
assert_eq!(manager.server_next, 1);
manager.create_from_server(DummyProtocol::Region);
assert_eq!(
&server_objects(&manager)[..3],
&[Some(DummyProtocol::Region); 3]
);
assert_eq!(manager.server_next, 3);
manager.remove(0xFF000001);
manager.remove(0xFF000002);
assert_eq!(manager.server_next, 1);
manager.create_from_server(DummyProtocol::Region);
assert_eq!(
&server_objects(&manager)[..2],
&[Some(DummyProtocol::Region); 2]
);
assert_eq!(manager.server_next, 2);
manager.create_from_server(DummyProtocol::Region);
assert_eq!(
&server_objects(&manager)[..3],
&[Some(DummyProtocol::Region); 3]
);
assert_eq!(manager.server_next, 3);
}
}