Browse Source

Redo how names in AML are represented

This is closer to how they're actually treated in AML, and mean we need a
lot less dodgy string manipulation. It does come with a slightly higher
memory footprint though, which might need to be looked at in the future.
Isaac Woods 5 years ago
parent
commit
f7e301fb0e
3 changed files with 100 additions and 28 deletions
  1. 24 2
      aml_parser/src/lib.rs
  2. 75 25
      aml_parser/src/name_object.rs
  3. 1 1
      aml_parser/src/term_object.rs

+ 24 - 2
aml_parser/src/lib.rs

@@ -18,8 +18,9 @@ pub mod value;
 
 pub use crate::value::AmlValue;
 
-use alloc::{collections::BTreeMap, string::String};
+use alloc::collections::BTreeMap;
 use log::{error, trace};
+use name_object::AmlName;
 use parser::Parser;
 use pkg_length::PkgLength;
 
@@ -43,7 +44,7 @@ pub enum AmlError {
 
 #[derive(Debug)]
 pub struct AmlContext {
-    pub namespace: BTreeMap<String, AmlValue>,
+    namespace: BTreeMap<AmlName, AmlValue>,
 }
 
 impl AmlContext {
@@ -63,6 +64,27 @@ impl AmlContext {
                 error!("Failed to parse AML stream. Err = {:?}", err);
                 Err(err)
             }
+
+    /// Resolves a given path relative to the current scope (if the given path is not absolute).
+    /// The returned path can be used to index the namespace.
+    pub fn resolve_path(&mut self, path: &AmlName) -> AmlName {
+        // TODO: we should normalize the path by resolving prefix chars etc.
+
+        // If the path is absolute, just return it.
+        if path.is_absolute() {
+            return path.clone();
         }
+
+        // Otherwise, it's relative to the current scope so append it onto that.
+        let mut new_path = self.current_scope.clone();
+        new_path.0.extend_from_slice(&(path.0));
+        new_path
+    }
+
+    /// Add an `AmlValue` to the namespace. `path` can either be absolute, or relative (in which
+    /// case it's treated as relative to the current scope).
+    pub fn add_to_namespace(&mut self, path: AmlName, value: AmlValue) {
+        let resolved_path = self.resolve_path(&path);
+        self.namespace.insert(resolved_path, value);
     }
 }

+ 75 - 25
aml_parser/src/name_object.rs

@@ -4,10 +4,51 @@ use crate::{
     AmlContext,
     AmlError,
 };
-use alloc::string::String;
-use core::str;
+use alloc::{
+    string::{String, ToString},
+    vec::Vec,
+};
+use core::{fmt, str};
+
+#[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 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 name_string<'a, 'c>() -> impl Parser<'a, 'c, String>
+    pub fn is_absolute(&self) -> bool {
+        self.0.first() == Some(&NameComponent::Root)
+    }
+}
+
+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),
+}
+
+pub fn name_string<'a, 'c>() -> impl Parser<'a, 'c, AmlName>
 where
     'c: 'a,
 {
@@ -15,28 +56,29 @@ where
      * NameString := <RootChar('\') NamePath> | <PrefixPath NamePath>
      * PrefixPath := Nothing | <'^' PrefixPath>
      */
-    let root_name_string = opcode(ROOT_CHAR)
-        .then(name_path())
-        .map(|((), name_path)| Ok(String::from("\\") + &name_path));
-
-    comment_scope_verbose("NameString", move |input: &'a [u8], context: &'c mut AmlContext| {
+    let root_name_string = opcode(ROOT_CHAR).then(name_path()).map(|((), ref name_path)| {
+        let mut name = alloc::vec![NameComponent::Root];
+        name.extend_from_slice(name_path);
+        Ok(AmlName(name))
+    });
+
+    // TODO: combinator to select a parser based on a peeked byte?
+    comment_scope_verbose("NameString", move |input: &'a [u8], context| {
         let first_char = match input.first() {
             Some(&c) => c,
             None => return Err((input, context, AmlError::UnexpectedEndOfStream)),
         };
 
+        // TODO: parse <PrefixPath NamePath> where there are actually PrefixChars
         match first_char {
             ROOT_CHAR => root_name_string.parse(input, context),
-            PREFIX_CHAR => {
-                // TODO: parse <PrefixPath NamePath> where there are actually PrefixChars
-                unimplemented!();
-            }
-            _ => name_path().parse(input, context),
+            PREFIX_CHAR => unimplemented!(),
+            _ => name_path().map(|path| Ok(AmlName(path))).parse(input, context),
         }
     })
 }
 
-pub fn name_path<'a, 'c>() -> impl Parser<'a, 'c, String>
+pub fn name_path<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
 where
     'c: 'a,
 {
@@ -47,34 +89,35 @@ where
         null_name(),
         dual_name_path(),
         multi_name_path(),
-        name_seg().map(|seg| Ok(String::from(seg.as_str())))
+        name_seg().map(|seg| Ok(alloc::vec![NameComponent::Segment(seg)]))
     )
 }
 
-pub fn null_name<'a, 'c>() -> impl Parser<'a, 'c, String>
+pub fn null_name<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
 where
     'c: 'a,
 {
     /*
      * NullName := 0x00
+     *
+     * This doesn't actually allocate because the `Vec`'s capacity is zero.
      */
-    opcode(NULL_NAME).map(|_| Ok(String::from("")))
+    opcode(NULL_NAME).map(|_| Ok(Vec::with_capacity(0)))
 }
 
-pub fn dual_name_path<'a, 'c>() -> impl Parser<'a, 'c, String>
+pub fn dual_name_path<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
 where
     'c: 'a,
 {
     /*
      * DualNamePath := 0x2e NameSeg NameSeg
      */
-    opcode(DUAL_NAME_PREFIX)
-        .then(name_seg())
-        .then(name_seg())
-        .map(|(((), first), second)| Ok(String::from(first.as_str()) + second.as_str()))
+    opcode(DUAL_NAME_PREFIX).then(name_seg()).then(name_seg()).map(|(((), first), second)| {
+        Ok(alloc::vec![NameComponent::Segment(first), NameComponent::Segment(second)])
+    })
 }
 
-pub fn multi_name_path<'a, 'c>() -> impl Parser<'a, 'c, String>
+pub fn multi_name_path<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
 where
     'c: 'a,
 {
@@ -88,7 +131,7 @@ where
             Ok((new_input, context, name_segs)) => Ok((
                 new_input,
                 context,
-                name_segs.iter().fold(String::new(), |name, name_seg| name + name_seg.as_str()),
+                name_segs.iter().map(|&seg| NameComponent::Segment(seg)).collect(),
             )),
             // Correct returned input to the one we haven't touched
             Err((_, context, err)) => Err((input, context, err)),
@@ -96,7 +139,7 @@ where
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 pub struct NameSeg([u8; 4]);
 
 impl NameSeg {
@@ -111,6 +154,13 @@ impl NameSeg {
     }
 }
 
+// A list of ASCII codes is pretty much never useful, so we always just show it as a string
+impl fmt::Debug for NameSeg {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:?}", self.as_str())
+    }
+}
+
 pub fn name_seg<'a, 'c>() -> impl Parser<'a, 'c, NameSeg>
 where
     'c: 'a,

+ 1 - 1
aml_parser/src/term_object.rs

@@ -1,5 +1,5 @@
 use crate::{
-    name_object::{name_seg, name_string},
+    name_object::{name_seg, name_string, AmlName},
     opcode::{self, ext_opcode, opcode},
     parser::{
         choice,