use crate::{name_object::NameSeg, value::AmlValue, AmlError};
use alloc::{
collections::BTreeMap,
string::{String, ToString},
vec::Vec,
};
use core::fmt;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AmlHandle(u32);
impl AmlHandle {
pub(self) fn increment(&mut self) {
self.0 += 1;
}
}
pub struct Namespace {
next_handle: AmlHandle,
object_map: BTreeMap<AmlHandle, AmlValue>,
name_map: BTreeMap<AmlName, AmlHandle>,
}
impl Namespace {
pub fn new() -> Namespace {
Namespace { next_handle: AmlHandle(0), object_map: BTreeMap::new(), name_map: BTreeMap::new() }
}
pub fn add(&mut self, path: AmlName, value: AmlValue) -> Result<AmlHandle, AmlError> {
assert!(path.is_absolute());
assert!(path.is_normal());
if self.name_map.contains_key(&path) {
return Err(AmlError::NameCollision(path.clone()));
}
let handle = self.next_handle;
self.next_handle.increment();
self.object_map.insert(handle, value);
self.name_map.insert(path, handle);
Ok(handle)
}
pub fn add_at_resolved_path(
&mut self,
path: AmlName,
scope: &AmlName,
value: AmlValue,
) -> Result<AmlHandle, AmlError> {
self.add(path.resolve(scope)?, value)
}
pub fn get(&self, handle: AmlHandle) -> Result<&AmlValue, AmlError> {
self.object_map.get(&handle).ok_or(AmlError::HandleDoesNotExist(handle))
}
pub fn get_by_path(&self, path: &AmlName) -> Result<&AmlValue, AmlError> {
let handle = *self.name_map.get(path).ok_or(AmlError::ObjectDoesNotExist(path.as_string()))?;
self.get(handle).map_err(|_| AmlError::ObjectDoesNotExist(path.as_string()))
}
pub fn get_mut(&mut self, handle: AmlHandle) -> Result<&mut AmlValue, AmlError> {
self.object_map.get_mut(&handle).ok_or(AmlError::HandleDoesNotExist(handle))
}
pub fn get_by_path_mut(&mut self, path: &AmlName) -> Result<&mut AmlValue, AmlError> {
let handle = *self.name_map.get(path).ok_or(AmlError::ObjectDoesNotExist(path.as_string()))?;
self.get_mut(handle).map_err(|_| AmlError::ObjectDoesNotExist(path.as_string()))
}
pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<AmlHandle, AmlError> {
if path.search_rules_apply() {
let mut scope = starting_scope.clone();
assert!(scope.is_absolute());
loop {
if let Some(handle) = self.name_map.get(&path.resolve(&scope)?) {
return Ok(*handle);
}
match scope.parent() {
Ok(parent) => scope = parent,
Err(AmlError::RootHasNoParent) => {
return Err(AmlError::ObjectDoesNotExist(path.as_string()))
}
Err(err) => return Err(err),
}
}
} else {
self.name_map
.get(&path.resolve(starting_scope)?)
.map(|&handle| handle)
.ok_or(AmlError::ObjectDoesNotExist(path.as_string()))
}
}
}
impl fmt::Debug for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (name, handle) in self.name_map.iter() {
write!(f, "{}: {:?}\n", name, self.object_map.get(handle).unwrap())?;
}
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AmlName(pub(crate) Vec<NameComponent>);
impl AmlName {
pub fn root() -> AmlName {
AmlName(alloc::vec![NameComponent::Root])
}
pub fn from_name_seg(seg: NameSeg) -> AmlName {
AmlName(alloc::vec![NameComponent::Segment(seg)])
}
pub fn from_str(mut string: &str) -> Option<AmlName> {
if string.len() == 0 {
return None;
}
let mut components = Vec::new();
if string.starts_with('\\') {
components.push(NameComponent::Root);
string = &string[1..];
}
if string.len() > 0 {
for mut part in string.split('.') {
while part.starts_with('^') {
components.push(NameComponent::Prefix);
part = &part[1..];
}
components.push(NameComponent::Segment(NameSeg::from_str(part)?));
}
}
Some(AmlName(components))
}
pub fn as_string(&self) -> String {
self.0
.iter()
.fold(String::new(), |name, component| match component {
NameComponent::Root => name + "\\",
NameComponent::Prefix => name + "^",
NameComponent::Segment(seg) => name + seg.as_str() + ".",
})
.trim_end_matches('.')
.to_string()
}
pub fn is_normal(&self) -> bool {
!self.0.contains(&NameComponent::Prefix)
}
pub fn is_absolute(&self) -> bool {
self.0.first() == Some(&NameComponent::Root)
}
pub fn search_rules_apply(&self) -> bool {
if self.0.len() != 1 {
return false;
}
match self.0[0] {
NameComponent::Segment(_) => true,
_ => false,
}
}
pub fn normalize(self) -> Result<AmlName, AmlError> {
Ok(self)
}
pub fn parent(&self) -> Result<AmlName, AmlError> {
let mut normalized_self = self.clone().normalize()?;
match normalized_self.0.last() {
None | Some(NameComponent::Root) => Err(AmlError::RootHasNoParent),
Some(NameComponent::Segment(_)) => {
normalized_self.0.pop();
Ok(normalized_self)
}
Some(NameComponent::Prefix) => unreachable!(),
}
}
pub fn resolve(&self, scope: &AmlName) -> Result<AmlName, AmlError> {
assert!(scope.is_absolute());
if self.is_absolute() {
return Ok(self.clone());
}
let mut resolved_path = scope.clone();
resolved_path.0.extend_from_slice(&(self.0));
resolved_path.normalize()
}
}
impl fmt::Display for AmlName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_string())
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum NameComponent {
Root,
Prefix,
Segment(NameSeg),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aml_name_from_str() {
assert_eq!(AmlName::from_str(""), None);
assert_eq!(AmlName::from_str("\\"), Some(AmlName::root()));
assert_eq!(
AmlName::from_str("\\_SB.PCI0"),
Some(AmlName(alloc::vec![
NameComponent::Root,
NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
]))
);
assert_eq!(
AmlName::from_str("\\_SB.^^^PCI0"),
Some(AmlName(alloc::vec![
NameComponent::Root,
NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
NameComponent::Prefix,
NameComponent::Prefix,
NameComponent::Prefix,
NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
]))
);
}
#[test]
fn test_is_normal() {
assert_eq!(AmlName::root().is_normal(), true);
assert_eq!(AmlName::from_str("\\_SB.PCI0.VGA").unwrap().is_normal(), true);
assert_eq!(AmlName::from_str("\\_SB.^PCI0.VGA").unwrap().is_normal(), false);
assert_eq!(AmlName::from_str("\\^_SB.^^PCI0.VGA").unwrap().is_normal(), false);
assert_eq!(AmlName::from_str("_SB.^^PCI0.VGA").unwrap().is_normal(), false);
assert_eq!(AmlName::from_str("_SB.PCI0.VGA").unwrap().is_normal(), true);
}
#[test]
fn test_is_absolute() {
assert_eq!(AmlName::root().is_absolute(), true);
assert_eq!(AmlName::from_str("\\_SB.PCI0.VGA").unwrap().is_absolute(), true);
assert_eq!(AmlName::from_str("\\_SB.^PCI0.VGA").unwrap().is_absolute(), true);
assert_eq!(AmlName::from_str("\\^_SB.^^PCI0.VGA").unwrap().is_absolute(), true);
assert_eq!(AmlName::from_str("_SB.^^PCI0.VGA").unwrap().is_absolute(), false);
assert_eq!(AmlName::from_str("_SB.PCI0.VGA").unwrap().is_absolute(), false);
}
#[test]
fn test_search_rules_apply() {
assert_eq!(AmlName::root().search_rules_apply(), false);
assert_eq!(AmlName::from_str("\\_SB").unwrap().search_rules_apply(), false);
assert_eq!(AmlName::from_str("^VGA").unwrap().search_rules_apply(), false);
assert_eq!(AmlName::from_str("_SB.PCI0.VGA").unwrap().search_rules_apply(), false);
assert_eq!(AmlName::from_str("VGA").unwrap().search_rules_apply(), true);
assert_eq!(AmlName::from_str("_SB").unwrap().search_rules_apply(), true);
}
#[test]
fn test_aml_name_parent() {
assert_eq!(AmlName::from_str("\\").unwrap().parent(), Err(AmlError::RootHasNoParent));
assert_eq!(AmlName::from_str("\\_SB").unwrap().parent(), Ok(AmlName::root()));
assert_eq!(
AmlName::from_str("\\_SB.PCI0").unwrap().parent(),
Ok(AmlName::from_str("\\_SB").unwrap())
);
assert_eq!(AmlName::from_str("\\_SB.PCI0").unwrap().parent().unwrap().parent(), Ok(AmlName::root()));
}
}