<commit_before>use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use multimap::MultiMap;
use rand::{Rng, thread_rng};
use dns_parser::{self, QueryClass, Name, RRData};

pub type AnswerBuilder = dns_parser::Builder<dns_parser::Answers>;

pub type Services = Arc<RwLock<ServicesInner>>;

pub struct ServicesInner {
    hostname: Name<'static>,
    /// main index
    by_id: HashMap<usize, ServiceData>,
    /// maps to id
    by_type: MultiMap<Name<'static>, usize>,
    /// maps to id
    by_name: HashMap<Name<'static>, usize>
}

impl ServicesInner {
    pub fn new(hostname: String) -> Self {
        ServicesInner {
            hostname: Name::from_str(hostname).unwrap(),
            by_id: HashMap::new(),
            by_type: MultiMap::new(),
            by_name: HashMap::new(),
        }
    }

    pub fn get_hostname(&self) -> &Name<'static> {
        &self.hostname
    }

    pub fn find_by_name<'a>(&'a self, name: &'a Name<'a>) -> Option<&ServiceData> {
        self.by_name.get(name)
            .and_then(|id| self.by_id.get(id))
    }

    pub fn find_by_type<'a>(&'a self, ty: &'a Name<'a>) -> FindByType<'a> {
        let ids = self.by_type.get_vec(ty)
            .map(|ids| &ids[..])
            .unwrap_or(&[]);
        FindByType {
            services: self,
            ids: ids
        }
    }

    pub fn register(&mut self, svc: ServiceData) -> usize {
        let mut id = thread_rng().gen::<usize>();
        while self.by_id.contains_key(&id) {
            id = thread_rng().gen::<usize>();
        }

        self.by_type.insert(svc.typ.clone(), id);
        self.by_name.insert(svc.name.clone(), id);
        self.by_id.insert(id, svc);

        id
    }

    pub fn unregister(&mut self, id: usize) -> ServiceData {
        use std::collections::hash_map::Entry;

        let svc = self.by_id.remove(&id).expect("unknown service");

        if let Some(entries) = self.by_type.get_vec_mut(&svc.typ) {
            entries.retain(|&e| e == id);
        }

        match self.by_name.entry(svc.name.clone()) {
            Entry::Occupied(entry) => {
                assert_eq!(*entry.get(), id);
                entry.remove();
            }
            _ => {
                panic!("unknown/wrong service for id {}", id);
            }
        }

        svc
    }
}

pub struct FindByType<'a> {
    services: &'a ServicesInner,
    ids: &'a [usize],
}

impl<'a> Iterator for FindByType<'a> {
    type Item = &'a ServiceData;

    fn next(&mut self) -> Option<Self::Item> {
        match self.ids.split_first() {
            Some((id, rest)) => {
                self.ids = rest;
                self.services.by_id.get(id)
            }
            None => None
        }
    }
}

#[derive(Clone)]
pub struct ServiceData {
    pub name: Name<'static>,
    pub typ: Name<'static>,
    pub port: u16,
    pub txt: Vec<u8>,
}

impl ServiceData {
    pub fn add_ptr_rr(&self, builder: AnswerBuilder, ttl: u32) -> AnswerBuilder {
        builder.add_answer(&self.typ, QueryClass::IN, ttl, &RRData::PTR(self.name.clone()))
    }

    pub fn add_srv_rr(&self, hostname: &Name, builder: AnswerBuilder, ttl: u32) -> AnswerBuilder {
        builder.add_answer(&self.name, QueryClass::IN, ttl, &RRData::SRV {
            priority: 0,
            weight: 0,
            port: self.port,
            target: hostname.clone(),
        })
    }

    pub fn add_txt_rr(&self, builder: AnswerBuilder, ttl: u32) -> AnswerBuilder {
        builder.add_answer(&self.name, QueryClass::IN, ttl, &RRData::TXT(&self.txt))
    }
}
<commit_msg>services: doc<commit_after>use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use multimap::MultiMap;
use rand::{Rng, thread_rng};
use dns_parser::{self, QueryClass, Name, RRData};

pub type AnswerBuilder = dns_parser::Builder<dns_parser::Answers>;

/// A collection of registered services is shared between threads.
pub type Services = Arc<RwLock<ServicesInner>>;

pub struct ServicesInner {
    hostname: Name<'static>,
    /// main index
    by_id: HashMap<usize, ServiceData>,
    /// maps to id
    by_type: MultiMap<Name<'static>, usize>,
    /// maps to id
    by_name: HashMap<Name<'static>, usize>
}

impl ServicesInner {
    pub fn new(hostname: String) -> Self {
        ServicesInner {
            hostname: Name::from_str(hostname).unwrap(),
            by_id: HashMap::new(),
            by_type: MultiMap::new(),
            by_name: HashMap::new(),
        }
    }

    pub fn get_hostname(&self) -> &Name<'static> {
        &self.hostname
    }

    pub fn find_by_name<'a>(&'a self, name: &'a Name<'a>) -> Option<&ServiceData> {
        self.by_name.get(name)
            .and_then(|id| self.by_id.get(id))
    }

    pub fn find_by_type<'a>(&'a self, ty: &'a Name<'a>) -> FindByType<'a> {
        let ids = self.by_type.get_vec(ty)
            .map(|ids| &ids[..])
            .unwrap_or(&[]);
        FindByType {
            services: self,
            ids: ids
        }
    }

    pub fn register(&mut self, svc: ServiceData) -> usize {
        let mut id = thread_rng().gen::<usize>();
        while self.by_id.contains_key(&id) {
            id = thread_rng().gen::<usize>();
        }

        self.by_type.insert(svc.typ.clone(), id);
        self.by_name.insert(svc.name.clone(), id);
        self.by_id.insert(id, svc);

        id
    }

    pub fn unregister(&mut self, id: usize) -> ServiceData {
        use std::collections::hash_map::Entry;

        let svc = self.by_id.remove(&id).expect("unknown service");

        if let Some(entries) = self.by_type.get_vec_mut(&svc.typ) {
            entries.retain(|&e| e == id);
        }

        match self.by_name.entry(svc.name.clone()) {
            Entry::Occupied(entry) => {
                assert_eq!(*entry.get(), id);
                entry.remove();
            }
            _ => {
                panic!("unknown/wrong service for id {}", id);
            }
        }

        svc
    }
}

/// Returned by [`ServicesInner.find_by_type`](struct.ServicesInner.html#method.find_by_type)
pub struct FindByType<'a> {
    services: &'a ServicesInner,
    ids: &'a [usize],
}

impl<'a> Iterator for FindByType<'a> {
    type Item = &'a ServiceData;

    fn next(&mut self) -> Option<Self::Item> {
        match self.ids.split_first() {
            Some((id, rest)) => {
                self.ids = rest;
                self.services.by_id.get(id)
            }
            None => None
        }
    }
}

#[derive(Clone)]
pub struct ServiceData {
    pub name: Name<'static>,
    pub typ: Name<'static>,
    pub port: u16,
    pub txt: Vec<u8>,
}

/// Packet building helpers for `fsm` to respond with `ServiceData`
impl ServiceData {
    pub fn add_ptr_rr(&self, builder: AnswerBuilder, ttl: u32) -> AnswerBuilder {
        builder.add_answer(&self.typ, QueryClass::IN, ttl, &RRData::PTR(self.name.clone()))
    }

    pub fn add_srv_rr(&self, hostname: &Name, builder: AnswerBuilder, ttl: u32) -> AnswerBuilder {
        builder.add_answer(&self.name, QueryClass::IN, ttl, &RRData::SRV {
            priority: 0,
            weight: 0,
            port: self.port,
            target: hostname.clone(),
        })
    }

    pub fn add_txt_rr(&self, builder: AnswerBuilder, ttl: u32) -> AnswerBuilder {
        builder.add_answer(&self.name, QueryClass::IN, ttl, &RRData::TXT(&self.txt))
    }
}
<|endoftext|>


<commit_before>import test1::*;
import test2::*;

mod circ1 {
    import circ1::*;
    export f1;
    export f2;
    export common;
    fn f1() -> uint { ret 1u }
    fn common() -> uint { ret 1u; }
}

mod circ2 {
    import circ2::*;
    export f1;
    export f2;
    export common;
    fn f2() -> uint { ret 2u; }
    fn common() -> uint { ret 2u; }
}

mod test1 {
    import circ1::*;
    fn test1() {
        assert (f1() == 1u);
        //make sure that cached lookups work...
        assert (f1() == 1u);
        //assert(f2() == 2u);  //TODO: renable when 'reexport' is implemented
        //assert(f2() == 2u);
        assert (common() == 1u);
        assert (common() == 1u);
    }
}

mod test2 {
    import circ2::*;
    fn test2() {
        //assert(f1() == 1u);  //TODO: renable when 'reexport' is implemented
        ////make sure that cached lookups work...
        //assert(f1() == 1u);
        assert (f2() == 2u);
        assert (f2() == 2u);
        assert (common() == 2u);
        assert (common() == 2u);
    }
}

fn main() { test1(); test2(); }
<commit_msg>test: Remove the import-glob-circular test since the name resolution semantics it depends on are probably undecidable<commit_after><|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use Result;

use chrono::Duration;
use serde_ini;

use std::io;
use std::path::PathBuf;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[cfg(not(test))]
const SERVER_URL: &str = "https://api.updatehub.io";

// When running inside a test environment we default to the mock
// server
#[cfg(test)]
use mockito;
#[cfg(test)]
const SERVER_URL: &str = mockito::SERVER_URL;

#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    /// Loads the settings from the filesystem. If
    /// `/etc/updatehub.conf` does not exists, it uses the default
    /// settings.
    pub fn load() -> Result<Self> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(Settings::default())
        }
    }

    // This parses the configuration file, taking into account the
    // needed validations for all fields, and returns either `Self` or
    // `Err`.
    fn parse(content: &str) -> Result<Self> {
        let settings = serde_ini::from_str::<Settings>(content)?;

        if settings.polling.interval < Duration::seconds(60) {
            error!(
                "Invalid setting for polling interval. The interval cannot be less than 60 seconds"
            );
            return Err(SettingsError::InvalidInterval.into());
        }

        if !&settings.network.server_address.starts_with("http://")
            && !&settings.network.server_address.starts_with("https://")
        {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress.into());
        }

        Ok(settings)
    }
}

#[derive(Debug, Fail)]
pub enum SettingsError {
    #[cause]
    #[fail(display = "IO error")]
    Io(io::Error),
    #[cause]
    #[fail(display = "Invalid INI fail")]
    Ini(serde_ini::de::Error),
    #[fail(display = "Invalid interval")]
    InvalidInterval,
    #[fail(display = "Invalid server address")]
    InvalidServerAddress,
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::days(1),
            enabled: true,
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub/runtime_settings.conf".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: PathBuf,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network {
            server_address: SERVER_URL.into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected = Settings {
        polling: Polling {
            interval: Duration::seconds(60),
            enabled: false,
        },
        storage: Storage {
            read_only: true,
            runtime_settings: "/run/updatehub/state".into(),
        },
        update: Update {
            download_dir: "/tmp/download".into(),
            install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
        },
        network: Network {
            server_address: "http://localhost".into(),
        },
        firmware: Firmware {
            metadata_path: "/tmp/metadata".into(),
        },
    };

    assert_eq!(
        serde_ini::from_str::<Settings>(ini)
            .map_err(|e| println!("{}", e))
            .ok(),
        Some(expected)
    );
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::default();
    let expected = Settings {
        polling: Polling {
            interval: Duration::days(1),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub/runtime_settings.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: SERVER_URL.into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert_eq!(Some(settings), Some(expected));
}
<commit_msg>settings: Sort definition of fields in `Settings` struct<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use Result;

use chrono::Duration;
use serde_ini;

use std::io;
use std::path::PathBuf;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[cfg(not(test))]
const SERVER_URL: &str = "https://api.updatehub.io";

// When running inside a test environment we default to the mock
// server
#[cfg(test)]
use mockito;
#[cfg(test)]
const SERVER_URL: &str = mockito::SERVER_URL;

#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub firmware: Firmware,
    pub network: Network,
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
}

impl Settings {
    /// Loads the settings from the filesystem. If
    /// `/etc/updatehub.conf` does not exists, it uses the default
    /// settings.
    pub fn load() -> Result<Self> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(Settings::default())
        }
    }

    // This parses the configuration file, taking into account the
    // needed validations for all fields, and returns either `Self` or
    // `Err`.
    fn parse(content: &str) -> Result<Self> {
        let settings = serde_ini::from_str::<Settings>(content)?;

        if settings.polling.interval < Duration::seconds(60) {
            error!(
                "Invalid setting for polling interval. The interval cannot be less than 60 seconds"
            );
            return Err(SettingsError::InvalidInterval.into());
        }

        if !&settings.network.server_address.starts_with("http://")
            && !&settings.network.server_address.starts_with("https://")
        {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress.into());
        }

        Ok(settings)
    }
}

#[derive(Debug, Fail)]
pub enum SettingsError {
    #[cause]
    #[fail(display = "IO error")]
    Io(io::Error),
    #[cause]
    #[fail(display = "Invalid INI fail")]
    Ini(serde_ini::de::Error),
    #[fail(display = "Invalid interval")]
    InvalidInterval,
    #[fail(display = "Invalid server address")]
    InvalidServerAddress,
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::days(1),
            enabled: true,
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub/runtime_settings.conf".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: PathBuf,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network {
            server_address: SERVER_URL.into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected = Settings {
        polling: Polling {
            interval: Duration::seconds(60),
            enabled: false,
        },
        storage: Storage {
            read_only: true,
            runtime_settings: "/run/updatehub/state".into(),
        },
        update: Update {
            download_dir: "/tmp/download".into(),
            install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
        },
        network: Network {
            server_address: "http://localhost".into(),
        },
        firmware: Firmware {
            metadata_path: "/tmp/metadata".into(),
        },
    };

    assert_eq!(
        serde_ini::from_str::<Settings>(ini)
            .map_err(|e| println!("{}", e))
            .ok(),
        Some(expected)
    );
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::default();
    let expected = Settings {
        polling: Polling {
            interval: Duration::days(1),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub/runtime_settings.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: SERVER_URL.into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert_eq!(Some(settings), Some(expected));
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32))
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Reading zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // We want to read at most `count` bytes
            let mut bytes = vec![0; count as usize];
            let result = handle.file.read(&mut bytes).map(|c| c as i64);
            let written_count = this.try_unwrap_io_result(result)?;
            // `try_unwrap_io_result` returns Ok(`-1`) if `result` is an error. There is no other
            // way of returning `-1` because the `Ok` variant of `result` contains the number of
            // written bytes, which is a possitive value.
            if written_count != -1 {
                // If reading to `bytes` did not fail, we write those bytes to the buffer.
                this.memory.write_bytes(buf, bytes)?;
            }
            Ok(written_count)
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Writing zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes).map(|c| c as i64);
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<commit_msg>Drop files explicitly when closing them<commit_after>use std::collections::HashMap;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            // `File::sync_all` does the checks that are done when closing a file. We do this to
            // to handle possible errors correctly.
            let result = this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32));
            // Now we actually drop the handle.
            drop(handle);
            // And return the result.
            result
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Reading zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // We want to read at most `count` bytes
            let mut bytes = vec![0; count as usize];
            let result = handle.file.read(&mut bytes).map(|c| c as i64);
            let written_count = this.try_unwrap_io_result(result)?;
            // `try_unwrap_io_result` returns Ok(`-1`) if `result` is an error. There is no other
            // way of returning `-1` because the `Ok` variant of `result` contains the number of
            // written bytes, which is a possitive value.
            if written_count != -1 {
                // If reading to `bytes` did not fail, we write those bytes to the buffer.
                this.memory.write_bytes(buf, bytes)?;
            }
            Ok(written_count)
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Writing zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes).map(|c| c as i64);
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<|endoftext|>


<commit_before>use anyhow::*;
use ape;
use id3;
use lewton::inside_ogg::OggStreamReader;
use log::error;
use metaflac;
use mp3_duration;
use mp4ameta;
use regex::Regex;
use std::fs;
use std::path::Path;
use opus_headers;

use crate::utils;
use crate::utils::AudioFormat;

#[derive(Debug, Clone, PartialEq)]
pub struct SongTags {
	pub disc_number: Option<u32>,
	pub track_number: Option<u32>,
	pub title: Option<String>,
	pub duration: Option<u32>,
	pub artist: Option<String>,
	pub album_artist: Option<String>,
	pub album: Option<String>,
	pub year: Option<i32>,
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn read(path: &Path) -> Option<SongTags> {
	let data = match utils::get_audio_format(path) {
		Some(AudioFormat::FLAC) => Some(read_flac(path)),
		Some(AudioFormat::MP3) => Some(read_id3(path)),
		Some(AudioFormat::MP4) => Some(read_mp4(path)),
		Some(AudioFormat::MPC) => Some(read_ape(path)),
		Some(AudioFormat::OGG) => Some(read_vorbis(path)),
		Some(AudioFormat::OPUS) => Some(read_opus(path)),
		_ => None,
	};
	match data {
		Some(Ok(d)) => Some(d),
		Some(Err(e)) => {
			error!("Error while reading file metadata for '{:?}': {}", path, e);
			None
		}
		None => None,
	}
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_id3(path: &Path) -> Result<SongTags> {
	let tag = {
		#[cfg(feature = "profile-index")]
		let _guard = flame::start_guard("id3_tag_read");
		match id3::Tag::read_from_path(&path) {
			Ok(t) => Ok(t),
			Err(e) => {
				if let Some(t) = e.partial_tag {
					Ok(t)
				} else {
					Err(e)
				}
			}
		}?
	};
	let duration = {
		#[cfg(feature = "profile-index")]
		let _guard = flame::start_guard("mp3_duration");
		mp3_duration::from_path(&path)
			.map(|d| d.as_secs() as u32)
			.ok()
	};

	let artist = tag.artist().map(|s| s.to_string());
	let album_artist = tag.album_artist().map(|s| s.to_string());
	let album = tag.album().map(|s| s.to_string());
	let title = tag.title().map(|s| s.to_string());
	let disc_number = tag.disc();
	let track_number = tag.track();
	let year = tag
		.year()
		.map(|y| y as i32)
		.or_else(|| tag.date_released().and_then(|d| Some(d.year)))
		.or_else(|| tag.date_recorded().and_then(|d| Some(d.year)));

	Ok(SongTags {
		artist,
		album_artist,
		album,
		title,
		duration,
		disc_number,
		track_number,
		year,
	})
}

fn read_ape_string(item: &ape::Item) -> Option<String> {
	match item.value {
		ape::ItemValue::Text(ref s) => Some(s.clone()),
		_ => None,
	}
}

fn read_ape_i32(item: &ape::Item) -> Option<i32> {
	match item.value {
		ape::ItemValue::Text(ref s) => s.parse::<i32>().ok(),
		_ => None,
	}
}

fn read_ape_x_of_y(item: &ape::Item) -> Option<u32> {
	match item.value {
		ape::ItemValue::Text(ref s) => {
			let format = Regex::new(r#"^\d+"#).unwrap();
			if let Some(m) = format.find(s) {
				s[m.start()..m.end()].parse().ok()
			} else {
				None
			}
		}
		_ => None,
	}
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_ape(path: &Path) -> Result<SongTags> {
	let tag = ape::read(path)?;
	let artist = tag.item("Artist").and_then(read_ape_string);
	let album = tag.item("Album").and_then(read_ape_string);
	let album_artist = tag.item("Album artist").and_then(read_ape_string);
	let title = tag.item("Title").and_then(read_ape_string);
	let year = tag.item("Year").and_then(read_ape_i32);
	let disc_number = tag.item("Disc").and_then(read_ape_x_of_y);
	let track_number = tag.item("Track").and_then(read_ape_x_of_y);
	Ok(SongTags {
		artist,
		album_artist,
		album,
		title,
		duration: None,
		disc_number,
		track_number,
		year,
	})
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_vorbis(path: &Path) -> Result<SongTags> {
	let file = fs::File::open(path)?;
	let source = OggStreamReader::new(file)?;

	let mut tags = SongTags {
		artist: None,
		album_artist: None,
		album: None,
		title: None,
		duration: None,
		disc_number: None,
		track_number: None,
		year: None,
	};

	for (key, value) in source.comment_hdr.comment_list {
		match key.as_str() {
			"TITLE" => tags.title = Some(value),
			"ALBUM" => tags.album = Some(value),
			"ARTIST" => tags.artist = Some(value),
			"ALBUMARTIST" => tags.album_artist = Some(value),
			"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
			"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
			"DATE" => tags.year = value.parse::<i32>().ok(),
			_ => (),
		}
	}

	Ok(tags)
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_opus(path: &Path) -> Result<SongTags> {
	let headers = opus_headers::parse_from_path(path)?;

	let mut tags = SongTags {
		artist: None,
		album_artist: None,
		album: None,
		title: None,
		duration: None,
		disc_number: None,
		track_number: None,
		year: None,
	};

	for (key, value) in headers.comments.user_comments {
		match key.to_uppercase().as_str() {
			"TITLE" => tags.title = Some(value),
			"ALBUM" => tags.album = Some(value),
			"ARTIST" => tags.artist = Some(value),
			"ALBUMARTIST" => tags.album_artist = Some(value),
			"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
			"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
			"DATE" => tags.year = value.parse::<i32>().ok(),
			_ => (),
		}
	}

	Ok(tags)
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_flac(path: &Path) -> Result<SongTags> {
	let tag = metaflac::Tag::read_from_path(path)?;
	let vorbis = tag
		.vorbis_comments()
		.ok_or(anyhow!("Missing Vorbis comments"))?;
	let disc_number = vorbis
		.get("DISCNUMBER")
		.and_then(|d| d[0].parse::<u32>().ok());
	let year = vorbis.get("DATE").and_then(|d| d[0].parse::<i32>().ok());
	let mut streaminfo = tag.get_blocks(metaflac::BlockType::StreamInfo);
	let duration = match streaminfo.next() {
		Some(&metaflac::Block::StreamInfo(ref s)) => {
			Some((s.total_samples as u32 / s.sample_rate) as u32)
		}
		_ => None,
	};

	Ok(SongTags {
		artist: vorbis.artist().map(|v| v[0].clone()),
		album_artist: vorbis.album_artist().map(|v| v[0].clone()),
		album: vorbis.album().map(|v| v[0].clone()),
		title: vorbis.title().map(|v| v[0].clone()),
		duration,
		disc_number,
		track_number: vorbis.track(),
		year,
	})
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_mp4(path: &Path) -> Result<SongTags> {
	let tag = mp4ameta::Tag::read_from_path(path)?;
	let mut tags = SongTags {
		artist: None,
		album_artist: None,
		album: None,
		title: None,
		duration: None,
		disc_number: None,
		track_number: None,
		year: None,
	};
	tags.artist = tag.artist().map(|v| v.to_string());
	tags.album_artist = tag.album_artist().map(|v| v.to_string());
	tags.album = tag.album().map(|v| v.to_string());
	tags.title = tag.title().map(|v| v.to_string());
	tags.duration = tag.duration().map(|v| v as u32);
	tags.disc_number = tag.disk_number().0.and_then(|d| Some(d as u32));
	tags.track_number = tag.track_number().0.and_then(|d| Some(d as u32));
	tags.year = tag.year().and_then(|v| v.parse::<i32>().ok());

	Ok(tags)
}

#[test]
fn test_read_metadata() {
	let sample_tags = SongTags {
		disc_number: Some(3),
		track_number: Some(1),
		title: Some("TEST TITLE".into()),
		artist: Some("TEST ARTIST".into()),
		album_artist: Some("TEST ALBUM ARTIST".into()),
		album: Some("TEST ALBUM".into()),
		duration: None,
		year: Some(2016),
	};
	let flac_sample_tag = SongTags {
		duration: Some(0),
		..sample_tags.clone()
	};
	let mp3_sample_tag = SongTags {
		duration: Some(0),
		..sample_tags.clone()
	};
	let m4a_sample_tag = SongTags {
		duration: Some(0),
		..sample_tags.clone()
	};
	assert_eq!(read(Path::new("test/sample.mp3")).unwrap(), mp3_sample_tag);
	assert_eq!(read(Path::new("test/sample.ogg")).unwrap(), sample_tags);
	assert_eq!(read(Path::new("test/sample.flac")).unwrap(), flac_sample_tag);
	assert_eq!(read(Path::new("test/sample.m4a")).unwrap(), m4a_sample_tag);
}
<commit_msg>use eq_ignore_ascii_case instead of to_uppercase<commit_after>use anyhow::*;
use ape;
use id3;
use lewton::inside_ogg::OggStreamReader;
use log::error;
use metaflac;
use mp3_duration;
use mp4ameta;
use regex::Regex;
use std::fs;
use std::path::Path;
use opus_headers;

use crate::utils;
use crate::utils::AudioFormat;

#[derive(Debug, Clone, PartialEq)]
pub struct SongTags {
	pub disc_number: Option<u32>,
	pub track_number: Option<u32>,
	pub title: Option<String>,
	pub duration: Option<u32>,
	pub artist: Option<String>,
	pub album_artist: Option<String>,
	pub album: Option<String>,
	pub year: Option<i32>,
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn read(path: &Path) -> Option<SongTags> {
	let data = match utils::get_audio_format(path) {
		Some(AudioFormat::FLAC) => Some(read_flac(path)),
		Some(AudioFormat::MP3) => Some(read_id3(path)),
		Some(AudioFormat::MP4) => Some(read_mp4(path)),
		Some(AudioFormat::MPC) => Some(read_ape(path)),
		Some(AudioFormat::OGG) => Some(read_vorbis(path)),
		Some(AudioFormat::OPUS) => Some(read_opus(path)),
		_ => None,
	};
	match data {
		Some(Ok(d)) => Some(d),
		Some(Err(e)) => {
			error!("Error while reading file metadata for '{:?}': {}", path, e);
			None
		}
		None => None,
	}
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_id3(path: &Path) -> Result<SongTags> {
	let tag = {
		#[cfg(feature = "profile-index")]
		let _guard = flame::start_guard("id3_tag_read");
		match id3::Tag::read_from_path(&path) {
			Ok(t) => Ok(t),
			Err(e) => {
				if let Some(t) = e.partial_tag {
					Ok(t)
				} else {
					Err(e)
				}
			}
		}?
	};
	let duration = {
		#[cfg(feature = "profile-index")]
		let _guard = flame::start_guard("mp3_duration");
		mp3_duration::from_path(&path)
			.map(|d| d.as_secs() as u32)
			.ok()
	};

	let artist = tag.artist().map(|s| s.to_string());
	let album_artist = tag.album_artist().map(|s| s.to_string());
	let album = tag.album().map(|s| s.to_string());
	let title = tag.title().map(|s| s.to_string());
	let disc_number = tag.disc();
	let track_number = tag.track();
	let year = tag
		.year()
		.map(|y| y as i32)
		.or_else(|| tag.date_released().and_then(|d| Some(d.year)))
		.or_else(|| tag.date_recorded().and_then(|d| Some(d.year)));

	Ok(SongTags {
		artist,
		album_artist,
		album,
		title,
		duration,
		disc_number,
		track_number,
		year,
	})
}

fn read_ape_string(item: &ape::Item) -> Option<String> {
	match item.value {
		ape::ItemValue::Text(ref s) => Some(s.clone()),
		_ => None,
	}
}

fn read_ape_i32(item: &ape::Item) -> Option<i32> {
	match item.value {
		ape::ItemValue::Text(ref s) => s.parse::<i32>().ok(),
		_ => None,
	}
}

fn read_ape_x_of_y(item: &ape::Item) -> Option<u32> {
	match item.value {
		ape::ItemValue::Text(ref s) => {
			let format = Regex::new(r#"^\d+"#).unwrap();
			if let Some(m) = format.find(s) {
				s[m.start()..m.end()].parse().ok()
			} else {
				None
			}
		}
		_ => None,
	}
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_ape(path: &Path) -> Result<SongTags> {
	let tag = ape::read(path)?;
	let artist = tag.item("Artist").and_then(read_ape_string);
	let album = tag.item("Album").and_then(read_ape_string);
	let album_artist = tag.item("Album artist").and_then(read_ape_string);
	let title = tag.item("Title").and_then(read_ape_string);
	let year = tag.item("Year").and_then(read_ape_i32);
	let disc_number = tag.item("Disc").and_then(read_ape_x_of_y);
	let track_number = tag.item("Track").and_then(read_ape_x_of_y);
	Ok(SongTags {
		artist,
		album_artist,
		album,
		title,
		duration: None,
		disc_number,
		track_number,
		year,
	})
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_vorbis(path: &Path) -> Result<SongTags> {
	let file = fs::File::open(path)?;
	let source = OggStreamReader::new(file)?;

	let mut tags = SongTags {
		artist: None,
		album_artist: None,
		album: None,
		title: None,
		duration: None,
		disc_number: None,
		track_number: None,
		year: None,
	};

	for (key, value) in source.comment_hdr.comment_list {
		match key.as_str() {
			"TITLE" => tags.title = Some(value),
			"ALBUM" => tags.album = Some(value),
			"ARTIST" => tags.artist = Some(value),
			"ALBUMARTIST" => tags.album_artist = Some(value),
			"TRACKNUMBER" => tags.track_number = value.parse::<u32>().ok(),
			"DISCNUMBER" => tags.disc_number = value.parse::<u32>().ok(),
			"DATE" => tags.year = value.parse::<i32>().ok(),
			_ => (),
		}
	}

	Ok(tags)
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_opus(path: &Path) -> Result<SongTags> {
	let headers = opus_headers::parse_from_path(path)?;

	let mut tags = SongTags {
		artist: None,
		album_artist: None,
		album: None,
		title: None,
		duration: None,
		disc_number: None,
		track_number: None,
		year: None,
	};

	for (key, value) in headers.comments.user_comments {
		if "TITLE".eq_ignore_ascii_case(key.as_str()) {
			tags.title = Some(value);
		} else if "ALBUM".eq_ignore_ascii_case(key.as_str()) {
			tags.album = Some(value);
		} else if "ARTIST".eq_ignore_ascii_case(key.as_str()) {
			tags.artist = Some(value);
		} else if "ALBUMARTIST".eq_ignore_ascii_case(key.as_str()) {
			tags.album_artist = Some(value);
		} else if "TRACKNUMBER".eq_ignore_ascii_case(key.as_str()) {
			tags.track_number = value.parse::<u32>().ok();
		} else if "DISCNUMBER".eq_ignore_ascii_case(key.as_str()) {
			tags.disc_number = value.parse::<u32>().ok();
		} else if "DATE".eq_ignore_ascii_case(key.as_str()) {
			tags.year = value.parse::<i32>().ok();
		}
	}

	Ok(tags)
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_flac(path: &Path) -> Result<SongTags> {
	let tag = metaflac::Tag::read_from_path(path)?;
	let vorbis = tag
		.vorbis_comments()
		.ok_or(anyhow!("Missing Vorbis comments"))?;
	let disc_number = vorbis
		.get("DISCNUMBER")
		.and_then(|d| d[0].parse::<u32>().ok());
	let year = vorbis.get("DATE").and_then(|d| d[0].parse::<i32>().ok());
	let mut streaminfo = tag.get_blocks(metaflac::BlockType::StreamInfo);
	let duration = match streaminfo.next() {
		Some(&metaflac::Block::StreamInfo(ref s)) => {
			Some((s.total_samples as u32 / s.sample_rate) as u32)
		}
		_ => None,
	};

	Ok(SongTags {
		artist: vorbis.artist().map(|v| v[0].clone()),
		album_artist: vorbis.album_artist().map(|v| v[0].clone()),
		album: vorbis.album().map(|v| v[0].clone()),
		title: vorbis.title().map(|v| v[0].clone()),
		duration,
		disc_number,
		track_number: vorbis.track(),
		year,
	})
}

#[cfg_attr(feature = "profile-index", flame)]
fn read_mp4(path: &Path) -> Result<SongTags> {
	let tag = mp4ameta::Tag::read_from_path(path)?;
	let mut tags = SongTags {
		artist: None,
		album_artist: None,
		album: None,
		title: None,
		duration: None,
		disc_number: None,
		track_number: None,
		year: None,
	};
	tags.artist = tag.artist().map(|v| v.to_string());
	tags.album_artist = tag.album_artist().map(|v| v.to_string());
	tags.album = tag.album().map(|v| v.to_string());
	tags.title = tag.title().map(|v| v.to_string());
	tags.duration = tag.duration().map(|v| v as u32);
	tags.disc_number = tag.disk_number().0.and_then(|d| Some(d as u32));
	tags.track_number = tag.track_number().0.and_then(|d| Some(d as u32));
	tags.year = tag.year().and_then(|v| v.parse::<i32>().ok());

	Ok(tags)
}

#[test]
fn test_read_metadata() {
	let sample_tags = SongTags {
		disc_number: Some(3),
		track_number: Some(1),
		title: Some("TEST TITLE".into()),
		artist: Some("TEST ARTIST".into()),
		album_artist: Some("TEST ALBUM ARTIST".into()),
		album: Some("TEST ALBUM".into()),
		duration: None,
		year: Some(2016),
	};
	let flac_sample_tag = SongTags {
		duration: Some(0),
		..sample_tags.clone()
	};
	let mp3_sample_tag = SongTags {
		duration: Some(0),
		..sample_tags.clone()
	};
	let m4a_sample_tag = SongTags {
		duration: Some(0),
		..sample_tags.clone()
	};
	assert_eq!(read(Path::new("test/sample.mp3")).unwrap(), mp3_sample_tag);
	assert_eq!(read(Path::new("test/sample.ogg")).unwrap(), sample_tags);
	assert_eq!(read(Path::new("test/sample.flac")).unwrap(), flac_sample_tag);
	assert_eq!(read(Path::new("test/sample.m4a")).unwrap(), m4a_sample_tag);
}
<|endoftext|>


<commit_before>use Parameter;
use agents::PredictionAgent;
use fa::{VFunction, Projector, Projection, Linear};
use geometry::Space;

pub struct GTD2<S: Space, P: Projector<S>> {
    pub v_func: Linear<S, P>,
    pub a_func: Linear<S, P>,

    pub alpha: Parameter,
    pub beta: Parameter,
    pub gamma: Parameter,
}

impl<S: Space, P: Projector<S>> GTD2<S, P> {
    pub fn new<T1, T2, T3>(v_func: Linear<S, P>,
                           a_func: Linear<S, P>,
                           alpha: T1,
                           beta: T2,
                           gamma: T3)
                           -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>,
              T3: Into<Parameter>
    {
        if !v_func.projector.equivalent(&a_func.projector) {
            panic!("v_func and a_func must be equivalent function approximators.")
        }

        GTD2 {
            v_func: v_func,
            a_func: a_func,

            alpha: alpha.into(),
            beta: beta.into(),
            gamma: gamma.into(),
        }
    }
}

impl<S: Space, V> PredictionAgent<S> for GTD2<S, V>
    where V: VFunction<S> + Projector<S>
{
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let phi_s = self.v_func.projector.project(s);
        let phi_ns = self.v_func.projector.project(ns);

        let td_error = r + self.gamma*self.v_func.evaluate_phi(&phi_ns) -
            self.v_func.evaluate_phi(&phi_s);
        let td_estimate = self.a_func.evaluate_phi(&phi_s);

        self.a_func.update_phi(&phi_s, self.beta*(td_error - td_estimate));

        {
            let phi_s = self.v_func.projector.expand_projection(phi_s);
            let phi_ns = self.v_func.projector.expand_projection(phi_ns);
            let update = &phi_s - &(self.gamma.value()*&phi_ns);

            self.v_func.update_phi(&Projection::Dense(update), self.alpha*td_estimate);
        }

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.beta = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}

pub struct TDC<S: Space, P: Projector<S>> {
    pub v_func: Linear<S, P>,
    pub a_func: Linear<S, P>,

    pub alpha: Parameter,
    pub beta: Parameter,
    pub gamma: Parameter,
}

impl<S: Space, P: Projector<S>> TDC<S, P> {
    pub fn new<T1, T2, T3>(v_func: Linear<S, P>,
                           a_func: Linear<S, P>,
                           alpha: T1,
                           beta: T2,
                           gamma: T3)
                           -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>,
              T3: Into<Parameter>
    {
        if !v_func.projector.equivalent(&a_func.projector) {
            panic!("v_func and a_func must be equivalent function approximators.")
        }

        TDC {
            v_func: v_func,
            a_func: a_func,

            alpha: alpha.into(),
            beta: beta.into(),
            gamma: gamma.into(),
        }
    }
}

impl<S: Space, V> PredictionAgent<S> for TDC<S, V>
    where V: VFunction<S> + Projector<S>
{
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let phi_s = self.v_func.projector.project(s);
        let phi_ns = self.v_func.projector.project(ns);

        let td_error = r + self.gamma*self.v_func.evaluate_phi(&phi_ns) -
            self.v_func.evaluate_phi(&phi_s);
        let td_estimate = self.a_func.evaluate_phi(&phi_s);

        self.a_func.update_phi(&phi_s, self.beta*(td_error - td_estimate));

        {
            let phi_s = self.v_func.projector.expand_projection(phi_s);
            let phi_ns = self.v_func.projector.expand_projection(phi_ns);
            let update = &(td_error*&phi_s) - &(self.gamma.value()*td_estimate*&phi_ns);

            self.v_func.update_phi(&Projection::Dense(update), self.alpha.value());
        }

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.beta = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}

// TODO:
// GQ(lambda) - http://agi-conf.org/2010/wp-content/uploads/2009/06/paper_21.pdf
// GTD(lambda) - https://era.library.ualberta.ca/files/8s45q967t/Hamid_Maei_PhDThesis.pdf
// True online GTD(lambda) - http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.487.2451&rep=rep1&type=pdf
// GTD2(lambda)-MP - https://arxiv.org/pdf/1602.08771.pdf
// TDC(lambda)-MP - https://arxiv.org/pdf/1602.08771.pdf
<commit_msg>chore: add TODO in prediction agents<commit_after>use Parameter;
use agents::PredictionAgent;
use fa::{VFunction, Projector, Projection, Linear};
use geometry::Space;

pub struct GTD2<S: Space, P: Projector<S>> {
    pub v_func: Linear<S, P>,
    pub a_func: Linear<S, P>,

    pub alpha: Parameter,
    pub beta: Parameter,
    pub gamma: Parameter,
}

impl<S: Space, P: Projector<S>> GTD2<S, P> {
    pub fn new<T1, T2, T3>(v_func: Linear<S, P>,
                           a_func: Linear<S, P>,
                           alpha: T1,
                           beta: T2,
                           gamma: T3)
                           -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>,
              T3: Into<Parameter>
    {
        if !v_func.projector.equivalent(&a_func.projector) {
            panic!("v_func and a_func must be equivalent function approximators.")
        }

        GTD2 {
            v_func: v_func,
            a_func: a_func,

            alpha: alpha.into(),
            beta: beta.into(),
            gamma: gamma.into(),
        }
    }
}

impl<S: Space, V> PredictionAgent<S> for GTD2<S, V>
    where V: VFunction<S> + Projector<S>
{
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let phi_s = self.v_func.projector.project(s);
        let phi_ns = self.v_func.projector.project(ns);

        let td_error = r + self.gamma*self.v_func.evaluate_phi(&phi_ns) -
            self.v_func.evaluate_phi(&phi_s);
        let td_estimate = self.a_func.evaluate_phi(&phi_s);

        self.a_func.update_phi(&phi_s, self.beta*(td_error - td_estimate));

        {
            let phi_s = self.v_func.projector.expand_projection(phi_s);
            let phi_ns = self.v_func.projector.expand_projection(phi_ns);
            let update = &phi_s - &(self.gamma.value()*&phi_ns);

            self.v_func.update_phi(&Projection::Dense(update), self.alpha*td_estimate);
        }

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.beta = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}

pub struct TDC<S: Space, P: Projector<S>> {
    pub v_func: Linear<S, P>,
    pub a_func: Linear<S, P>,

    pub alpha: Parameter,
    pub beta: Parameter,
    pub gamma: Parameter,
}

impl<S: Space, P: Projector<S>> TDC<S, P> {
    pub fn new<T1, T2, T3>(v_func: Linear<S, P>,
                           a_func: Linear<S, P>,
                           alpha: T1,
                           beta: T2,
                           gamma: T3)
                           -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>,
              T3: Into<Parameter>
    {
        if !v_func.projector.equivalent(&a_func.projector) {
            panic!("v_func and a_func must be equivalent function approximators.")
        }

        TDC {
            v_func: v_func,
            a_func: a_func,

            alpha: alpha.into(),
            beta: beta.into(),
            gamma: gamma.into(),
        }
    }
}

impl<S: Space, V> PredictionAgent<S> for TDC<S, V>
    where V: VFunction<S> + Projector<S>
{
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let phi_s = self.v_func.projector.project(s);
        let phi_ns = self.v_func.projector.project(ns);

        let td_error = r + self.gamma*self.v_func.evaluate_phi(&phi_ns) -
            self.v_func.evaluate_phi(&phi_s);
        let td_estimate = self.a_func.evaluate_phi(&phi_s);

        self.a_func.update_phi(&phi_s, self.beta*(td_error - td_estimate));

        {
            let phi_s = self.v_func.projector.expand_projection(phi_s);
            let phi_ns = self.v_func.projector.expand_projection(phi_ns);
            let update = &(td_error*&phi_s) - &(self.gamma.value()*td_estimate*&phi_ns);

            self.v_func.update_phi(&Projection::Dense(update), self.alpha.value());
        }

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.beta = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}

// TODO:
// ABQ(lambda) - https://arxiv.org/pdf/1702.03006.pdf
// GQ(lambda) - http://agi-conf.org/2010/wp-content/uploads/2009/06/paper_21.pdf
// GTD(lambda) - https://era.library.ualberta.ca/files/8s45q967t/Hamid_Maei_PhDThesis.pdf
// True online GTD(lambda) - http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.487.2451&rep=rep1&type=pdf
// GTD2(lambda)-MP - https://arxiv.org/pdf/1602.08771.pdf
// TDC(lambda)-MP - https://arxiv.org/pdf/1602.08771.pdf
<|endoftext|>


<commit_before>
use std::collections::BTreeMap;

use assembler::assembler::Emittable;
use assembler::opcode::Opcode;
use assembler::parser::{Literal, InstructionField};
use assembler::u12::*;

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct Instruction(Opcode);

/**
 Resolves the label from the label map.
 @param label The name of the label (case-sensitive).
 @param label_map The mapping of labels to their resolved addresses.
 @return The address from the label map if defined, or an error describing the failure.
 */
fn resolve_label_with_map<'a>(label: &'a str, label_map: &BTreeMap<&'a str, U12>) -> Result<U12, String> {
  match label_map.get(label) {
    Some(value) => Ok(*value),
           None => Err(format!("Unable to resolve address of label {}", label))
  }
}

/**
 Converts a 12-bit (unsigned) literal value to 8-bits if possible to do so without data loss.
 @param literal 12-bit literal.
 @return A result consisting of the low 8 bits of the 12-bit literal or an explanation as to why conversion failed.
 */
fn numeric_literal_to_8_bit_field(literal: U12) -> Result<u8, String> {
  match literal.as_u8() {
    Some(value) => Ok(value),
           None => Err(format!("Found 12-bit numeric literal ${:X}, expecting 8-bit value.", literal.as_usize()))
  }
}

impl<'a> Instruction {
	/**
	 Converts a mnemonic string and a list of associated fields into an opcode (with the help of a
	 label resolution map). Produces an error message in the event of failure.
	 @param mnemonic The (all lowercase) instruction mnemonic.
	 @param fields A vector of instruction fields.
	 @param label_map A reference to a map of labels to their defined address values.
	 @return The assembled instruction if successful or a string describing the failure otherwise.
	 */
	pub fn from_mnemonic_and_parameters(mnemonic: &'a str, fields: Vec<InstructionField<'a>>, label_map: &BTreeMap<&'a str, U12>) -> Result<Self, String> {
		let mut opcode: Option<Opcode> = None;

    // Mnemonics with zero parameters.
    if fields.len() == 0 {
      opcode = match mnemonic {
        "nop"     => Some(Opcode::NOP),
        "cls"     => Some(Opcode::CLS),
        "ret"     => Some(Opcode::RET),
        "trap"    => Some(Opcode::TRAP),
        "trapret" => Some(Opcode::TRAPRET),
        _         => None
      };
    }

    // Mnemonics with one parameter.
    if fields.len() == 1 {
      opcode = match (mnemonic, fields.get(0).unwrap()) {

        ("jp", &InstructionField::NumericLiteral(target)) => Some(Opcode::JP { target: target }),

        // The 'jp' mnemonic may reference a label.
        ("jp", &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::JP { target: address })
        }

        ("call", &InstructionField::NumericLiteral(target)) => Some(Opcode::CALL { target: target }),

        // The 'call' mnemonic may reference a label.
        ("call", &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::CALL { target: address })
        }

        _ => None
      }
    }

    // Mnemonics with two parameters.
    if fields.len() == 2 {
      opcode = match (mnemonic, fields.get(0).unwrap(), fields.get(1).unwrap()) {
        ("se", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::SE_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("sne", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::SNE_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("se", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SE_REGISTER { register_x: x, register_y: y })
        }

        ("sne", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SNE_REGISTER { register_x: x, register_y: y })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::LD_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("add", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::ADD_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::LD_REGISTER { register_x: x, register_y: y })
        }

        ("or", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::OR { register_x: x, register_y: y })
        }

        ("and", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::AND { register_x: x, register_y: y })
        }

        ("xor", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::XOR { register_x: x, register_y: y })
        }

        ("add", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::ADD_REGISTER { register_x: x, register_y: y })
        }

        ("sub", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SUB { register_x: x, register_y: y })
        }

        ("shr", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SHR { register_x: x, register_y: y })
        }

        ("subn", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SUBN { register_x: x, register_y: y })
        }

        ("shl", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SHL { register_x: x, register_y: y })
        }

        ("ld", &InstructionField::IndexRegister, &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::LD_I { value: value })
        }

        // The mnemonic 'ld i' can take a label parameter.
        ("ld", &InstructionField::IndexRegister, &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::LD_I { value: address })
        }

        ("jp", &InstructionField::GeneralPurposeRegister(0), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::JP_V0 { value: value })
        }

        // The mnemonic 'jp v0' can take a label parameter.
        ("jp", &InstructionField::GeneralPurposeRegister(0), &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::JP_V0 { value: address })
        }

        ("rnd", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::RND { register_x: x, mask: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("se", &InstructionField::GeneralPurposeRegister(x), &InstructionField::KeypadRegister) => {
          Some(Opcode::SE_K { register_x: x })
        }

        ("sne", &InstructionField::GeneralPurposeRegister(x), &InstructionField::KeypadRegister) => {
          Some(Opcode::SNE_K { register_x: x })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::DelayTimer) => {
          Some(Opcode::LD_X_DT { register_x: x })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::KeypadRegister) => {
          Some(Opcode::LD_X_K { register_x: x })
        }

        ("ld", &InstructionField::DelayTimer, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::LD_DT_X { register_x: x })
        }

        ("ld", &InstructionField::SoundTimer, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::LD_ST_X { register_x: x })
        }

        ("add", &InstructionField::IndexRegister, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::ADD_I_X { register_x: x })
        }

        ("sprite", &InstructionField::IndexRegister, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::SPRITE_I { register_x: x })
        }

        ("bcd", &InstructionField::IndexRegisterIndirect, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::BCD_I { register_x: x })
        }

        ("save", &InstructionField::IndexRegisterIndirect, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::SAVE_I { register_x: x })
        }

        ("restore", &InstructionField::IndexRegisterIndirect, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::RESTORE_I { register_x: x })
        }

        _ => None
      };
    }

    // Mnemonics with three parameter.
    if fields.len() == 3 {
      let tuple = (mnemonic, fields.get(0).unwrap(), fields.get(1).unwrap(), fields.get(2).unwrap());
      if let (
        "drw",
        &InstructionField::GeneralPurposeRegister(x),
        &InstructionField::GeneralPurposeRegister(y),
        &InstructionField::NumericLiteral(value)) = tuple {
        opcode = Some(Opcode::DRW {register_x: x, register_y: y, bytes: try!(numeric_literal_to_8_bit_field(value)) } );
      }
    }

    // Assemble the matched instruction.
    match opcode {
      Some(value) => Ok(Instruction(value)),
      None => Err(format!("Unable to assemble instruction {} {:?}", mnemonic, fields))
    }
	}
}

impl Emittable for Instruction {

  /**
   @return The size of the instruction in the output stream in bytes (always 2).
   */
  fn size(&self) -> usize { 2 }

  /**
   Consuming conversion from a directive element into the byte representation in the output stream.
   @return A Vec of bytes to be written into the output stream for the directive.
   */
  fn into_slice(self) -> Vec<u8> {
    let instruction: u16 = self.0.as_instruction();
    vec![
      ((instruction & 0xFF00) >> 8) as u8,
      ((instruction & 0x00FF) >> 0) as u8
    ]
  }

}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use assembler::assembler::Emittable;
  use assembler::parser::Literal;
  use assembler::u12;

  use super::*;

}
<commit_msg>Add tests for nop, cls, ret and trapret.<commit_after>
use std::collections::BTreeMap;

use assembler::assembler::Emittable;
use assembler::opcode::Opcode;
use assembler::parser::{Literal, InstructionField};
use assembler::u12::*;

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct Instruction(Opcode);

/**
 Resolves the label from the label map.
 @param label The name of the label (case-sensitive).
 @param label_map The mapping of labels to their resolved addresses.
 @return The address from the label map if defined, or an error describing the failure.
 */
fn resolve_label_with_map<'a>(label: &'a str, label_map: &BTreeMap<&'a str, U12>) -> Result<U12, String> {
  match label_map.get(label) {
    Some(value) => Ok(*value),
           None => Err(format!("Unable to resolve address of label {}", label))
  }
}

/**
 Converts a 12-bit (unsigned) literal value to 8-bits if possible to do so without data loss.
 @param literal 12-bit literal.
 @return A result consisting of the low 8 bits of the 12-bit literal or an explanation as to why conversion failed.
 */
fn numeric_literal_to_8_bit_field(literal: U12) -> Result<u8, String> {
  match literal.as_u8() {
    Some(value) => Ok(value),
           None => Err(format!("Found 12-bit numeric literal ${:X}, expecting 8-bit value.", literal.as_usize()))
  }
}

impl<'a> Instruction {
	/**
	 Converts a mnemonic string and a list of associated fields into an opcode (with the help of a
	 label resolution map). Produces an error message in the event of failure.
	 @param mnemonic The (all lowercase) instruction mnemonic.
	 @param fields A vector of instruction fields.
	 @param label_map A reference to a map of labels to their defined address values.
	 @return The assembled instruction if successful or a string describing the failure otherwise.
	 */
	pub fn from_mnemonic_and_parameters(mnemonic: &'a str, fields: Vec<InstructionField<'a>>, label_map: &BTreeMap<&'a str, U12>) -> Result<Self, String> {
		let mut opcode: Option<Opcode> = None;

    // Mnemonics with zero parameters.
    if fields.len() == 0 {
      opcode = match mnemonic {
        "nop"     => Some(Opcode::NOP),
        "cls"     => Some(Opcode::CLS),
        "ret"     => Some(Opcode::RET),
        "trap"    => Some(Opcode::TRAP),
        "trapret" => Some(Opcode::TRAPRET),
        _         => None
      };
    }

    // Mnemonics with one parameter.
    if fields.len() == 1 {
      opcode = match (mnemonic, fields.get(0).unwrap()) {

        ("jp", &InstructionField::NumericLiteral(target)) => Some(Opcode::JP { target: target }),

        // The 'jp' mnemonic may reference a label.
        ("jp", &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::JP { target: address })
        }

        ("call", &InstructionField::NumericLiteral(target)) => Some(Opcode::CALL { target: target }),

        // The 'call' mnemonic may reference a label.
        ("call", &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::CALL { target: address })
        }

        _ => None
      }
    }

    // Mnemonics with two parameters.
    if fields.len() == 2 {
      opcode = match (mnemonic, fields.get(0).unwrap(), fields.get(1).unwrap()) {
        ("se", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::SE_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("sne", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::SNE_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("se", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SE_REGISTER { register_x: x, register_y: y })
        }

        ("sne", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SNE_REGISTER { register_x: x, register_y: y })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::LD_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("add", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::ADD_IMMEDIATE { register_x: x, value: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::LD_REGISTER { register_x: x, register_y: y })
        }

        ("or", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::OR { register_x: x, register_y: y })
        }

        ("and", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::AND { register_x: x, register_y: y })
        }

        ("xor", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::XOR { register_x: x, register_y: y })
        }

        ("add", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::ADD_REGISTER { register_x: x, register_y: y })
        }

        ("sub", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SUB { register_x: x, register_y: y })
        }

        ("shr", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SHR { register_x: x, register_y: y })
        }

        ("subn", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SUBN { register_x: x, register_y: y })
        }

        ("shl", &InstructionField::GeneralPurposeRegister(x), &InstructionField::GeneralPurposeRegister(y)) => {
          Some(Opcode::SHL { register_x: x, register_y: y })
        }

        ("ld", &InstructionField::IndexRegister, &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::LD_I { value: value })
        }

        // The mnemonic 'ld i' can take a label parameter.
        ("ld", &InstructionField::IndexRegister, &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::LD_I { value: address })
        }

        ("jp", &InstructionField::GeneralPurposeRegister(0), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::JP_V0 { value: value })
        }

        // The mnemonic 'jp v0' can take a label parameter.
        ("jp", &InstructionField::GeneralPurposeRegister(0), &InstructionField::Identifier(label)) => {
          let address = try!(resolve_label_with_map(label, label_map));
          Some(Opcode::JP_V0 { value: address })
        }

        ("rnd", &InstructionField::GeneralPurposeRegister(x), &InstructionField::NumericLiteral(value)) => {
          Some(Opcode::RND { register_x: x, mask: try!(numeric_literal_to_8_bit_field(value)) })
        }

        ("se", &InstructionField::GeneralPurposeRegister(x), &InstructionField::KeypadRegister) => {
          Some(Opcode::SE_K { register_x: x })
        }

        ("sne", &InstructionField::GeneralPurposeRegister(x), &InstructionField::KeypadRegister) => {
          Some(Opcode::SNE_K { register_x: x })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::DelayTimer) => {
          Some(Opcode::LD_X_DT { register_x: x })
        }

        ("ld", &InstructionField::GeneralPurposeRegister(x), &InstructionField::KeypadRegister) => {
          Some(Opcode::LD_X_K { register_x: x })
        }

        ("ld", &InstructionField::DelayTimer, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::LD_DT_X { register_x: x })
        }

        ("ld", &InstructionField::SoundTimer, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::LD_ST_X { register_x: x })
        }

        ("add", &InstructionField::IndexRegister, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::ADD_I_X { register_x: x })
        }

        ("sprite", &InstructionField::IndexRegister, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::SPRITE_I { register_x: x })
        }

        ("bcd", &InstructionField::IndexRegisterIndirect, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::BCD_I { register_x: x })
        }

        ("save", &InstructionField::IndexRegisterIndirect, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::SAVE_I { register_x: x })
        }

        ("restore", &InstructionField::IndexRegisterIndirect, &InstructionField::GeneralPurposeRegister(x)) => {
          Some(Opcode::RESTORE_I { register_x: x })
        }

        _ => None
      };
    }

    // Mnemonics with three parameter.
    if fields.len() == 3 {
      let tuple = (mnemonic, fields.get(0).unwrap(), fields.get(1).unwrap(), fields.get(2).unwrap());
      if let (
        "drw",
        &InstructionField::GeneralPurposeRegister(x),
        &InstructionField::GeneralPurposeRegister(y),
        &InstructionField::NumericLiteral(value)) = tuple {
        opcode = Some(Opcode::DRW {register_x: x, register_y: y, bytes: try!(numeric_literal_to_8_bit_field(value)) } );
      }
    }

    // Assemble the matched instruction.
    return match opcode {
      Some(value) => Ok(Instruction(value)),
      None => {
        match fields.len() {
          0 => Err(format!("No matching format for instruction: {}", mnemonic)),
          _ => {
            let field_strings = fields.iter().map(|field| format!("{}", field));
            let field_description = field_strings.collect::<Vec<_>>().connect(",");
            Err(format!("No matching format for instruction: {} {}", mnemonic, field_description))
          }
        }
      }
    };
	}
}

impl Emittable for Instruction {

  /**
   @return The size of the instruction in the output stream in bytes (always 2).
   */
  fn size(&self) -> usize { 2 }

  /**
   Consuming conversion from a directive element into the byte representation in the output stream.
   @return A Vec of bytes to be written into the output stream for the directive.
   */
  fn into_slice(self) -> Vec<u8> {
    let instruction: u16 = self.0.as_instruction();
    vec![
      ((instruction & 0xFF00) >> 8) as u8,
      ((instruction & 0x00FF) >> 0) as u8
    ]
  }

}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use std::collections::BTreeMap;

  use assembler::assembler::Emittable;
  use assembler::opcode::Opcode;
  use assembler::parser::{InstructionField, Literal};
  use assembler::u12;

  use super::*;

  #[test]
  pub fn test_nop() {
    let empty_map = BTreeMap::new();
    let nop = Instruction::from_mnemonic_and_parameters("nop", vec![], &empty_map).unwrap();
    assert_eq!(nop.size(), 2);
    assert_eq!(nop.0, Opcode::NOP);
  }

  #[test]
  pub fn test_cls() {
    let empty_map = BTreeMap::new();
    let cls = Instruction::from_mnemonic_and_parameters("cls", vec![], &empty_map).unwrap();
    assert_eq!(cls.size(), 2);
    assert_eq!(cls.0, Opcode::CLS);
  }

  #[test]
  pub fn test_ret() {
    let empty_map = BTreeMap::new();
    let ret = Instruction::from_mnemonic_and_parameters("ret", vec![], &empty_map).unwrap();
    assert_eq!(ret.size(), 2);
    assert_eq!(ret.0, Opcode::RET);
  }

  #[test]
  pub fn test_trapret() {
    let empty_map = BTreeMap::new();
    let trapret = Instruction::from_mnemonic_and_parameters("trapret", vec![], &empty_map).unwrap();
    assert_eq!(trapret.size(), 2);
    assert_eq!(trapret.0, Opcode::TRAPRET);
  }

  #[test]
  pub fn test_trap() {
    let empty_map = BTreeMap::new();
    let trap = Instruction::from_mnemonic_and_parameters("trap", vec![], &empty_map).unwrap();
    assert_eq!(trap.size(), 2);
    assert_eq!(trap.0, Opcode::TRAP);
  }

}
<|endoftext|>


<commit_before>pub mod insertion;

/// Returns whether the slice is sorted in ascending order or not.
pub fn is_ascending<T : Ord>(data : & [T]) -> bool {
    let n = data.len();
    for i in range(0, n-1){
        if data[i] > data[i + 1]{
            return false;
        }
    }
    true
}<commit_msg>replaced Ord with PartialOrd<commit_after>pub mod insertion;

/// Returns whether the slice is sorted in ascending order or not.
pub fn is_ascending<T : PartialOrd>(data : & [T]) -> bool {
    let n = data.len();
    for i in range(0, n-1){
        if data[i] > data[i + 1]{
            return false;
        }
    }
    true
}<|endoftext|>