<commit_before>use std::path::Path;

use regex::Regex;

use crate::rustc_stderr::Level;

use color_eyre::eyre::{bail, ensure, eyre, Result};

#[cfg(test)]
mod tests;

/// This crate supports various magic comments that get parsed as file-specific
/// configuration values. This struct parses them all in one go and then they
/// get processed by their respective use sites.
#[derive(Default, Debug)]
pub(crate) struct Comments {
    /// List of revision names to execute. Can only be speicified once
    pub revisions: Option<Vec<String>>,
    /// Don't run this test if any of these filters apply
    pub ignore: Vec<Condition>,
    /// Only run this test if all of these filters apply
    pub only: Vec<Condition>,
    /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar
    pub stderr_per_bitwidth: bool,
    /// Additional flags to pass to the executable
    pub compile_flags: Vec<String>,
    /// Additional env vars to set for the executable
    pub env_vars: Vec<(String, String)>,
    /// Normalizations to apply to the stderr output before emitting it to disk
    pub normalize_stderr: Vec<(Regex, String)>,
    /// An arbitrary pattern to look for in the stderr.
    pub error_pattern: Option<(String, usize)>,
    pub error_matches: Vec<ErrorMatch>,
}

/// The conditions used for "ignore" and "only" filters.
#[derive(Debug)]
pub(crate) enum Condition {
    /// The given string must appear in the target.
    Target(String),
    /// Tests that the bitwidth is the given one.
    Bitwidth(u8),
}

#[derive(Debug)]
pub(crate) struct ErrorMatch {
    pub matched: String,
    pub revision: Option<String>,
    pub level: Option<Level>,
    /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found).
    pub definition_line: usize,
    /// The line this pattern is expecting to find a message in.
    pub line: usize,
}

impl Condition {
    fn parse(c: &str) -> Self {
        if let Some(bits) = c.strip_suffix("bit") {
            let bits: u8 = bits.parse().expect(
                "ignore/only filter ending in 'bit' must be of the form 'Nbit' for some integer N",
            );
            Condition::Bitwidth(bits)
        } else {
            Condition::Target(c.to_owned())
        }
    }
}

impl Comments {
    pub(crate) fn parse_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        Self::parse(path, &content)
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::default();

        let mut fallthrough_to = None; // The line that a `|` will refer to.
        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| {
                err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display()))
            })?;
        }
        Ok(this)
    }

    fn parse_checked_line(
        &mut self,
        l: usize,
        fallthrough_to: &mut Option<usize>,
        line: &str,
    ) -> Result<()> {
        if let Some((_, command)) = line.split_once("//@") {
            self.parse_command(command.trim(), l)
        } else if let Some((_, pattern)) = line.split_once("//~") {
            self.parse_pattern(pattern, fallthrough_to, l)
        } else if let Some((_, pattern)) = line.split_once("//[") {
            self.parse_revisioned_pattern(pattern, fallthrough_to, l)
        } else {
            *fallthrough_to = None;
            Ok(())
        }
    }

    fn parse_command(&mut self, command: &str, l: usize) -> Result<()> {
        // Commands are letters or dashes, grab everything until the first character that is neither of those.
        let (command, args) =
            match command.chars().position(|c: char| !c.is_alphanumeric() && c != '-') {
                None => (command, ""),
                Some(i) => {
                    let (command, args) = command.split_at(i);
                    let mut args = args.chars();
                    // Commands are separated from their arguments by ':' or ' '
                    let next = args
                        .next()
                        .expect("the `position` above guarantees that there is at least one char");
                    ensure!(next == ':', "test command must be followed by : (or end the line)");
                    (command, args.as_str().trim())
                }
            };

        match command {
            "revisions" => {
                ensure!(self.revisions.is_none(), "cannot specifiy revisions twice");
                self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
            }
            "compile-flags" => {
                self.compile_flags.extend(args.split_whitespace().map(|s| s.to_string()));
            }
            "rustc-env" =>
                for env in args.split_whitespace() {
                    let (k, v) = env.split_once('=').ok_or_else(|| {
                        eyre!("environment variables must be key/value pairs separated by a `=`")
                    })?;
                    self.env_vars.push((k.to_string(), v.to_string()));
                },
            "normalize-stderr-test" => {
                /// Parses a string literal. `s` has to start with `"`; everything until the next `"` is
                /// returned in the first component. `\` can be used to escape arbitrary character.
                /// Second return component is the rest of the string with leading whitespace removed.
                fn parse_str(s: &str) -> Result<(&str, &str)> {
                    let mut chars = s.char_indices();
                    match chars.next().ok_or_else(|| eyre!("missing arguments"))?.1 {
                        '"' => {
                            let s = chars.as_str();
                            let mut escaped = false;
                            for (i, c) in chars {
                                if escaped {
                                    // Accept any character as literal after a `\`.
                                    escaped = false;
                                } else if c == '"' {
                                    return Ok((&s[..(i - 1)], s[i..].trim_start()));
                                } else {
                                    escaped = c == '\\';
                                }
                            }
                            bail!("no closing quotes found for {s}")
                        }
                        c => bail!("expected '\"', got {c}"),
                    }
                }

                let (from, rest) = parse_str(args)?;

                let to = rest.strip_prefix("->").ok_or_else(|| {
                    eyre!("normalize-stderr-test needs a pattern and replacement separated by `->`")
                })?.trim_start();
                let (to, rest) = parse_str(to)?;

                ensure!(rest.is_empty(), "trailing text after pattern replacement: {rest}");

                let from = Regex::new(from)?;
                self.normalize_stderr.push((from, to.to_string()));
            }
            "error-pattern" => {
                ensure!(
                    self.error_pattern.is_none(),
                    "cannot specifiy error_pattern twice, previous: {:?}",
                    self.error_pattern
                );
                self.error_pattern = Some((args.trim().to_string(), l));
            }
            "stderr-per-bitwidth" => {
                // args are ignored (can be sue as comment)
                ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
                self.stderr_per_bitwidth = true;
            }
            command => {
                if let Some(s) = command.strip_prefix("ignore-") {
                    // args are ignored (can be sue as comment)
                    self.ignore.push(Condition::parse(s));
                    return Ok(());
                }

                if let Some(s) = command.strip_prefix("only-") {
                    // args are ignored (can be sue as comment)
                    self.only.push(Condition::parse(s));
                    return Ok(());
                }
                bail!("unknown command {command}");
            }
        }

        Ok(())
    }

    fn parse_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        self.parse_pattern_inner(pattern, fallthrough_to, None, l)
    }

    fn parse_revisioned_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        let (revision, pattern) =
            pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?;
        if let Some(pattern) = pattern.strip_prefix('~') {
            self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l)
        } else {
            bail!("revisioned pattern must have `~` following the `]`");
        }
    }

    // parse something like (?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE)?:?(?P<text>.*)
    fn parse_pattern_inner(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        revision: Option<String>,
        l: usize,
    ) -> Result<()> {
        let (match_line, pattern) =
            match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? {
                '|' =>
                    (
                        *fallthrough_to
                            .as_mut()
                            .ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?,
                        &pattern[1..],
                    ),
                '^' => {
                    let offset = pattern.chars().take_while(|&c| c == '^').count();
                    (l - offset, &pattern[offset..])
                }
                _ => (l, pattern),
            };

        let (level, pattern) = match pattern.trim_start().split_once(|c| matches!(c, ':' | ' ')) {
            None => (None, pattern),
            Some((level, pattern_without_level)) =>
                match level.parse().ok() {
                    Some(level) => (Some(level), pattern_without_level),
                    None => (None, pattern),
                },
        };

        let matched = pattern.trim().to_string();

        ensure!(!matched.is_empty(), "no pattern specified");

        *fallthrough_to = Some(match_line);

        self.error_matches.push(ErrorMatch {
            matched,
            revision,
            level,
            definition_line: l,
            line: match_line,
        });

        Ok(())
    }
}
<commit_msg>typo<commit_after>use std::path::Path;

use regex::Regex;

use crate::rustc_stderr::Level;

use color_eyre::eyre::{bail, ensure, eyre, Result};

#[cfg(test)]
mod tests;

/// This crate supports various magic comments that get parsed as file-specific
/// configuration values. This struct parses them all in one go and then they
/// get processed by their respective use sites.
#[derive(Default, Debug)]
pub(crate) struct Comments {
    /// List of revision names to execute. Can only be speicified once
    pub revisions: Option<Vec<String>>,
    /// Don't run this test if any of these filters apply
    pub ignore: Vec<Condition>,
    /// Only run this test if all of these filters apply
    pub only: Vec<Condition>,
    /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar
    pub stderr_per_bitwidth: bool,
    /// Additional flags to pass to the executable
    pub compile_flags: Vec<String>,
    /// Additional env vars to set for the executable
    pub env_vars: Vec<(String, String)>,
    /// Normalizations to apply to the stderr output before emitting it to disk
    pub normalize_stderr: Vec<(Regex, String)>,
    /// An arbitrary pattern to look for in the stderr.
    pub error_pattern: Option<(String, usize)>,
    pub error_matches: Vec<ErrorMatch>,
}

/// The conditions used for "ignore" and "only" filters.
#[derive(Debug)]
pub(crate) enum Condition {
    /// The given string must appear in the target.
    Target(String),
    /// Tests that the bitwidth is the given one.
    Bitwidth(u8),
}

#[derive(Debug)]
pub(crate) struct ErrorMatch {
    pub matched: String,
    pub revision: Option<String>,
    pub level: Option<Level>,
    /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found).
    pub definition_line: usize,
    /// The line this pattern is expecting to find a message in.
    pub line: usize,
}

impl Condition {
    fn parse(c: &str) -> Self {
        if let Some(bits) = c.strip_suffix("bit") {
            let bits: u8 = bits.parse().expect(
                "ignore/only filter ending in 'bit' must be of the form 'Nbit' for some integer N",
            );
            Condition::Bitwidth(bits)
        } else {
            Condition::Target(c.to_owned())
        }
    }
}

impl Comments {
    pub(crate) fn parse_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        Self::parse(path, &content)
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::default();

        let mut fallthrough_to = None; // The line that a `|` will refer to.
        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| {
                err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display()))
            })?;
        }
        Ok(this)
    }

    fn parse_checked_line(
        &mut self,
        l: usize,
        fallthrough_to: &mut Option<usize>,
        line: &str,
    ) -> Result<()> {
        if let Some((_, command)) = line.split_once("//@") {
            self.parse_command(command.trim(), l)
        } else if let Some((_, pattern)) = line.split_once("//~") {
            self.parse_pattern(pattern, fallthrough_to, l)
        } else if let Some((_, pattern)) = line.split_once("//[") {
            self.parse_revisioned_pattern(pattern, fallthrough_to, l)
        } else {
            *fallthrough_to = None;
            Ok(())
        }
    }

    fn parse_command(&mut self, command: &str, l: usize) -> Result<()> {
        // Commands are letters or dashes, grab everything until the first character that is neither of those.
        let (command, args) =
            match command.chars().position(|c: char| !c.is_alphanumeric() && c != '-') {
                None => (command, ""),
                Some(i) => {
                    let (command, args) = command.split_at(i);
                    let mut args = args.chars();
                    // Commands are separated from their arguments by ':' or ' '
                    let next = args
                        .next()
                        .expect("the `position` above guarantees that there is at least one char");
                    ensure!(next == ':', "test command must be followed by : (or end the line)");
                    (command, args.as_str().trim())
                }
            };

        match command {
            "revisions" => {
                ensure!(self.revisions.is_none(), "cannot specifiy revisions twice");
                self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
            }
            "compile-flags" => {
                self.compile_flags.extend(args.split_whitespace().map(|s| s.to_string()));
            }
            "rustc-env" =>
                for env in args.split_whitespace() {
                    let (k, v) = env.split_once('=').ok_or_else(|| {
                        eyre!("environment variables must be key/value pairs separated by a `=`")
                    })?;
                    self.env_vars.push((k.to_string(), v.to_string()));
                },
            "normalize-stderr-test" => {
                /// Parses a string literal. `s` has to start with `"`; everything until the next `"` is
                /// returned in the first component. `\` can be used to escape arbitrary character.
                /// Second return component is the rest of the string with leading whitespace removed.
                fn parse_str(s: &str) -> Result<(&str, &str)> {
                    let mut chars = s.char_indices();
                    match chars.next().ok_or_else(|| eyre!("missing arguments"))?.1 {
                        '"' => {
                            let s = chars.as_str();
                            let mut escaped = false;
                            for (i, c) in chars {
                                if escaped {
                                    // Accept any character as literal after a `\`.
                                    escaped = false;
                                } else if c == '"' {
                                    return Ok((&s[..(i - 1)], s[i..].trim_start()));
                                } else {
                                    escaped = c == '\\';
                                }
                            }
                            bail!("no closing quotes found for {s}")
                        }
                        c => bail!("expected '\"', got {c}"),
                    }
                }

                let (from, rest) = parse_str(args)?;

                let to = rest.strip_prefix("->").ok_or_else(|| {
                    eyre!("normalize-stderr-test needs a pattern and replacement separated by `->`")
                })?.trim_start();
                let (to, rest) = parse_str(to)?;

                ensure!(rest.is_empty(), "trailing text after pattern replacement: {rest}");

                let from = Regex::new(from)?;
                self.normalize_stderr.push((from, to.to_string()));
            }
            "error-pattern" => {
                ensure!(
                    self.error_pattern.is_none(),
                    "cannot specifiy error_pattern twice, previous: {:?}",
                    self.error_pattern
                );
                self.error_pattern = Some((args.trim().to_string(), l));
            }
            "stderr-per-bitwidth" => {
                // args are ignored (can be used as comment)
                ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
                self.stderr_per_bitwidth = true;
            }
            command => {
                if let Some(s) = command.strip_prefix("ignore-") {
                    // args are ignored (can be sue as comment)
                    self.ignore.push(Condition::parse(s));
                    return Ok(());
                }

                if let Some(s) = command.strip_prefix("only-") {
                    // args are ignored (can be sue as comment)
                    self.only.push(Condition::parse(s));
                    return Ok(());
                }
                bail!("unknown command {command}");
            }
        }

        Ok(())
    }

    fn parse_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        self.parse_pattern_inner(pattern, fallthrough_to, None, l)
    }

    fn parse_revisioned_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        let (revision, pattern) =
            pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?;
        if let Some(pattern) = pattern.strip_prefix('~') {
            self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l)
        } else {
            bail!("revisioned pattern must have `~` following the `]`");
        }
    }

    // parse something like (?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE)?:?(?P<text>.*)
    fn parse_pattern_inner(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        revision: Option<String>,
        l: usize,
    ) -> Result<()> {
        let (match_line, pattern) =
            match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? {
                '|' =>
                    (
                        *fallthrough_to
                            .as_mut()
                            .ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?,
                        &pattern[1..],
                    ),
                '^' => {
                    let offset = pattern.chars().take_while(|&c| c == '^').count();
                    (l - offset, &pattern[offset..])
                }
                _ => (l, pattern),
            };

        let (level, pattern) = match pattern.trim_start().split_once(|c| matches!(c, ':' | ' ')) {
            None => (None, pattern),
            Some((level, pattern_without_level)) =>
                match level.parse().ok() {
                    Some(level) => (Some(level), pattern_without_level),
                    None => (None, pattern),
                },
        };

        let matched = pattern.trim().to_string();

        ensure!(!matched.is_empty(), "no pattern specified");

        *fallthrough_to = Some(match_line);

        self.error_matches.push(ErrorMatch {
            matched,
            revision,
            level,
            definition_line: l,
            line: match_line,
        });

        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Handles IPC for controlling the main VM process.
//!
//! The VM Control IPC protocol is synchronous, meaning that each `VmRequest` sent over a connection
//! will receive a `VmResponse` for that request next time data is received over that connection.
//!
//! The wire message format is a little-endian C-struct of fixed size, along with a file descriptor
//! if the request type expects one.

use std::fmt::{self, Display};
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};

use libc::{EINVAL, EIO, ENODEV};

use byteorder::{LittleEndian, WriteBytesExt};
use kvm::{Datamatch, IoeventAddress, Vm};
use msg_socket::{MsgOnSocket, MsgReceiver, MsgResult, MsgSender, MsgSocket};
use resources::{GpuMemoryDesc, SystemAllocator};
use sys_util::{
    error, net::UnixSeqpacket, Error as SysError, EventFd, GuestAddress, MemoryMapping, MmapError,
    Result,
};

/// A file descriptor either borrowed or owned by this.
#[derive(Debug)]
pub enum MaybeOwnedFd {
    /// Owned by this enum variant, and will be destructed automatically if not moved out.
    Owned(File),
    /// A file descriptor borrwed by this enum.
    Borrowed(RawFd),
}

impl AsRawFd for MaybeOwnedFd {
    fn as_raw_fd(&self) -> RawFd {
        match self {
            MaybeOwnedFd::Owned(f) => f.as_raw_fd(),
            MaybeOwnedFd::Borrowed(fd) => *fd,
        }
    }
}

// When sent, it could be owned or borrowed. On the receiver end, it always owned.
impl MsgOnSocket for MaybeOwnedFd {
    fn msg_size() -> usize {
        0usize
    }
    fn max_fd_count() -> usize {
        1usize
    }
    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        let (fd, size) = RawFd::read_from_buffer(buffer, fds)?;
        let file = File::from_raw_fd(fd);
        Ok((MaybeOwnedFd::Owned(file), size))
    }
    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
        let fd = self.as_raw_fd();
        fd.write_to_buffer(buffer, fds)
    }
}

/// Mode of execution for the VM.
#[derive(Debug)]
pub enum VmRunMode {
    /// The default run mode indicating the VCPUs are running.
    Running,
    /// Indicates that the VCPUs are suspending execution until the `Running` mode is set.
    Suspending,
    /// Indicates that the VM is exiting all processes.
    Exiting,
}

impl Display for VmRunMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::VmRunMode::*;

        match self {
            Running => write!(f, "running"),
            Suspending => write!(f, "suspending"),
            Exiting => write!(f, "exiting"),
        }
    }
}

impl Default for VmRunMode {
    fn default() -> Self {
        VmRunMode::Running
    }
}

#[derive(MsgOnSocket, Debug)]
pub enum UsbControlCommand {
    AttachDevice {
        bus: u8,
        addr: u8,
        vid: u16,
        pid: u16,
        fd: Option<MaybeOwnedFd>,
    },
    DetachDevice {
        port: u8,
    },
    ListDevice {
        port: u8,
    },
}

#[derive(MsgOnSocket, Debug)]
pub enum UsbControlResult {
    Ok { port: u8 },
    NoAvailablePort,
    NoSuchDevice,
    NoSuchPort,
    FailedToOpenDevice,
    Device { port: u8, vid: u16, pid: u16 },
}

impl Display for UsbControlResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::UsbControlResult::*;

        match self {
            Ok { port } => write!(f, "ok {}", port),
            NoAvailablePort => write!(f, "no_available_port"),
            NoSuchDevice => write!(f, "no_such_device"),
            NoSuchPort => write!(f, "no_such_port"),
            FailedToOpenDevice => write!(f, "failed_to_open_device"),
            Device { port, vid, pid } => write!(f, "device {} {:04x} {:04x}", port, vid, pid),
        }
    }
}

pub type UsbControlSocket = MsgSocket<UsbControlCommand, UsbControlResult>;
pub type VmControlRequestSocket = MsgSocket<VmRequest, VmResponse>;
pub type VmControlResponseSocket = MsgSocket<VmResponse, VmRequest>;

/// A request to the main process to perform some operation on the VM.
///
/// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success.
#[derive(MsgOnSocket, Debug)]
pub enum VmRequest {
    /// Set the size of the VM's balloon in bytes.
    BalloonAdjust(u64),
    /// Break the VM's run loop and exit.
    Exit,
    /// Suspend the VM's VCPUs until resume.
    Suspend,
    /// Resume the VM's VCPUs that were previously suspended.
    Resume,
    /// Register the given ioevent address along with given datamatch to trigger the `EventFd`.
    RegisterIoevent(EventFd, IoeventAddress, u32),
    /// Register the given IRQ number to be triggered when the `EventFd` is triggered.
    RegisterIrqfd(EventFd, u32),
    /// Register shared memory represented by the given fd into guest address space. The response
    /// variant is `VmResponse::RegisterMemory`.
    RegisterMemory(MaybeOwnedFd, usize),
    /// Unregister the given memory slot that was previously registereed with `RegisterMemory`.
    UnregisterMemory(u32),
    /// Allocate GPU buffer of a given size/format and register the memory into guest address space.
    /// The response variant is `VmResponse::AllocateAndRegisterGpuMemory`
    AllocateAndRegisterGpuMemory {
        width: u32,
        height: u32,
        format: u32,
    },
    /// Resize a disk chosen by `disk_index` to `new_size` in bytes.
    /// `disk_index` is a 0-based count of `--disk`, `--rwdisk`, and `-r` command-line options.
    DiskResize { disk_index: usize, new_size: u64 },
    /// Command to use controller.
    UsbCommand(UsbControlCommand),
}

fn register_memory(
    vm: &mut Vm,
    allocator: &mut SystemAllocator,
    fd: &dyn AsRawFd,
    size: usize,
) -> Result<(u64, u32)> {
    let mmap = match MemoryMapping::from_fd(fd, size) {
        Ok(v) => v,
        Err(MmapError::SystemCallFailed(e)) => return Err(e),
        _ => return Err(SysError::new(EINVAL)),
    };
    let addr = match allocator.allocate_device_addresses(size as u64) {
        Some(a) => a,
        None => return Err(SysError::new(EINVAL)),
    };
    let slot = match vm.add_device_memory(GuestAddress(addr), mmap, false, false) {
        Ok(v) => v,
        Err(e) => return Err(e),
    };

    Ok((addr >> 12, slot))
}

impl VmRequest {
    /// Executes this request on the given Vm and other mutable state.
    ///
    /// # Arguments
    /// * `vm` - The `Vm` to perform the request on.
    /// * `allocator` - Used to allocate addresses.
    /// * `run_mode` - Out argument that is set to a run mode if one was requested.
    ///
    /// This does not return a result, instead encapsulating the success or failure in a
    /// `VmResponse` with the intended purpose of sending the response back over the  socket that
    /// received this `VmRequest`.
    pub fn execute(
        &self,
        vm: &mut Vm,
        sys_allocator: &mut SystemAllocator,
        run_mode: &mut Option<VmRunMode>,
        balloon_host_socket: &UnixSeqpacket,
        disk_host_sockets: &[MsgSocket<VmRequest, VmResponse>],
        usb_control_socket: &UsbControlSocket,
    ) -> VmResponse {
        match *self {
            VmRequest::Exit => {
                *run_mode = Some(VmRunMode::Exiting);
                VmResponse::Ok
            }
            VmRequest::Suspend => {
                *run_mode = Some(VmRunMode::Suspending);
                VmResponse::Ok
            }
            VmRequest::Resume => {
                *run_mode = Some(VmRunMode::Running);
                VmResponse::Ok
            }
            VmRequest::RegisterIoevent(ref evt, addr, datamatch) => {
                match vm.register_ioevent(evt, addr, Datamatch::U32(Some(datamatch))) {
                    Ok(_) => VmResponse::Ok,
                    Err(e) => VmResponse::Err(e),
                }
            }
            VmRequest::RegisterIrqfd(ref evt, irq) => match vm.register_irqfd(evt, irq) {
                Ok(_) => VmResponse::Ok,
                Err(e) => VmResponse::Err(e),
            },
            VmRequest::RegisterMemory(ref fd, size) => {
                match register_memory(vm, sys_allocator, fd, size) {
                    Ok((pfn, slot)) => VmResponse::RegisterMemory { pfn, slot },
                    Err(e) => VmResponse::Err(e),
                }
            }
            VmRequest::UnregisterMemory(slot) => match vm.remove_device_memory(slot) {
                Ok(_) => VmResponse::Ok,
                Err(e) => VmResponse::Err(e),
            },
            VmRequest::BalloonAdjust(num_pages) => {
                let mut buf = [0u8; 8];
                // write_u64 can't fail as the buffer is 8 bytes long.
                (&mut buf[0..])
                    .write_u64::<LittleEndian>(num_pages)
                    .unwrap();
                match balloon_host_socket.send(&buf) {
                    Ok(_) => VmResponse::Ok,
                    Err(_) => VmResponse::Err(SysError::last()),
                }
            }
            VmRequest::AllocateAndRegisterGpuMemory {
                width,
                height,
                format,
            } => {
                let (mut fd, desc) = match sys_allocator.gpu_memory_allocator() {
                    Some(gpu_allocator) => match gpu_allocator.allocate(width, height, format) {
                        Ok(v) => v,
                        Err(e) => return VmResponse::Err(e),
                    },
                    None => return VmResponse::Err(SysError::new(ENODEV)),
                };
                // Determine size of buffer using 0 byte seek from end. This is preferred over
                // `stride * height` as it's not limited to packed pixel formats.
                let size = match fd.seek(SeekFrom::End(0)) {
                    Ok(v) => v,
                    Err(e) => return VmResponse::Err(SysError::from(e)),
                };
                match register_memory(vm, sys_allocator, &fd, size as usize) {
                    Ok((pfn, slot)) => VmResponse::AllocateAndRegisterGpuMemory {
                        fd: MaybeOwnedFd::Owned(fd),
                        pfn,
                        slot,
                        desc,
                    },
                    Err(e) => VmResponse::Err(e),
                }
            }
            VmRequest::DiskResize { disk_index, .. } => {
                // Forward the request to the block device process via its control socket.
                if let Some(sock) = disk_host_sockets.get(disk_index) {
                    if let Err(e) = sock.send(self) {
                        error!("disk socket send failed: {}", e);
                        VmResponse::Err(SysError::new(EINVAL))
                    } else {
                        match sock.recv() {
                            Ok(result) => result,
                            Err(e) => {
                                error!("disk socket recv failed: {}", e);
                                VmResponse::Err(SysError::new(EINVAL))
                            }
                        }
                    }
                } else {
                    VmResponse::Err(SysError::new(ENODEV))
                }
            }
            VmRequest::UsbCommand(ref cmd) => {
                let res = usb_control_socket.send(cmd);
                if let Err(e) = res {
                    error!("fail to send command to usb control socket: {}", e);
                    return VmResponse::Err(SysError::new(EIO));
                }
                match usb_control_socket.recv() {
                    Ok(response) => VmResponse::UsbResponse(response),
                    Err(e) => {
                        error!("fail to recv command from usb control socket: {}", e);
                        VmResponse::Err(SysError::new(EIO))
                    }
                }
            }
        }
    }
}

/// Indication of success or failure of a `VmRequest`.
///
/// Success is usually indicated `VmResponse::Ok` unless there is data associated with the response.
#[derive(MsgOnSocket, Debug)]
pub enum VmResponse {
    /// Indicates the request was executed successfully.
    Ok,
    /// Indicates the request encountered some error during execution.
    Err(SysError),
    /// The request to register memory into guest address space was successfully done at page frame
    /// number `pfn` and memory slot number `slot`.
    RegisterMemory { pfn: u64, slot: u32 },
    /// The request to allocate and register GPU memory into guest address space was successfully
    /// done at page frame number `pfn` and memory slot number `slot` for buffer with `desc`.
    AllocateAndRegisterGpuMemory {
        fd: MaybeOwnedFd,
        pfn: u64,
        slot: u32,
        desc: GpuMemoryDesc,
    },
    /// Results of usb control commands.
    UsbResponse(UsbControlResult),
}

impl Display for VmResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::VmResponse::*;

        match self {
            Ok => write!(f, "ok"),
            Err(e) => write!(f, "error: {}", e),
            RegisterMemory { pfn, slot } => write!(
                f,
                "memory registered to page frame number {:#x} and memory slot {}",
                pfn, slot
            ),
            AllocateAndRegisterGpuMemory { pfn, slot, .. } => write!(
                f,
                "gpu memory allocated and registered to page frame number {:#x} and memory slot {}",
                pfn, slot
            ),
            UsbResponse(result) => write!(f, "usb control request get result {:?}", result),
        }
    }
}
<commit_msg>Removes RegisterIoevent and RegisterIrqfd.<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Handles IPC for controlling the main VM process.
//!
//! The VM Control IPC protocol is synchronous, meaning that each `VmRequest` sent over a connection
//! will receive a `VmResponse` for that request next time data is received over that connection.
//!
//! The wire message format is a little-endian C-struct of fixed size, along with a file descriptor
//! if the request type expects one.

use std::fmt::{self, Display};
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};

use libc::{EINVAL, EIO, ENODEV};

use byteorder::{LittleEndian, WriteBytesExt};
use kvm::Vm;
use msg_socket::{MsgOnSocket, MsgReceiver, MsgResult, MsgSender, MsgSocket};
use resources::{GpuMemoryDesc, SystemAllocator};
use sys_util::{
    error, net::UnixSeqpacket, Error as SysError, GuestAddress, MemoryMapping, MmapError, Result,
};

/// A file descriptor either borrowed or owned by this.
#[derive(Debug)]
pub enum MaybeOwnedFd {
    /// Owned by this enum variant, and will be destructed automatically if not moved out.
    Owned(File),
    /// A file descriptor borrwed by this enum.
    Borrowed(RawFd),
}

impl AsRawFd for MaybeOwnedFd {
    fn as_raw_fd(&self) -> RawFd {
        match self {
            MaybeOwnedFd::Owned(f) => f.as_raw_fd(),
            MaybeOwnedFd::Borrowed(fd) => *fd,
        }
    }
}

// When sent, it could be owned or borrowed. On the receiver end, it always owned.
impl MsgOnSocket for MaybeOwnedFd {
    fn msg_size() -> usize {
        0usize
    }
    fn max_fd_count() -> usize {
        1usize
    }
    unsafe fn read_from_buffer(buffer: &[u8], fds: &[RawFd]) -> MsgResult<(Self, usize)> {
        let (fd, size) = RawFd::read_from_buffer(buffer, fds)?;
        let file = File::from_raw_fd(fd);
        Ok((MaybeOwnedFd::Owned(file), size))
    }
    fn write_to_buffer(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> MsgResult<usize> {
        let fd = self.as_raw_fd();
        fd.write_to_buffer(buffer, fds)
    }
}

/// Mode of execution for the VM.
#[derive(Debug)]
pub enum VmRunMode {
    /// The default run mode indicating the VCPUs are running.
    Running,
    /// Indicates that the VCPUs are suspending execution until the `Running` mode is set.
    Suspending,
    /// Indicates that the VM is exiting all processes.
    Exiting,
}

impl Display for VmRunMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::VmRunMode::*;

        match self {
            Running => write!(f, "running"),
            Suspending => write!(f, "suspending"),
            Exiting => write!(f, "exiting"),
        }
    }
}

impl Default for VmRunMode {
    fn default() -> Self {
        VmRunMode::Running
    }
}

#[derive(MsgOnSocket, Debug)]
pub enum UsbControlCommand {
    AttachDevice {
        bus: u8,
        addr: u8,
        vid: u16,
        pid: u16,
        fd: Option<MaybeOwnedFd>,
    },
    DetachDevice {
        port: u8,
    },
    ListDevice {
        port: u8,
    },
}

#[derive(MsgOnSocket, Debug)]
pub enum UsbControlResult {
    Ok { port: u8 },
    NoAvailablePort,
    NoSuchDevice,
    NoSuchPort,
    FailedToOpenDevice,
    Device { port: u8, vid: u16, pid: u16 },
}

impl Display for UsbControlResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::UsbControlResult::*;

        match self {
            Ok { port } => write!(f, "ok {}", port),
            NoAvailablePort => write!(f, "no_available_port"),
            NoSuchDevice => write!(f, "no_such_device"),
            NoSuchPort => write!(f, "no_such_port"),
            FailedToOpenDevice => write!(f, "failed_to_open_device"),
            Device { port, vid, pid } => write!(f, "device {} {:04x} {:04x}", port, vid, pid),
        }
    }
}

pub type UsbControlSocket = MsgSocket<UsbControlCommand, UsbControlResult>;
pub type VmControlRequestSocket = MsgSocket<VmRequest, VmResponse>;
pub type VmControlResponseSocket = MsgSocket<VmResponse, VmRequest>;

/// A request to the main process to perform some operation on the VM.
///
/// Unless otherwise noted, each request should expect a `VmResponse::Ok` to be received on success.
#[derive(MsgOnSocket, Debug)]
pub enum VmRequest {
    /// Set the size of the VM's balloon in bytes.
    BalloonAdjust(u64),
    /// Break the VM's run loop and exit.
    Exit,
    /// Suspend the VM's VCPUs until resume.
    Suspend,
    /// Resume the VM's VCPUs that were previously suspended.
    Resume,
    /// Register shared memory represented by the given fd into guest address space. The response
    /// variant is `VmResponse::RegisterMemory`.
    RegisterMemory(MaybeOwnedFd, usize),
    /// Unregister the given memory slot that was previously registereed with `RegisterMemory`.
    UnregisterMemory(u32),
    /// Allocate GPU buffer of a given size/format and register the memory into guest address space.
    /// The response variant is `VmResponse::AllocateAndRegisterGpuMemory`
    AllocateAndRegisterGpuMemory {
        width: u32,
        height: u32,
        format: u32,
    },
    /// Resize a disk chosen by `disk_index` to `new_size` in bytes.
    /// `disk_index` is a 0-based count of `--disk`, `--rwdisk`, and `-r` command-line options.
    DiskResize { disk_index: usize, new_size: u64 },
    /// Command to use controller.
    UsbCommand(UsbControlCommand),
}

fn register_memory(
    vm: &mut Vm,
    allocator: &mut SystemAllocator,
    fd: &dyn AsRawFd,
    size: usize,
) -> Result<(u64, u32)> {
    let mmap = match MemoryMapping::from_fd(fd, size) {
        Ok(v) => v,
        Err(MmapError::SystemCallFailed(e)) => return Err(e),
        _ => return Err(SysError::new(EINVAL)),
    };
    let addr = match allocator.allocate_device_addresses(size as u64) {
        Some(a) => a,
        None => return Err(SysError::new(EINVAL)),
    };
    let slot = match vm.add_device_memory(GuestAddress(addr), mmap, false, false) {
        Ok(v) => v,
        Err(e) => return Err(e),
    };

    Ok((addr >> 12, slot))
}

impl VmRequest {
    /// Executes this request on the given Vm and other mutable state.
    ///
    /// # Arguments
    /// * `vm` - The `Vm` to perform the request on.
    /// * `allocator` - Used to allocate addresses.
    /// * `run_mode` - Out argument that is set to a run mode if one was requested.
    ///
    /// This does not return a result, instead encapsulating the success or failure in a
    /// `VmResponse` with the intended purpose of sending the response back over the  socket that
    /// received this `VmRequest`.
    pub fn execute(
        &self,
        vm: &mut Vm,
        sys_allocator: &mut SystemAllocator,
        run_mode: &mut Option<VmRunMode>,
        balloon_host_socket: &UnixSeqpacket,
        disk_host_sockets: &[MsgSocket<VmRequest, VmResponse>],
        usb_control_socket: &UsbControlSocket,
    ) -> VmResponse {
        match *self {
            VmRequest::Exit => {
                *run_mode = Some(VmRunMode::Exiting);
                VmResponse::Ok
            }
            VmRequest::Suspend => {
                *run_mode = Some(VmRunMode::Suspending);
                VmResponse::Ok
            }
            VmRequest::Resume => {
                *run_mode = Some(VmRunMode::Running);
                VmResponse::Ok
            }
            VmRequest::RegisterMemory(ref fd, size) => {
                match register_memory(vm, sys_allocator, fd, size) {
                    Ok((pfn, slot)) => VmResponse::RegisterMemory { pfn, slot },
                    Err(e) => VmResponse::Err(e),
                }
            }
            VmRequest::UnregisterMemory(slot) => match vm.remove_device_memory(slot) {
                Ok(_) => VmResponse::Ok,
                Err(e) => VmResponse::Err(e),
            },
            VmRequest::BalloonAdjust(num_pages) => {
                let mut buf = [0u8; 8];
                // write_u64 can't fail as the buffer is 8 bytes long.
                (&mut buf[0..])
                    .write_u64::<LittleEndian>(num_pages)
                    .unwrap();
                match balloon_host_socket.send(&buf) {
                    Ok(_) => VmResponse::Ok,
                    Err(_) => VmResponse::Err(SysError::last()),
                }
            }
            VmRequest::AllocateAndRegisterGpuMemory {
                width,
                height,
                format,
            } => {
                let (mut fd, desc) = match sys_allocator.gpu_memory_allocator() {
                    Some(gpu_allocator) => match gpu_allocator.allocate(width, height, format) {
                        Ok(v) => v,
                        Err(e) => return VmResponse::Err(e),
                    },
                    None => return VmResponse::Err(SysError::new(ENODEV)),
                };
                // Determine size of buffer using 0 byte seek from end. This is preferred over
                // `stride * height` as it's not limited to packed pixel formats.
                let size = match fd.seek(SeekFrom::End(0)) {
                    Ok(v) => v,
                    Err(e) => return VmResponse::Err(SysError::from(e)),
                };
                match register_memory(vm, sys_allocator, &fd, size as usize) {
                    Ok((pfn, slot)) => VmResponse::AllocateAndRegisterGpuMemory {
                        fd: MaybeOwnedFd::Owned(fd),
                        pfn,
                        slot,
                        desc,
                    },
                    Err(e) => VmResponse::Err(e),
                }
            }
            VmRequest::DiskResize { disk_index, .. } => {
                // Forward the request to the block device process via its control socket.
                if let Some(sock) = disk_host_sockets.get(disk_index) {
                    if let Err(e) = sock.send(self) {
                        error!("disk socket send failed: {}", e);
                        VmResponse::Err(SysError::new(EINVAL))
                    } else {
                        match sock.recv() {
                            Ok(result) => result,
                            Err(e) => {
                                error!("disk socket recv failed: {}", e);
                                VmResponse::Err(SysError::new(EINVAL))
                            }
                        }
                    }
                } else {
                    VmResponse::Err(SysError::new(ENODEV))
                }
            }
            VmRequest::UsbCommand(ref cmd) => {
                let res = usb_control_socket.send(cmd);
                if let Err(e) = res {
                    error!("fail to send command to usb control socket: {}", e);
                    return VmResponse::Err(SysError::new(EIO));
                }
                match usb_control_socket.recv() {
                    Ok(response) => VmResponse::UsbResponse(response),
                    Err(e) => {
                        error!("fail to recv command from usb control socket: {}", e);
                        VmResponse::Err(SysError::new(EIO))
                    }
                }
            }
        }
    }
}

/// Indication of success or failure of a `VmRequest`.
///
/// Success is usually indicated `VmResponse::Ok` unless there is data associated with the response.
#[derive(MsgOnSocket, Debug)]
pub enum VmResponse {
    /// Indicates the request was executed successfully.
    Ok,
    /// Indicates the request encountered some error during execution.
    Err(SysError),
    /// The request to register memory into guest address space was successfully done at page frame
    /// number `pfn` and memory slot number `slot`.
    RegisterMemory { pfn: u64, slot: u32 },
    /// The request to allocate and register GPU memory into guest address space was successfully
    /// done at page frame number `pfn` and memory slot number `slot` for buffer with `desc`.
    AllocateAndRegisterGpuMemory {
        fd: MaybeOwnedFd,
        pfn: u64,
        slot: u32,
        desc: GpuMemoryDesc,
    },
    /// Results of usb control commands.
    UsbResponse(UsbControlResult),
}

impl Display for VmResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::VmResponse::*;

        match self {
            Ok => write!(f, "ok"),
            Err(e) => write!(f, "error: {}", e),
            RegisterMemory { pfn, slot } => write!(
                f,
                "memory registered to page frame number {:#x} and memory slot {}",
                pfn, slot
            ),
            AllocateAndRegisterGpuMemory { pfn, slot, .. } => write!(
                f,
                "gpu memory allocated and registered to page frame number {:#x} and memory slot {}",
                pfn, slot
            ),
            UsbResponse(result) => write!(f, "usb control request get result {:?}", result),
        }
    }
}
<|endoftext|>


<commit_before>use core::str;
use spin::Mutex;

use crate::memory::Frame;
use crate::paging::{ActivePageTable, Page, PageFlags, PhysicalAddress, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::paging::mapper::PageFlushAll;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable, env: &[u8]) {
    println!("Starting graphical debug");

    let mut width = 0;
    let mut height = 0;
    let mut physbaseptr = 0;

    //TODO: should errors be reported?
    for line in str::from_utf8(env).unwrap_or("").lines() {
        let mut parts = line.splitn(2, '=');
        let name = parts.next().unwrap_or("");
        let value = parts.next().unwrap_or("");

        if name == "FRAMEBUFFER_ADDR" {
            physbaseptr = usize::from_str_radix(value, 16).unwrap_or(0);
        }

        if name == "FRAMEBUFFER_WIDTH" {
            width = usize::from_str_radix(value, 16).unwrap_or(0);
        }

        if name == "FRAMEBUFFER_HEIGHT" {
            height = usize::from_str_radix(value, 16).unwrap_or(0);
        }
    }

    if physbaseptr == 0 || width == 0 || height == 0 {
        println!("Framebuffer not found");
        return;
    }

    println!("Framebuffer {}x{} at {:X}", width, height, physbaseptr);

    {
        let size = width * height;

        let onscreen = physbaseptr + crate::PHYS_OFFSET;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().data() - crate::PHYS_OFFSET));
                let flags = PageFlags::new().write(true).custom_flag(EntryFlags::HUGE_PAGE.bits(), true);
                let result = active_table.map_to(page, frame, flags);
                flush_all.consume(result);
            }
            flush_all.flush();
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    let debug_display_opt = DEBUG_DISPLAY.lock().take();
    if let Some(debug_display) = debug_display_opt {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.onscreen.len() * 4;
        //TODO: fix crash if we unmap this memory
        if false {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size));
            for page in Page::range_inclusive(start_page, end_page) {
                let (result, _frame) = active_table.unmap_return(page, false);
                flush_all.consume(result);
            }
            flush_all.flush();
        }
    }

    println!("Finished graphical debug");
}
<commit_msg>Fix additional page mapping in graphical debug<commit_after>use core::str;
use spin::Mutex;

use crate::memory::Frame;
use crate::paging::{ActivePageTable, Page, PageFlags, PhysicalAddress, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::paging::mapper::PageFlushAll;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable, env: &[u8]) {
    println!("Starting graphical debug");

    let mut width = 0;
    let mut height = 0;
    let mut physbaseptr = 0;

    //TODO: should errors be reported?
    for line in str::from_utf8(env).unwrap_or("").lines() {
        let mut parts = line.splitn(2, '=');
        let name = parts.next().unwrap_or("");
        let value = parts.next().unwrap_or("");

        if name == "FRAMEBUFFER_ADDR" {
            physbaseptr = usize::from_str_radix(value, 16).unwrap_or(0);
        }

        if name == "FRAMEBUFFER_WIDTH" {
            width = usize::from_str_radix(value, 16).unwrap_or(0);
        }

        if name == "FRAMEBUFFER_HEIGHT" {
            height = usize::from_str_radix(value, 16).unwrap_or(0);
        }
    }

    if physbaseptr == 0 || width == 0 || height == 0 {
        println!("Framebuffer not found");
        return;
    }

    println!("Framebuffer {}x{} at {:X}", width, height, physbaseptr);

    {
        let size = width * height * 4;

        let onscreen = physbaseptr + crate::PHYS_OFFSET;
        {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().data() - crate::PHYS_OFFSET));
                let flags = PageFlags::new().write(true).custom_flag(EntryFlags::HUGE_PAGE.bits(), true);
                let result = active_table.map_to(page, frame, flags);
                flush_all.consume(result);
            }
            flush_all.flush();
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/8) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    let debug_display_opt = DEBUG_DISPLAY.lock().take();
    if let Some(debug_display) = debug_display_opt {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.onscreen.len() * 4;
        //TODO: fix crash if we unmap this memory
        if false {
            let flush_all = PageFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size - 1));
            for page in Page::range_inclusive(start_page, end_page) {
                let (result, _frame) = active_table.unmap_return(page, false);
                flush_all.consume(result);
            }
            flush_all.flush();
        }
    }

    println!("Finished graphical debug");
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Common functionality for the `x86` and `x86_64` Interrupt Descriptor Table.

use core::{fmt, ptr};

use arch::cpu::dtable::DTable;

extern {
    /// Array of interrupt handlers exported by ASM
    //  TODO: hopefully, we will not need this much longer.
    #[link_name = "isrs"]
    static ISRs: [*const u8; ENTRIES];
}

/// An interrupt handler function.
pub type Handler = unsafe extern "C" fn() -> !;

/// Number of entries in the system's Interrupt Descriptor Table.
pub const ENTRIES: usize = 256;

//==------------------------------------------------------------------------==
// IDT Gates
#[cfg(target_arch = "x86")]    #[path = "gate32.rs"] pub mod gate;
#[cfg(target_arch = "x86_64")] #[path = "gate64.rs"] pub mod gate;
pub use self::gate::*;

/// `x86` interrupt gate types.
///
/// Bit-and this with the attribute half-byte to produce the
/// `type_attr` field for a `Gate`
#[repr(u8)]
#[derive(Copy,Clone,Debug)]
pub enum GateType { Absent    = 0b0000_0000
                  , Interrupt = 0b1000_1110
                  , Call      = 0b1000_1100
                  , Trap      = 0b1000_1111
                  }

impl fmt::Display for GateType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self { GateType::Absent    => write!(f, "Absent")
                    , GateType::Interrupt => write!(f, "Interrupt")
                    , GateType::Call      => write!(f, "Call")
                    , GateType::Trap      => write!(f, "Trap")
                    }
    }
}

//==------------------------------------------------------------------------==
//  IDT implementation
/// An Interrupt Descriptor Table
///
/// The IDT is either 64-bit or 32-bit.
pub struct Idt([Gate; ENTRIES]);

impl Idt {

    /// Construct a new IDT with all interrupt gates set to `absent`.
    pub const fn new() -> Self {
        Idt([Gate::absent(); ENTRIES])
    }

    /// Enable interrupts
    pub unsafe fn enable_interrupts() { asm!("sti") }
    /// Disable interrupts
    pub unsafe fn disable_interrupts() { asm!("cli") }

    /// Add a new interrupt gate pointing to the given handler
    pub fn add_gate(&mut self, idx: usize, handler: Handler) {
        self.0[idx] = Gate::from(handler)
    }

    /// Add interrupt handlers exported by assembly to the IDT.
    pub unsafe fn add_handlers(&mut self) -> &mut Self {
        for (i, &handler_ptr) in ISRs.iter()
            .enumerate()
            .filter(|&(_, &ptr)| ptr != ptr::null() ) {
                self.0[i] = Gate::from(handler_ptr)
        }

        println!("{:<38}{:>40}", " . . Adding interrupt handlers to IDT"
             , "[ OKAY ]");
        self
    }

}

impl DTable for Idt {
    type Entry = Gate;

    #[inline] fn entry_count(&self) -> usize { ENTRIES }

    #[inline] unsafe fn load(&self) {
        asm!(  "lidt ($0)"
            :: "r"(&self.get_ptr())
            :  "memory" );
        println!("{:<38}{:>40}", " . . Loading IDT", "[ OKAY ]");
    }
}
<commit_msg>[x86_all] start making IDT gate API friendlier too<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Common functionality for the `x86` and `x86_64` Interrupt Descriptor Table.

use core::{fmt, ptr};

use arch::cpu::dtable::DTable;

extern {
    /// Array of interrupt handlers exported by ASM
    //  TODO: hopefully, we will not need this much longer.
    #[link_name = "isrs"]
    static ISRs: [*const u8; ENTRIES];
}

/// An interrupt handler function.
pub type Handler = unsafe extern "C" fn() -> !;

/// Number of entries in the system's Interrupt Descriptor Table.
pub const ENTRIES: usize = 256;

//==------------------------------------------------------------------------==
// IDT Gates
#[cfg(target_arch = "x86")]    #[path = "gate32.rs"] pub mod gate;
#[cfg(target_arch = "x86_64")] #[path = "gate64.rs"] pub mod gate;
pub use self::gate::*;

/// `x86` interrupt gate types.
///
/// Bit-and this with the attribute half-byte to produce the
/// `type_attr` field for a `Gate`
#[repr(u8)]
#[derive(Copy,Clone,Debug)]
pub enum GateType { Absent    = 0b0000_0000
                  , Interrupt = 0b1000_1110
                  , Call      = 0b1000_1100
                  , Trap      = 0b1000_1111
                  }

impl fmt::Display for GateType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self { GateType::Absent    => write!(f, "Absent")
                    , GateType::Interrupt => write!(f, "Interrupt")
                    , GateType::Call      => write!(f, "Call")
                    , GateType::Trap      => write!(f, "Trap")
                    }
    }
}

bitflags! {
    pub flags GateOptions: u8 {
        /// Indicates whether or not this gate is present.
        /// An interrupt on a non-present gate will trigger a
        /// General Protection Fault.
        const PRESENT = 1 << 7
      , const CALL_GATE = 0b0000_1100
      , const TRAP_GATE = 0b0000_1111
      , const IRQ_GATE  = 0b0000_1110
      ,
    }
}

impl GateOptions {
    #[inline] fn is_trap(&self) -> bool {
        self.contains(TRAP_GATE)
    }

    #[inline] fn is_present(&self) -> bool {
        self.contains(PRESENT)
    }

    /// Sets the present bit for this gate
    #[inline] fn set_present(&mut self, present: bool) -> &mut Self {
        if present { self.insert(PRESENT) }
        else { self.remove(PRESENT) }
        self
    }
}

//==------------------------------------------------------------------------==
//  IDT implementation
/// An Interrupt Descriptor Table
///
/// The IDT is either 64-bit or 32-bit.
pub struct Idt([Gate; ENTRIES]);

impl Idt {

    /// Construct a new IDT with all interrupt gates set to `absent`.
    pub const fn new() -> Self {
        Idt([Gate::absent(); ENTRIES])
    }

    /// Enable interrupts
    pub unsafe fn enable_interrupts() { asm!("sti") }
    /// Disable interrupts
    pub unsafe fn disable_interrupts() { asm!("cli") }

    /// Add a new interrupt gate pointing to the given handler
    pub fn add_gate(&mut self, idx: usize, handler: Handler) {
        self.0[idx] = Gate::from(handler)
    }

    /// Add interrupt handlers exported by assembly to the IDT.
    pub unsafe fn add_handlers(&mut self) -> &mut Self {
        for (i, &handler_ptr) in ISRs.iter()
            .enumerate()
            .filter(|&(_, &ptr)| ptr != ptr::null() ) {
                self.0[i] = Gate::from(handler_ptr)
        }

        println!("{:<38}{:>40}", " . . Adding interrupt handlers to IDT"
             , "[ OKAY ]");
        self
    }

}

impl DTable for Idt {
    type Entry = Gate;

    #[inline] fn entry_count(&self) -> usize { ENTRIES }

    #[inline] unsafe fn load(&self) {
        asm!(  "lidt ($0)"
            :: "r"(&self.get_ptr())
            :  "memory" );
        println!("{:<38}{:>40}", " . . Loading IDT", "[ OKAY ]");
    }
}
<|endoftext|>


<commit_before>// Lumol, an extensible molecular simulation engine
// Copyright (C) 2015-2016 G. Fraux — BSD license

use rand::distributions::{Sample, Range};
use rand::Rng;

use std::usize;

use super::MCMove;
use super::select_molecule;

use types::Vector3D;
use sys::{System, EnergyCache};

/// Monte-Carlo move for translating a molecule
pub struct Translate {
    /// Type of molecule to translate. `None` means all molecules.
    moltype: Option<u64>,
    /// Index of the molecule to translate
    molid: usize,
    /// New positions of the atom in the translated molecule
    newpos: Vec<Vector3D>,
    /// Maximum displacement value
    dr: f64,
    /// Translation range for random number generation
    range: Range<f64>,
}

impl Translate {
    /// Create a new `Translate` move, with maximum displacement of `dr`.
    /// Translating all the molecules in the system.
    pub fn new(dr: f64) -> Translate {
        Translate::create(dr, None)
    }

    /// Create a new `Translate` move, with maximum displacement of `dr`.
    /// Translating only molecules with `moltype` type.
    pub fn with_moltype(dr: f64, moltype: u64) -> Translate {
        Translate::create(dr, Some(moltype))
    }

    /// Factorizing the constructors
    fn create(dr: f64, moltype: Option<u64>) -> Translate {
        assert!(dr > 0.0, "dr must be positive in Translate move");
        let dr = dr / f64::sqrt(3.0);
        Translate {
            moltype: moltype,
            molid: usize::MAX,
            newpos: Vec::new(),
            dr: dr,
            range: Range::new(-dr, dr),
        }
    }
}

impl Default for Translate {
    fn default() -> Translate {
        Translate::new(1.0)
    }
}

impl MCMove for Translate {
    fn describe(&self) -> &str {
        "molecular translation"
    }

    fn setup(&mut self, _: &System) {
        // TODO: introduce a limitation for dr, dr_max, such that it can not
        // be larger than the largest cutoff in the system? We would initialize
        // the value here.
    }

    fn prepare(&mut self, system: &mut System, rng: &mut Box<Rng>) -> bool {
        if let Some(id) = select_molecule(system, self.moltype, rng) {
            self.molid = id;
        } else {
            warn!("Can not translate molecule: no molecule of this type in the system.");
            return false;
        }

        let delta = Vector3D::new(
            self.range.sample(rng),
            self.range.sample(rng),
            self.range.sample(rng)
        );

        self.newpos.clear();
        for i in system.molecule(self.molid) {
            self.newpos.push(system[i].position + delta);
        }
        return true;
    }

    fn cost(&self, system: &System, beta: f64, cache: &mut EnergyCache) -> f64 {
        let idxes = system.molecule(self.molid).iter().collect::<Vec<_>>();
        let cost = cache.move_particles_cost(system, idxes, &self.newpos);
        return cost*beta;
    }

    fn apply(&mut self, system: &mut System) {
        for (i, pi) in system.molecule(self.molid).iter().enumerate() {
            system[pi].position = self.newpos[i];
        }
    }

    fn restore(&mut self, _: &mut System) {
        // Nothing to do.
    }

    fn update_amplitude(&mut self, scaling_factor: Option<f64>) {
        if let Some(s) = scaling_factor {
            self.dr *= s;
            self.range = Range::new(-self.dr, self.dr);
        }
    }
}
<commit_msg>Molecules are now wrapped when a move is accepted. Added comments to make functions more transparent.<commit_after>// Lumol, an extensible molecular simulation engine
// Copyright (C) 2015-2016 G. Fraux — BSD license

use rand::distributions::{Sample, Range};
use rand::Rng;

use std::usize;

use super::MCMove;
use super::select_molecule;

use types::Vector3D;
use sys::{System, EnergyCache};

/// Monte-Carlo move for translating a molecule
pub struct Translate {
    /// Type of molecule to translate. `None` means all molecules.
    moltype: Option<u64>,
    /// Index of the molecule to translate
    molid: usize,
    /// New positions of the atom in the translated molecule
    newpos: Vec<Vector3D>,
    /// Maximum displacement value
    dr: f64,
    /// Translation range for random number generation
    range: Range<f64>,
}

impl Translate {
    /// Create a new `Translate` move, with maximum displacement of `dr`.
    /// Translating all the molecules in the system.
    pub fn new(dr: f64) -> Translate {
        Translate::create(dr, None)
    }

    /// Create a new `Translate` move, with maximum displacement of `dr`.
    /// Translating only molecules with `moltype` type.
    pub fn with_moltype(dr: f64, moltype: u64) -> Translate {
        Translate::create(dr, Some(moltype))
    }

    /// Factorizing the constructors
    fn create(dr: f64, moltype: Option<u64>) -> Translate {
        assert!(dr > 0.0, "dr must be positive in Translate move");
        let dr = dr / f64::sqrt(3.0);
        Translate {
            moltype: moltype,
            molid: usize::MAX,
            newpos: Vec::new(),
            dr: dr,
            range: Range::new(-dr, dr),
        }
    }
}

impl Default for Translate {
    fn default() -> Translate {
        Translate::new(1.0)
    }
}

impl MCMove for Translate {
    fn describe(&self) -> &str {
        "molecular translation"
    }

    fn setup(&mut self, _: &System) {
        // TODO: introduce a limitation for dr, dr_max, such that it can not
        // be larger than the largest cutoff in the system? We would initialize
        // the value here.
    }

    fn prepare(&mut self, system: &mut System, rng: &mut Box<Rng>) -> bool {
        if let Some(id) = select_molecule(system, self.moltype, rng) {
            self.molid = id;
        } else {
            warn!("Can not translate molecule: no molecule of this type in the system.");
            return false;
        }

        // Create random displacement vector.
        let delta = Vector3D::new(
            self.range.sample(rng),
            self.range.sample(rng),
            self.range.sample(rng)
        );

        self.newpos.clear();
        // Create new position.
        // Note that this may move a particles' center-
        // of-mass (com) out of the cell.
        // If the move is accepted, we have to wrap the
        // com such that it lies inside the cell.
        for i in system.molecule(self.molid) {
            self.newpos.push(system[i].position + delta);
        }
        return true;
    }

    fn cost(&self, system: &System, beta: f64, cache: &mut EnergyCache) -> f64 {
        let idxes = system.molecule(self.molid).iter().collect::<Vec<_>>();
        let cost = cache.move_particles_cost(system, idxes, &self.newpos);
        return cost*beta;
    }

    fn apply(&mut self, system: &mut System) {
        // Update positions.
        for (i, pi) in system.molecule(self.molid).iter().enumerate() {
            system[pi].position = self.newpos[i];
        }
        // Move molecule such that its center-of-mass
        // is inside the simulation cell.
        // Note that particles of the molecule may still be
        // outside the cell, but that is not important.
        system.wrap_molecule(self.molid)
    }

    fn restore(&mut self, _: &mut System) {
        // Nothing to do.
    }

    fn update_amplitude(&mut self, scaling_factor: Option<f64>) {
        if let Some(s) = scaling_factor {
            self.dr *= s;
            self.range = Range::new(-self.dr, self.dr);
        }
    }
}
<|endoftext|>


<commit_before>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

#[cfg(test)] extern crate extra;
#[cfg(test)] use extra::test::BenchHarness;

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;
static CR: u8 = '\r' as u8;
static TAB: u8 = '\t' as u8;
static STAR: u8 = '*' as u8;
static DASH: u8 = '-' as u8;
static UNDERSCORE: u8 = '_' as u8;

//
// Skip up to three leading spaces.
//
// Up to three leading spaces are allowed for many elements.
//
// We don't need to care about a TAB here as in this position it is equivalent
// to 4 spaces, which means that when we find a TAB here we would not parse the
// corresponding element.
//
fn skip_initial_three_spaces<'a>(buf: &'a[u8])-> &'a[u8] {
    let mut buf = buf;
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail(); } } }
    return buf;
}

//
// Return Some(`rem`) if the line is a horizontal rule, with `rem` being the
// buf after the hrule. Otherwise return None.
//
fn is_hrule<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let buf = skip_initial_three_spaces(buf);

    let item = match buf.head() {
        Some(&c) if c == STAR ||
                    c == DASH ||
                    c == UNDERSCORE => c,
        _                           => return None
    };

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    // Counts the consumed spaces (and the final NL)
    let mut spc: uint = 0;

    for &ch in buf.iter() {
        if      ch == item { cnt += 1; }
        else if ch == NL   { spc += 1; break; }
        else if ch == SP   { spc += 1; }
        else               { return None; }
    }

    if cnt >= 3 {
        Some(buf.slice_from(cnt + spc))
    } else {
        None
    }
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert!(is_hrule(bytes!("* * *\n")).is_some());
    assert!(is_hrule(bytes!("***\n")).is_some());
    assert!(is_hrule(bytes!("*****\n")).is_some());
    assert!(is_hrule(bytes!("- - -\n")).is_some());
    assert!(is_hrule(bytes!("---------------------------------------\n")).is_some());

    // up to three spaces ignored
    assert!(is_hrule(bytes!(" ***\n")).is_some());
    assert!(is_hrule(bytes!("  ***\n")).is_some());
    assert!(is_hrule(bytes!("   ***\n")).is_some());

    // but not four, or a tab which is equivalent to four spaces
    assert!(is_hrule(bytes!("    ***\n")).is_none());
    assert!(is_hrule(bytes!("\t***\n")).is_none());

    // need at least three
    assert!(is_hrule(bytes!("*\n")).is_none());
    assert!(is_hrule(bytes!("**\n")).is_none());
    assert!(is_hrule(bytes!("* *\n")).is_none());
    assert!(is_hrule(bytes!("   * *\n")).is_none());

    // Also works without newline at the end
    assert!(is_hrule(bytes!("* * *")).is_some());

    // And underscores also supported
    assert!(is_hrule(bytes!("___")).is_some());
    assert!(is_hrule(bytes!("______________")).is_some());
    assert!(is_hrule(bytes!(" ______________")).is_some());

    // Test if the remaining buf actually works.
    let s = bytes!("   * * *\nremaining");
    let res = is_hrule(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}

#[bench]
fn bench_is_hrule(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule(s));
}

//
// Return Some(`rem`) if the line is an empty line, with `rem` being the buf
// after the empty line. Otherwise return None.
//
fn is_empty<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let mut cnt: uint = 0;

    for &ch in buf.iter() {
        if      ch == NL  { cnt += 1; break; }
        else if ch == CR  { cnt += 1; }
        else if ch == SP  { cnt += 1; }
        else if ch == TAB { cnt += 1; }
        else              { return None; }
    }

    if cnt > 0 {
        Some(buf.slice_from(cnt))
    } else {
        None
    }
}

#[test]
fn test_is_empty() {
    assert!(is_empty(bytes!("\n")).is_some());
    assert!(is_empty(bytes!("    \n")).is_some());
    assert!(is_empty(bytes!("  \t  \n")).is_some());
    assert!(is_empty(bytes!("  \t  \r\n")).is_some());
    assert!(is_empty(bytes!("  \t  \nabc")).is_some());
    assert!(is_empty(bytes!("  \t  ")).is_some());

    assert!(is_empty(bytes!("a")).is_none());
    assert!(is_empty(bytes!(" a")).is_none());
    assert!(is_empty(bytes!(" a\n")).is_none());
    assert!(is_empty(bytes!(" \ta\n")).is_none());

    // Test if the remaining buf actually works.
    let s = bytes!("   \t\r\nremaining");
    let res = is_empty(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}
<commit_msg>Remove benchmarks for now<commit_after>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;
static CR: u8 = '\r' as u8;
static TAB: u8 = '\t' as u8;
static STAR: u8 = '*' as u8;
static DASH: u8 = '-' as u8;
static UNDERSCORE: u8 = '_' as u8;

//
// Skip up to three leading spaces.
//
// Up to three leading spaces are allowed for many elements.
//
// We don't need to care about a TAB here as in this position it is equivalent
// to 4 spaces, which means that when we find a TAB here we would not parse the
// corresponding element.
//
fn skip_initial_three_spaces<'a>(buf: &'a[u8])-> &'a[u8] {
    let mut buf = buf;
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail(); } } }
    return buf;
}

//
// Return Some(`rem`) if the line is a horizontal rule, with `rem` being the
// buf after the hrule. Otherwise return None.
//
fn is_hrule<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let buf = skip_initial_three_spaces(buf);

    let item = match buf.head() {
        Some(&c) if c == STAR ||
                    c == DASH ||
                    c == UNDERSCORE => c,
        _                           => return None
    };

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    // Counts the consumed spaces (and the final NL)
    let mut spc: uint = 0;

    for &ch in buf.iter() {
        if      ch == item { cnt += 1; }
        else if ch == NL   { spc += 1; break; }
        else if ch == SP   { spc += 1; }
        else               { return None; }
    }

    if cnt >= 3 {
        Some(buf.slice_from(cnt + spc))
    } else {
        None
    }
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert!(is_hrule(bytes!("* * *\n")).is_some());
    assert!(is_hrule(bytes!("***\n")).is_some());
    assert!(is_hrule(bytes!("*****\n")).is_some());
    assert!(is_hrule(bytes!("- - -\n")).is_some());
    assert!(is_hrule(bytes!("---------------------------------------\n")).is_some());

    // up to three spaces ignored
    assert!(is_hrule(bytes!(" ***\n")).is_some());
    assert!(is_hrule(bytes!("  ***\n")).is_some());
    assert!(is_hrule(bytes!("   ***\n")).is_some());

    // but not four, or a tab which is equivalent to four spaces
    assert!(is_hrule(bytes!("    ***\n")).is_none());
    assert!(is_hrule(bytes!("\t***\n")).is_none());

    // need at least three
    assert!(is_hrule(bytes!("*\n")).is_none());
    assert!(is_hrule(bytes!("**\n")).is_none());
    assert!(is_hrule(bytes!("* *\n")).is_none());
    assert!(is_hrule(bytes!("   * *\n")).is_none());

    // Also works without newline at the end
    assert!(is_hrule(bytes!("* * *")).is_some());

    // And underscores also supported
    assert!(is_hrule(bytes!("___")).is_some());
    assert!(is_hrule(bytes!("______________")).is_some());
    assert!(is_hrule(bytes!(" ______________")).is_some());

    // Test if the remaining buf actually works.
    let s = bytes!("   * * *\nremaining");
    let res = is_hrule(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}

//
// Return Some(`rem`) if the line is an empty line, with `rem` being the buf
// after the empty line. Otherwise return None.
//
fn is_empty<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let mut cnt: uint = 0;

    for &ch in buf.iter() {
        if      ch == NL  { cnt += 1; break; }
        else if ch == CR  { cnt += 1; }
        else if ch == SP  { cnt += 1; }
        else if ch == TAB { cnt += 1; }
        else              { return None; }
    }

    if cnt > 0 {
        Some(buf.slice_from(cnt))
    } else {
        None
    }
}

#[test]
fn test_is_empty() {
    assert!(is_empty(bytes!("\n")).is_some());
    assert!(is_empty(bytes!("    \n")).is_some());
    assert!(is_empty(bytes!("  \t  \n")).is_some());
    assert!(is_empty(bytes!("  \t  \r\n")).is_some());
    assert!(is_empty(bytes!("  \t  \nabc")).is_some());
    assert!(is_empty(bytes!("  \t  ")).is_some());

    assert!(is_empty(bytes!("a")).is_none());
    assert!(is_empty(bytes!(" a")).is_none());
    assert!(is_empty(bytes!(" a\n")).is_none());
    assert!(is_empty(bytes!(" \ta\n")).is_none());

    // Test if the remaining buf actually works.
    let s = bytes!("   \t\r\nremaining");
    let res = is_empty(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}
<|endoftext|>


<commit_before>use std::sync::{ RwLock, RwLockReadGuard, Arc };
use std::sync::mpsc::{ Receiver };

use models::Grid;
use grid_displayers::GridDisplayer;

pub struct SimpleTerminal;

impl SimpleTerminal {
    pub fn new() -> SimpleTerminal {
        SimpleTerminal
    }
}

impl GridDisplayer for SimpleTerminal {
    fn draw(&self, receiver: Receiver<Arc<RwLock<Grid>>>) {
        loop {
            let locked_grid: Arc<RwLock<Grid>> = receiver.recv().unwrap();
            let lock_guard: RwLockReadGuard<Grid> = locked_grid.read().unwrap();
            let ref grid: Grid = *lock_guard;

            for row in grid {
                for cell in row {
                    match cell.is_alive() {
                        true => print!("o"),
                        false => print!("_")
                    };
                }
                print!("\n");
            }
            print!("\n\n");
        }
    }
}
<commit_msg>Add allow(dead_code) on the SimpleTerminal GridDisplayer<commit_after>use std::sync::{ RwLock, RwLockReadGuard, Arc };
use std::sync::mpsc::{ Receiver };

use models::Grid;
use grid_displayers::GridDisplayer;

#[allow(dead_code)]
pub struct SimpleTerminal;

impl SimpleTerminal {
    #[allow(dead_code)]
    pub fn new() -> SimpleTerminal {
        SimpleTerminal
    }
}

impl GridDisplayer for SimpleTerminal {
    fn draw(&self, receiver: Receiver<Arc<RwLock<Grid>>>) {
        loop {
            let locked_grid: Arc<RwLock<Grid>> = receiver.recv().unwrap();
            let lock_guard: RwLockReadGuard<Grid> = locked_grid.read().unwrap();
            let ref grid: Grid = *lock_guard;

            for row in grid {
                for cell in row {
                    match cell.is_alive() {
                        true => print!("o"),
                        false => print!("_")
                    };
                }
                print!("\n");
            }
            print!("\n\n");
        }
    }
}
<|endoftext|>