Browse Source

Factor out value comparison code from DefLEqual

Isaac Woods 4 years ago
parent
commit
438bd9e4cc
3 changed files with 28 additions and 12 deletions
  1. 2 3
      aml/src/lib.rs
  2. 6 9
      aml/src/type2.rs
  3. 20 0
      aml/src/value.rs

+ 2 - 3
aml/src/lib.rs

@@ -69,7 +69,7 @@ use namespace::LevelType;
 use parser::Parser;
 use pkg_length::PkgLength;
 use term_object::term_list;
-use value::Args;
+use value::{AmlType, Args};
 
 /// AML has a `RevisionOp` operator that returns the "AML interpreter revision". It's not clear
 /// what this is actually used for, but this is ours.
@@ -330,8 +330,6 @@ impl AmlContext {
     /// be altered during a store in some circumstances. If the target is a `Name`, this also performs required
     /// implicit conversions. Stores to other targets are semantically equivalent to a `CopyObject`.
     pub(crate) fn store(&mut self, target: Target, value: AmlValue) -> Result<AmlValue, AmlError> {
-        use value::AmlType;
-
         match target {
             Target::Name(ref path) => {
                 let (_, handle) = self.namespace.search(path, &self.current_scope)?;
@@ -687,4 +685,5 @@ pub enum AmlError {
     FieldRegionIsNotOpRegion,
     FieldInvalidAddress,
     FieldInvalidAccessSize,
+    TypeCannotBeCompared(AmlType),
 }

+ 6 - 9
aml/src/type2.rs

@@ -18,7 +18,7 @@ use crate::{
     DebugVerbosity,
 };
 use alloc::vec::Vec;
-use core::convert::TryInto;
+use core::{cmp::Ordering, convert::TryInto};
 
 /// Type 2 opcodes return a value and so can be used in expressions.
 pub fn type2_opcode<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
@@ -138,14 +138,11 @@ where
             DebugVerbosity::AllScopes,
             "DefLEqual",
             term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
-                /*
-                 * TODO: we should also be able to compare strings and buffers. `left_arg` decides the type that we
-                 * need to use - we have to try and convert `right_arg` into that type and then compare them in the
-                 * correct way.
-                 */
-                let left = try_with_context!(context, left_arg.as_integer(context));
-                let right = try_with_context!(context, right_arg.as_integer(context));
-                (Ok(AmlValue::Boolean(left == right)), context)
+                let ord = try_with_context!(context, left_arg.cmp(right_arg, context));
+                (Ok(AmlValue::Boolean(ord == Ordering::Equal)), context)
+            }),
+        ))
+        .map(|((), result)| Ok(result))
             }),
         ))
         .map(|((), result)| Ok(result))

+ 20 - 0
aml/src/value.rs

@@ -1,6 +1,7 @@
 use crate::{misc::ArgNum, AmlContext, AmlError, AmlHandle, AmlName};
 use alloc::{string::String, vec::Vec};
 use bit_field::BitField;
+use core::cmp;
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum RegionSpace {
@@ -381,6 +382,25 @@ impl AmlValue {
             Err(AmlError::IncompatibleValueConversion)
         }
     }
+
+    /// Logically compare two `AmlValue`s, according to the rules that govern opcodes like `DefLEqual`, `DefLLess`,
+    /// etc. The type of `self` dictates the type that `other` will be converted to, and the method by which the
+    /// values will be compared:
+    ///    - `Integer`s are simply compared by numeric comparison
+    ///    - `String`s and `Buffer`s are compared lexicographically - `other` is compared byte-wise until a byte
+    ///      is discovered that is either less or greater than the corresponding byte of `self`. If the bytes are
+    ///      identical, the lengths are compared.
+    pub fn cmp(&self, other: AmlValue, context: &mut AmlContext) -> Result<cmp::Ordering, AmlError> {
+        let self_inner =
+            if self.type_of() == AmlType::FieldUnit { self.read_field(context)? } else { self.clone() };
+
+        match self_inner.type_of() {
+            AmlType::Integer => Ok(self.as_integer(context)?.cmp(&other.as_integer(context)?)),
+            AmlType::Buffer => unimplemented!(),
+            AmlType::String => unimplemented!(),
+            typ => Err(AmlError::TypeCannotBeCompared(typ)),
+        }
+    }
 }
 
 /// A control method can take up to 7 arguments, each of which can be an `AmlValue`.