use std::{hash::Hash, sync::Arc};
use atomic::AtomicValue;
use derive_more::{Display, Error};
use hashbrown::{HashMap, hash_map::EntryRef};
use super::{Address, Descriptor, SharedAtomicValue, Value, atomic};
const INITIAL_CAPACITY: usize = 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(transparent)]
pub struct RegisteredId(usize);
#[derive(Debug)]
struct AddressToIdMap {
inner: HashMap<Address, RegisteredId>,
}
impl AddressToIdMap {
#[must_use]
fn with_capacity(initial_capacity: usize) -> Self {
Self {
inner: HashMap::with_capacity(initial_capacity),
}
}
fn len(&self) -> usize {
self.inner.len()
}
fn iter(&self) -> impl Iterator<Item = (&Address, RegisteredId)> {
self.inner.iter().map(|(address, &id)| (address, id))
}
fn get_or_add(
&mut self,
addressable: impl AsRef<str> + Into<Address>,
) -> (Address, RegisteredId) {
let next_id = RegisteredId(self.len());
match self.inner.entry_ref(addressable.as_ref()) {
EntryRef::Occupied(entry) => {
let address = entry.key();
let id = entry.get();
debug_assert_eq!(*address, addressable.into());
debug_assert_ne!(*id, next_id);
(address.clone(), *id)
}
EntryRef::Vacant(entry) => {
let entry = entry.insert_entry(next_id);
let address = entry.key();
let id = entry.get();
debug_assert_eq!(*id, next_id);
(address.clone(), *id)
}
}
}
fn get(&self, addressable: impl AsRef<str>) -> Option<RegisteredId> {
self.inner.get(addressable.as_ref()).map(ToOwned::to_owned)
}
}
#[derive(Debug)]
enum RegistryEntry {
Pending {
address: Address,
},
Ready {
address: Address,
descriptor: Descriptor,
shared_value: SharedAtomicValue,
},
}
#[derive(Debug, Clone, Copy)]
pub struct RegistryEntryRef<'a>(&'a RegistryEntry);
impl RegistryEntryRef<'_> {
#[must_use]
pub const fn address(&self) -> &'_ Address {
let Self(entry) = self;
match entry {
RegistryEntry::Pending { address } | RegistryEntry::Ready { address, .. } => address,
}
}
#[must_use]
pub const fn descriptor(&self) -> Option<&'_ Descriptor> {
let Self(entry) = self;
match entry {
RegistryEntry::Pending { address: _ } => None,
RegistryEntry::Ready { descriptor, .. } => Some(descriptor),
}
}
#[must_use]
pub fn load_consume_shared_value(&self) -> Option<Value> {
let Self(entry) = self;
match entry {
RegistryEntry::Pending { address: _ } => None,
RegistryEntry::Ready { shared_value, .. } => Some(shared_value.load_consume()),
}
}
#[must_use]
pub fn load_relaxed_shared_value(&self) -> Option<Value> {
let Self(entry) = self;
match entry {
RegistryEntry::Pending { address: _ } => None,
RegistryEntry::Ready { shared_value, .. } => Some(shared_value.load_relaxed()),
}
}
#[expect(clippy::must_use_candidate)]
pub fn store_shared_value(&self, new_value: Value) -> bool {
let Self(entry) = self;
match entry {
RegistryEntry::Pending { address: _ } => false,
RegistryEntry::Ready { shared_value, .. } => {
shared_value.store(new_value);
true
}
}
}
}
#[derive(Debug, Display, Error)]
pub enum RegistrationError {
#[display("address occupied")]
AddressOccupied {
address: Address,
descriptor: Descriptor,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegistrationStatus {
NewlyRegistered,
AlreadyRegistered,
}
#[derive(Debug, Clone, Copy)]
pub struct Registration<'a> {
status: RegistrationStatus,
id: RegisteredId,
entry: &'a RegistryEntry,
}
impl Registration<'_> {
#[must_use]
pub const fn status(&self) -> RegistrationStatus {
self.status
}
#[must_use]
pub const fn id(&self) -> RegisteredId {
self.id
}
#[must_use]
pub const fn entry(&self) -> RegistryEntryRef<'_> {
RegistryEntryRef(self.entry)
}
}
#[derive(Debug)]
struct RegistrationMut<'a> {
status: RegistrationStatus,
id: RegisteredId,
entry: &'a mut RegistryEntry,
}
impl<'a> From<RegistrationMut<'a>> for Registration<'a> {
fn from(from: RegistrationMut<'a>) -> Self {
let RegistrationMut { status, id, entry } = from;
Self { status, id, entry }
}
}
#[expect(missing_debug_implementations)]
pub struct Registry {
address_to_id: AddressToIdMap,
entries: Vec<RegistryEntry>,
}
const fn registry_entry_id(param_id: RegisteredId) -> usize {
let RegisteredId(entry_id) = param_id;
entry_id
}
impl Registry {
pub fn address_to_id_iter(&self) -> impl Iterator<Item = (&Address, RegisteredId)> {
self.address_to_id.iter()
}
fn register(&mut self, addressable: impl AsRef<str> + Into<Address>) -> RegistrationMut<'_> {
debug_assert_eq!(self.address_to_id.len(), self.entries.len());
let (address, id) = self.address_to_id.get_or_add(addressable);
let entry_id = registry_entry_id(id);
if entry_id < self.entries.len() {
debug_assert_eq!(self.address_to_id.len(), self.entries.len());
#[expect(unsafe_code)]
let entry = unsafe { self.entries.get_unchecked_mut(registry_entry_id(id)) };
RegistrationMut {
status: RegistrationStatus::AlreadyRegistered,
id,
entry,
}
} else {
let new_entry = RegistryEntry::Pending { address };
self.entries.push(new_entry);
debug_assert_eq!(self.address_to_id.len(), self.entries.len());
let entry = self
.entries
.last_mut()
.expect("entry exists");
RegistrationMut {
status: RegistrationStatus::NewlyRegistered,
id,
entry,
}
}
}
pub fn register_descriptor(
&mut self,
addressable: impl AsRef<str> + Into<Address>,
descriptor: Descriptor,
) -> Result<Registration<'_>, RegistrationError> {
let RegistrationMut { status, id, entry } = self.register(addressable);
match entry {
RegistryEntry::Pending { address } => {
log::debug!("Registering descriptor @ {address}: {descriptor:?}");
let shared_value = Arc::new(AtomicValue::from(descriptor.value.default));
let address = std::mem::take(address);
*entry = RegistryEntry::Ready {
address,
descriptor,
shared_value,
};
Ok(Registration { status, id, entry })
}
RegistryEntry::Ready {
address,
descriptor: registered_descriptor,
shared_value: _,
} => {
log::debug!("Descriptor already registered @ {address}: {registered_descriptor:?}");
if descriptor != *registered_descriptor {
return Err(RegistrationError::AddressOccupied {
address: address.clone(),
descriptor,
});
}
Ok(Registration { status, id, entry })
}
}
}
pub fn register_address(
&mut self,
addressable: impl AsRef<str> + Into<Address>,
) -> Registration<'_> {
self.register(addressable).into()
}
#[must_use]
pub fn resolve_address(&self, addressable: impl AsRef<str>) -> Option<RegisteredId> {
self.address_to_id.get(addressable)
}
#[must_use]
pub fn get_entry(&self, id: RegisteredId) -> Option<RegistryEntryRef<'_>> {
self.entries
.get(registry_entry_id(id))
.map(RegistryEntryRef)
}
}
impl Default for Registry {
fn default() -> Self {
Self {
address_to_id: AddressToIdMap::with_capacity(INITIAL_CAPACITY + INITIAL_CAPACITY / 2),
entries: Vec::with_capacity(INITIAL_CAPACITY),
}
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use smol_str::SmolStr;
use crate::param::{Descriptor, Direction, RegistrationStatus, Value, ValueDescriptor};
use super::{RegisteredId, Registry};
#[test]
fn registry() {
let mut registry = Registry::default();
let consumer1 = registry.register_address("addr1");
assert_eq!(consumer1.id(), RegisteredId(0));
assert_eq!(consumer1.status(), RegistrationStatus::NewlyRegistered);
assert_eq!(consumer1.entry().address().as_str(), "addr1");
assert!(consumer1.entry().descriptor().is_none());
registry.register_address("addr2".to_owned());
registry.register_address(Cow::Borrowed("addr3"));
registry.register_address(SmolStr::new_static("addr4"));
let desc1 = Descriptor {
name: "name1".into(),
unit: None,
direction: Direction::Input,
value: ValueDescriptor::default(Value::Bool(true)),
};
let provider1 = registry
.register_descriptor("addr1", desc1.clone())
.unwrap();
assert_eq!(provider1.id(), RegisteredId(0));
assert_eq!(provider1.status(), RegistrationStatus::AlreadyRegistered);
assert_eq!(provider1.entry().address().as_str(), "addr1");
assert_eq!(provider1.entry().descriptor(), Some(&desc1));
}
}