<commit_before>#![macro_escape]

#[macro_export]
macro_rules! werr(
    ($($arg:tt)*) => (
        if let Err(err) = std::io::stderr().write_str(&*format!($($arg)*)) {
            panic!("{}", err);
        }
    )
)

/// Creates a vector of string-slices from a vector of strings.
///
/// The slice vector lives as long as the original vector.
pub fn slicify<'a, Sized? S: Sized+Str>(vec: &'a Vec<S>) -> Vec<&'a str> {
    map_collect(vec.iter(), |a| a.as_slice())
}

/// Creates a vector of strings from a slice of string-slices.
pub fn unslicify<'a, Sized? S: Sized+Str>(slices: &'a[S]) -> Vec<String> {
    map_collect(slices.iter(), |a: &S| a.as_slice().to_string())
}

/// Performs a map and collects the results.
pub fn map_collect<A, B, I, F, C>(iter: I, f: C) -> F
  where I: Iterator<A>,
        F: FromIterator<B>,
        C: FnMut(A) -> B {
    iter.map(f).collect::<F>()
}<commit_msg>Add doctests for [un]slicify.<commit_after>#![macro_escape]

#[macro_export]
macro_rules! werr(
    ($($arg:tt)*) => (
        if let Err(err) = std::io::stderr().write_str(&*format!($($arg)*)) {
            panic!("{}", err);
        }
    )
)

/// Creates a vector of string-slices from a vector of strings.
///
/// The slice vector lives as long as the original vector.
///
/// # Examples
/// ```rust
/// use baps3_cli::util::slicify;
///
/// let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
/// assert_eq!(slicify(&v), vec!["a", "b", "c"]);
/// ```
pub fn slicify<'a, Sized? S: Sized+Str>(vec: &'a Vec<S>) -> Vec<&'a str> {
    map_collect(vec.iter(), |a| a.as_slice())
}

/// Creates a vector of strings from a slice of string-slices.
///
/// # Examples
/// ```rust
/// use baps3_cli::util::unslicify;
///
/// let v = ["a", "b", "c"];
/// assert_eq!(unslicify(&v),
///            vec!["a".to_string(), "b".to_string(), "c".to_string()]);
/// ```
pub fn unslicify<'a, Sized? S: Sized+Str>(slices: &'a[S]) -> Vec<String> {
    map_collect(slices.iter(), |a: &S| a.as_slice().to_string())
}

/// Performs a map and collects the results.
pub fn map_collect<A, B, I, F, C>(iter: I, f: C) -> F
  where I: Iterator<A>,
        F: FromIterator<B>,
        C: FnMut(A) -> B {
    iter.map(f).collect::<F>()
}<|endoftext|>


<commit_before>// Copyright (C) 2021 Red Hat, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause

//! Trait to control vhost-vdpa backend drivers.

use vmm_sys_util::eventfd::EventFd;

use crate::backend::VhostBackend;
use crate::Result;

/// vhost vdpa IOVA range
pub struct VhostVdpaIovaRange {
    /// First address that can be mapped by vhost-vDPA.
    pub first: u64,
    /// Last address that can be mapped by vhost-vDPA.
    pub last: u64,
}

/// Trait to control vhost-vdpa backend drivers.
pub trait VhostVdpa: VhostBackend {
    /// Get the device id.
    /// The device ids follow the same definition of the device id defined in virtio-spec.
    fn get_device_id(&self) -> Result<u32>;

    /// Get the status.
    /// The status bits follow the same definition of the device status defined in virtio-spec.
    fn get_status(&self) -> Result<u8>;

    /// Set the status.
    /// The status bits follow the same definition of the device status defined in virtio-spec.
    ///
    /// # Arguments
    /// * `status` - Status bits to set
    fn set_status(&self, status: u8) -> Result<()>;

    /// Get the device configuration.
    ///
    /// # Arguments
    /// * `offset` - Offset in the device configuration space
    /// * `buffer` - Buffer for configuration data
    fn get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()>;

    /// Set the device configuration.
    ///
    /// # Arguments
    /// * `offset` - Offset in the device configuration space
    /// * `buffer` - Buffer for configuration data
    fn set_config(&self, offset: u32, buffer: &[u8]) -> Result<()>;

    /// Set the status for a given vring.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to enable/disable.
    /// * `enabled` - true to enable the vring, false to disable it.
    fn set_vring_enable(&self, queue_index: usize, enabled: bool) -> Result<()>;

    /// Get the maximum number of descriptors in the vring supported by the device.
    fn get_vring_num(&self) -> Result<u16>;

    /// Set the eventfd to trigger when device configuration change.
    ///
    /// # Arguments
    /// * `fd` - EventFd to trigger.
    fn set_config_call(&self, fd: &EventFd) -> Result<()>;

    /// Get the valid I/O virtual addresses range supported by the device.
    fn get_iova_range(&self) -> Result<VhostVdpaIovaRange>;

    /// Map DMA region.
    ///
    /// # Arguments
    /// * `iova` - I/O virtual address.
    /// * `size` - Size of the I/O mapping.
    /// * `vaddr` - Virtual address in the current process.
    /// * `readonly` - true if the region is read-only, false if reads and writes are allowed.
    fn dma_map(&self, iova: u64, size: u64, vaddr: *const u8, readonly: bool) -> Result<()>;

    /// Unmap DMA region.
    ///
    /// # Arguments
    /// * `iova` - I/O virtual address.
    /// * `size` - Size of the I/O mapping.
    fn dma_unmap(&self, iova: u64, size: u64) -> Result<()>;
}
<commit_msg>vdpa: add vDPA and vhost-vdpa simple description<commit_after>// Copyright (C) 2021 Red Hat, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause

//! Trait to control vhost-vdpa backend drivers.

use vmm_sys_util::eventfd::EventFd;

use crate::backend::VhostBackend;
use crate::Result;

/// vhost vdpa IOVA range
pub struct VhostVdpaIovaRange {
    /// First address that can be mapped by vhost-vDPA.
    pub first: u64,
    /// Last address that can be mapped by vhost-vDPA.
    pub last: u64,
}

/// Trait to control vhost-vdpa backend drivers.
///
/// vDPA (virtio Data Path Acceleration) devices has datapath compliant with the
/// virtio specification and the control path is vendor specific.
/// vDPA devices can be both physically located on the hardware or emulated
/// by software.
///
/// Compared to vhost acceleration, vDPA offers more control over the device
/// lifecycle.
/// For this reason, the vhost-vdpa interface extends the vhost API, offering
/// additional APIs for controlling the device (e.g. changing the state or
/// accessing the configuration space
pub trait VhostVdpa: VhostBackend {
    /// Get the device id.
    /// The device ids follow the same definition of the device id defined in virtio-spec.
    fn get_device_id(&self) -> Result<u32>;

    /// Get the status.
    /// The status bits follow the same definition of the device status defined in virtio-spec.
    fn get_status(&self) -> Result<u8>;

    /// Set the status.
    /// The status bits follow the same definition of the device status defined in virtio-spec.
    ///
    /// # Arguments
    /// * `status` - Status bits to set
    fn set_status(&self, status: u8) -> Result<()>;

    /// Get the device configuration.
    ///
    /// # Arguments
    /// * `offset` - Offset in the device configuration space
    /// * `buffer` - Buffer for configuration data
    fn get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()>;

    /// Set the device configuration.
    ///
    /// # Arguments
    /// * `offset` - Offset in the device configuration space
    /// * `buffer` - Buffer for configuration data
    fn set_config(&self, offset: u32, buffer: &[u8]) -> Result<()>;

    /// Set the status for a given vring.
    ///
    /// # Arguments
    /// * `queue_index` - Index of the queue to enable/disable.
    /// * `enabled` - true to enable the vring, false to disable it.
    fn set_vring_enable(&self, queue_index: usize, enabled: bool) -> Result<()>;

    /// Get the maximum number of descriptors in the vring supported by the device.
    fn get_vring_num(&self) -> Result<u16>;

    /// Set the eventfd to trigger when device configuration change.
    ///
    /// # Arguments
    /// * `fd` - EventFd to trigger.
    fn set_config_call(&self, fd: &EventFd) -> Result<()>;

    /// Get the valid I/O virtual addresses range supported by the device.
    fn get_iova_range(&self) -> Result<VhostVdpaIovaRange>;

    /// Map DMA region.
    ///
    /// # Arguments
    /// * `iova` - I/O virtual address.
    /// * `size` - Size of the I/O mapping.
    /// * `vaddr` - Virtual address in the current process.
    /// * `readonly` - true if the region is read-only, false if reads and writes are allowed.
    fn dma_map(&self, iova: u64, size: u64, vaddr: *const u8, readonly: bool) -> Result<()>;

    /// Unmap DMA region.
    ///
    /// # Arguments
    /// * `iova` - I/O virtual address.
    /// * `size` - Size of the I/O mapping.
    fn dma_unmap(&self, iova: u64, size: u64) -> Result<()>;
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::ops::Drop;
use std::path::PathBuf;
use std::result::Result as RResult;
use std::sync::Arc;
use std::sync::{RwLock, Mutex};

use fs2::FileExt;

pub use entry::Entry;
pub use error::StoreError;

pub type Result<T> = RResult<T, StoreError>;

pub type StoreId = PathBuf;

trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl<'a> IntoStoreId for &'a str {
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl<'a> IntoStoreId for &'a String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> StoreId {
        self
    }
}

impl<'a> IntoStoreId for &'a PathBuf {
    fn into_storeid(self) -> StoreId {
        self.clone()
    }
}

impl<ISI: IntoStoreId> IntoStoreId for (ISI, ISI) {
    fn into_storeid(self) -> StoreId {
        let (first, second) = self;
        let mut res : StoreId = first.into_storeid();
        res.push(second.into_storeid());
        res
    }
}

pub struct Store {
    location: PathBuf,

    /**
     * Internal Path->File cache map
     *
     * Caches the files, so they remain flock()ed
     *
     * Could be optimized for a threadsafe HashMap
     */
    entries: Arc<RwLock<HashMap<StoreId, (File, Option<Entry>)>>>,
}

impl Store {

    fn create(&self, entry: Entry) -> Result<()> {
        unimplemented!();
    }
    fn retrieve<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> {
        unimplemented!();
    }
    fn update<'a>(&'a self, entry: FileLockEntry<'a>) -> Result<()> {
        unimplemented!();
    }
    fn retrieve_copy(&self, id: StoreId) -> Result<Entry> {
        unimplemented!();
    }
    fn delete(&self, id: StoreId) -> Result<()> {
        unimplemented!();
    }
}

impl Drop for Store {

    /**
     * Unlock all files on drop
     *
     * TODO: Error message when file cannot be unlocked?
     */
    fn drop(&mut self) {
        self.entries.write().unwrap()
            .iter().map(|f| (f.1).0.unlock());
    }

}

pub struct FileLockEntry<'a> {
    store: &'a Store,
    entry: Entry,
    key: StoreId,
}

impl<'a> FileLockEntry<'a, > {
    fn new(store: &'a Store, entry: Entry, key: StoreId) -> FileLockEntry<'a> {
        FileLockEntry {
            store: store,
            entry: entry,
            key: key,
        }
    }
}

impl<'a> ::std::ops::Deref for FileLockEntry<'a> {
    type Target = Entry;

    fn deref(&self) -> &Self::Target {
        &self.entry
    }
}

impl<'a> ::std::ops::DerefMut for FileLockEntry<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.entry
    }
}

impl<'a> Drop for FileLockEntry<'a> {
    fn drop(&mut self) {
        let mut map = self.store.entries.write().unwrap();
        let (_, ref mut en) = *map.get_mut(&self.key).unwrap();
        *en = Some(self.entry.clone());
    }
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;
    use store::{StoreId, IntoStoreId};

    #[test]
    fn into_storeid_trait() {
        let buf = PathBuf::from("abc/def");

        let test = ("abc", "def");
        assert_eq!(buf, test.into_storeid());

        let test = "abc/def";
        assert_eq!(buf, test.into_storeid());

        let test = String::from("abc/def");
        assert_eq!(buf, test.into_storeid());

        let test = PathBuf::from("abc/def");
        assert_eq!(buf, test.into_storeid());
    }
}

<commit_msg>Add whitespace between implementations<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::ops::Drop;
use std::path::PathBuf;
use std::result::Result as RResult;
use std::sync::Arc;
use std::sync::{RwLock, Mutex};

use fs2::FileExt;

pub use entry::Entry;
pub use error::StoreError;

pub type Result<T> = RResult<T, StoreError>;

pub type StoreId = PathBuf;

trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl<'a> IntoStoreId for &'a str {
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl<'a> IntoStoreId for &'a String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> StoreId {
        self
    }
}

impl<'a> IntoStoreId for &'a PathBuf {
    fn into_storeid(self) -> StoreId {
        self.clone()
    }
}

impl<ISI: IntoStoreId> IntoStoreId for (ISI, ISI) {
    fn into_storeid(self) -> StoreId {
        let (first, second) = self;
        let mut res : StoreId = first.into_storeid();
        res.push(second.into_storeid());
        res
    }
}

pub struct Store {
    location: PathBuf,

    /**
     * Internal Path->File cache map
     *
     * Caches the files, so they remain flock()ed
     *
     * Could be optimized for a threadsafe HashMap
     */
    entries: Arc<RwLock<HashMap<StoreId, (File, Option<Entry>)>>>,
}

impl Store {
    fn create(&self, entry: Entry) -> Result<()> {
        unimplemented!();
    }

    fn retrieve<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> {
        unimplemented!();
    }

    fn update<'a>(&'a self, entry: FileLockEntry<'a>) -> Result<()> {
        unimplemented!();
    }

    fn retrieve_copy(&self, id: StoreId) -> Result<Entry> {
        unimplemented!();
    }

    fn delete(&self, id: StoreId) -> Result<()> {
        unimplemented!();
    }
}

impl Drop for Store {

    /**
     * Unlock all files on drop
     *
     * TODO: Error message when file cannot be unlocked?
     */
    fn drop(&mut self) {
        self.entries.write().unwrap()
            .iter().map(|f| (f.1).0.unlock());
    }

}

pub struct FileLockEntry<'a> {
    store: &'a Store,
    entry: Entry,
    key: StoreId,
}

impl<'a> FileLockEntry<'a, > {
    fn new(store: &'a Store, entry: Entry, key: StoreId) -> FileLockEntry<'a> {
        FileLockEntry {
            store: store,
            entry: entry,
            key: key,
        }
    }
}

impl<'a> ::std::ops::Deref for FileLockEntry<'a> {
    type Target = Entry;

    fn deref(&self) -> &Self::Target {
        &self.entry
    }
}

impl<'a> ::std::ops::DerefMut for FileLockEntry<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.entry
    }
}

impl<'a> Drop for FileLockEntry<'a> {
    fn drop(&mut self) {
        let mut map = self.store.entries.write().unwrap();
        let (_, ref mut en) = *map.get_mut(&self.key).unwrap();
        *en = Some(self.entry.clone());
    }
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;
    use store::{StoreId, IntoStoreId};

    #[test]
    fn into_storeid_trait() {
        let buf = PathBuf::from("abc/def");

        let test = ("abc", "def");
        assert_eq!(buf, test.into_storeid());

        let test = "abc/def";
        assert_eq!(buf, test.into_storeid());

        let test = String::from("abc/def");
        assert_eq!(buf, test.into_storeid());

        let test = PathBuf::from("abc/def");
        assert_eq!(buf, test.into_storeid());
    }
}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::Result;
use std::path::PathBuf;

use libtiny_wire as wire;

pub struct Logger {
    /// Log file directory
    log_dir: PathBuf,

    /// Server name for this logger. All file names will be prefixed with this.
    serv_name: String,

    /// File for the server logs.
    server_fd: File,

    /// Maps channels/users to their files.
    fds: HashMap<String, File>,
}

impl Logger {
    pub fn new(log_dir: PathBuf, serv_name: String) -> Result<Logger> {
        if let Err(err) = fs::create_dir(&log_dir) {
            if err.kind() != io::ErrorKind::AlreadyExists {
                return Err(err);
            }
        }

        let mut server_fd_path = log_dir.clone();
        server_fd_path.push(&format!("{}.txt", serv_name));
        let server_fd = OpenOptions::new().append(true).open(server_fd_path)?;

        // TODO: Write a "logs started" lines

        Ok(Logger {
            log_dir,
            serv_name,
            server_fd,
            fds: HashMap::new(),
        })
    }

    pub fn log_incoming_msg(&mut self, msg: &wire::Msg) -> Result<()> {
        // TODO
        Ok(())
    }

    pub fn log_outgoing_msg(&mut self, target: &str, msg: &str, is_action: bool) -> Result<()> {
        // TODO
        Ok(())
    }

    pub fn log_outgoing_raw_msg(&mut self, msg: &str) -> Result<()> {
        // TODO
        Ok(())
    }
}
<commit_msg>Start implementing logging<commit_after>use std::collections::HashMap;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::Result;
use std::path::PathBuf;

use libtiny_wire as wire;

pub struct Logger {
    /// Log file directory
    log_dir: PathBuf,

    /// Server name for this logger. All file names will be prefixed with this.
    serv_name: String,

    /// File for the server logs.
    server_fd: File,

    /// Maps channels/users to their files.
    fds: HashMap<String, File>,
}

impl Logger {
    pub fn new(log_dir: PathBuf, serv_name: String) -> Result<Logger> {
        if let Err(err) = fs::create_dir(&log_dir) {
            if err.kind() != io::ErrorKind::AlreadyExists {
                return Err(err);
            }
        }

        let mut server_fd_path = log_dir.clone();
        server_fd_path.push(&format!("{}.txt", serv_name));
        let server_fd = OpenOptions::new().append(true).open(server_fd_path)?;

        // TODO: Write a "logs started" lines

        Ok(Logger {
            log_dir,
            serv_name,
            server_fd,
            fds: HashMap::new(),
        })
    }

    fn get_file(&mut self, target: &str) -> Result<&mut File> {
        // *sigh* Double lookup to make borrowchk happy
        if self.fds.contains_key(target) {
            return Ok(self.fds.get_mut(target).unwrap());
        }

        let mut file_path = self.log_dir.clone();
        file_path.push(&format!("{}.txt", target));
        let fd = OpenOptions::new().append(true).open(file_path)?;
        self.fds.insert(target.to_owned(), fd);
        self.get_file(target)
    }

    pub fn log_incoming_msg(&mut self, msg: &wire::Msg) -> Result<()> {
        let wire::Msg { pfx, cmd } = msg;
        let sender = match pfx {
            Some(sender) => sender,
            None => { return Ok(()); }
        };

        use wire::Cmd::*;
        use wire::MsgTarget;
        match cmd {
            PRIVMSG { target, msg, is_notice, is_action } => {
                let target_file = match target {
                    MsgTarget::Chan(target) | MsgTarget::User(target) =>
                        self.get_file(target)?
                };
            }
            _ => {}
        }

        Ok(())
    }

    pub fn log_outgoing_msg(&mut self, target: &str, msg: &str, is_action: bool) -> Result<()> {
        // TODO
        Ok(())
    }

    pub fn log_outgoing_raw_msg(&mut self, msg: &str) -> Result<()> {
        // TODO
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::net::SocketAddr;
use std::collections::HashMap;
use rand::random;
use mio::net::TcpStream;

use network::{AppId,Backend,ConnectionError};

pub struct BackendMap {
  pub instances:    HashMap<AppId, BackendList>,
  pub max_failures: usize,
}

impl BackendMap {
  pub fn new() -> BackendMap {
    BackendMap {
      instances:    HashMap::new(),
      max_failures: 3,
    }
  }

  pub fn add_instance(&mut self, app_id: &str, instance_address: &SocketAddr) {
    self.instances.entry(app_id.to_string()).or_insert(BackendList::new()).add_instance(instance_address);
  }

  pub fn remove_instance(&mut self, app_id: &str, instance_address: &SocketAddr) {
    if let Some(instances) = self.instances.get_mut(app_id) {
      instances.remove_instance(instance_address);
    } else {
      error!("Instance was already removed");
    }
  }

  pub fn close_backend_connection(&mut self, app_id: &str, addr: &SocketAddr) {
    if let Some(app_instances) = self.instances.get_mut(app_id) {
      if let Some(ref mut backend) = app_instances.find_instance(addr) {
        (*backend.borrow_mut()).dec_connections();
      }
    }
  }

  pub fn backend_from_app_id(&mut self, app_id: &str) -> Result<(Rc<RefCell<Backend>>,TcpStream),ConnectionError> {
    if let Some(ref mut app_instances) = self.instances.get_mut(app_id) {
      if app_instances.instances.len() == 0 {
        return Err(ConnectionError::NoBackendAvailable);
      }

      for _ in 0..self.max_failures {
        //FIXME: it's probably pretty wasteful to refilter every time here
        let mut instances:Vec<&mut Rc<RefCell<Backend>>> = app_instances.available_instances();
        if instances.is_empty() {
          error!("no more available backends for app {}", app_id);
          return Err(ConnectionError::NoBackendAvailable);
        }
        let rnd = random::<usize>();
        let idx = rnd % instances.len();

        let conn = instances.get_mut(idx).ok_or(ConnectionError::NoBackendAvailable).and_then(|ref mut b| {
          let ref mut backend = *b.borrow_mut();
          info!("Connecting {} -> {:?}", app_id, (backend.address, backend.active_connections, backend.failures));
          let conn = backend.try_connect();
          if backend.failures >= max_failures_per_backend {
            error!("backend {:?} connections failed {} times, disabling it", (backend.address, backend.active_connections), backend.failures);
          }

          conn.map(|c| (b.clone(), c))
        });

        if conn.is_ok() {
          return conn;
        }
      }
      Err(ConnectionError::NoBackendAvailable)
    } else {
      Err(ConnectionError::NoBackendAvailable)
    }
  }

  pub fn backend_from_sticky_session(&mut self, app_id: &str, sticky_session: u32) -> Result<(Rc<RefCell<Backend>>,TcpStream),ConnectionError> {
    let sticky_conn: Option<Result<(Rc<RefCell<Backend>>,TcpStream),ConnectionError>> = self.instances
      .get_mut(app_id)
      .and_then(|app_instances| app_instances.find_sticky(sticky_session))
      .map(|b| {
        let ref mut backend = *b.borrow_mut();
        let conn = backend.try_connect();
        info!("Connecting {} -> {:?} using session {}", app_id, (backend.address, backend.active_connections, backend.failures), sticky_session);
        if backend.failures >= max_failures_per_backend {
          error!("backend {:?} connections failed {} times, disabling it", (backend.address, backend.active_connections), backend.failures);
        }

        conn.map(|c| (b.clone(), c))
    });

    if let Some(res) = sticky_conn {
      return res;
    } else {
      debug!("Couldn't find a backend corresponding to sticky_session {} for app {}", sticky_session, app_id);
      return self.backend_from_app_id(app_id);
    }
  }
}

const max_failures_per_backend: usize = 10;

pub struct BackendList {
  pub instances: Vec<Rc<RefCell<Backend>>>,
  pub next_id:   u32,
}

impl BackendList {
  pub fn new() -> BackendList {
    BackendList {
      instances: Vec::new(),
      next_id:   0,
    }
  }

  pub fn add_instance(&mut self, instance_address: &SocketAddr) {
    if self.instances.iter().find(|b| &(*b.borrow()).address == instance_address).is_none() {
      let backend = Rc::new(RefCell::new(Backend::new(*instance_address, self.next_id)));
      self.instances.push(backend);
      self.next_id += 1;
    }
  }

  pub fn remove_instance(&mut self, instance_address: &SocketAddr) {
    self.instances.retain(|backend| &(*backend.borrow()).address != instance_address);
  }

  pub fn find_instance(&mut self, instance_address: &SocketAddr) -> Option<&mut Rc<RefCell<Backend>>> {
    self.instances.iter_mut().find(|backend| &(*backend.borrow()).address == instance_address)
  }

  pub fn find_sticky(&mut self, sticky_session: u32) -> Option<&mut Rc<RefCell<Backend>>> {
    self.instances.iter_mut().find(|b| {
      let backend = &*b.borrow();
      backend.id == sticky_session && backend.can_open(max_failures_per_backend)
    })
  }

  pub fn available_instances(&mut self) -> Vec<&mut Rc<RefCell<Backend>>> {
    self.instances.iter_mut()
      .filter(|backend| (*backend.borrow()).can_open(max_failures_per_backend))
      .collect()
  }
}
<commit_msg>Backends::find_sticky: break when sticky backend is found<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::net::SocketAddr;
use std::collections::HashMap;
use rand::random;
use mio::net::TcpStream;

use network::{AppId,Backend,ConnectionError};

pub struct BackendMap {
  pub instances:    HashMap<AppId, BackendList>,
  pub max_failures: usize,
}

impl BackendMap {
  pub fn new() -> BackendMap {
    BackendMap {
      instances:    HashMap::new(),
      max_failures: 3,
    }
  }

  pub fn add_instance(&mut self, app_id: &str, instance_address: &SocketAddr) {
    self.instances.entry(app_id.to_string()).or_insert(BackendList::new()).add_instance(instance_address);
  }

  pub fn remove_instance(&mut self, app_id: &str, instance_address: &SocketAddr) {
    if let Some(instances) = self.instances.get_mut(app_id) {
      instances.remove_instance(instance_address);
    } else {
      error!("Instance was already removed");
    }
  }

  pub fn close_backend_connection(&mut self, app_id: &str, addr: &SocketAddr) {
    if let Some(app_instances) = self.instances.get_mut(app_id) {
      if let Some(ref mut backend) = app_instances.find_instance(addr) {
        (*backend.borrow_mut()).dec_connections();
      }
    }
  }

  pub fn backend_from_app_id(&mut self, app_id: &str) -> Result<(Rc<RefCell<Backend>>,TcpStream),ConnectionError> {
    if let Some(ref mut app_instances) = self.instances.get_mut(app_id) {
      if app_instances.instances.len() == 0 {
        return Err(ConnectionError::NoBackendAvailable);
      }

      for _ in 0..self.max_failures {
        //FIXME: it's probably pretty wasteful to refilter every time here
        let mut instances:Vec<&mut Rc<RefCell<Backend>>> = app_instances.available_instances();
        if instances.is_empty() {
          error!("no more available backends for app {}", app_id);
          return Err(ConnectionError::NoBackendAvailable);
        }
        let rnd = random::<usize>();
        let idx = rnd % instances.len();

        let conn = instances.get_mut(idx).ok_or(ConnectionError::NoBackendAvailable).and_then(|ref mut b| {
          let ref mut backend = *b.borrow_mut();
          info!("Connecting {} -> {:?}", app_id, (backend.address, backend.active_connections, backend.failures));
          let conn = backend.try_connect();
          if backend.failures >= max_failures_per_backend {
            error!("backend {:?} connections failed {} times, disabling it", (backend.address, backend.active_connections), backend.failures);
          }

          conn.map(|c| (b.clone(), c))
        });

        if conn.is_ok() {
          return conn;
        }
      }
      Err(ConnectionError::NoBackendAvailable)
    } else {
      Err(ConnectionError::NoBackendAvailable)
    }
  }

  pub fn backend_from_sticky_session(&mut self, app_id: &str, sticky_session: u32) -> Result<(Rc<RefCell<Backend>>,TcpStream),ConnectionError> {
    let sticky_conn: Option<Result<(Rc<RefCell<Backend>>,TcpStream),ConnectionError>> = self.instances
      .get_mut(app_id)
      .and_then(|app_instances| app_instances.find_sticky(sticky_session))
      .map(|b| {
        let ref mut backend = *b.borrow_mut();
        let conn = backend.try_connect();
        info!("Connecting {} -> {:?} using session {}", app_id, (backend.address, backend.active_connections, backend.failures), sticky_session);
        if backend.failures >= max_failures_per_backend {
          error!("backend {:?} connections failed {} times, disabling it", (backend.address, backend.active_connections), backend.failures);
        }

        conn.map(|c| (b.clone(), c))
    });

    if let Some(res) = sticky_conn {
      return res;
    } else {
      debug!("Couldn't find a backend corresponding to sticky_session {} for app {}", sticky_session, app_id);
      return self.backend_from_app_id(app_id);
    }
  }
}

const max_failures_per_backend: usize = 10;

pub struct BackendList {
  pub instances: Vec<Rc<RefCell<Backend>>>,
  pub next_id:   u32,
}

impl BackendList {
  pub fn new() -> BackendList {
    BackendList {
      instances: Vec::new(),
      next_id:   0,
    }
  }

  pub fn add_instance(&mut self, instance_address: &SocketAddr) {
    if self.instances.iter().find(|b| &(*b.borrow()).address == instance_address).is_none() {
      let backend = Rc::new(RefCell::new(Backend::new(*instance_address, self.next_id)));
      self.instances.push(backend);
      self.next_id += 1;
    }
  }

  pub fn remove_instance(&mut self, instance_address: &SocketAddr) {
    self.instances.retain(|backend| &(*backend.borrow()).address != instance_address);
  }

  pub fn find_instance(&mut self, instance_address: &SocketAddr) -> Option<&mut Rc<RefCell<Backend>>> {
    self.instances.iter_mut().find(|backend| &(*backend.borrow()).address == instance_address)
  }

  pub fn find_sticky(&mut self, sticky_session: u32) -> Option<&mut Rc<RefCell<Backend>>> {
    self.instances.iter_mut()
      .find(|b| b.borrow().id == sticky_session )
      .and_then(|b| {
        if b.borrow().can_open(max_failures_per_backend) {
          Some(b)
        } else {
          None
        }
      })
  }

  pub fn available_instances(&mut self) -> Vec<&mut Rc<RefCell<Backend>>> {
    self.instances.iter_mut()
      .filter(|backend| (*backend.borrow()).can_open(max_failures_per_backend))
      .collect()
  }
}
<|endoftext|>


<commit_before>pub struct Post {
    state: Option<Box<State>>,
    content: String
}

impl Post {
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new()
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        ""
    }

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }

    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<State>;
}

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<State> {
        Box::new(PendingReview {})
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(&self: Box<Self>) -> Box<State> {
        self
    }
}
<commit_msg>Implement approve for PendingReview, implement Published struct and State trait methods for that struct<commit_after>pub struct Post {
    state: Option<Box<State>>,
    content: String
}

impl Post {
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new()
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        ""
    }

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }

    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<State>;
    fn approve(self: Box<Self>) -> Box<State>;
}

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<State> {
        Box::new(PendingReview {})
    }

    fn approve(self: Box<Self>) -> Box<State> {
        self
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(&self: Box<Self>) -> Box<State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<State> {
        Box::new(Published {})
    }
}

struct Published {}

impl State for Published {
    fn request_review(&self: Box<Self>) -> Box<State> {
        self
    }

    fn approve(&self: Box<Self>) -> Box<State> {
        self
    }
}
<|endoftext|>


<commit_before>//! Abstractions for reading and modifying the mapping of pages.

pub use self::mapped_page_table::{MappedPageTable, PhysToVirt};
#[cfg(target_arch = "x86_64")]
pub use self::{offset_page_table::OffsetPageTable, recursive_page_table::RecursivePageTable};

use crate::structures::paging::{
    frame_alloc::{FrameAllocator, UnusedPhysFrame},
    page_table::PageTableFlags,
    Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
};
use crate::{PhysAddr, VirtAddr};

mod mapped_page_table;
mod offset_page_table;
mod recursive_page_table;

/// This trait defines page table operations that work for all page sizes of the x86_64
/// architecture.
pub trait MapperAllSizes: Mapper<Size4KiB> + Mapper<Size2MiB> + Mapper<Size1GiB> {
    /// Return the frame that the given virtual address is mapped to and the offset within that
    /// frame.
    ///
    /// If the given address has a valid mapping, the mapped frame and the offset within that
    /// frame is returned. Otherwise an error value is returned.
    ///
    /// This function works with huge pages of all sizes.
    fn translate(&self, addr: VirtAddr) -> TranslateResult;

    /// Translates the given virtual address to the physical address that it maps to.
    ///
    /// Returns `None` if there is no valid mapping for the given address.
    ///
    /// This is a convenience method. For more information about a mapping see the
    /// [`translate`](MapperAllSizes::translate) method.
    fn translate_addr(&self, addr: VirtAddr) -> Option<PhysAddr> {
        match self.translate(addr) {
            TranslateResult::PageNotMapped | TranslateResult::InvalidFrameAddress(_) => None,
            TranslateResult::Frame4KiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame2MiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame1GiB { frame, offset } => Some(frame.start_address() + offset),
        }
    }
}

/// The return value of the [`MapperAllSizes::translate`] function.
///
/// If the given address has a valid mapping, a `Frame4KiB`, `Frame2MiB`, or `Frame1GiB` variant
/// is returned, depending on the size of the mapped page. The remaining variants indicate errors.
#[derive(Debug)]
pub enum TranslateResult {
    /// The page is mapped to a physical frame of size 4KiB.
    Frame4KiB {
        /// The mapped frame.
        frame: PhysFrame<Size4KiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame2MiB {
        /// The mapped frame.
        frame: PhysFrame<Size2MiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame1GiB {
        /// The mapped frame.
        frame: PhysFrame<Size1GiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// A trait for common page table operations on pages of size `S`.
pub trait Mapper<S: PageSize> {
    /// Creates a new mapping in the page table.
    ///
    /// This function might need additional physical frames to create new page tables. These
    /// frames are allocated from the `allocator` argument. At most three frames are required.
    fn map_to<A>(
        &mut self,
        page: Page<S>,
        frame: UnusedPhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        A: FrameAllocator<Size4KiB>;

    /// Removes a mapping from the page table and returns the frame that used to be mapped.
    ///
    /// Note that no page tables or pages are deallocated.
    fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;

    /// Updates the flags of an existing mapping.
    fn update_flags(
        &mut self,
        page: Page<S>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<S>, FlagUpdateError>;

    /// Return the frame that the specified page is mapped to.
    ///
    /// This function assumes that the page is mapped to a frame of size `S` and returns an
    /// error otherwise.
    fn translate_page(&self, page: Page<S>) -> Result<PhysFrame<S>, TranslateError>;

    /// Maps the given frame to the virtual page with the same address.
    unsafe fn identity_map<A>(
        &mut self,
        frame: UnusedPhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
        S: PageSize,
        Self: Mapper<S>,
    {
        let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
        self.map_to(page, frame, flags, frame_allocator)
    }
}

/// This type represents a page whose mapping has changed in the page table.
///
/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
/// to be flushed from the TLB before it's accessed. This type is returned from function that
/// change the mapping of a page to ensure that the TLB flush is not forgotten.
#[derive(Debug)]
#[must_use = "Page Table changes must be flushed or ignored."]
pub struct MapperFlush<S: PageSize>(Page<S>);

impl<S: PageSize> MapperFlush<S> {
    /// Create a new flush promise
    fn new(page: Page<S>) -> Self {
        MapperFlush(page)
    }

    /// Flush the page from the TLB to ensure that the newest mapping is used.
    #[cfg(target_arch = "x86_64")]
    pub fn flush(self) {
        crate::instructions::tlb::flush(self.0.start_address());
    }

    /// Don't flush the TLB and silence the “must be used” warning.
    pub fn ignore(self) {}
}

/// This error is returned from `map_to` and similar methods.
#[derive(Debug)]
pub enum MapToError {
    /// An additional frame was needed for the mapping process, but the frame allocator
    /// returned `None`.
    FrameAllocationFailed,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of an already mapped huge page.
    ParentEntryHugePage,
    /// The given page is already mapped to a physical frame.
    PageAlreadyMapped,
}

/// An error indicating that an `unmap` call failed.
#[derive(Debug)]
pub enum UnmapError {
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// An error indicating that an `update_flags` call failed.
#[derive(Debug)]
pub enum FlagUpdateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
}

/// An error indicating that an `translate` call failed.
#[derive(Debug)]
pub enum TranslateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}
<commit_msg>Make Mapper trait object safe by adding `Self: Sized` bounds on generic functions<commit_after>//! Abstractions for reading and modifying the mapping of pages.

pub use self::mapped_page_table::{MappedPageTable, PhysToVirt};
#[cfg(target_arch = "x86_64")]
pub use self::{offset_page_table::OffsetPageTable, recursive_page_table::RecursivePageTable};

use crate::structures::paging::{
    frame_alloc::{FrameAllocator, UnusedPhysFrame},
    page_table::PageTableFlags,
    Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
};
use crate::{PhysAddr, VirtAddr};

mod mapped_page_table;
mod offset_page_table;
mod recursive_page_table;

/// This trait defines page table operations that work for all page sizes of the x86_64
/// architecture.
pub trait MapperAllSizes: Mapper<Size4KiB> + Mapper<Size2MiB> + Mapper<Size1GiB> {
    /// Return the frame that the given virtual address is mapped to and the offset within that
    /// frame.
    ///
    /// If the given address has a valid mapping, the mapped frame and the offset within that
    /// frame is returned. Otherwise an error value is returned.
    ///
    /// This function works with huge pages of all sizes.
    fn translate(&self, addr: VirtAddr) -> TranslateResult;

    /// Translates the given virtual address to the physical address that it maps to.
    ///
    /// Returns `None` if there is no valid mapping for the given address.
    ///
    /// This is a convenience method. For more information about a mapping see the
    /// [`translate`](MapperAllSizes::translate) method.
    fn translate_addr(&self, addr: VirtAddr) -> Option<PhysAddr> {
        match self.translate(addr) {
            TranslateResult::PageNotMapped | TranslateResult::InvalidFrameAddress(_) => None,
            TranslateResult::Frame4KiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame2MiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame1GiB { frame, offset } => Some(frame.start_address() + offset),
        }
    }
}

/// The return value of the [`MapperAllSizes::translate`] function.
///
/// If the given address has a valid mapping, a `Frame4KiB`, `Frame2MiB`, or `Frame1GiB` variant
/// is returned, depending on the size of the mapped page. The remaining variants indicate errors.
#[derive(Debug)]
pub enum TranslateResult {
    /// The page is mapped to a physical frame of size 4KiB.
    Frame4KiB {
        /// The mapped frame.
        frame: PhysFrame<Size4KiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame2MiB {
        /// The mapped frame.
        frame: PhysFrame<Size2MiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame1GiB {
        /// The mapped frame.
        frame: PhysFrame<Size1GiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// A trait for common page table operations on pages of size `S`.
pub trait Mapper<S: PageSize> {
    /// Creates a new mapping in the page table.
    ///
    /// This function might need additional physical frames to create new page tables. These
    /// frames are allocated from the `allocator` argument. At most three frames are required.
    fn map_to<A>(
        &mut self,
        page: Page<S>,
        frame: UnusedPhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        Self: Sized,
        A: FrameAllocator<Size4KiB>;

    /// Removes a mapping from the page table and returns the frame that used to be mapped.
    ///
    /// Note that no page tables or pages are deallocated.
    fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;

    /// Updates the flags of an existing mapping.
    fn update_flags(
        &mut self,
        page: Page<S>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<S>, FlagUpdateError>;

    /// Return the frame that the specified page is mapped to.
    ///
    /// This function assumes that the page is mapped to a frame of size `S` and returns an
    /// error otherwise.
    fn translate_page(&self, page: Page<S>) -> Result<PhysFrame<S>, TranslateError>;

    /// Maps the given frame to the virtual page with the same address.
    unsafe fn identity_map<A>(
        &mut self,
        frame: UnusedPhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        Self: Sized,
        A: FrameAllocator<Size4KiB>,
        S: PageSize,
        Self: Mapper<S>,
    {
        let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
        self.map_to(page, frame, flags, frame_allocator)
    }
}

/// This type represents a page whose mapping has changed in the page table.
///
/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
/// to be flushed from the TLB before it's accessed. This type is returned from function that
/// change the mapping of a page to ensure that the TLB flush is not forgotten.
#[derive(Debug)]
#[must_use = "Page Table changes must be flushed or ignored."]
pub struct MapperFlush<S: PageSize>(Page<S>);

impl<S: PageSize> MapperFlush<S> {
    /// Create a new flush promise
    fn new(page: Page<S>) -> Self {
        MapperFlush(page)
    }

    /// Flush the page from the TLB to ensure that the newest mapping is used.
    #[cfg(target_arch = "x86_64")]
    pub fn flush(self) {
        crate::instructions::tlb::flush(self.0.start_address());
    }

    /// Don't flush the TLB and silence the “must be used” warning.
    pub fn ignore(self) {}
}

/// This error is returned from `map_to` and similar methods.
#[derive(Debug)]
pub enum MapToError {
    /// An additional frame was needed for the mapping process, but the frame allocator
    /// returned `None`.
    FrameAllocationFailed,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of an already mapped huge page.
    ParentEntryHugePage,
    /// The given page is already mapped to a physical frame.
    PageAlreadyMapped,
}

/// An error indicating that an `unmap` call failed.
#[derive(Debug)]
pub enum UnmapError {
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// An error indicating that an `update_flags` call failed.
#[derive(Debug)]
pub enum FlagUpdateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
}

/// An error indicating that an `translate` call failed.
#[derive(Debug)]
pub enum TranslateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

static _ASSERT_OBJECT_SAFE: Option<&(dyn MapperAllSizes + Sync)> = None;
<|endoftext|>


<commit_before>use operand::Operand;
use memory::Memory;
use constants::*;
use super::{Result, Size, Exception,OpcodeInstance,generate};

fn decode_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Operand {
    let mode = ((opcode >> 3) & 7) as u8;
    let reg_y = (opcode & 7) as u8;
    match mode {
        0b000 => Operand::DataRegisterDirect(reg_y),
        0b001 => Operand::AddressRegisterDirect(reg_y),
        0b010 => Operand::AddressRegisterIndirect(reg_y),
        0b011 => Operand::AddressRegisterIndirectWithPostincrement(reg_y),
        0b100 => Operand::AddressRegisterIndirectWithPredecrement(reg_y),
        0b101 => Operand::AddressRegisterIndirectWithDisplacement(reg_y, mem.read_word(pc+2) as i16),
        0b110 => {
            let (indexinfo, displacement) = parse_extension_word(mem.read_word(pc+2));
            Operand::AddressRegisterIndirectWithIndex(reg_y, indexinfo, displacement)
            },
        0b111 => match reg_y {
            0b010 => Operand::PcWithDisplacement(mem.read_word(pc+2) as i16),
            0b011 => {
                let (indexinfo, displacement) = parse_extension_word(mem.read_word(pc+2));
                Operand::PcWithIndex(indexinfo, displacement)
                },
            0b000 => Operand::AbsoluteWord(mem.read_word(pc+2)),
            0b001 => Operand::AbsoluteLong((mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
            0b100 =>
                match size {
                    Size::Byte => Operand::Immediate(size, (mem.read_word(pc+2) & 0xFF) as u32),
                    Size::Word => Operand::Immediate(size, mem.read_word(pc+2) as u32),
                    Size::Long => Operand::Immediate(size, (mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
                    Size::Unsized => panic!("unsized Immediate"),
                },
            _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
        },
        _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
    }
}
fn parse_extension_word(extension: u16) -> (u8, i8) {
    // top four bits = (D/A RRR) matches our register array layout
    let xreg_ndx_size = (extension>>12) as u8;
    let displacement = extension as i8;
    (xreg_ndx_size, displacement)
}
fn decode_dx(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::DataRegisterDirect(((opcode >> 9) & 7) as u8)
}
fn decode_ax(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::AddressRegisterDirect(((opcode >> 9) & 7) as u8)
}
fn decode_imm(size: Size, pc: u32, mem: &Memory) -> Operand {
    match size {
        Size::Byte => Operand::Immediate(size, (mem.read_word(pc+2) & 0xFF) as u32),
        Size::Word => Operand::Immediate(size, mem.read_word(pc+2) as u32),
        Size::Long => Operand::Immediate(size, (mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
        Size::Unsized => panic!("unsized Immediate"),
    }
}
pub fn decode_ea_dx(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_dx(opcode, pc, mem)]
}
pub fn decode_ea_ax(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_ax(opcode, pc, mem)]
}
pub fn decode_dx_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_dx(opcode, pc, mem), decode_ea(opcode, size, pc, mem)]
}
pub fn decode_imm_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    let imm = decode_imm(size, pc, mem);
    vec![imm, decode_ea(opcode, size, pc + imm.extension_words()*2, mem)]
}

/* Check if opcode is using a valid ea mode */
fn valid_ea(opcode: u16, mask: u16) -> bool
{
    if mask == 0 {
        true
    } else {
        match opcode & 0x3f {
            0x00 ... 0x07 => (mask & EA_DATA_REGISTER_DIRECT) != 0,
            0x08 ... 0x0f => (mask & EA_ADDRESS_REGISTER_DIRECT) != 0,
            0x10 ... 0x17 => (mask & EA_ADDRESS_REGISTER_INDIRECT) != 0,
            0x18 ... 0x1f => (mask & EA_ARI_POSTINCREMENT) != 0,
            0x20 ... 0x27 => (mask & EA_ARI_PREDECREMENT) != 0,
            0x28 ... 0x2f => (mask & EA_ARI_DISPLACEMENT) != 0,
            0x30 ... 0x37 => (mask & EA_ARI_INDEX) != 0,
            0x38 => (mask & EA_ABSOLUTE_SHORT) != 0,
            0x39 => (mask & EA_ABSOLUTE_LONG) != 0,
            0x3a => (mask & EA_PC_DISPLACEMENT) != 0,
            0x3b => (mask & EA_PC_INDEX) != 0,
            0x3c => (mask & EA_IMMEDIATE) != 0,
            _ => false
        }
    }
}

pub fn disassemble_first(mem: &Memory) -> OpcodeInstance {
    disassemble(0, mem).unwrap()
}

pub fn disassemble(pc: u32, mem: &Memory) -> Result<OpcodeInstance> {
    let optable = generate();
    let opcode = mem.read_word(pc);
    // println!("opcode read was {:04x}", opcode);
    for op in optable {
        if ((opcode as u32) & op.mask) == op.matching && valid_ea(opcode, op.ea_mask) {
            let decoder = op.decoder;
            return Ok(OpcodeInstance {mnemonic: op.mnemonic, size: op.size, operands: decoder(opcode, op.size, pc, mem)});
        }
    }
    Err(Exception::IllegalInstruction(opcode, pc))
}

#[cfg(test)]
mod tests {
    use operand::Operand;
    use memory::{MemoryVec, Memory};
    use super::{disassemble, disassemble_first};
    use super::super::{Size, Exception};
    use regex::Regex;
    use constants::*;

    extern crate itertools;
    use self::itertools::assert_equal;

    #[test]
    fn decodes_add_8_er() {
        let mem = MemoryVec { mem: vec![0xd411]} ;
        let inst = disassemble_first(&mem);
        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[0]);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[1]);
        assert_eq!("(A1)", format!("{}", inst.operands[0]));
        assert_eq!("D2", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\t(A1),D2", format!("{}", inst));
    }
    #[test]
    fn decodes_add_8_re() {
        let mem = MemoryVec { mem: vec![0xd511]} ;
        let inst = disassemble_first(&mem);

        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[0]);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[1]);
        assert_eq!("D2", format!("{}", inst.operands[0]));
        assert_eq!("(A1)", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\tD2,(A1)", format!("{}", inst));
    }

    #[test]
    fn two_word_decode_imm_ea() {
        // ADDI #$12,$34(A0) is 0x0668 0x0012 0x0034
        let opcode = 0x0668;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x0012, 0x0034]} ;
        let ops = super::decode_imm_ea(opcode, Size::Byte, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Byte, 0x12));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(0, 0x34));
    }
    #[test]
    fn three_word_decode_imm_ea_di() {
        // ADDI.L #$1F,$77(A6) is 0x06AE 0x0000 0x001F 0x0077
        let opcode = 0x06AE;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x0000, 0x001F, 0x0077]} ;
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(6, 0x77));
    }
    #[test]
    fn three_word_decode_imm_ea_ix() {
        // ADDI.L #$1F00A4,52(A5,D2) is 0x06B5 0x001F 0x00A4 0x2034
        let opcode = 0x06B5;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x001F, 0x00A4, 0x2034]} ;
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithIndex(5, 2, 0x34));
    }
    #[test]
    fn four_word_decode_imm_ea_al() {
        // ADDI.L #$1F00A4,$12345678 is 0x06B9 0x001F 0x00A4 0x1234 0x5678
        let opcode = 0x06B9;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x001F, 0x00A4, 0x1234, 0x5678]} ;
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AbsoluteLong(0x12345678));
    }
}

<commit_msg>parse_extension_word -> decode_extension_word<commit_after>use operand::Operand;
use memory::Memory;
use constants::*;
use super::{Result, Size, Exception,OpcodeInstance,generate};

fn decode_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Operand {
    let mode = ((opcode >> 3) & 7) as u8;
    let reg_y = (opcode & 7) as u8;
    match mode {
        0b000 => Operand::DataRegisterDirect(reg_y),
        0b001 => Operand::AddressRegisterDirect(reg_y),
        0b010 => Operand::AddressRegisterIndirect(reg_y),
        0b011 => Operand::AddressRegisterIndirectWithPostincrement(reg_y),
        0b100 => Operand::AddressRegisterIndirectWithPredecrement(reg_y),
        0b101 => Operand::AddressRegisterIndirectWithDisplacement(reg_y, mem.read_word(pc+2) as i16),
        0b110 => {
            let (indexinfo, displacement) = decode_extension_word(mem.read_word(pc+2));
            Operand::AddressRegisterIndirectWithIndex(reg_y, indexinfo, displacement)
            },
        0b111 => match reg_y {
            0b010 => Operand::PcWithDisplacement(mem.read_word(pc+2) as i16),
            0b011 => {
                let (indexinfo, displacement) = decode_extension_word(mem.read_word(pc+2));
                Operand::PcWithIndex(indexinfo, displacement)
                },
            0b000 => Operand::AbsoluteWord(mem.read_word(pc+2)),
            0b001 => Operand::AbsoluteLong((mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
            0b100 =>
                match size {
                    Size::Byte => Operand::Immediate(size, (mem.read_word(pc+2) & 0xFF) as u32),
                    Size::Word => Operand::Immediate(size, mem.read_word(pc+2) as u32),
                    Size::Long => Operand::Immediate(size, (mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
                    Size::Unsized => panic!("unsized Immediate"),
                },
            _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
        },
        _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
    }
}
fn decode_extension_word(extension: u16) -> (u8, i8) {
    // top four bits = (D/A RRR) matches our register array layout
    let xreg_ndx_size = (extension>>12) as u8;
    let displacement = extension as i8;
    (xreg_ndx_size, displacement)
}
fn decode_dx(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::DataRegisterDirect(((opcode >> 9) & 7) as u8)
}
fn decode_ax(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::AddressRegisterDirect(((opcode >> 9) & 7) as u8)
}
fn decode_imm(size: Size, pc: u32, mem: &Memory) -> Operand {
    match size {
        Size::Byte => Operand::Immediate(size, (mem.read_word(pc+2) & 0xFF) as u32),
        Size::Word => Operand::Immediate(size, mem.read_word(pc+2) as u32),
        Size::Long => Operand::Immediate(size, (mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
        Size::Unsized => panic!("unsized Immediate"),
    }
}
pub fn decode_ea_dx(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_dx(opcode, pc, mem)]
}
pub fn decode_ea_ax(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_ax(opcode, pc, mem)]
}
pub fn decode_dx_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_dx(opcode, pc, mem), decode_ea(opcode, size, pc, mem)]
}
pub fn decode_imm_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    let imm = decode_imm(size, pc, mem);
    vec![imm, decode_ea(opcode, size, pc + imm.extension_words()*2, mem)]
}

/* Check if opcode is using a valid ea mode */
fn valid_ea(opcode: u16, mask: u16) -> bool
{
    if mask == 0 {
        true
    } else {
        match opcode & 0x3f {
            0x00 ... 0x07 => (mask & EA_DATA_REGISTER_DIRECT) != 0,
            0x08 ... 0x0f => (mask & EA_ADDRESS_REGISTER_DIRECT) != 0,
            0x10 ... 0x17 => (mask & EA_ADDRESS_REGISTER_INDIRECT) != 0,
            0x18 ... 0x1f => (mask & EA_ARI_POSTINCREMENT) != 0,
            0x20 ... 0x27 => (mask & EA_ARI_PREDECREMENT) != 0,
            0x28 ... 0x2f => (mask & EA_ARI_DISPLACEMENT) != 0,
            0x30 ... 0x37 => (mask & EA_ARI_INDEX) != 0,
            0x38 => (mask & EA_ABSOLUTE_SHORT) != 0,
            0x39 => (mask & EA_ABSOLUTE_LONG) != 0,
            0x3a => (mask & EA_PC_DISPLACEMENT) != 0,
            0x3b => (mask & EA_PC_INDEX) != 0,
            0x3c => (mask & EA_IMMEDIATE) != 0,
            _ => false
        }
    }
}

pub fn disassemble_first(mem: &Memory) -> OpcodeInstance {
    disassemble(0, mem).unwrap()
}

pub fn disassemble(pc: u32, mem: &Memory) -> Result<OpcodeInstance> {
    let optable = generate();
    let opcode = mem.read_word(pc);
    // println!("opcode read was {:04x}", opcode);
    for op in optable {
        if ((opcode as u32) & op.mask) == op.matching && valid_ea(opcode, op.ea_mask) {
            let decoder = op.decoder;
            return Ok(OpcodeInstance {mnemonic: op.mnemonic, size: op.size, operands: decoder(opcode, op.size, pc, mem)});
        }
    }
    Err(Exception::IllegalInstruction(opcode, pc))
}

#[cfg(test)]
mod tests {
    use operand::Operand;
    use memory::{MemoryVec, Memory};
    use super::{disassemble, disassemble_first};
    use super::super::{Size, Exception};
    use regex::Regex;
    use constants::*;

    extern crate itertools;
    use self::itertools::assert_equal;

    #[test]
    fn decodes_add_8_er() {
        let mem = MemoryVec { mem: vec![0xd411]} ;
        let inst = disassemble_first(&mem);
        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[0]);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[1]);
        assert_eq!("(A1)", format!("{}", inst.operands[0]));
        assert_eq!("D2", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\t(A1),D2", format!("{}", inst));
    }
    #[test]
    fn decodes_add_8_re() {
        let mem = MemoryVec { mem: vec![0xd511]} ;
        let inst = disassemble_first(&mem);

        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[0]);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[1]);
        assert_eq!("D2", format!("{}", inst.operands[0]));
        assert_eq!("(A1)", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\tD2,(A1)", format!("{}", inst));
    }

    #[test]
    fn two_word_decode_imm_ea() {
        // ADDI #$12,$34(A0) is 0x0668 0x0012 0x0034
        let opcode = 0x0668;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x0012, 0x0034]} ;
        let ops = super::decode_imm_ea(opcode, Size::Byte, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Byte, 0x12));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(0, 0x34));
    }
    #[test]
    fn three_word_decode_imm_ea_di() {
        // ADDI.L #$1F,$77(A6) is 0x06AE 0x0000 0x001F 0x0077
        let opcode = 0x06AE;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x0000, 0x001F, 0x0077]} ;
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(6, 0x77));
    }
    #[test]
    fn three_word_decode_imm_ea_ix() {
        // ADDI.L #$1F00A4,52(A5,D2) is 0x06B5 0x001F 0x00A4 0x2034
        let opcode = 0x06B5;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x001F, 0x00A4, 0x2034]} ;
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithIndex(5, 2, 0x34));
    }
    #[test]
    fn four_word_decode_imm_ea_al() {
        // ADDI.L #$1F00A4,$12345678 is 0x06B9 0x001F 0x00A4 0x1234 0x5678
        let opcode = 0x06B9;
        let dasm_mem = &mut MemoryVec { mem: vec![opcode, 0x001F, 0x00A4, 0x1234, 0x5678]} ;
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AbsoluteLong(0x12345678));
    }
}

<|endoftext|>


<commit_before>use operand::Operand;
use memory::Memory;
use constants::*;
use super::{Result, Size, Exception,OpcodeInstance,generate};

fn decode_destination_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Operand {
    let mode = ((opcode >> 6) & 0b111) as u8;
    let reg_y = ((opcode >> 9) & 0b111) as u8;
    effective_address(size, pc, mem, mode, reg_y)
}

fn decode_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Operand {
    let mode = ((opcode >> 3) & 0b111) as u8;
    let reg_y = (opcode & 0b111) as u8;
    effective_address(size, pc, mem, mode, reg_y)
}

fn effective_address(size: Size, pc: u32, mem: &Memory, mode: u8, reg_y: u8) -> Operand {
    match mode {
        0b000 => Operand::DataRegisterDirect(reg_y),
        0b001 => Operand::AddressRegisterDirect(reg_y),
        0b010 => Operand::AddressRegisterIndirect(reg_y),
        0b011 => Operand::AddressRegisterIndirectWithPostincrement(reg_y),
        0b100 => Operand::AddressRegisterIndirectWithPredecrement(reg_y),
        0b101 => Operand::AddressRegisterIndirectWithDisplacement(reg_y, mem.read_word(pc + 2) as i16),
        0b110 => {
            let (indexinfo, displacement) = decode_extension_word(mem.read_word(pc + 2));
            Operand::AddressRegisterIndirectWithIndex(reg_y, indexinfo, displacement)
        },
        0b111 => match reg_y {
            0b010 => Operand::PcWithDisplacement(mem.read_word(pc + 2) as i16),
            0b011 => {
                let (indexinfo, displacement) = decode_extension_word(mem.read_word(pc + 2));
                Operand::PcWithIndex(indexinfo, displacement)
            },
            0b000 => Operand::AbsoluteWord(mem.read_word(pc + 2)),
            0b001 => Operand::AbsoluteLong((mem.read_word(pc + 2) as u32) << 16 | mem.read_word(pc + 4) as u32),
            0b100 =>
                match size {
                    Size::Byte => Operand::Immediate(size, (mem.read_word(pc + 2) & 0xFF) as u32),
                    Size::Word => Operand::Immediate(size, mem.read_word(pc + 2) as u32),
                    Size::Long => Operand::Immediate(size, (mem.read_word(pc + 2) as u32) << 16 | mem.read_word(pc + 4) as u32),
                    Size::Unsized => panic!("unsized Immediate"),
                },
            _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
        },
        _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
    }
}

fn decode_extension_word(extension: u16) -> (u8, i8) {
    // top four bits = (D/A RRR) matches our register array layout
    let xreg_ndx_size = (extension>>12) as u8;
    let displacement = extension as i8;
    (xreg_ndx_size, displacement)
}
#[allow(unused_variables)]
fn decode_dx(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::DataRegisterDirect(((opcode >> 9) & 7) as u8)
}
#[allow(unused_variables)]
fn decode_ax(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::AddressRegisterDirect(((opcode >> 9) & 7) as u8)
}
fn decode_imm(size: Size, pc: u32, mem: &Memory) -> Operand {
    match size {
        Size::Byte => Operand::Immediate(size, (mem.read_word(pc+2) & 0xFF) as u32),
        Size::Word => Operand::Immediate(size, mem.read_word(pc+2) as u32),
        Size::Long => Operand::Immediate(size, (mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
        Size::Unsized => panic!("unsized Immediate"),
    }
}
pub fn decode_ea_sr(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), Operand::StatusRegister(Size::Word)]
}
pub fn decode_ea_ccr(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), Operand::StatusRegister(Size::Byte)]
}
pub fn decode_ea_dx(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_dx(opcode, pc, mem)]
}
pub fn decode_ea_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    let src = decode_ea(opcode, size, pc, mem);
    let dst = decode_destination_ea(opcode, size, pc + src.extension_words() * 2, mem);
    vec![src, dst]
}
pub fn decode_ea_ax(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_ax(opcode, pc, mem)]
}
pub fn decode_dx_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_dx(opcode, pc, mem), decode_ea(opcode, size, pc, mem)]
}
pub fn decode_imm_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    let imm = decode_imm(size, pc, mem);
    vec![imm, decode_ea(opcode, size, pc + imm.extension_words()*2, mem)]
}

/* Check if opcode is using a valid ea mode */
pub fn valid_ea(opcode: u16, ea_mask: u16) -> bool
{
    if ea_mask == 0 {
        true
    } else {
        // ea is the lower six bits of the opcode
        match opcode & 0x3f {
            0x00 ... 0x07 => (ea_mask & EA_DATA_REGISTER_DIRECT) != 0,
            0x08 ... 0x0f => (ea_mask & EA_ADDRESS_REGISTER_DIRECT) != 0,
            0x10 ... 0x17 => (ea_mask & EA_ADDRESS_REGISTER_INDIRECT) != 0,
            0x18 ... 0x1f => (ea_mask & EA_ARI_POSTINCREMENT) != 0,
            0x20 ... 0x27 => (ea_mask & EA_ARI_PREDECREMENT) != 0,
            0x28 ... 0x2f => (ea_mask & EA_ARI_DISPLACEMENT) != 0,
            0x30 ... 0x37 => (ea_mask & EA_ARI_INDEX) != 0,
            0x38 => (ea_mask & EA_ABSOLUTE_SHORT) != 0,
            0x39 => (ea_mask & EA_ABSOLUTE_LONG) != 0,
            0x3a => (ea_mask & EA_PC_DISPLACEMENT) != 0,
            0x3b => (ea_mask & EA_PC_INDEX) != 0,
            0x3c => (ea_mask & EA_IMMEDIATE) != 0,
            _ => false
        }
    }
}

pub fn valid_ea_ea(opcode: u16, mask: u16) -> bool
{
    if mask == 0 {
        true
    } else {
        // normally ea are the 6 least significant bits structured as mmmrrr and
        // we need to swap and shift that into place from rrrmmm000000
        let mode = ((opcode >> 3) & 0b111000);
        let reg_y = ((opcode >> 9) & 0b111);
        let swapped_mask = ((mask >> 3) & 0b111000) | ((mask >> 9) & 0b111);
        if opcode == 0x2e7c {
            println!("valid_ea_ea: pcode {:016b}, mask {:016b}, mode | reg_y {:016b} swapped_mask", opcode, mask, mode | reg_y)
        }
        valid_ea(opcode, mask) && valid_ea(mode | reg_y, swapped_mask)
    }
}

pub fn disassemble_first(mem: &Memory) -> OpcodeInstance {
    disassemble(0, mem).unwrap()
}

pub fn disassemble(pc: u32, mem: &Memory) -> Result<OpcodeInstance> {
    let optable = generate();
    let opcode = mem.read_word(pc);
    // println!("opcode read was {:04x}", opcode);
    for op in optable {
        if ((opcode as u32) & op.mask) == op.matching && (op.validator)(opcode, op.ea_mask) {
            let decoder = op.decoder;
            return Ok(OpcodeInstance {mnemonic: op.mnemonic, size: op.size, operands: decoder(opcode, op.size, pc, mem)});
        } else if ((opcode as u32) & op.mask) == op.matching {
            println!("{:04x}: match for {} without passing validator", opcode, op.mnemonic);
        }
    }
    Err(Exception::IllegalInstruction(opcode, pc))
}

#[cfg(test)]
mod tests {
    use operand::Operand;
    use memory::MemoryVec;
    use super::disassemble_first;
    use super::super::Size;

    #[test]
    fn decodes_add_8_er() {
        let mem = MemoryVec::new16(0, vec![0xd411]);
        let inst = disassemble_first(&mem);
        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[0]);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[1]);
        assert_eq!("(A1)", format!("{}", inst.operands[0]));
        assert_eq!("D2", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\t(A1),D2", format!("{}", inst));
    }
    #[test]
    fn decodes_add_8_re() {
        let mem = MemoryVec::new16(0, vec![0xd511]);
        let inst = disassemble_first(&mem);

        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[0]);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[1]);
        assert_eq!("D2", format!("{}", inst.operands[0]));
        assert_eq!("(A1)", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\tD2,(A1)", format!("{}", inst));
    }

    #[test]
    fn two_word_decode_imm_ea() {
        // ADDI #$12,$34(A0) is 0x0668 0x0012 0x0034
        let opcode = 0x0668;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x0012, 0x0034]) ;
        let ops = super::decode_imm_ea(opcode, Size::Byte, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Byte, 0x12));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(0, 0x34));
    }
    #[test]
    fn three_word_decode_imm_ea_di() {
        // ADDI.L #$1F,$77(A6) is 0x06AE 0x0000 0x001F 0x0077
        let opcode = 0x06AE;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x0000, 0x001F, 0x0077]);
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(6, 0x77));
    }
    #[test]
    fn three_word_decode_imm_ea_ix() {
        // ADDI.L #$1F00A4,52(A5,D2) is 0x06B5 0x001F 0x00A4 0x2034
        let opcode = 0x06B5;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x001F, 0x00A4, 0x2034]);
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithIndex(5, 2, 0x34));
    }
    #[test]
    fn four_word_decode_imm_ea_al() {
        // ADDI.L #$1F00A4,$12345678 is 0x06B9 0x001F 0x00A4 0x1234 0x5678
        let opcode = 0x06B9;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x001F, 0x00A4, 0x1234, 0x5678]);
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AbsoluteLong(0x12345678));
    }
}

<commit_msg>Check for mask/opcode inconsistency<commit_after>use operand::Operand;
use memory::Memory;
use constants::*;
use super::{Result, Size, Exception,OpcodeInstance,generate};

fn decode_destination_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Operand {
    let mode = ((opcode >> 6) & 0b111) as u8;
    let reg_y = ((opcode >> 9) & 0b111) as u8;
    effective_address(size, pc, mem, mode, reg_y)
}

fn decode_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Operand {
    let mode = ((opcode >> 3) & 0b111) as u8;
    let reg_y = (opcode & 0b111) as u8;
    effective_address(size, pc, mem, mode, reg_y)
}

fn effective_address(size: Size, pc: u32, mem: &Memory, mode: u8, reg_y: u8) -> Operand {
    match mode {
        0b000 => Operand::DataRegisterDirect(reg_y),
        0b001 => Operand::AddressRegisterDirect(reg_y),
        0b010 => Operand::AddressRegisterIndirect(reg_y),
        0b011 => Operand::AddressRegisterIndirectWithPostincrement(reg_y),
        0b100 => Operand::AddressRegisterIndirectWithPredecrement(reg_y),
        0b101 => Operand::AddressRegisterIndirectWithDisplacement(reg_y, mem.read_word(pc + 2) as i16),
        0b110 => {
            let (indexinfo, displacement) = decode_extension_word(mem.read_word(pc + 2));
            Operand::AddressRegisterIndirectWithIndex(reg_y, indexinfo, displacement)
        },
        0b111 => match reg_y {
            0b010 => Operand::PcWithDisplacement(mem.read_word(pc + 2) as i16),
            0b011 => {
                let (indexinfo, displacement) = decode_extension_word(mem.read_word(pc + 2));
                Operand::PcWithIndex(indexinfo, displacement)
            },
            0b000 => Operand::AbsoluteWord(mem.read_word(pc + 2)),
            0b001 => Operand::AbsoluteLong((mem.read_word(pc + 2) as u32) << 16 | mem.read_word(pc + 4) as u32),
            0b100 =>
                match size {
                    Size::Byte => Operand::Immediate(size, (mem.read_word(pc + 2) & 0xFF) as u32),
                    Size::Word => Operand::Immediate(size, mem.read_word(pc + 2) as u32),
                    Size::Long => Operand::Immediate(size, (mem.read_word(pc + 2) as u32) << 16 | mem.read_word(pc + 4) as u32),
                    Size::Unsized => panic!("unsized Immediate"),
                },
            _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
        },
        _ => panic!("Unknown addressing mode {:03b} reg {:03b}", mode, reg_y),
    }
}

fn decode_extension_word(extension: u16) -> (u8, i8) {
    // top four bits = (D/A RRR) matches our register array layout
    let xreg_ndx_size = (extension>>12) as u8;
    let displacement = extension as i8;
    (xreg_ndx_size, displacement)
}
#[allow(unused_variables)]
fn decode_dx(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::DataRegisterDirect(((opcode >> 9) & 7) as u8)
}
#[allow(unused_variables)]
fn decode_ax(opcode: u16, pc: u32, mem: &Memory) -> Operand {
    Operand::AddressRegisterDirect(((opcode >> 9) & 7) as u8)
}
fn decode_imm(size: Size, pc: u32, mem: &Memory) -> Operand {
    match size {
        Size::Byte => Operand::Immediate(size, (mem.read_word(pc+2) & 0xFF) as u32),
        Size::Word => Operand::Immediate(size, mem.read_word(pc+2) as u32),
        Size::Long => Operand::Immediate(size, (mem.read_word(pc+2) as u32) << 16 | mem.read_word(pc+4) as u32),
        Size::Unsized => panic!("unsized Immediate"),
    }
}
pub fn decode_ea_sr(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), Operand::StatusRegister(Size::Word)]
}
pub fn decode_ea_ccr(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), Operand::StatusRegister(Size::Byte)]
}
pub fn decode_ea_dx(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_dx(opcode, pc, mem)]
}
pub fn decode_ea_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    let src = decode_ea(opcode, size, pc, mem);
    let dst = decode_destination_ea(opcode, size, pc + src.extension_words() * 2, mem);
    vec![src, dst]
}
pub fn decode_ea_ax(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_ea(opcode, size, pc, mem), decode_ax(opcode, pc, mem)]
}
pub fn decode_dx_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    vec![decode_dx(opcode, pc, mem), decode_ea(opcode, size, pc, mem)]
}
pub fn decode_imm_ea(opcode: u16, size: Size, pc: u32, mem: &Memory) -> Vec<Operand> {
    let imm = decode_imm(size, pc, mem);
    vec![imm, decode_ea(opcode, size, pc + imm.extension_words()*2, mem)]
}

/* Check if opcode is using a valid ea mode */
pub fn valid_ea(opcode: u16, ea_mask: u16) -> bool
{
    if ea_mask == 0 {
        true
    } else {
        // ea is the lower six bits of the opcode
        match opcode & 0x3f {
            0x00 ... 0x07 => (ea_mask & EA_DATA_REGISTER_DIRECT) != 0,
            0x08 ... 0x0f => (ea_mask & EA_ADDRESS_REGISTER_DIRECT) != 0,
            0x10 ... 0x17 => (ea_mask & EA_ADDRESS_REGISTER_INDIRECT) != 0,
            0x18 ... 0x1f => (ea_mask & EA_ARI_POSTINCREMENT) != 0,
            0x20 ... 0x27 => (ea_mask & EA_ARI_PREDECREMENT) != 0,
            0x28 ... 0x2f => (ea_mask & EA_ARI_DISPLACEMENT) != 0,
            0x30 ... 0x37 => (ea_mask & EA_ARI_INDEX) != 0,
            0x38 => (ea_mask & EA_ABSOLUTE_SHORT) != 0,
            0x39 => (ea_mask & EA_ABSOLUTE_LONG) != 0,
            0x3a => (ea_mask & EA_PC_DISPLACEMENT) != 0,
            0x3b => (ea_mask & EA_PC_INDEX) != 0,
            0x3c => (ea_mask & EA_IMMEDIATE) != 0,
            _ => false
        }
    }
}

pub fn valid_ea_ea(opcode: u16, mask: u16) -> bool
{
    if mask == 0 {
        true
    } else {
        // normally ea are the 6 least significant bits structured as mmmrrr and
        // we need to swap and shift that into place from rrrmmm000000
        let mode = ((opcode >> 3) & 0b111000);
        let reg_y = ((opcode >> 9) & 0b111);
        let swapped_mask = ((mask >> 3) & 0b111000) | ((mask >> 9) & 0b111);
        if opcode == 0x2e7c {
            println!("valid_ea_ea: pcode {:016b}, mask {:016b}, mode | reg_y {:016b} swapped_mask", opcode, mask, mode | reg_y)
        }
        valid_ea(opcode, mask) && valid_ea(mode | reg_y, swapped_mask)
    }
}

pub fn disassemble_first(mem: &Memory) -> OpcodeInstance {
    disassemble(0, mem).unwrap()
}

pub fn disassemble(pc: u32, mem: &Memory) -> Result<OpcodeInstance> {
    let optable = generate();
    let opcode = mem.read_word(pc);
    // println!("opcode read was {:04x}", opcode);
    for op in optable {
        // check for mask/opcode inconsistency
        assert!(op.mask & op.matching == op.matching, format!("mask/matching mismatch {:04x} & {:04x} for {}{}", op.mask, op.matching, op.mnemonic, op.size));
        if ((opcode as u32) & op.mask) == op.matching && (op.validator)(opcode, op.ea_mask) {
            let decoder = op.decoder;
            return Ok(OpcodeInstance {mnemonic: op.mnemonic, size: op.size, operands: decoder(opcode, op.size, pc, mem)});
        } else if ((opcode as u32) & op.mask) == op.matching {
            println!("{:04x}: match for {} without passing validator", opcode, op.mnemonic);
        }
    }
    Err(Exception::IllegalInstruction(opcode, pc))
}

#[cfg(test)]
mod tests {
    use operand::Operand;
    use memory::MemoryVec;
    use super::disassemble_first;
    use super::super::Size;

    #[test]
    fn decodes_add_8_er() {
        let mem = MemoryVec::new16(0, vec![0xd411]);
        let inst = disassemble_first(&mem);
        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[0]);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[1]);
        assert_eq!("(A1)", format!("{}", inst.operands[0]));
        assert_eq!("D2", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\t(A1),D2", format!("{}", inst));
    }
    #[test]
    fn decodes_add_8_re() {
        let mem = MemoryVec::new16(0, vec![0xd511]);
        let inst = disassemble_first(&mem);

        assert_eq!("ADD", inst.mnemonic);
        assert_eq!(Size::Byte, inst.size);
        assert_eq!(Operand::DataRegisterDirect(2), inst.operands[0]);
        assert_eq!(Operand::AddressRegisterIndirect(1), inst.operands[1]);
        assert_eq!("D2", format!("{}", inst.operands[0]));
        assert_eq!("(A1)", format!("{}", inst.operands[1]));
        assert_eq!("ADD.B\tD2,(A1)", format!("{}", inst));
    }

    #[test]
    fn two_word_decode_imm_ea() {
        // ADDI #$12,$34(A0) is 0x0668 0x0012 0x0034
        let opcode = 0x0668;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x0012, 0x0034]) ;
        let ops = super::decode_imm_ea(opcode, Size::Byte, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Byte, 0x12));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(0, 0x34));
    }
    #[test]
    fn three_word_decode_imm_ea_di() {
        // ADDI.L #$1F,$77(A6) is 0x06AE 0x0000 0x001F 0x0077
        let opcode = 0x06AE;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x0000, 0x001F, 0x0077]);
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithDisplacement(6, 0x77));
    }
    #[test]
    fn three_word_decode_imm_ea_ix() {
        // ADDI.L #$1F00A4,52(A5,D2) is 0x06B5 0x001F 0x00A4 0x2034
        let opcode = 0x06B5;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x001F, 0x00A4, 0x2034]);
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AddressRegisterIndirectWithIndex(5, 2, 0x34));
    }
    #[test]
    fn four_word_decode_imm_ea_al() {
        // ADDI.L #$1F00A4,$12345678 is 0x06B9 0x001F 0x00A4 0x1234 0x5678
        let opcode = 0x06B9;
        let dasm_mem = &mut MemoryVec::new16(0, vec![opcode, 0x001F, 0x00A4, 0x1234, 0x5678]);
        let ops = super::decode_imm_ea(opcode, Size::Long, 0, dasm_mem);
        assert_eq!(ops[0], Operand::Immediate(Size::Long, 0x1F00A4));
        assert_eq!(ops[1], Operand::AbsoluteLong(0x12345678));
    }
}

<|endoftext|>


<commit_before>use std::default::Default;
use std::fs;
use std::path::Path;

use core::{Profiles, Workspace};
use util::Config;
use util::errors::{CargoResult, CargoResultExt};
use util::paths;
use ops::{self, BuildConfig, Context, Kind, Unit};

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    pub spec: Vec<String>,
    pub target: Option<String>,
    pub release: bool,
}

/// Cleans the project from build artifacts.
pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> {
    let target_dir = ws.target_dir();
    let config = ws.config();

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        let target_dir = target_dir.into_path_unlocked();
        return rm_rf(&target_dir, config);
    }

    let (packages, resolve) = ops::resolve_ws(ws)?;

    let profiles = ws.profiles();
    let host_triple = opts.config.rustc()?.host.clone();
    let mut cx = Context::new(
        ws,
        &resolve,
        &packages,
        opts.config,
        BuildConfig {
            host_triple,
            requested_target: opts.target.clone(),
            release: opts.release,
            jobs: 1,
            ..BuildConfig::default()
        },
        profiles,
    )?;
    let mut units = Vec::new();

    for spec in opts.spec.iter() {
        // Translate the spec to a Package
        let pkgid = resolve.query(spec)?;
        let pkg = packages.get(pkgid)?;

        // Generate all relevant `Unit` targets for this package
        for target in pkg.targets() {
            for kind in [Kind::Host, Kind::Target].iter() {
                let Profiles {
                    ref release,
                    ref dev,
                    ref test,
                    ref bench,
                    ref doc,
                    ref custom_build,
                    ref test_deps,
                    ref bench_deps,
                    ref check,
                    ref check_test,
                    ref doctest,
                } = *profiles;
                let profiles = [
                    release,
                    dev,
                    test,
                    bench,
                    doc,
                    custom_build,
                    test_deps,
                    bench_deps,
                    check,
                    check_test,
                    doctest,
                ];
                for profile in profiles.iter() {
                    units.push(Unit {
                        pkg,
                        target,
                        profile,
                        kind: *kind,
                    });
                }
            }
        }
    }

    cx.probe_target_info(&units)?;

    for unit in units.iter() {
        rm_rf(&cx.fingerprint_dir(unit), config)?;
        if unit.target.is_custom_build() {
            if unit.profile.run_custom_build {
                rm_rf(&cx.build_script_out_dir(unit), config)?;
            } else {
                rm_rf(&cx.build_script_dir(unit), config)?;
            }
            continue;
        }

        for &(ref src, ref link_dst, _) in cx.target_filenames(unit)?.iter() {
            rm_rf(src, config)?;
            if let Some(ref dst) = *link_dst {
                rm_rf(dst, config)?;
            }
        }
    }

    Ok(())
}

fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
    let m = fs::metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_dir_all(path).chain_err(|| format_err!("could not remove build directory"))?;
    } else if m.is_ok() {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_file(path).chain_err(|| format_err!("failed to remove build artifact"))?;
    }
    Ok(())
}
<commit_msg>Add docstrings to CleanOptions<commit_after>use std::default::Default;
use std::fs;
use std::path::Path;

use core::{Profiles, Workspace};
use util::Config;
use util::errors::{CargoResult, CargoResultExt};
use util::paths;
use ops::{self, BuildConfig, Context, Kind, Unit};

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    /// A list of packages to clean. If empty, everything is cleaned.
    pub spec: Vec<String>,
    /// The target arch triple to clean, or None for the host arch
    pub target: Option<String>,
    /// Whether to clean the release directory
    pub release: bool,
}

/// Cleans the project from build artifacts.
pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> {
    let target_dir = ws.target_dir();
    let config = ws.config();

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        let target_dir = target_dir.into_path_unlocked();
        return rm_rf(&target_dir, config);
    }

    let (packages, resolve) = ops::resolve_ws(ws)?;

    let profiles = ws.profiles();
    let host_triple = opts.config.rustc()?.host.clone();
    let mut cx = Context::new(
        ws,
        &resolve,
        &packages,
        opts.config,
        BuildConfig {
            host_triple,
            requested_target: opts.target.clone(),
            release: opts.release,
            jobs: 1,
            ..BuildConfig::default()
        },
        profiles,
    )?;
    let mut units = Vec::new();

    for spec in opts.spec.iter() {
        // Translate the spec to a Package
        let pkgid = resolve.query(spec)?;
        let pkg = packages.get(pkgid)?;

        // Generate all relevant `Unit` targets for this package
        for target in pkg.targets() {
            for kind in [Kind::Host, Kind::Target].iter() {
                let Profiles {
                    ref release,
                    ref dev,
                    ref test,
                    ref bench,
                    ref doc,
                    ref custom_build,
                    ref test_deps,
                    ref bench_deps,
                    ref check,
                    ref check_test,
                    ref doctest,
                } = *profiles;
                let profiles = [
                    release,
                    dev,
                    test,
                    bench,
                    doc,
                    custom_build,
                    test_deps,
                    bench_deps,
                    check,
                    check_test,
                    doctest,
                ];
                for profile in profiles.iter() {
                    units.push(Unit {
                        pkg,
                        target,
                        profile,
                        kind: *kind,
                    });
                }
            }
        }
    }

    cx.probe_target_info(&units)?;

    for unit in units.iter() {
        rm_rf(&cx.fingerprint_dir(unit), config)?;
        if unit.target.is_custom_build() {
            if unit.profile.run_custom_build {
                rm_rf(&cx.build_script_out_dir(unit), config)?;
            } else {
                rm_rf(&cx.build_script_dir(unit), config)?;
            }
            continue;
        }

        for &(ref src, ref link_dst, _) in cx.target_filenames(unit)?.iter() {
            rm_rf(src, config)?;
            if let Some(ref dst) = *link_dst {
                rm_rf(dst, config)?;
            }
        }
    }

    Ok(())
}

fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
    let m = fs::metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_dir_all(path).chain_err(|| format_err!("could not remove build directory"))?;
    } else if m.is_ok() {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_file(path).chain_err(|| format_err!("failed to remove build artifact"))?;
    }
    Ok(())
}
<|endoftext|>