<commit_before>use super::super::ast;
use super::virt;
use std::collections::HashSet;
use std::iter::FromIterator;
use crate::base::ast::{Expression};
use super::graph::{DependencyGraph};
use crate::css::runtime::evaulator::{evaluate as evaluate_css};
use crate::js::runtime::evaluator::{evaluate as evaluate_js};
use crate::js::runtime::virt as js_virt;
use crate::js::ast as js_ast;
use crate::css::runtime::virt as css_virt;
use crc::{crc32};

#[derive(Debug)]
pub struct Context<'a> {
  graph: &'a DependencyGraph,
  file_path: &'a String,
  import_ids: HashSet<&'a String>,
  scope: String,
  data: &'a js_virt::JsObject,
}

pub fn evaluate<'a>(node_expr: &Expression<ast::Node>, file_path: &String, graph: &'a DependencyGraph, data: &js_virt::JsObject) -> Result<Option<virt::Node>, &'static str>  {
  let context = create_context(node_expr, file_path, graph, data);

  let mut root_result = evaluate_node(node_expr, &context);

  // need to insert all styles into the root for efficiency
  match root_result {
    Ok(ref mut root_option) => {
      match root_option {
        Some(ref mut root) => {
          let style = evaluate_jumbo_style(node_expr, file_path, graph)?;
          root.prepend_child(style);
        },
        _ => { }
      }
    }
    _ => { }
  };

  root_result
}

pub fn evaluate_jumbo_style<'a>(entry_expr: &Expression<ast::Node>,  file_path: &String, graph: &'a DependencyGraph) -> Result<virt::Node, &'static str>  {

  let mut sheet = css_virt::CSSSheet {
    rules: vec![]
  };

  for dep in graph.flatten(file_path) {
    let children_option = ast::get_children(&dep.expression);
    let scope = get_component_scope(&dep.file_path);
    if let Some(children) = children_option {

      // style elements are only allowed in root, so no need to traverse
      for child in children {
        if let ast::Node::StyleElement(styleElement) = &child.item {
          sheet.extend(evaluate_css(&styleElement.sheet, &scope)?);
        }
      }
    }
  }

  Ok(virt::Node::StyleElement(virt::StyleElement {
    sheet
  }))
}

pub fn evaluate_component<'a>(node_expr: &Expression<ast::Node>, file_path: &String, graph: &'a DependencyGraph, data: &js_virt::JsObject) -> Result<Option<virt::Node>, &'static str>  {
  evaluate_node(node_expr, &create_context(node_expr, file_path, graph, data))
}

fn create_context<'a>(node_expr: &'a Expression<ast::Node>, file_path: &'a String, graph: &'a DependencyGraph, data: &'a js_virt::JsObject) -> Context<'a> {
  Context {
    graph,
    file_path,
    import_ids: HashSet::from_iter(ast::get_import_ids(node_expr)),
    scope: get_component_scope(file_path),
    data
  }
}

fn get_component_scope<'a>(file_path: &String) -> String {
  format!("_{:x}", crc32::checksum_ieee(file_path.as_bytes())).to_string()
}

fn evaluate_node<'a>(node_expr: &Expression<ast::Node>, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  match &node_expr.item {
    ast::Node::Element(el) => {
      evaluate_element(&el, context)
    },
    ast::Node::StyleElement(el) => {
      evaluate_style_element(&el, context)
    },
    ast::Node::Text(text) => {
      Ok(Some(virt::Node::Text(virt::Text { value: text.value.to_string() })))
    },
    ast::Node::Slot(slot) => {
      evaluate_slot(&slot, context)
    },
    ast::Node::Fragment(el) => {
      evaluate_fragment(&el, context)
    },
    ast::Node::Comment(_el) => {
      Ok(None)
    }
  }
}

fn evaluate_element<'a>(element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  match element.tag_name.as_str() {
    "import" => evaluate_import_element(element, context),
    _ => {
      if context.import_ids.contains(&element.tag_name) {
        evaluate_imported_component(element, context)
      } else {
        evaluate_basic_element(element, context)
      }
    }
  }
}

fn evaluate_slot<'a>(slot: &Expression<js_ast::Statement>, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  let mut js_value = evaluate_js(slot, &context.data)?;

  // if array of values, then treat as document fragment
  if let js_virt::JsValue::JsArray(ary) = &mut js_value {
    let mut children = vec![];
    for item in ary.values.drain(0..) {
      if let js_virt::JsValue::JsNode(child) = item {
        children.push(child);
      } else {
        children.push(virt::Node::Text(virt::Text {
          value: item.to_string()
        }))
      }
    }

    return Ok(Some(virt::Node::Fragment(virt::Fragment {
      children
    })));
  }

  Ok(Some(virt::Node::Text(virt::Text { value: js_value.to_string() })))
}

fn evaluate_imported_component<'a>(element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {

  // let attributes = vec![];
  let selfDep  = &context.graph.dependencies.get(context.file_path).unwrap();
  let dep_file_path = &selfDep.dependencies.get(&element.tag_name).unwrap();
  let dep = &context.graph.dependencies.get(&dep_file_path.to_string()).unwrap();
  let mut data = js_virt::JsObject::new();

  for attr_expr in &element.attributes {
    let attr = &attr_expr.item;
    if attr.value == None {
      data.values.insert(attr.name.to_string(), js_virt::JsValue::JsBoolean(true));
    } else {
      let value = evaluate_attribute_value(&attr.value.as_ref().unwrap().item, context)?;

      data.values.insert(attr.name.to_string(), js_virt::JsValue::JsString(value.unwrap().to_string()));
    }
  }

  let mut js_self = js_virt::JsObject::new();
  let mut js_children = js_virt::JsArray::new();
  let children: Vec<js_virt::JsValue> = evaluate_children(&element.children, &context)?.into_iter().map(|child| {
    js_virt::JsValue::JsNode(child)
  }).collect();

  js_children.values.extend(children);

  data.values.insert("children".to_string(), js_virt::JsValue::JsArray(js_children));

  // TODO when
  // data.values.insert("self".to_string(), js_virt::JsValue::JsObject(js_self));

  // TODO: if fragment, then wrap in span. If not, then copy these attributes to root element
  evaluate_component(&dep.expression, dep_file_path, &context.graph, &data)
}

fn evaluate_basic_element<'a>(element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {

  let mut attributes = vec![];

  let tag_name = element.tag_name.clone();
  let mut class_name_found = false;

  for attr_expr in &element.attributes {
    let attr = &attr_expr.item;

    let mut value;

    if attr.value == None {
      value = None;
    } else {
      value = evaluate_attribute_value(&attr.value.as_ref().unwrap().item, context)?;
    }

    if attr.name == "class" && value != None {
      if let Some(original) = value {
        class_name_found = true;
        value = Some(format!("{} {}", original, context.scope));
      }
    }

    attributes.push(virt::Attribute {
      name: attr.name.clone(),
      value,
    });
  }

  if !class_name_found {
    attributes.push(virt::Attribute {
      name: "class".to_string(),
      value: Some(context.scope.to_string())
    });
  }

  let children = evaluate_children(&element.children, context)?;

  Ok(Some(virt::Node::Element(virt::Element {
    tag_name: tag_name,
    attributes,
    children
  })))
}

fn evaluate_import_element<'a>(_element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {

  // TODO - import into context
  Ok(None)
}

fn evaluate_style_element<'a>(element: &ast::StyleElement, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  Ok(None)
  // Ok(Some(virt::Node::StyleElement(virt::StyleElement {
  //   sheet: evaluate_css(&element.sheet, &context.scope)?
  // })))
}

fn evaluate_children<'a>(children_expr: &Vec<Expression<ast::Node>>, context: &'a Context) -> Result<Vec<virt::Node>, &'static str> {

  let mut children: Vec<virt::Node> = vec![];

  for child_expr in children_expr {
    match evaluate_node(child_expr, context)? {
      Some(c) => { children.push(c); },
      None => { }
    }
  }

  Ok(children)
}

fn evaluate_fragment<'a>(fragment: &ast::Fragment, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  let mut children = evaluate_children(&fragment.children, context)?;
  if children.len() == 1 {
    return Ok(children.pop());
  }
  Ok(Some(virt::Node::Fragment(virt::Fragment {
    children
  })))
}

fn evaluate_attribute_value<'a>(value: &ast::AttributeValue, context: &'a Context) -> Result<Option<String>, &'static str> {
  match value {
    ast::AttributeValue::String(st) => {
      Ok(Some(st.value.clone()))
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use super::super::graph::*;
  use super::super::super::parser::*;

  #[test]
  fn can_evaluate_a_style() {
    let case = "

";
    let ast = parse(case).unwrap();
    let graph = DependencyGraph::new();
    let node = evaluate(&ast, &"something".to_string(), &graph, &js_virt::JsObject::new()).unwrap().unwrap();
  }
}
<commit_msg>remove js_self code<commit_after>use super::super::ast;
use super::virt;
use std::collections::HashSet;
use std::iter::FromIterator;
use crate::base::ast::{Expression};
use super::graph::{DependencyGraph};
use crate::css::runtime::evaulator::{evaluate as evaluate_css};
use crate::js::runtime::evaluator::{evaluate as evaluate_js};
use crate::js::runtime::virt as js_virt;
use crate::js::ast as js_ast;
use crate::css::runtime::virt as css_virt;
use crc::{crc32};

#[derive(Debug)]
pub struct Context<'a> {
  graph: &'a DependencyGraph,
  file_path: &'a String,
  import_ids: HashSet<&'a String>,
  scope: String,
  data: &'a js_virt::JsObject,
}

pub fn evaluate<'a>(node_expr: &Expression<ast::Node>, file_path: &String, graph: &'a DependencyGraph, data: &js_virt::JsObject) -> Result<Option<virt::Node>, &'static str>  {
  let context = create_context(node_expr, file_path, graph, data);

  let mut root_result = evaluate_node(node_expr, &context);

  // need to insert all styles into the root for efficiency
  match root_result {
    Ok(ref mut root_option) => {
      match root_option {
        Some(ref mut root) => {
          let style = evaluate_jumbo_style(node_expr, file_path, graph)?;
          root.prepend_child(style);
        },
        _ => { }
      }
    }
    _ => { }
  };

  root_result
}

pub fn evaluate_jumbo_style<'a>(entry_expr: &Expression<ast::Node>,  file_path: &String, graph: &'a DependencyGraph) -> Result<virt::Node, &'static str>  {

  let mut sheet = css_virt::CSSSheet {
    rules: vec![]
  };

  for dep in graph.flatten(file_path) {
    let children_option = ast::get_children(&dep.expression);
    let scope = get_component_scope(&dep.file_path);
    if let Some(children) = children_option {

      // style elements are only allowed in root, so no need to traverse
      for child in children {
        if let ast::Node::StyleElement(styleElement) = &child.item {
          sheet.extend(evaluate_css(&styleElement.sheet, &scope)?);
        }
      }
    }
  }

  Ok(virt::Node::StyleElement(virt::StyleElement {
    sheet
  }))
}

pub fn evaluate_component<'a>(node_expr: &Expression<ast::Node>, file_path: &String, graph: &'a DependencyGraph, data: &js_virt::JsObject) -> Result<Option<virt::Node>, &'static str>  {
  evaluate_node(node_expr, &create_context(node_expr, file_path, graph, data))
}

fn create_context<'a>(node_expr: &'a Expression<ast::Node>, file_path: &'a String, graph: &'a DependencyGraph, data: &'a js_virt::JsObject) -> Context<'a> {
  Context {
    graph,
    file_path,
    import_ids: HashSet::from_iter(ast::get_import_ids(node_expr)),
    scope: get_component_scope(file_path),
    data
  }
}

fn get_component_scope<'a>(file_path: &String) -> String {
  format!("_{:x}", crc32::checksum_ieee(file_path.as_bytes())).to_string()
}

fn evaluate_node<'a>(node_expr: &Expression<ast::Node>, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  match &node_expr.item {
    ast::Node::Element(el) => {
      evaluate_element(&el, context)
    },
    ast::Node::StyleElement(el) => {
      evaluate_style_element(&el, context)
    },
    ast::Node::Text(text) => {
      Ok(Some(virt::Node::Text(virt::Text { value: text.value.to_string() })))
    },
    ast::Node::Slot(slot) => {
      evaluate_slot(&slot, context)
    },
    ast::Node::Fragment(el) => {
      evaluate_fragment(&el, context)
    },
    ast::Node::Comment(_el) => {
      Ok(None)
    }
  }
}

fn evaluate_element<'a>(element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  match element.tag_name.as_str() {
    "import" => evaluate_import_element(element, context),
    _ => {
      if context.import_ids.contains(&element.tag_name) {
        evaluate_imported_component(element, context)
      } else {
        evaluate_basic_element(element, context)
      }
    }
  }
}

fn evaluate_slot<'a>(slot: &Expression<js_ast::Statement>, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  let mut js_value = evaluate_js(slot, &context.data)?;

  // if array of values, then treat as document fragment
  if let js_virt::JsValue::JsArray(ary) = &mut js_value {
    let mut children = vec![];
    for item in ary.values.drain(0..) {
      if let js_virt::JsValue::JsNode(child) = item {
        children.push(child);
      } else {
        children.push(virt::Node::Text(virt::Text {
          value: item.to_string()
        }))
      }
    }

    return Ok(Some(virt::Node::Fragment(virt::Fragment {
      children
    })));
  }

  Ok(Some(virt::Node::Text(virt::Text { value: js_value.to_string() })))
}

fn evaluate_imported_component<'a>(element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {

  // let attributes = vec![];
  let selfDep  = &context.graph.dependencies.get(context.file_path).unwrap();
  let dep_file_path = &selfDep.dependencies.get(&element.tag_name).unwrap();
  let dep = &context.graph.dependencies.get(&dep_file_path.to_string()).unwrap();
  let mut data = js_virt::JsObject::new();

  for attr_expr in &element.attributes {
    let attr = &attr_expr.item;
    if attr.value == None {
      data.values.insert(attr.name.to_string(), js_virt::JsValue::JsBoolean(true));
    } else {
      let value = evaluate_attribute_value(&attr.value.as_ref().unwrap().item, context)?;

      data.values.insert(attr.name.to_string(), js_virt::JsValue::JsString(value.unwrap().to_string()));
    }
  }

  let mut js_children = js_virt::JsArray::new();
  let children: Vec<js_virt::JsValue> = evaluate_children(&element.children, &context)?.into_iter().map(|child| {
    js_virt::JsValue::JsNode(child)
  }).collect();

  js_children.values.extend(children);

  data.values.insert("children".to_string(), js_virt::JsValue::JsArray(js_children));

  // TODO: if fragment, then wrap in span. If not, then copy these attributes to root element
  evaluate_component(&dep.expression, dep_file_path, &context.graph, &data)
}

fn evaluate_basic_element<'a>(element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {

  let mut attributes = vec![];

  let tag_name = element.tag_name.clone();
  let mut class_name_found = false;

  for attr_expr in &element.attributes {
    let attr = &attr_expr.item;

    let mut value;

    if attr.value == None {
      value = None;
    } else {
      value = evaluate_attribute_value(&attr.value.as_ref().unwrap().item, context)?;
    }

    if attr.name == "class" && value != None {
      if let Some(original) = value {
        class_name_found = true;
        value = Some(format!("{} {}", original, context.scope));
      }
    }

    attributes.push(virt::Attribute {
      name: attr.name.clone(),
      value,
    });
  }

  if !class_name_found {
    attributes.push(virt::Attribute {
      name: "class".to_string(),
      value: Some(context.scope.to_string())
    });
  }

  let children = evaluate_children(&element.children, context)?;

  Ok(Some(virt::Node::Element(virt::Element {
    tag_name: tag_name,
    attributes,
    children
  })))
}

fn evaluate_import_element<'a>(_element: &ast::Element, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {

  // TODO - import into context
  Ok(None)
}

fn evaluate_style_element<'a>(element: &ast::StyleElement, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  Ok(None)
  // Ok(Some(virt::Node::StyleElement(virt::StyleElement {
  //   sheet: evaluate_css(&element.sheet, &context.scope)?
  // })))
}

fn evaluate_children<'a>(children_expr: &Vec<Expression<ast::Node>>, context: &'a Context) -> Result<Vec<virt::Node>, &'static str> {

  let mut children: Vec<virt::Node> = vec![];

  for child_expr in children_expr {
    match evaluate_node(child_expr, context)? {
      Some(c) => { children.push(c); },
      None => { }
    }
  }

  Ok(children)
}

fn evaluate_fragment<'a>(fragment: &ast::Fragment, context: &'a Context) -> Result<Option<virt::Node>, &'static str> {
  let mut children = evaluate_children(&fragment.children, context)?;
  if children.len() == 1 {
    return Ok(children.pop());
  }
  Ok(Some(virt::Node::Fragment(virt::Fragment {
    children
  })))
}

fn evaluate_attribute_value<'a>(value: &ast::AttributeValue, context: &'a Context) -> Result<Option<String>, &'static str> {
  match value {
    ast::AttributeValue::String(st) => {
      Ok(Some(st.value.clone()))
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use super::super::graph::*;
  use super::super::super::parser::*;

  #[test]
  fn can_evaluate_a_style() {
    let case = "

";
    let ast = parse(case).unwrap();
    let graph = DependencyGraph::new();
    let node = evaluate(&ast, &"something".to_string(), &graph, &js_virt::JsObject::new()).unwrap().unwrap();
  }
}
<|endoftext|>


<commit_before>//! Client-side Proc-Macro crate
//!
//! We separate proc-macro expanding logic to an extern program to allow
//! different implementations (e.g. wasm or dylib loading). And this crate
//! is used to provide basic infrastructure for communication between two
//! processes: Client (RA itself), Server (the external program)

mod rpc;
mod process;
pub mod msg;

use std::{
    ffi::OsStr,
    io,
    path::{Path, PathBuf},
    sync::Arc,
};

use tt::{SmolStr, Subtree};

use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};

pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};

#[derive(Debug, Clone)]
pub struct ProcMacroProcessExpander {
    process: Arc<ProcMacroProcessSrv>,
    dylib_path: PathBuf,
    name: SmolStr,
}

impl Eq for ProcMacroProcessExpander {}
impl PartialEq for ProcMacroProcessExpander {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
            && self.dylib_path == other.dylib_path
            && Arc::ptr_eq(&self.process, &other.process)
    }
}

impl tt::TokenExpander for ProcMacroProcessExpander {
    fn expand(
        &self,
        subtree: &Subtree,
        attr: Option<&Subtree>,
    ) -> Result<Subtree, tt::ExpansionError> {
        let task = ExpansionTask {
            macro_body: subtree.clone(),
            macro_name: self.name.to_string(),
            attributes: attr.cloned(),
            lib: self.dylib_path.to_path_buf(),
        };

        let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?;
        Ok(result.expansion)
    }
}

#[derive(Debug)]
enum ProcMacroClientKind {
    Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread },
    Dummy,
}

#[derive(Debug)]
pub struct ProcMacroClient {
    kind: ProcMacroClientKind,
}

impl ProcMacroClient {
    pub fn extern_process(
        process_path: PathBuf,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<ProcMacroClient> {
        let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
        Ok(ProcMacroClient {
            kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
        })
    }

    pub fn dummy() -> ProcMacroClient {
        ProcMacroClient { kind: ProcMacroClientKind::Dummy }
    }

    pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<(SmolStr, Arc<dyn tt::TokenExpander>)> {
        match &self.kind {
            ProcMacroClientKind::Dummy => vec![],
            ProcMacroClientKind::Process { process, .. } => {
                let macros = match process.find_proc_macros(dylib_path) {
                    Err(err) => {
                        eprintln!("Failed to find proc macros. Error: {:#?}", err);
                        return vec![];
                    }
                    Ok(macros) => macros,
                };

                macros
                    .into_iter()
                    .filter_map(|(name, kind)| {
                        match kind {
                            ProcMacroKind::CustomDerive | ProcMacroKind::FuncLike => {
                                let name = SmolStr::new(&name);
                                let expander: Arc<dyn tt::TokenExpander> =
                                    Arc::new(ProcMacroProcessExpander {
                                        process: process.clone(),
                                        name: name.clone(),
                                        dylib_path: dylib_path.into(),
                                    });
                                Some((name, expander))
                            }
                            // FIXME: Attribute macro are currently unsupported.
                            ProcMacroKind::Attr => None,
                        }
                    })
                    .collect()
            }
        }
    }
}
<commit_msg>Make `ProcMacroProcessExpander` private<commit_after>//! Client-side Proc-Macro crate
//!
//! We separate proc-macro expanding logic to an extern program to allow
//! different implementations (e.g. wasm or dylib loading). And this crate
//! is used to provide basic infrastructure for communication between two
//! processes: Client (RA itself), Server (the external program)

mod rpc;
mod process;
pub mod msg;

use std::{
    ffi::OsStr,
    io,
    path::{Path, PathBuf},
    sync::Arc,
};

use tt::{SmolStr, Subtree};

use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};

pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};

#[derive(Debug, Clone)]
struct ProcMacroProcessExpander {
    process: Arc<ProcMacroProcessSrv>,
    dylib_path: PathBuf,
    name: SmolStr,
}

impl Eq for ProcMacroProcessExpander {}
impl PartialEq for ProcMacroProcessExpander {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
            && self.dylib_path == other.dylib_path
            && Arc::ptr_eq(&self.process, &other.process)
    }
}

impl tt::TokenExpander for ProcMacroProcessExpander {
    fn expand(
        &self,
        subtree: &Subtree,
        attr: Option<&Subtree>,
    ) -> Result<Subtree, tt::ExpansionError> {
        let task = ExpansionTask {
            macro_body: subtree.clone(),
            macro_name: self.name.to_string(),
            attributes: attr.cloned(),
            lib: self.dylib_path.to_path_buf(),
        };

        let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?;
        Ok(result.expansion)
    }
}

#[derive(Debug)]
enum ProcMacroClientKind {
    Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread },
    Dummy,
}

#[derive(Debug)]
pub struct ProcMacroClient {
    kind: ProcMacroClientKind,
}

impl ProcMacroClient {
    pub fn extern_process(
        process_path: PathBuf,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<ProcMacroClient> {
        let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
        Ok(ProcMacroClient {
            kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
        })
    }

    pub fn dummy() -> ProcMacroClient {
        ProcMacroClient { kind: ProcMacroClientKind::Dummy }
    }

    pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<(SmolStr, Arc<dyn tt::TokenExpander>)> {
        match &self.kind {
            ProcMacroClientKind::Dummy => vec![],
            ProcMacroClientKind::Process { process, .. } => {
                let macros = match process.find_proc_macros(dylib_path) {
                    Err(err) => {
                        eprintln!("Failed to find proc macros. Error: {:#?}", err);
                        return vec![];
                    }
                    Ok(macros) => macros,
                };

                macros
                    .into_iter()
                    .filter_map(|(name, kind)| {
                        match kind {
                            ProcMacroKind::CustomDerive | ProcMacroKind::FuncLike => {
                                let name = SmolStr::new(&name);
                                let expander: Arc<dyn tt::TokenExpander> =
                                    Arc::new(ProcMacroProcessExpander {
                                        process: process.clone(),
                                        name: name.clone(),
                                        dylib_path: dylib_path.into(),
                                    });
                                Some((name, expander))
                            }
                            // FIXME: Attribute macro are currently unsupported.
                            ProcMacroKind::Attr => None,
                        }
                    })
                    .collect()
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::num::Wrapping;
use std::mem;
use std::hash::Hasher;
use utils::*;

const K0: Wrapping<u64> = Wrapping(0xC83A91E1);
const K1: Wrapping<u64> = Wrapping(0x8648DBDB);
const K2: Wrapping<u64> = Wrapping(0x7BDEC03B);
const K3: Wrapping<u64> = Wrapping(0x2F5870A5);

pub struct MetroHash128 {
    v: [Wrapping<u64>; 4],
    b: [u64; 4],
    bytes: usize,
}

impl Default for MetroHash128 {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl MetroHash128 {
    #[inline]
    pub fn new() -> MetroHash128 {
        Self::with_seed(0)
    }

    #[inline]
    pub fn with_seed(seed: u64) -> MetroHash128 {
        let seed = Wrapping(seed);
        MetroHash128 {
            b: unsafe { mem::uninitialized() },
            v: [
                (seed - K0) * K3,
                (seed + K1) * K2,
                (seed + K0) * K2,
                (seed - K1) * K3,
            ],
            bytes: 0,
        }
    }

    #[inline]
    fn finish128(&self) -> (u64, u64) {
        // copy internal state
        let mut v = self.v;

        // finalize bulk loop, if used
        if self.bytes >= 32 {
            v[2] = v[2] ^ (rotate_right(((v[0] + v[3]) * K0) + v[1], 21) * K1);
            v[3] = v[3] ^ (rotate_right(((v[1] + v[2]) * K1) + v[0], 21) * K0);
            v[0] = v[0] ^ (rotate_right(((v[0] + v[2]) * K0) + v[3], 21) * K1);
            v[1] = v[1] ^ (rotate_right(((v[1] + v[3]) * K1) + v[2], 21) * K0);
        }

        // process any self.bytes remaining in the input buffer
        let mut ptr = &self.b as *const _ as usize;
        let end = ptr + self.bytes % 32;

        if (end - ptr) >= 16 {
            v[0] = v[0] + (read_u64(ptr) * K2);
            ptr += 8;
            v[0] = rotate_right(v[0], 33) * K3;
            v[1] = v[1] + (read_u64(ptr) * K2);
            ptr += 8;
            v[1] = rotate_right(v[1], 33) * K3;
            v[0] = v[0] ^ (rotate_right((v[0] * K2) + v[1], 45) * K1);
            v[1] = v[1] ^ (rotate_right((v[1] * K3) + v[0], 45) * K0);
        }

        if (end - ptr) >= 8 {
            v[0] = v[0] + (read_u64(ptr) * K2);
            ptr += 8;
            v[0] = rotate_right(v[0], 33) * K3;
            v[0] = v[0] ^ (rotate_right((v[0] * K2) + v[1], 27) * K1);
        }

        if (end - ptr) >= 4 {
            v[1] = v[1] + (read_u32(ptr) * K2);
            ptr += 4;
            v[1] = rotate_right(v[1], 33) * K3;
            v[1] = v[1] ^ (rotate_right((v[1] * K3) + v[0], 46) * K0);
        }

        if (end - ptr) >= 2 {
            v[0] = v[0] + (read_u16(ptr) * K2);
            ptr += 2;
            v[0] = rotate_right(v[0], 33) * K3;
            v[0] = v[0] ^ (rotate_right((v[0] * K2) + v[1], 22) * K1);
        }

        if (end - ptr) >= 1 {
            v[1] = v[1] + (read_u8(ptr) * K2);
            v[1] = rotate_right(v[1], 33) * K3;
            v[1] = v[1] ^ (rotate_right((v[1] * K3) + v[0], 58) * K0);
        }

        v[0] = v[0] + (rotate_right((v[0] * K0) + v[1], 13));
        v[1] = v[1] + (rotate_right((v[1] * K1) + v[0], 37));
        v[0] = v[0] + (rotate_right((v[0] * K2) + v[1], 13));
        v[1] = v[1] + (rotate_right((v[1] * K3) + v[0], 37));

        (v[0].0, v[1].0)
    }
}

impl Hasher for MetroHash128 {
    #[inline]
    fn write(&mut self, bytes: &[u8]) {
        let mut ptr = bytes.as_ptr() as usize;
        let end = ptr + bytes.len();
        // input buffer may be partially filled
        if self.bytes % 32 != 0 {
            let mut fill = 32 - (self.bytes % 32);
            if fill > bytes.len() {
                fill = bytes.len();
            }

            unsafe {
                copy_32(
                    ptr as *const u8,
                    (&mut self.b[0] as *mut _ as *mut u8).offset((self.bytes % 32) as isize),
                    fill,
                );
            }
            ptr += fill;
            self.bytes += fill;

            // input buffer is still partially filled
            if self.bytes % 32 != 0 {
                return;
            }

            // process full input buffer
            self.v[0] = self.v[0] + (read_u64(&self.b[0] as *const _ as usize) * K0);
            self.v[0] = rotate_right(self.v[0], 29) + self.v[2];
            self.v[1] = self.v[1] + (read_u64(&self.b[1] as *const _ as usize) * K1);
            self.v[1] = rotate_right(self.v[1], 29) + self.v[3];
            self.v[2] = self.v[2] + (read_u64(&self.b[2] as *const _ as usize) * K2);
            self.v[2] = rotate_right(self.v[2], 29) + self.v[0];
            self.v[3] = self.v[3] + (read_u64(&self.b[3] as *const _ as usize) * K3);
            self.v[3] = rotate_right(self.v[3], 29) + self.v[1];
        }

        // bulk update
        self.bytes += end - ptr;
        while ptr + 32 <= end {
            // process directly from the source, bypassing the input buffer
            self.v[0] = self.v[0] + (read_u64(ptr) * K0);
            ptr += 8;
            self.v[0] = rotate_right(self.v[0], 29) + self.v[2];
            self.v[1] = self.v[1] + (read_u64(ptr) * K1);
            ptr += 8;
            self.v[1] = rotate_right(self.v[1], 29) + self.v[3];
            self.v[2] = self.v[2] + (read_u64(ptr) * K2);
            ptr += 8;
            self.v[2] = rotate_right(self.v[2], 29) + self.v[0];
            self.v[3] = self.v[3] + (read_u64(ptr) * K3);
            ptr += 8;
            self.v[3] = rotate_right(self.v[3], 29) + self.v[1];
        }

        // store remaining self.bytes in input buffer
        if ptr < end {
            unsafe {
                copy_32(
                    ptr as *const u8,
                    &mut self.b[0] as *mut _ as *mut u8,
                    end - ptr,
                );
            }
        }
    }

    #[inline]
    fn finish(&self) -> u64 {
        self.finish128().0
    }
}
<commit_msg>Expose MetroHash128::finish128 function<commit_after>use std::num::Wrapping;
use std::mem;
use std::hash::Hasher;
use utils::*;

const K0: Wrapping<u64> = Wrapping(0xC83A91E1);
const K1: Wrapping<u64> = Wrapping(0x8648DBDB);
const K2: Wrapping<u64> = Wrapping(0x7BDEC03B);
const K3: Wrapping<u64> = Wrapping(0x2F5870A5);

pub struct MetroHash128 {
    v: [Wrapping<u64>; 4],
    b: [u64; 4],
    bytes: usize,
}

impl Default for MetroHash128 {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl MetroHash128 {
    #[inline]
    pub fn new() -> MetroHash128 {
        Self::with_seed(0)
    }

    #[inline]
    pub fn with_seed(seed: u64) -> MetroHash128 {
        let seed = Wrapping(seed);
        MetroHash128 {
            b: unsafe { mem::uninitialized() },
            v: [
                (seed - K0) * K3,
                (seed + K1) * K2,
                (seed + K0) * K2,
                (seed - K1) * K3,
            ],
            bytes: 0,
        }
    }

    #[inline]
    pub fn finish128(&self) -> (u64, u64) {
        // copy internal state
        let mut v = self.v;

        // finalize bulk loop, if used
        if self.bytes >= 32 {
            v[2] = v[2] ^ (rotate_right(((v[0] + v[3]) * K0) + v[1], 21) * K1);
            v[3] = v[3] ^ (rotate_right(((v[1] + v[2]) * K1) + v[0], 21) * K0);
            v[0] = v[0] ^ (rotate_right(((v[0] + v[2]) * K0) + v[3], 21) * K1);
            v[1] = v[1] ^ (rotate_right(((v[1] + v[3]) * K1) + v[2], 21) * K0);
        }

        // process any self.bytes remaining in the input buffer
        let mut ptr = &self.b as *const _ as usize;
        let end = ptr + self.bytes % 32;

        if (end - ptr) >= 16 {
            v[0] = v[0] + (read_u64(ptr) * K2);
            ptr += 8;
            v[0] = rotate_right(v[0], 33) * K3;
            v[1] = v[1] + (read_u64(ptr) * K2);
            ptr += 8;
            v[1] = rotate_right(v[1], 33) * K3;
            v[0] = v[0] ^ (rotate_right((v[0] * K2) + v[1], 45) * K1);
            v[1] = v[1] ^ (rotate_right((v[1] * K3) + v[0], 45) * K0);
        }

        if (end - ptr) >= 8 {
            v[0] = v[0] + (read_u64(ptr) * K2);
            ptr += 8;
            v[0] = rotate_right(v[0], 33) * K3;
            v[0] = v[0] ^ (rotate_right((v[0] * K2) + v[1], 27) * K1);
        }

        if (end - ptr) >= 4 {
            v[1] = v[1] + (read_u32(ptr) * K2);
            ptr += 4;
            v[1] = rotate_right(v[1], 33) * K3;
            v[1] = v[1] ^ (rotate_right((v[1] * K3) + v[0], 46) * K0);
        }

        if (end - ptr) >= 2 {
            v[0] = v[0] + (read_u16(ptr) * K2);
            ptr += 2;
            v[0] = rotate_right(v[0], 33) * K3;
            v[0] = v[0] ^ (rotate_right((v[0] * K2) + v[1], 22) * K1);
        }

        if (end - ptr) >= 1 {
            v[1] = v[1] + (read_u8(ptr) * K2);
            v[1] = rotate_right(v[1], 33) * K3;
            v[1] = v[1] ^ (rotate_right((v[1] * K3) + v[0], 58) * K0);
        }

        v[0] = v[0] + (rotate_right((v[0] * K0) + v[1], 13));
        v[1] = v[1] + (rotate_right((v[1] * K1) + v[0], 37));
        v[0] = v[0] + (rotate_right((v[0] * K2) + v[1], 13));
        v[1] = v[1] + (rotate_right((v[1] * K3) + v[0], 37));

        (v[0].0, v[1].0)
    }
}

impl Hasher for MetroHash128 {
    #[inline]
    fn write(&mut self, bytes: &[u8]) {
        let mut ptr = bytes.as_ptr() as usize;
        let end = ptr + bytes.len();
        // input buffer may be partially filled
        if self.bytes % 32 != 0 {
            let mut fill = 32 - (self.bytes % 32);
            if fill > bytes.len() {
                fill = bytes.len();
            }

            unsafe {
                copy_32(
                    ptr as *const u8,
                    (&mut self.b[0] as *mut _ as *mut u8).offset((self.bytes % 32) as isize),
                    fill,
                );
            }
            ptr += fill;
            self.bytes += fill;

            // input buffer is still partially filled
            if self.bytes % 32 != 0 {
                return;
            }

            // process full input buffer
            self.v[0] = self.v[0] + (read_u64(&self.b[0] as *const _ as usize) * K0);
            self.v[0] = rotate_right(self.v[0], 29) + self.v[2];
            self.v[1] = self.v[1] + (read_u64(&self.b[1] as *const _ as usize) * K1);
            self.v[1] = rotate_right(self.v[1], 29) + self.v[3];
            self.v[2] = self.v[2] + (read_u64(&self.b[2] as *const _ as usize) * K2);
            self.v[2] = rotate_right(self.v[2], 29) + self.v[0];
            self.v[3] = self.v[3] + (read_u64(&self.b[3] as *const _ as usize) * K3);
            self.v[3] = rotate_right(self.v[3], 29) + self.v[1];
        }

        // bulk update
        self.bytes += end - ptr;
        while ptr + 32 <= end {
            // process directly from the source, bypassing the input buffer
            self.v[0] = self.v[0] + (read_u64(ptr) * K0);
            ptr += 8;
            self.v[0] = rotate_right(self.v[0], 29) + self.v[2];
            self.v[1] = self.v[1] + (read_u64(ptr) * K1);
            ptr += 8;
            self.v[1] = rotate_right(self.v[1], 29) + self.v[3];
            self.v[2] = self.v[2] + (read_u64(ptr) * K2);
            ptr += 8;
            self.v[2] = rotate_right(self.v[2], 29) + self.v[0];
            self.v[3] = self.v[3] + (read_u64(ptr) * K3);
            ptr += 8;
            self.v[3] = rotate_right(self.v[3], 29) + self.v[1];
        }

        // store remaining self.bytes in input buffer
        if ptr < end {
            unsafe {
                copy_32(
                    ptr as *const u8,
                    &mut self.b[0] as *mut _ as *mut u8,
                    end - ptr,
                );
            }
        }
    }

    #[inline]
    fn finish(&self) -> u64 {
        self.finish128().0
    }
}
<|endoftext|>


<commit_before>/* diosix hypervisor physical memory management
 *
 * allocate/free contiguous regions of physical memory.
 * these regions are categorized into two groups,
 * depending on the region size.
 *
 * large: >= PHYS_RAM_LARGE_REGION_MIN_SIZE
 * large regions are sized in multiples of
 * PHYS_RAM_LARGE_REGION_MIN_SIZE and are allocated
 * from the top of free region blocks, descending.
 * these are aimed at large blocks of contiguous
 * memory for guest supervisor OSes.
 *
 * small: < PHYS_RAM_LARGE_REGION_MIN_SIZE
 * small regions are sized in multiples of
 * PHYS_RAM_SMALL_REGION_MIN_SIZE and are allocated
 * from the bottom of free region blocks, ascending
 * these are aimed at small blocks of memory
 * for the hypervisor's private per-CPU heaps.
 *
 * this arrangement is to avoid large and small
 * allocations fragmenting free region blocks
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use platform;
use spin::Mutex;
use alloc::vec::Vec;
use platform::physmem::{PhysMemBase, PhysMemEnd, PhysMemSize, AccessPermissions, validate_ram};
use super::error::Cause;
use super::hardware;

/* return the physical RAM region covering the entirely of the boot capsule's supervisor */
pub fn boot_supervisor() -> Region
{
    let (base, end) = platform::physmem::boot_supervisor();
    Region { base: base, size: end - base }
}

/* to avoid fragmentation, round up physical memory region allocations into multiples of these totals,
depending on the region type. this only applies when creating regions with alloc_region() */
const PHYS_RAM_LARGE_REGION_MIN_SIZE: PhysMemSize = 64 * 1024 * 1024; /* 64MB ought to be enough for anyone */
const PHYS_RAM_SMALL_REGION_MIN_SIZE: PhysMemSize =  1 * 1024 * 1024; /* smaller blocks are multiples of 1MB in size */

/* ensure large region bases are aligned down to multiples of this value
   note: region minimum size must be a non-zero multiple of region base alignment */
const PHYS_RAM_LARGE_REGION_ALIGNMENT: PhysMemSize = 4 * 1024 * 1024; /* 4MB alignment */

/* define whether to split a region N bytes from the top or from the bottom */
#[derive(Clone, Copy, Debug)]
pub enum RegionSplit
{
    FromBottom,
    FromTop
}

/* describe a physical memory region */
#[derive(Copy, Clone)]
pub struct Region
{
    base: PhysMemBase,
    size: PhysMemSize,
}

impl Region
{
    /* create a new region */
    pub fn new(base: PhysMemBase, size: PhysMemSize) -> Region
    {
        Region
        {
            base: base,
            size: size
        }
    }

    /* wipe the contents of a region with zeros */
    pub fn zero(&self)
    {
        let block = self.base;
        let step = core::mem::size_of::<usize>();
        hvdebug!("Clearing physical RAM region 0x{:x}, {} bytes total, {} bytes at a time. Please wait...", self.base, self. size, step);
        debughousekeeper!();

        for index in 0..(self.size / step)
        {
            unsafe
            {
                *((block + (index * step)) as *mut usize) = 0;
            }
        }
    }

    /* allow the currently running supervisor kernel to access this region of physical memory */
    pub fn grant_access(&self)
    {
        hvdebug!("Granting {:?} access to 0x{:x}, {} bytes", AccessPermissions::ReadWriteExecute, self.base, self.size);
        platform::physmem::protect(self.base, self.base + self.size, AccessPermissions::ReadWriteExecute);
    }

    /* return or change attributes */
    pub fn base(&self) -> PhysMemBase { self.base }
    pub fn end(&self) -> PhysMemEnd { self.base + self.size }
    pub fn size(&self) -> PhysMemSize { self.size }

    /* split the region into two portions, lower and upper, and return the two portions
    => count = split the region this number of bytes into the block
       measure_from = FromBottom: count is number of bytes from bottom of the block, ascending
                      FromTop: count is number of bytes from the top of the block, descending
    <= return two portions as regions, lower and upper, or a failure code */
    pub fn split(&self, count: PhysMemSize, measure_from: RegionSplit) -> Result<(Region, Region), Cause>
    {
        /* check the split mark is within bounds */
        if count > self.size
        {
            return Err(Cause::PhysRegionSplitOutOfBounds);
        }

        /* return (lower, upper) */
        Ok(match measure_from
        {
            RegionSplit::FromBottom =>
            (
                Region::new(self.base, count),
                Region::new(self.base + count, self.size - count)
            ),

            RegionSplit::FromTop =>
            (
                Region::new(self.base, self.size - count),
                Region::new(self.base + self.size - count, count)
            ),
        })
    }
}

/* gather up all physical RAM areas from which future capsule and heap physical
RAM allocations will be drawn into the REGIONS list. this list is built from
available, free physical RAM: it must *not* include any RAM areas already in use by
the hypervisor, boot supervisor image, peripherals, etc. the underlying
platform code needs to exclude those off-limits areas.

this list must also be sorted, by base address, lowest first. this is so that
adjoining regions can be merged into one. this list also contains only free
and available regions. if a region is in use, it must be removed from the list. */
lazy_static!
{
    /* acquire REGIONS lock before accessing any physical RAM regions */
    static ref REGIONS: Mutex<SortedRegions> = Mutex::new(SortedRegions::new());
}

/* implement a sorted list of regions */
struct SortedRegions
{
    regions: Vec<Region>
}

impl SortedRegions
{
    /* create an empty list */
    pub fn new() -> SortedRegions
    {
        SortedRegions
        {
            regions: Vec::new()
        }
    }

    /* find a region that has a size equal to or greater than the required size.
       if one is found, remove the region and return it. if one can't be found,
       return an error code. */
    pub fn find(&mut self, required_size: PhysMemSize) -> Result<Region, Cause>
    {
        for index in 0..self.regions.len()
        {
            if self.regions[index].size() >= required_size
            {
                /* remove from the list and return */
                return Ok(self.regions.remove(index));
            }
        }

        Err(Cause::PhysRegionNoMatch) /* can't find a region large enough */
    }

    /* insert a region into the list, sorted by base addresses, lowest first */
    pub fn insert(&mut self, to_insert: Region) -> Result<(), Cause>
    {
        /* ignore zero-size inserts */
        if to_insert.size() == 0
        {
            return Ok(())
        }

        for index in 0..self.regions.len()
        {
            if to_insert.end() <= self.regions[index].base()
            {
                self.regions.insert(index, to_insert);
                return Ok(())
            }

            /* check to make sure we're not adding a region that will collide with another */
            if to_insert.base() >= self.regions[index].base() && to_insert.base() < self.regions[index].end()
            {
                return Err(Cause::PhysRegionCollision);
            }
        }

        /* insert at the end: region greater than all others */
        self.regions.push(to_insert);
        Ok(())
    }

    /* merge all adjoining free regions. this requires the list to be sorted by base address ascending */
    pub fn merge(&mut self)
    {
        let mut cursor = 0;
        loop
        {
            /* prevent search from going out of bounds */
            if (cursor + 1) >= self.regions.len()
            {
                break;
            }

            if self.regions[cursor].end() == self.regions[cursor + 1].base()
            {
                /* absorb the next region's size into this region */
                self.regions[cursor].size = self.regions[cursor].size() + self.regions.remove(cursor + 1).size();
            }
            else
            {
                /* move onto next region */
                cursor = cursor + 1;
            }
        }
    }
}

/* initialize the physical memory system by registering all physical RAM available for use as allocatable regions */
pub fn init() -> Result<(), Cause>
{
    /* we need to know the CPU count so that any memory preallocated or reserved for the cores can be skipped */
    let nr_cpu_cores = match hardware::get_nr_cpu_cores()
    {
        Some(c) => c,
        None => return Err(Cause::PhysicalCoreCountUnknown)
    };

    /* the device tree defines chunks of memory that may or may not be entirely available for use */
    let chunks = match hardware::get_phys_ram_chunks()
    {
        Some(c) => c,
        None => return Err(Cause::PhysNoRAMFound)
    };

    /* iterate over the physical memory chunks... */
    let mut regions = REGIONS.lock();
    for chunk in chunks
    {
        /* ...and let validate_ram break each chunk in sections we can safely use */
        for section in validate_ram(nr_cpu_cores, chunk)
        {
            hvdebug!("Enabling RAM region 0x{:x}, size {} MB", section.base, section.size / 1024 / 1024);
            regions.insert(Region::new(section.base, section.size))?;
        }
    }

    Ok(())
}

/* perform housekeeping duties on idle physical CPU cores */
macro_rules! physmemhousekeeper
{
    () => ($crate::physmem::coalesce_regions());
}

pub fn coalesce_regions()
{
    REGIONS.lock().merge();
}

/* allocate a region of available physical memory for guest capsule or hypervisor heap use.
   capsules should use large regions, and the heap should use small, ideally.
   => size = number of bytes for the region, which will be rounded up to next multiple of:
     PHYS_RAM_LARGE_REGION_MIN_SIZE if the size >= PHYS_RAM_LARGE_REGION_MIN_SIZE (large type)
     PHYS_RAM_SMALL_REGION_MIN_SIZE if the size < PHYS_RAM_LARGE_REGION_MIN_SIZE (small type)

     note, large type regions will have a base address aligned down to PHYS_RAM_LARGE_REGION_ALIGNMENT
     this is so that guests that require 2MB or 4MB kernel alignment (eg RV64GC Linux) work as expected
     see: https://patchwork.kernel.org/patch/10868465/
     this code assumes the top of physically available RAM is aligned to PHYS_RAM_LARGE_REGION_ALIGNMENT

   <= Region structure for the space, or an error code */
pub fn alloc_region(size: PhysMemSize) -> Result<Region, Cause>
{
    /* determine where to split the free region block, and the region type */
    let (split_from, region_multiple) = if size >= PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        (RegionSplit::FromTop, PHYS_RAM_LARGE_REGION_MIN_SIZE)
    }
    else
    {
        (RegionSplit::FromBottom, PHYS_RAM_SMALL_REGION_MIN_SIZE)
    };

    /* round up to a multiple of the minimum size of a region type to avoid fragmentation */
    let adjusted_size = match size % region_multiple
    {
        0 => size,
        d => (size - d) + region_multiple
    };

    let mut regions = REGIONS.lock();
    match regions.find(adjusted_size) // find will remove found region from free list if successful
    {
        Ok(found) =>
        {
            /* split the found region into two parts: one portion for the newly
            allocated region, and the remaining portion is returned to the free list.
            adjusted_size defines whwre in the region the split point occurs.
            split_from defines whether adjusted_size is measured from the top or
            bottom of the region block */
            match (found.split(adjusted_size, split_from), split_from)
            {
                /* split so that the lower portion is allocated, and the upper portion is returned to the free list */
                (Ok((lower, upper)), RegionSplit::FromBottom) =>
                {
                    regions.insert(upper)?;
                    lower.zero();
                    Ok(lower)
                },

                /* split so that the upper portion is allocated, and the lower portion is returned to the free list */
                (Ok((lower, upper)), RegionSplit::FromTop) =>
                {
                    /* bring the base of the upper portion down to alignment mark */
                    let aligned_upper = match upper.base % PHYS_RAM_LARGE_REGION_ALIGNMENT
                    {
                        0 => Region::new(upper.base, upper.size),
                        d => Region::new(upper.base - d, upper.size + d)
                    };

                    /* fail out if upper portion crashes through the lower portion base after alignment */
                    if lower.size < aligned_upper.size - upper.size
                    {
                        return Err(Cause::PhysRegionRegionAlignmentFailure)
                    }

                    /* adjust the size of the lower portion if the upper portion was aligned down */
                    let adjusted_lower = match aligned_upper.size - upper.size
                    {
                        0 => lower,
                        d => Region::new(lower.base, lower.size - d)
                    };

                    regions.insert(adjusted_lower)?;
                    aligned_upper.zero();
                    Ok(aligned_upper)
                },

                (Err(e), _) => Err(e)
            }
        },
        Err(_) => Err(Cause::PhysNotEnoughFreeRAM)
    }
}

/* deallocate a region so that its physical RAM can be reallocated.
   only accept samll regions that are multiples of PHYS_RAM_SMALL_REGION_MIN_SIZE
   and large regions that are multiples of PHYS_RAM_LARGE_REGION_MIN_SIZE
   => to_free = region to deallocate
   <= Ok for success, or an error code for failure */
pub fn dealloc_region(to_free: Region) -> Result<(), Cause>
{
    let size = to_free.size();

    /* police the size of the region */
    if size < PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        if size % PHYS_RAM_SMALL_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionSmallNotMultiple);
        }
    }
    else
    {
        if size % PHYS_RAM_LARGE_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionLargeNotMultiple);
        }
    }

    REGIONS.lock().insert(to_free)
}
<commit_msg>Zero any bytes left over at the end of a region<commit_after>/* diosix hypervisor physical memory management
 *
 * allocate/free contiguous regions of physical memory.
 * these regions are categorized into two groups,
 * depending on the region size.
 *
 * large: >= PHYS_RAM_LARGE_REGION_MIN_SIZE
 * large regions are sized in multiples of
 * PHYS_RAM_LARGE_REGION_MIN_SIZE and are allocated
 * from the top of free region blocks, descending.
 * these are aimed at large blocks of contiguous
 * memory for guest supervisor OSes.
 *
 * small: < PHYS_RAM_LARGE_REGION_MIN_SIZE
 * small regions are sized in multiples of
 * PHYS_RAM_SMALL_REGION_MIN_SIZE and are allocated
 * from the bottom of free region blocks, ascending
 * these are aimed at small blocks of memory
 * for the hypervisor's private per-CPU heaps.
 *
 * this arrangement is to avoid large and small
 * allocations fragmenting free region blocks
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use platform;
use spin::Mutex;
use alloc::vec::Vec;
use platform::physmem::{PhysMemBase, PhysMemEnd, PhysMemSize, AccessPermissions, validate_ram};
use super::error::Cause;
use super::hardware;

/* return the physical RAM region covering the entirely of the boot capsule's supervisor */
pub fn boot_supervisor() -> Region
{
    let (base, end) = platform::physmem::boot_supervisor();
    Region { base: base, size: end - base }
}

/* to avoid fragmentation, round up physical memory region allocations into multiples of these totals,
depending on the region type. this only applies when creating regions with alloc_region() */
const PHYS_RAM_LARGE_REGION_MIN_SIZE: PhysMemSize = 64 * 1024 * 1024; /* 64MB ought to be enough for anyone */
const PHYS_RAM_SMALL_REGION_MIN_SIZE: PhysMemSize =  1 * 1024 * 1024; /* smaller blocks are multiples of 1MB in size */

/* ensure large region bases are aligned down to multiples of this value
   note: region minimum size must be a non-zero multiple of region base alignment */
const PHYS_RAM_LARGE_REGION_ALIGNMENT: PhysMemSize = 4 * 1024 * 1024; /* 4MB alignment */

/* define whether to split a region N bytes from the top or from the bottom */
#[derive(Clone, Copy, Debug)]
pub enum RegionSplit
{
    FromBottom,
    FromTop
}

/* describe a physical memory region */
#[derive(Copy, Clone)]
pub struct Region
{
    base: PhysMemBase,
    size: PhysMemSize,
}

impl Region
{
    /* create a new region */
    pub fn new(base: PhysMemBase, size: PhysMemSize) -> Region
    {
        Region
        {
            base: base,
            size: size
        }
    }

    /* wipe the contents of a region with zeros */
    pub fn zero(&self)
    {
        let block = self.base;
        let step = core::mem::size_of::<usize>();
        hvdebug!("Clearing physical RAM region 0x{:x}, {} bytes total, {} bytes at a time. Please wait...", self.base, self. size, step);
        debughousekeeper!();

        /* deal with the whole number of step bytes */
        for index in 0..(self.size / step) as usize
        {
            unsafe
            {
                *((block + (index * step)) as *mut usize) = 0;
            }
        }

        /* finish off any leftover bytes */
        let diff = self.size % step;
        let end = self.size - diff;
        for index in 0..diff
        {
            unsafe
            {
                *((block + end + index) as *mut u8) = 0;
            }
        }
    }

    /* allow the currently running supervisor kernel to access this region of physical memory */
    pub fn grant_access(&self)
    {
        hvdebug!("Granting {:?} access to 0x{:x}, {} bytes", AccessPermissions::ReadWriteExecute, self.base, self.size);
        platform::physmem::protect(self.base, self.base + self.size, AccessPermissions::ReadWriteExecute);
    }

    /* return or change attributes */
    pub fn base(&self) -> PhysMemBase { self.base }
    pub fn end(&self) -> PhysMemEnd { self.base + self.size }
    pub fn size(&self) -> PhysMemSize { self.size }

    /* split the region into two portions, lower and upper, and return the two portions
    => count = split the region this number of bytes into the block
       measure_from = FromBottom: count is number of bytes from bottom of the block, ascending
                      FromTop: count is number of bytes from the top of the block, descending
    <= return two portions as regions, lower and upper, or a failure code */
    pub fn split(&self, count: PhysMemSize, measure_from: RegionSplit) -> Result<(Region, Region), Cause>
    {
        /* check the split mark is within bounds */
        if count > self.size
        {
            return Err(Cause::PhysRegionSplitOutOfBounds);
        }

        /* return (lower, upper) */
        Ok(match measure_from
        {
            RegionSplit::FromBottom =>
            (
                Region::new(self.base, count),
                Region::new(self.base + count, self.size - count)
            ),

            RegionSplit::FromTop =>
            (
                Region::new(self.base, self.size - count),
                Region::new(self.base + self.size - count, count)
            ),
        })
    }
}

/* gather up all physical RAM areas from which future capsule and heap physical
RAM allocations will be drawn into the REGIONS list. this list is built from
available, free physical RAM: it must *not* include any RAM areas already in use by
the hypervisor, boot supervisor image, peripherals, etc. the underlying
platform code needs to exclude those off-limits areas.

this list must also be sorted, by base address, lowest first. this is so that
adjoining regions can be merged into one. this list also contains only free
and available regions. if a region is in use, it must be removed from the list. */
lazy_static!
{
    /* acquire REGIONS lock before accessing any physical RAM regions */
    static ref REGIONS: Mutex<SortedRegions> = Mutex::new(SortedRegions::new());
}

/* implement a sorted list of regions */
struct SortedRegions
{
    regions: Vec<Region>
}

impl SortedRegions
{
    /* create an empty list */
    pub fn new() -> SortedRegions
    {
        SortedRegions
        {
            regions: Vec::new()
        }
    }

    /* find a region that has a size equal to or greater than the required size.
       if one is found, remove the region and return it. if one can't be found,
       return an error code. */
    pub fn find(&mut self, required_size: PhysMemSize) -> Result<Region, Cause>
    {
        for index in 0..self.regions.len()
        {
            if self.regions[index].size() >= required_size
            {
                /* remove from the list and return */
                return Ok(self.regions.remove(index));
            }
        }

        Err(Cause::PhysRegionNoMatch) /* can't find a region large enough */
    }

    /* insert a region into the list, sorted by base addresses, lowest first */
    pub fn insert(&mut self, to_insert: Region) -> Result<(), Cause>
    {
        /* ignore zero-size inserts */
        if to_insert.size() == 0
        {
            return Ok(())
        }

        for index in 0..self.regions.len()
        {
            if to_insert.end() <= self.regions[index].base()
            {
                self.regions.insert(index, to_insert);
                return Ok(())
            }

            /* check to make sure we're not adding a region that will collide with another */
            if to_insert.base() >= self.regions[index].base() && to_insert.base() < self.regions[index].end()
            {
                return Err(Cause::PhysRegionCollision);
            }
        }

        /* insert at the end: region greater than all others */
        self.regions.push(to_insert);
        Ok(())
    }

    /* merge all adjoining free regions. this requires the list to be sorted by base address ascending */
    pub fn merge(&mut self)
    {
        let mut cursor = 0;
        loop
        {
            /* prevent search from going out of bounds */
            if (cursor + 1) >= self.regions.len()
            {
                break;
            }

            if self.regions[cursor].end() == self.regions[cursor + 1].base()
            {
                /* absorb the next region's size into this region */
                self.regions[cursor].size = self.regions[cursor].size() + self.regions.remove(cursor + 1).size();
            }
            else
            {
                /* move onto next region */
                cursor = cursor + 1;
            }
        }
    }
}

/* initialize the physical memory system by registering all physical RAM available for use as allocatable regions */
pub fn init() -> Result<(), Cause>
{
    /* we need to know the CPU count so that any memory preallocated or reserved for the cores can be skipped */
    let nr_cpu_cores = match hardware::get_nr_cpu_cores()
    {
        Some(c) => c,
        None => return Err(Cause::PhysicalCoreCountUnknown)
    };

    /* the device tree defines chunks of memory that may or may not be entirely available for use */
    let chunks = match hardware::get_phys_ram_chunks()
    {
        Some(c) => c,
        None => return Err(Cause::PhysNoRAMFound)
    };

    /* iterate over the physical memory chunks... */
    let mut regions = REGIONS.lock();
    for chunk in chunks
    {
        /* ...and let validate_ram break each chunk in sections we can safely use */
        for section in validate_ram(nr_cpu_cores, chunk)
        {
            hvdebug!("Enabling RAM region 0x{:x}, size {} MB", section.base, section.size / 1024 / 1024);
            regions.insert(Region::new(section.base, section.size))?;
        }
    }

    Ok(())
}

/* perform housekeeping duties on idle physical CPU cores */
macro_rules! physmemhousekeeper
{
    () => ($crate::physmem::coalesce_regions());
}

pub fn coalesce_regions()
{
    REGIONS.lock().merge();
}

/* allocate a region of available physical memory for guest capsule or hypervisor heap use.
   capsules should use large regions, and the heap should use small, ideally.
   => size = number of bytes for the region, which will be rounded up to next multiple of:
     PHYS_RAM_LARGE_REGION_MIN_SIZE if the size >= PHYS_RAM_LARGE_REGION_MIN_SIZE (large type)
     PHYS_RAM_SMALL_REGION_MIN_SIZE if the size < PHYS_RAM_LARGE_REGION_MIN_SIZE (small type)

     note, large type regions will have a base address aligned down to PHYS_RAM_LARGE_REGION_ALIGNMENT
     this is so that guests that require 2MB or 4MB kernel alignment (eg RV64GC Linux) work as expected
     see: https://patchwork.kernel.org/patch/10868465/
     this code assumes the top of physically available RAM is aligned to PHYS_RAM_LARGE_REGION_ALIGNMENT

   <= Region structure for the space, or an error code */
pub fn alloc_region(size: PhysMemSize) -> Result<Region, Cause>
{
    /* determine where to split the free region block, and the region type */
    let (split_from, region_multiple) = if size >= PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        (RegionSplit::FromTop, PHYS_RAM_LARGE_REGION_MIN_SIZE)
    }
    else
    {
        (RegionSplit::FromBottom, PHYS_RAM_SMALL_REGION_MIN_SIZE)
    };

    /* round up to a multiple of the minimum size of a region type to avoid fragmentation */
    let adjusted_size = match size % region_multiple
    {
        0 => size,
        d => (size - d) + region_multiple
    };

    let mut regions = REGIONS.lock();
    match regions.find(adjusted_size) // find will remove found region from free list if successful
    {
        Ok(found) =>
        {
            /* split the found region into two parts: one portion for the newly
            allocated region, and the remaining portion is returned to the free list.
            adjusted_size defines whwre in the region the split point occurs.
            split_from defines whether adjusted_size is measured from the top or
            bottom of the region block */
            match (found.split(adjusted_size, split_from), split_from)
            {
                /* split so that the lower portion is allocated, and the upper portion is returned to the free list */
                (Ok((lower, upper)), RegionSplit::FromBottom) =>
                {
                    regions.insert(upper)?;
                    lower.zero();
                    Ok(lower)
                },

                /* split so that the upper portion is allocated, and the lower portion is returned to the free list */
                (Ok((lower, upper)), RegionSplit::FromTop) =>
                {
                    /* bring the base of the upper portion down to alignment mark */
                    let aligned_upper = match upper.base % PHYS_RAM_LARGE_REGION_ALIGNMENT
                    {
                        0 => Region::new(upper.base, upper.size),
                        d => Region::new(upper.base - d, upper.size + d)
                    };

                    /* fail out if upper portion crashes through the lower portion base after alignment */
                    if lower.size < aligned_upper.size - upper.size
                    {
                        return Err(Cause::PhysRegionRegionAlignmentFailure)
                    }

                    /* adjust the size of the lower portion if the upper portion was aligned down */
                    let adjusted_lower = match aligned_upper.size - upper.size
                    {
                        0 => lower,
                        d => Region::new(lower.base, lower.size - d)
                    };

                    regions.insert(adjusted_lower)?;
                    aligned_upper.zero();
                    Ok(aligned_upper)
                },

                (Err(e), _) => Err(e)
            }
        },
        Err(_) => Err(Cause::PhysNotEnoughFreeRAM)
    }
}

/* deallocate a region so that its physical RAM can be reallocated.
   only accept samll regions that are multiples of PHYS_RAM_SMALL_REGION_MIN_SIZE
   and large regions that are multiples of PHYS_RAM_LARGE_REGION_MIN_SIZE
   => to_free = region to deallocate
   <= Ok for success, or an error code for failure */
pub fn dealloc_region(to_free: Region) -> Result<(), Cause>
{
    let size = to_free.size();

    /* police the size of the region */
    if size < PHYS_RAM_LARGE_REGION_MIN_SIZE
    {
        if size % PHYS_RAM_SMALL_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionSmallNotMultiple);
        }
    }
    else
    {
        if size % PHYS_RAM_LARGE_REGION_MIN_SIZE != 0
        {
            return Err(Cause::PhysRegionLargeNotMultiple);
        }
    }

    REGIONS.lock().insert(to_free)
}
<|endoftext|>


<commit_before>//! The universe, where everything to do with server-wide functions resides

pub mod world;

/// The maximum number of worlds to be hosted per universe
///
/// This is to be tested and will be increased, decreased, or set to a user-defined value prior to release
pub const MAX_WORLDS: u8 = 16;<commit_msg>Add max players<commit_after>//! The universe, where everything to do with server-wide functions resides

pub mod world;

/// The maximum number of worlds to be hosted per universe
///
/// This is to be tested and will be increased, decreased, or set to a user-defined value prior to release
pub const MAX_WORLDS: u8 = 16;

/// The maximum number of players accepted by this universe
///
/// This is to be tested and will be set to a user-defined value (or perhaps automatically depending on hardware) prior to release
pub const MAX_PLAYERS: u16 = 200;<|endoftext|>


<commit_before>//! Objective-C Runtime bindings and wrapper for Rust.

#![crate_name = "objc"]
#![crate_type = "lib"]

#![feature(unsafe_destructor)]
#![warn(missing_docs)]

extern crate libc;
extern crate malloc_buf;

#[cfg(test)]
extern crate objc_test_utils;

pub use id::{Id, Owned, Ownership, Shared, ShareId};
pub use encode::{Encode, Encoding};
pub use message::{Message, MessageArguments};
pub use weak::WeakId;

#[macro_use]
mod macros;

pub mod runtime;
mod id;
pub mod block;
pub mod declare;
mod encode;
mod message;
mod weak;

#[cfg(test)]
mod test_utils;
<commit_msg>Removed unsafe_destructor feature.<commit_after>//! Objective-C Runtime bindings and wrapper for Rust.

#![crate_name = "objc"]
#![crate_type = "lib"]

#![warn(missing_docs)]

extern crate libc;
extern crate malloc_buf;

#[cfg(test)]
extern crate objc_test_utils;

pub use id::{Id, Owned, Ownership, Shared, ShareId};
pub use encode::{Encode, Encoding};
pub use message::{Message, MessageArguments};
pub use weak::WeakId;

#[macro_use]
mod macros;

pub mod runtime;
mod id;
pub mod block;
pub mod declare;
mod encode;
mod message;
mod weak;

#[cfg(test)]
mod test_utils;
<|endoftext|>


<commit_before>use time::{self, Tm as Time};
use fastup::{parse_for_first_node, Node};
use super::error_node;

#[derive(Debug, Copy, Clone, Default)]
pub struct Widget;

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            0 => node_from_time(time::now()),
            _ => error_node("(ccdd44: time) takes no argument"),
        }
    }
}

fn node_from_time(t: Time) -> Node {
    let source = format!("(000000: [ff8800: \\ {} ] \
                                   (88ff00: {:04}-{:02}-{:02}) \
                                   (55aaff: {:02}:{:02}:{:02}))",
                         stylised_weekday(t.tm_wday),
                         t.tm_year + 1900,
                         t.tm_mon + 1,
                         t.tm_mday,
                         t.tm_hour,
                         t.tm_min,
                         t.tm_sec);
    parse_for_first_node(&source).unwrap()
}

const WEEKDAYS: &'static str = "×12345·";
fn stylised_weekday(weekday: i32) -> char {
    WEEKDAYS.chars().nth(weekday as usize).unwrap()
}

#[test]
fn test_stylised_weekdays() {
    assert_eq!(stylised_weekday(0), '×');
    assert_eq!(stylised_weekday(1), '1');
    assert_eq!(stylised_weekday(2), '2');
    assert_eq!(stylised_weekday(3), '3');
    assert_eq!(stylised_weekday(4), '4');
    assert_eq!(stylised_weekday(5), '5');
    assert_eq!(stylised_weekday(6), '·');
}

#[test]
#[should_panic]
fn stylised_weekdays_panic_lower_bound() {
    stylised_weekday(-1);
}

#[test]
#[should_panic]
fn stylised_weekdays_panic_upper_bound() {
    stylised_weekday(7);
}

<commit_msg>shrink more space in date and time widget<commit_after>use time::{self, Tm as Time};
use fastup::{parse_for_first_node, Node};
use super::error_node;

#[derive(Debug, Copy, Clone, Default)]
pub struct Widget;

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            0 => node_from_time(time::now()),
            _ => error_node("(ccdd44: time) takes no argument"),
        }
    }
}

fn node_from_time(t: Time) -> Node {
    let source = format!("\
        (000000:\
            (88ff00:{:04}-{:02}-{:02})\
            (ff8800:▐)[ff8800:{}](ff8800:▌)\
            (33ccff:{:02}:{:02}:{:02})\
        )",
        t.tm_year + 1900,
        t.tm_mon + 1,
        t.tm_mday,
        stylised_weekday(t.tm_wday),
        t.tm_hour,
        t.tm_min,
        t.tm_sec);
    parse_for_first_node(&source).unwrap()
}

const WEEKDAYS: &'static str = "×12345·";
fn stylised_weekday(weekday: i32) -> char {
    WEEKDAYS.chars().nth(weekday as usize).unwrap()
}

#[test]
fn test_stylised_weekdays() {
    assert_eq!(stylised_weekday(0), '×');
    assert_eq!(stylised_weekday(1), '1');
    assert_eq!(stylised_weekday(2), '2');
    assert_eq!(stylised_weekday(3), '3');
    assert_eq!(stylised_weekday(4), '4');
    assert_eq!(stylised_weekday(5), '5');
    assert_eq!(stylised_weekday(6), '·');
}

#[test]
#[should_panic]
fn stylised_weekdays_panic_lower_bound() {
    stylised_weekday(-1);
}

#[test]
#[should_panic]
fn stylised_weekdays_panic_upper_bound() {
    stylised_weekday(7);
}

<|endoftext|>


<commit_before>#![feature(proc_macro, proc_macro_lib)]

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate asn1_cereal;
#[macro_use]
extern crate nom;

mod seq;

use proc_macro::TokenStream;
use syn::{MetaItem, Lit};
use nom::{space, digit};
use std::str::from_utf8;
use asn1_cereal::tag;

/// Parse a string as an ASN.1 tag definition.
///
/// IE: /\[ \(UNIVERSAL|APPLICATION|CONTEXT|PRIVATE|) [0-9]+ \]/
named!(parse_tag<quote::Tokens>, chain!(
  space? ~
  tag: delimited!(
    tag!("["),
    chain!(
      space? ~
      class: alt!(
        tag!("UNIVERSAL") |
        tag!("APPLICATION") |
        tag!("CONTEXT") |
        tag!("PRIVATE")
      )? ~
      space? ~
      tagnum: digit ~
      space? ~
      constructed: tag!("PRIMITIVE")? ~
      space?,
      || {
        let class = match class.map(|e| from_utf8(e).unwrap()).unwrap_or("CONTEXT") {
          "UNIVERSAL" =>   "::asn1_cereal::tag::Class::Universal",
          "APPLICATION" => "::asn1_cereal::tag::Class::Application",
          "CONTEXT" =>     "::asn1_cereal::tag::Class::ContextSpecific",
          "PRIVATE" =>     "::asn1_cereal::tag::Class::Private",
          class => panic!("Unknown class variant {}", class),
        };
        let mut class_tokens = quote::Tokens::new();
        class_tokens.append(class);
        let tagnum: tag::TagNum = from_utf8(tagnum).unwrap().parse().unwrap();
        let constructed = constructed.is_none();
        quote!(
          Some(::asn1_cereal::tag::Tag {
            class: #class_tokens,
            tagnum: #tagnum,
            constructed: #constructed,
          })
        )
      }
    ),
    tag!("]")
  ),
  || tag
));

#[proc_macro_derive(Asn1Info)]
pub fn asn1_info(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).unwrap();

  // Tag and asn1 type for this rust type.
  let mut tag = quote!(None);
  let mut asn1_type = ast.ident.as_ref().to_owned();

  // Parse attributes.
  for attr in &ast.attrs {
    if attr.name() != "asn1" { continue; }
    if let MetaItem::List(_, ref items) = attr.value {
      for item in items {
        match *item {
          MetaItem::NameValue(ref _name, Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "tag" => tag = parse_tag(value.as_bytes()).unwrap().1,
              "asn1_type" => asn1_type = value.clone(),
              _ => (),
            };
          }
          _ => (),
        };
      }
    }
  }

  // Used in the quasi-quotation below as `#name`
  let name = &ast.ident;

  // Helper is provided for handling complex generic types correctly and effortlessly
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  let expanded = quote! {
    // Preserve the input struct unmodified
    #ast

    // The generated impl
    impl #impl_generics ::asn1_cereal::Asn1Info for #name #ty_generics #where_clause {
      fn asn1_tag() -> Option<::asn1_cereal::tag::Tag> {
        #tag
      }

      fn asn1_type() -> ::asn1_cereal::tag::Type {
        #asn1_type.to_owned()
      }
    }
  };
  expanded.to_string().parse().unwrap()
}
<commit_msg>Move parse_tag into a separate module<commit_after>#![feature(proc_macro, proc_macro_lib)]

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate asn1_cereal;
#[macro_use]
extern crate nom;

mod seq;
mod tag;

use proc_macro::TokenStream;
use syn::{MetaItem, Lit};
use std::str::from_utf8;

use tag::parse_tag;

#[proc_macro_derive(Asn1Info)]
pub fn asn1_info(input: TokenStream) -> TokenStream {
  let source = input.to_string();
  let ast = syn::parse_macro_input(&source).unwrap();

  // Tag and asn1 type for this rust type.
  let mut tag = quote!(None);
  let mut asn1_type = ast.ident.as_ref().to_owned();

  // Parse attributes.
  for attr in &ast.attrs {
    if attr.name() != "asn1" { continue; }
    if let MetaItem::List(_, ref items) = attr.value {
      for item in items {
        match *item {
          MetaItem::NameValue(ref _name, Lit::Str(ref value, _)) => {
            let name: &str = _name.as_ref();
            match name {
              "tag" => tag = parse_tag(value.as_bytes()).unwrap().1,
              "asn1_type" => asn1_type = value.clone(),
              _ => (),
            };
          }
          _ => (),
        };
      }
    }
  }

  // Used in the quasi-quotation below as `#name`
  let name = &ast.ident;

  // Helper is provided for handling complex generic types correctly and effortlessly
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  let expanded = quote! {
    // Preserve the input struct unmodified
    #ast

    // The generated impl
    impl #impl_generics ::asn1_cereal::Asn1Info for #name #ty_generics #where_clause {
      fn asn1_tag() -> Option<::asn1_cereal::tag::Tag> {
        #tag
      }

      fn asn1_type() -> ::asn1_cereal::tag::Type {
        #asn1_type.to_owned()
      }
    }
  };
  expanded.to_string().parse().unwrap()
}
<|endoftext|>


<commit_before>#[allow(dead_code)];

extern mod extra;
extern mod arena;

use std::os;
use expr::{Expr, Value};
use std::str;
use std::io::{stdout,stderr};
use std::io::fs::File;

mod session;
mod expr;
mod grammar;
mod bitv;
mod ast;
mod resolve;
mod clock_event;

fn main() {
	let mut sess = session::Session::new();

	let args = os::args();
	let source_utf8 = File::open(&Path::new(args[1])).read_to_end();
	let source = str::from_utf8(source_utf8);
	let module = grammar::module(source.unwrap());

	println!("ast: {:?}\n", module);

	let module = module.unwrap();

	let mut prelude = resolve::Scope::new();
	let timer = clock_event::timer();
	prelude.names.insert(~"time", resolve::EventItem(timer));

	let mut ctx = session::Context::new(&sess);

	let mut modscope = resolve::resolve_module(&mut ctx, &prelude, &module);
	println!("modscope: {:?}\n", modscope);

	let main = match modscope.get("main").unwrap() {
		resolve::EventItem(s) => s,
		_ => fail!("Main is not an event"),
	};

	let w = clock_event::wire_config();
	let event = main.resolve_call(&mut ctx, &[resolve::EntityItem(&w)], None);
	println!("main: {:?}\n", event);

	resolve::print_step_tree(&event, 0);
}

fn E(s: &str) -> Expr {
	grammar::valexpr(s).unwrap()
}

fn S(s: &str) -> Value {
	expr::SymbolValue(s.to_owned())
}

#[test]
fn test_parse_number() {
	let e = E("55");
	assert_eq!(e.get_type(), expr::NumberType);
	assert_eq!(e.const_down(), Some(expr::NumberValue(55.)));
	assert_eq!(e.const_up(&expr::NumberValue(55.)), true);
	assert_eq!(e.const_up(&expr::NumberValue(99.)), false);
	assert_eq!(e.const_down().unwrap().to_str(), ~"55");
}

#[test]
fn test_parse_symbol() {
	let e = E("#foo");
	assert_eq!(e.get_type(), expr::SymbolType);
	assert_eq!(e.const_down(), Some(S("foo")));
	assert_eq!(e.const_up(&S("foo")), true);
	assert_eq!(e.const_up(&S("bar")), false);
	assert_eq!(e.const_down().unwrap().to_str(), ~"$foo");
}

#[test]
fn test_add() {
	let e = E("2 + 3");
	assert_eq!(e.get_type(), expr::NumberType);
	assert_eq!(e.const_down(), Some(expr::NumberValue(5.)));
}

#[test]
fn test_add_ignore() {
	let e = E("2 + ignore");
	assert_eq!(e.get_type(), expr::NumberType);
	assert_eq!(e.const_down(), None);
}

#[test]
fn test_add_wrongtype() {
	let e = E("2 + #test");
	assert_eq!(e.get_type(), expr::InvalidType);
	assert_eq!(e.const_down(), None);
}

#[test]
fn test_bit_literals() {
	assert_eq!(E("'101").const_down().unwrap().to_str(), ~"'b101");
	assert_eq!(E("'b101").const_down().unwrap().to_str(), ~"'b101");
	assert_eq!(E("'hA5").const_down().unwrap().to_str(), ~"'b10100101");
}

#[test]
fn test_choice_expr() {
	let e = E("(#bar)[#foo=#a, #bar=#b, #baz=#c]");
	assert_eq!(e.const_down(), Some(S("b")));
	assert_eq!(e.const_up(&S("c")), false);
	assert_eq!(e.const_up(&S("x")), false);
	assert_eq!(e.const_up(&S("b")), true);
}

#[test]
fn test_flip() {
	let e = E("#l!#r");
	assert_eq!(e.const_down(), Some(S("l")));
	assert_eq!(e.const_up(&S("l")), false);
	assert_eq!(e.const_up(&S("r")), true);
}

#[test]
fn test_range_expr() {
	let e = E("5..10");
	assert_eq!(e.const_down(), None);

	// should fail if the value is below the range
	assert_eq!(e.const_up(&expr::NumberValue(2.)), false);

	// should be bottom-inclusive
	assert_eq!(e.const_up(&expr::NumberValue(5.)), true);

	// should succeed if the value is inside the range
	assert_eq!(e.const_up(&expr::NumberValue(7.)), true);

	// should be top-exclusive
	assert_eq!(e.const_up(&expr::NumberValue(10.)), false);

	// should fail if the value is above the range
	assert_eq!(e.const_up(&expr::NumberValue(12.)), false);

	// should fail if the value is not a number
	assert_eq!(e.const_up(&S("foo")), false);
}

#[test]
fn test_concat() {
	let e = E("['101, '0010]");
	assert_eq!(e.get_type(), expr::BitsType(7));
	assert_eq!(e.const_down().unwrap().to_str(), ~"'b1010010");
	assert_eq!(e.const_up(&E("'1010010").const_down().unwrap()), true);
	assert_eq!(e.const_up(&E("'1010011").const_down().unwrap()), false);
}
<commit_msg>rust: fix for IO condition removal<commit_after>#[allow(dead_code)];

extern mod extra;
extern mod arena;

use std::os;
use expr::{Expr, Value};
use std::str;
use std::io::{stdout,stderr};
use std::io::fs::File;

mod session;
mod expr;
mod grammar;
mod bitv;
mod ast;
mod resolve;
mod clock_event;

fn main() {
	let mut sess = session::Session::new();

	let args = os::args();
	let source_utf8 = File::open(&Path::new(args[1])).read_to_end().unwrap();
	let source = str::from_utf8(source_utf8);
	let module = grammar::module(source.unwrap());

	println!("ast: {:?}\n", module);

	let module = module.unwrap();

	let mut prelude = resolve::Scope::new();
	let timer = clock_event::timer();
	prelude.names.insert(~"time", resolve::EventItem(timer));

	let mut ctx = session::Context::new(&sess);

	let mut modscope = resolve::resolve_module(&mut ctx, &prelude, &module);
	println!("modscope: {:?}\n", modscope);

	let main = match modscope.get("main").unwrap() {
		resolve::EventItem(s) => s,
		_ => fail!("Main is not an event"),
	};

	let w = clock_event::wire_config();
	let event = main.resolve_call(&mut ctx, &[resolve::EntityItem(&w)], None);
	println!("main: {:?}\n", event);

	resolve::print_step_tree(&event, 0);
}

fn E(s: &str) -> Expr {
	grammar::valexpr(s).unwrap()
}

fn S(s: &str) -> Value {
	expr::SymbolValue(s.to_owned())
}

#[test]
fn test_parse_number() {
	let e = E("55");
	assert_eq!(e.get_type(), expr::NumberType);
	assert_eq!(e.const_down(), Some(expr::NumberValue(55.)));
	assert_eq!(e.const_up(&expr::NumberValue(55.)), true);
	assert_eq!(e.const_up(&expr::NumberValue(99.)), false);
	assert_eq!(e.const_down().unwrap().to_str(), ~"55");
}

#[test]
fn test_parse_symbol() {
	let e = E("#foo");
	assert_eq!(e.get_type(), expr::SymbolType);
	assert_eq!(e.const_down(), Some(S("foo")));
	assert_eq!(e.const_up(&S("foo")), true);
	assert_eq!(e.const_up(&S("bar")), false);
	assert_eq!(e.const_down().unwrap().to_str(), ~"$foo");
}

#[test]
fn test_add() {
	let e = E("2 + 3");
	assert_eq!(e.get_type(), expr::NumberType);
	assert_eq!(e.const_down(), Some(expr::NumberValue(5.)));
}

#[test]
fn test_add_ignore() {
	let e = E("2 + ignore");
	assert_eq!(e.get_type(), expr::NumberType);
	assert_eq!(e.const_down(), None);
}

#[test]
fn test_add_wrongtype() {
	let e = E("2 + #test");
	assert_eq!(e.get_type(), expr::InvalidType);
	assert_eq!(e.const_down(), None);
}

#[test]
fn test_bit_literals() {
	assert_eq!(E("'101").const_down().unwrap().to_str(), ~"'b101");
	assert_eq!(E("'b101").const_down().unwrap().to_str(), ~"'b101");
	assert_eq!(E("'hA5").const_down().unwrap().to_str(), ~"'b10100101");
}

#[test]
fn test_choice_expr() {
	let e = E("(#bar)[#foo=#a, #bar=#b, #baz=#c]");
	assert_eq!(e.const_down(), Some(S("b")));
	assert_eq!(e.const_up(&S("c")), false);
	assert_eq!(e.const_up(&S("x")), false);
	assert_eq!(e.const_up(&S("b")), true);
}

#[test]
fn test_flip() {
	let e = E("#l!#r");
	assert_eq!(e.const_down(), Some(S("l")));
	assert_eq!(e.const_up(&S("l")), false);
	assert_eq!(e.const_up(&S("r")), true);
}

#[test]
fn test_range_expr() {
	let e = E("5..10");
	assert_eq!(e.const_down(), None);

	// should fail if the value is below the range
	assert_eq!(e.const_up(&expr::NumberValue(2.)), false);

	// should be bottom-inclusive
	assert_eq!(e.const_up(&expr::NumberValue(5.)), true);

	// should succeed if the value is inside the range
	assert_eq!(e.const_up(&expr::NumberValue(7.)), true);

	// should be top-exclusive
	assert_eq!(e.const_up(&expr::NumberValue(10.)), false);

	// should fail if the value is above the range
	assert_eq!(e.const_up(&expr::NumberValue(12.)), false);

	// should fail if the value is not a number
	assert_eq!(e.const_up(&S("foo")), false);
}

#[test]
fn test_concat() {
	let e = E("['101, '0010]");
	assert_eq!(e.get_type(), expr::BitsType(7));
	assert_eq!(e.const_down().unwrap().to_str(), ~"'b1010010");
	assert_eq!(e.const_up(&E("'1010010").const_down().unwrap()), true);
	assert_eq!(e.const_up(&E("'1010011").const_down().unwrap()), false);
}
<|endoftext|>