<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use libsodium_sys;
use secstr;
use argon2rs;

struct PublicKey(secstr::SecStr);
struct SecretKey(secstr::SecStr);

pub struct Keeper {
    universal_key: secstr::SecStr,
    fingerprint_key: Option<secstr::SecStr>,

    data_key_pk: Option<PublicKey>,
    data_key_sk: Option<SecretKey>,

    naming_key_pk: Option<PublicKey>,
    naming_key_sk: Option<SecretKey>,

    access_key_pk: Option<PublicKey>,
    access_key_sk: Option<SecretKey>,
}

impl Keeper {
    pub fn new(universal: &str) -> Keeper {
        let app: &str = "hat-backup:universal-key";
        let mut keeper = Keeper {
            universal_key: Keeper::strengthen(universal, app),
            fingerprint_key: None,
            data_key_pk: None,
            data_key_sk: None,
            access_key_pk: None,
            access_key_sk: None,
            naming_key_pk: None,
            naming_key_sk: None,
        };
        keeper.init();
        keeper
    }

    #[cfg(test)]
    pub fn new_for_testing() -> Keeper {
        let mut keeper = Keeper {
            universal_key: secstr::SecStr::new(vec![0; 32]),
            fingerprint_key: None,
            data_key_pk: None,
            data_key_sk: None,
            access_key_pk: None,
            access_key_sk: None,
            naming_key_pk: None,
            naming_key_sk: None,
        };
        keeper.init();
        keeper
    }

    fn init(&mut self) {
        // Generate key used for fingerprinting.
        self.fingerprint_key = Some(self.from_nonce("hat:FINGERPRINT-key".as_bytes(), 64));

        // Generate data key.
        // Required for reading blob data without a direct reference.
        let (pk, sk) = self.x25519_key_pair_from_nonce("hat:DATA-key-x25519".as_bytes());
        self.data_key_pk = Some(pk);
        self.data_key_sk = Some(sk);

        // Generate access key.
        // Required for reading any blob data (with direct reference or with data key).
        let (pk, sk) = self.x25519_key_pair_from_nonce("hat:ACCESS-key-x25519".as_bytes());
        self.access_key_pk = Some(pk);
        self.access_key_sk = Some(sk);

        // Generate naming key.
        // Required for reading blob names.
        let (pk, sk) = self.x25519_key_pair_from_nonce("hat:NAMING-key-x25519".as_bytes());
        self.naming_key_pk = Some(pk);
        self.naming_key_sk = Some(sk);
    }

    fn strengthen(phrase: &str, salt: &str) -> secstr::SecStr {
        let passes = 5;
        let threads = 2;
        let kib = 16 * 1024;

        let argon2 = argon2rs::Argon2::new(passes, threads, kib, argon2rs::Variant::Argon2d)
            .unwrap();

        let mut out = vec![0; 64];
        argon2.hash(&mut out[..], phrase.as_bytes(), salt.as_bytes(), &[], &[]);
        secstr::SecStr::new(out)
    }

    pub fn from_nonce(&self, nonce: &[u8], outlen: usize) -> secstr::SecStr {
        let mut out = secstr::SecStr::new(vec![0; outlen]);
        Keeper::keyed_fingerprint(&self.universal_key, &nonce[..], out.unsecure_mut(), outlen);
        out
    }

    fn x25519_key_pair_from_nonce(&self, nonce: &[u8]) -> (PublicKey, SecretKey) {
        let mut pk = secstr::SecStr::new(vec![0; 32]);
        let mut sk = secstr::SecStr::new(vec![0; 32]);

        let seed = self.from_nonce(nonce, 32);

        let ret = unsafe {
            libsodium_sys::crypto_box_seed_keypair(pk.unsecure_mut().as_mut_ptr() as *mut [u8; 32],
                                                   sk.unsecure_mut().as_mut_ptr() as *mut [u8; 32],
                                                   seed.unsecure().as_ptr() as *const [u8; 32])
        };
        assert_eq!(ret, 0);

        (PublicKey(pk), SecretKey(sk))
    }

    fn asymmetric_lock(pk: &PublicKey, msg: &[u8]) -> Vec<u8> {
        let mut out = vec![0; msg.len() + libsodium_sys::crypto_box_SEALBYTES];
        let ret = unsafe {
            libsodium_sys::crypto_box_seal(out.as_mut_ptr(),
                                           msg.as_ptr(),
                                           msg.len() as u64,
                                           pk.0.unsecure().as_ptr() as *const [u8; 32])
        };
        assert_eq!(0, ret);

        out
    }

    fn asymmetric_unlock(pk: &PublicKey, sk: &SecretKey, ciphertext: &[u8]) -> Vec<u8> {
        let mut out = vec![0; ciphertext.len() - libsodium_sys::crypto_box_SEALBYTES];
        let ret = unsafe {
            libsodium_sys::crypto_box_seal_open(out.as_mut_ptr(),
                                                ciphertext.as_ptr(),
                                                ciphertext.len() as u64,
                                                pk.0.unsecure().as_ptr() as *const [u8; 32],
                                                sk.0.unsecure().as_ptr() as *const [u8; 32])
        };
        assert_eq!(0, ret);

        out
    }

    pub fn data_lock(&self, msg: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_lock(self.data_key_pk.as_ref().expect("need data public key"),
                                msg)
    }

    pub fn data_unlock(&self, ciphertext: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_unlock(self.data_key_pk.as_ref().expect("need data public key"),
                                  self.data_key_sk
                                      .as_ref()
                                      .expect("need data private key"),
                                  ciphertext)
    }

    pub fn access_lock(&self, msg: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_lock(self.access_key_pk
                                    .as_ref()
                                    .expect("need access publick key"),
                                msg)
    }

    pub fn access_unlock(&self, ciphertext: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_unlock(self.access_key_pk
                                      .as_ref()
                                      .expect("need access public key"),
                                  self.access_key_sk
                                      .as_ref()
                                      .expect("need access private key"),
                                  ciphertext)
    }

    pub fn naming_lock(&self, msg: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_lock(self.naming_key_pk
                                    .as_ref()
                                    .expect("need naming public key"),
                                msg)
    }

    pub fn naming_unlock(&self, ciphertext: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_unlock(self.naming_key_pk
                                      .as_ref()
                                      .expect("need naming public key"),
                                  self.naming_key_sk
                                      .as_ref()
                                      .expect("need naming private key"),
                                  ciphertext)
    }

    fn keyed_fingerprint(sk: &secstr::SecStr, msg: &[u8], out: &mut [u8], outlen: usize) {
        assert!(outlen <= out.len());
        let sk_len = sk.unsecure().len();

        let ret = unsafe {
            libsodium_sys::crypto_generichash_blake2b(out.as_mut_ptr(),
                                                      outlen,
                                                      msg.as_ptr(),
                                                      msg.len() as u64,
                                                      sk.unsecure().as_ptr(),
                                                      sk_len)
        };
        assert_eq!(ret, 0);
    }

    pub fn fingerprint(&self, msg: &[u8], out: &mut [u8], outlen: usize) {
        assert!(outlen <= out.len());

        let key = self.fingerprint_key
            .as_ref()
            .expect("need fingerprint key");
        Keeper::keyed_fingerprint(&key, msg, out, outlen);
    }

    pub fn symmetric_lock(msg: &[u8], data: &[u8], nonce: &[u8; 8]) -> (secstr::SecStr, Vec<u8>) {
        let sk_len = libsodium_sys::crypto_aead_chacha20poly1305_KEYBYTES;
        let mut sk = secstr::SecStr::new(vec![0u8; sk_len]);
        unsafe { libsodium_sys::randombytes_buf(sk.unsecure_mut().as_mut_ptr(), sk_len) }

        let mut out = vec![0u8; msg.len() + libsodium_sys::crypto_aead_chacha20poly1305_ABYTES];
        let mut out_len = 0;

        let ret = unsafe {
            libsodium_sys::crypto_aead_chacha20poly1305_encrypt(out.as_mut_ptr(),
                                                                &mut out_len,
                                                                msg.as_ptr(),
                                                                msg.len() as u64,
                                                                data.as_ptr(),
                                                                data.len() as u64,
                                                                &[0u8; 0],
                                                                nonce.as_ptr() as *const [u8; 8],
                                                                sk.unsecure().as_ptr() as
                                                                *const [u8; 32])
        };
        assert_eq!(0, ret);
        assert_eq!(out_len, out.len() as u64);

        (sk, out)
    }

    pub fn symmetric_unlock(sk: &secstr::SecStr,
                            ciphertext: &[u8],
                            data: &[u8],
                            nonce: &[u8; 8])
                            -> Vec<u8> {
        let mut out =
            vec![0u8; ciphertext.len() - libsodium_sys::crypto_aead_chacha20poly1305_ABYTES];
        let mut out_len = 0;

        let ret = unsafe {
            libsodium_sys::crypto_aead_chacha20poly1305_decrypt(out.as_mut_ptr(),
                                                                &mut out_len,
                                                                &mut [0u8; 0],
                                                                ciphertext.as_ptr(),
                                                                ciphertext.len() as u64,
                                                                data.as_ptr(),
                                                                data.len() as u64,
                                                                nonce.as_ptr() as *const [u8; 8],
                                                                sk.unsecure().as_ptr() as
                                                                *const [u8; 32])
        };
        assert_eq!(0, ret);
        assert_eq!(out_len, out.len() as u64);

        out
    }
}
<commit_msg>Minor formatting.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use libsodium_sys;
use secstr;
use argon2rs;

struct PublicKey(secstr::SecStr);
struct SecretKey(secstr::SecStr);

pub struct Keeper {
    universal_key: secstr::SecStr,
    fingerprint_key: Option<secstr::SecStr>,

    data_key_pk: Option<PublicKey>,
    data_key_sk: Option<SecretKey>,

    naming_key_pk: Option<PublicKey>,
    naming_key_sk: Option<SecretKey>,

    access_key_pk: Option<PublicKey>,
    access_key_sk: Option<SecretKey>,
}

impl Keeper {
    pub fn new(universal: &str) -> Keeper {
        let app: &str = "hat-backup:universal-key";
        let mut keeper = Keeper {
            universal_key: Keeper::strengthen(universal, app),
            fingerprint_key: None,
            data_key_pk: None,
            data_key_sk: None,
            access_key_pk: None,
            access_key_sk: None,
            naming_key_pk: None,
            naming_key_sk: None,
        };
        keeper.init();
        keeper
    }

    #[cfg(test)]
    pub fn new_for_testing() -> Keeper {
        let mut keeper = Keeper {
            universal_key: secstr::SecStr::new(vec![0; 32]),
            fingerprint_key: None,
            data_key_pk: None,
            data_key_sk: None,
            access_key_pk: None,
            access_key_sk: None,
            naming_key_pk: None,
            naming_key_sk: None,
        };
        keeper.init();
        keeper
    }

    fn init(&mut self) {
        // Generate key used for fingerprinting.
        self.fingerprint_key = Some(self.from_nonce("hat:FINGERPRINT-key".as_bytes(), 64));

        // Generate data key.
        // Required for reading blob data without a direct reference.
        let (pk, sk) = self.x25519_key_pair_from_nonce("hat:DATA-key-x25519".as_bytes());
        self.data_key_pk = Some(pk);
        self.data_key_sk = Some(sk);

        // Generate access key.
        // Required for reading any blob data (with direct reference or with data key).
        let (pk, sk) = self.x25519_key_pair_from_nonce("hat:ACCESS-key-x25519".as_bytes());
        self.access_key_pk = Some(pk);
        self.access_key_sk = Some(sk);

        // Generate naming key.
        // Required for reading blob names.
        let (pk, sk) = self.x25519_key_pair_from_nonce("hat:NAMING-key-x25519".as_bytes());
        self.naming_key_pk = Some(pk);
        self.naming_key_sk = Some(sk);
    }

    fn strengthen(phrase: &str, salt: &str) -> secstr::SecStr {
        let passes = 5;
        let threads = 2;
        let kib = 16 * 1024;

        let argon2 = argon2rs::Argon2::new(passes, threads, kib, argon2rs::Variant::Argon2d)
            .unwrap();

        let mut out = vec![0; 64];
        argon2.hash(&mut out[..], phrase.as_bytes(), salt.as_bytes(), &[], &[]);
        secstr::SecStr::new(out)
    }

    pub fn from_nonce(&self, nonce: &[u8], outlen: usize) -> secstr::SecStr {
        let mut out = secstr::SecStr::new(vec![0; outlen]);
        Keeper::keyed_fingerprint(&self.universal_key, &nonce[..], out.unsecure_mut(), outlen);
        out
    }

    fn x25519_key_pair_from_nonce(&self, nonce: &[u8]) -> (PublicKey, SecretKey) {
        let mut pk = secstr::SecStr::new(vec![0; 32]);
        let mut sk = secstr::SecStr::new(vec![0; 32]);

        let seed = self.from_nonce(nonce, 32);

        let ret = unsafe {
            libsodium_sys::crypto_box_seed_keypair(pk.unsecure_mut().as_mut_ptr() as *mut [u8; 32],
                                                   sk.unsecure_mut().as_mut_ptr() as *mut [u8; 32],
                                                   seed.unsecure().as_ptr() as *const [u8; 32])
        };
        assert_eq!(ret, 0);

        (PublicKey(pk), SecretKey(sk))
    }

    fn asymmetric_lock(pk: &PublicKey, msg: &[u8]) -> Vec<u8> {
        let mut out = vec![0; msg.len() + libsodium_sys::crypto_box_SEALBYTES];
        let ret = unsafe {
            libsodium_sys::crypto_box_seal(out.as_mut_ptr(),
                                           msg.as_ptr(),
                                           msg.len() as u64,
                                           pk.0.unsecure().as_ptr() as *const [u8; 32])
        };
        assert_eq!(0, ret);

        out
    }

    fn asymmetric_unlock(pk: &PublicKey, sk: &SecretKey, ciphertext: &[u8]) -> Vec<u8> {
        let mut out = vec![0; ciphertext.len() - libsodium_sys::crypto_box_SEALBYTES];
        let ret = unsafe {
            libsodium_sys::crypto_box_seal_open(out.as_mut_ptr(),
                                                ciphertext.as_ptr(),
                                                ciphertext.len() as u64,
                                                pk.0.unsecure().as_ptr() as *const [u8; 32],
                                                sk.0.unsecure().as_ptr() as *const [u8; 32])
        };
        assert_eq!(0, ret);

        out
    }

    pub fn data_lock(&self, msg: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_lock(self.data_key_pk.as_ref().expect("need data public key"),
                                msg)
    }

    pub fn data_unlock(&self, ciphertext: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_unlock(self.data_key_pk.as_ref().expect("need data public key"),
                                  self.data_key_sk
                                      .as_ref()
                                      .expect("need data private key"),
                                  ciphertext)
    }

    pub fn access_lock(&self, msg: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_lock(self.access_key_pk
                                    .as_ref()
                                    .expect("need access publick key"),
                                msg)
    }

    pub fn access_unlock(&self, ciphertext: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_unlock(self.access_key_pk
                                      .as_ref()
                                      .expect("need access public key"),
                                  self.access_key_sk
                                      .as_ref()
                                      .expect("need access private key"),
                                  ciphertext)
    }

    pub fn naming_lock(&self, msg: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_lock(self.naming_key_pk
                                    .as_ref()
                                    .expect("need naming public key"),
                                msg)
    }

    pub fn naming_unlock(&self, ciphertext: &[u8]) -> Vec<u8> {
        Keeper::asymmetric_unlock(self.naming_key_pk
                                      .as_ref()
                                      .expect("need naming public key"),
                                  self.naming_key_sk
                                      .as_ref()
                                      .expect("need naming private key"),
                                  ciphertext)
    }

    fn keyed_fingerprint(sk: &secstr::SecStr, msg: &[u8], out: &mut [u8], outlen: usize) {
        assert!(outlen <= out.len());

        let sk_ref = sk.unsecure();
        let ret = unsafe {
            libsodium_sys::crypto_generichash_blake2b(out.as_mut_ptr(),
                                                      outlen,
                                                      msg.as_ptr(),
                                                      msg.len() as u64,
                                                      sk_ref.as_ptr(),
                                                      sk_ref.len())
        };
        assert_eq!(ret, 0);
    }

    pub fn fingerprint(&self, msg: &[u8], out: &mut [u8], outlen: usize) {
        assert!(outlen <= out.len());

        let key = self.fingerprint_key
            .as_ref()
            .expect("need fingerprint key");
        Keeper::keyed_fingerprint(&key, msg, out, outlen);
    }

    pub fn symmetric_lock(msg: &[u8], data: &[u8], nonce: &[u8; 8]) -> (secstr::SecStr, Vec<u8>) {
        let sk_len = libsodium_sys::crypto_aead_chacha20poly1305_KEYBYTES;
        let mut sk = secstr::SecStr::new(vec![0u8; sk_len]);
        unsafe { libsodium_sys::randombytes_buf(sk.unsecure_mut().as_mut_ptr(), sk_len) }

        let mut out = vec![0u8; msg.len() + libsodium_sys::crypto_aead_chacha20poly1305_ABYTES];
        let mut out_len = 0;

        let ret = unsafe {
            libsodium_sys::crypto_aead_chacha20poly1305_encrypt(out.as_mut_ptr(),
                                                                &mut out_len,
                                                                msg.as_ptr(),
                                                                msg.len() as u64,
                                                                data.as_ptr(),
                                                                data.len() as u64,
                                                                &[0u8; 0],
                                                                nonce.as_ptr() as *const [u8; 8],
                                                                sk.unsecure().as_ptr() as
                                                                *const [u8; 32])
        };
        assert_eq!(0, ret);
        assert_eq!(out_len, out.len() as u64);

        (sk, out)
    }

    pub fn symmetric_unlock(sk: &secstr::SecStr,
                            ciphertext: &[u8],
                            data: &[u8],
                            nonce: &[u8; 8])
                            -> Vec<u8> {
        let mut out =
            vec![0u8; ciphertext.len() - libsodium_sys::crypto_aead_chacha20poly1305_ABYTES];
        let mut out_len = 0;

        let ret = unsafe {
            libsodium_sys::crypto_aead_chacha20poly1305_decrypt(out.as_mut_ptr(),
                                                                &mut out_len,
                                                                &mut [0u8; 0],
                                                                ciphertext.as_ptr(),
                                                                ciphertext.len() as u64,
                                                                data.as_ptr(),
                                                                data.len() as u64,
                                                                nonce.as_ptr() as *const [u8; 8],
                                                                sk.unsecure().as_ptr() as
                                                                *const [u8; 32])
        };
        assert_eq!(0, ret);
        assert_eq!(out_len, out.len() as u64);

        out
    }
}
<|endoftext|>


<commit_before>//extern crate serde_json;
use rustc_serialize::json;

use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::{ BufReader, BufRead, Read };
use std::path::{ Path, PathBuf };
use std::str::FromStr;

use download;
use verify;

macro_rules! custom_try {
    ($x:expr) => (match $x {
        Ok(x) => x,
        Err(why) => return Err(format!("An error occured during package finding. {}", why)),
    });
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct Package {
    name: String,
    version: String,
    crc32: String,
    url: String,
    dependencies: Vec<Package>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PackageList {
    packages: Vec<Package>,
    version: i32
}

pub fn update_package_list(url: &str, path: &Path) -> Result<bool, String> {
    if (path.extension().is_some() && path.extension().unwrap() != ".json") ||
       (path.extension().is_none())
    {
        return Err(String::from(
            format!("Extension of package list is not .json: {}", path.to_str().unwrap_or("Invalid File"))
        ));
    }

    let mut path_temp = PathBuf::new();
    path_temp.push(path);
    path_temp.set_extension("json.tmp");

    if download::download_file(url, path_temp.as_path()).is_err() {
        return Err(String::from("Error occured while downloading package list"))
    }

    if verify::verify_file_crc32(&custom_try!(File::open(&path_temp))) ==
       verify::verify_file_crc32(&custom_try!(File::open(&path)))
    {
        fs::remove_file(path_temp);
        return Ok(false)
    } else {
        fs::remove_file(path);
        fs::rename(path_temp, path);
        return Ok(true)
    }
}

pub fn find_package(name: &str, path:&Path) -> Result<Vec<Package>, String> {
    let mut list = custom_try!(File::open(path));
    let mut reader = BufReader::new(list);
    let mut buffer = String::new();

    reader.read_to_string(&mut buffer);
    let package_list: PackageList = custom_try!(json::decode(&buffer));

    let filtered = package_list.packages
                    .into_iter()
                    .filter(|x| !x.name.contains(name))
                    .collect::<Vec<_>>();

    match filtered.len() {
        0 => Err(String::from("Package not found")),
        _ => Ok(filtered)
    }
}
<commit_msg>Bugfix.<commit_after>//extern crate serde_json;
use rustc_serialize::json;

use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::{ BufReader, BufRead, Read };
use std::path::{ Path, PathBuf };
use std::str::FromStr;

use download;
use verify;

macro_rules! custom_try {
    ($x:expr) => (match $x {
        Ok(x) => x,
        Err(why) => return Err(format!("An error occured during package finding. {}", why)),
    });
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct Package {
    name: String,
    version: String,
    crc32: String,
    url: String,
    dependencies: Vec<Package>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PackageList {
    packages: Vec<Package>,
    version: i32
}

pub fn update_package_list(url: &str, path: &Path) -> Result<bool, String> {
    if (path.extension().is_some() && path.extension().unwrap_or("") != ".json") ||
       (path.extension().is_none())
    {
        return Err(String::from(
            format!("Extension of package list is not .json: {}", path.to_str().unwrap_or("Invalid File"))
        ));
    }

    let mut path_temp = PathBuf::new();
    path_temp.push(path);
    path_temp.set_extension("json.tmp");

    if download::download_file(url, path_temp.as_path()).is_err() {
        return Err(String::from("Error occured while downloading package list"))
    }

    if verify::verify_file_crc32(&custom_try!(File::open(&path_temp))) ==
       verify::verify_file_crc32(&custom_try!(File::open(&path)))
    {
        fs::remove_file(path_temp);
        return Ok(false)
    } else {
        fs::remove_file(path);
        fs::rename(path_temp, path);
        return Ok(true)
    }
}

pub fn find_package(name: &str, path:&Path) -> Result<Vec<Package>, String> {
    let mut list = custom_try!(File::open(path));
    let mut reader = BufReader::new(list);
    let mut buffer = String::new();

    reader.read_to_string(&mut buffer);
    let package_list: PackageList = custom_try!(json::decode(&buffer));

    let filtered = package_list.packages
                    .into_iter()
                    .filter(|x| !x.name.contains(name))
                    .collect::<Vec<_>>();

    match filtered.len() {
        0 => Err(String::from("Package not found")),
        _ => Ok(filtered)
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![unstable(feature = "futures_api",
            reason = "futures in libcore are unstable",
            issue = "50547")]

use fmt;
use future::Future;
use marker::{PhantomData, Unpin};
use mem::PinMut;
use task::{Context, Poll};

/// A custom trait object for polling futures, roughly akin to
/// `Box<dyn Future<Output = T> + 'a>`.
///
/// This custom trait object was introduced for two reasons:
/// - Currently it is not possible to take `dyn Trait` by value and
///   `Box<dyn Trait>` is not available in no_std contexts.
/// - The `Future` trait is currently not object safe: The `Future::poll`
///   method makes uses the arbitrary self types feature and traits in which
///   this feature is used are currently not object safe due to current compiler
///   limitations. (See tracking issue for arbitray self types for more
///   information #44874)
pub struct LocalFutureObj<'a, T> {
    ptr: *mut (),
    poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<T>,
    drop_fn: unsafe fn(*mut ()),
    _marker: PhantomData<&'a ()>,
}

impl<'a, T> LocalFutureObj<'a, T> {
    /// Create a `LocalFutureObj` from a custom trait object representation.
    #[inline]
    pub fn new<F: UnsafeFutureObj<'a, T> + 'a>(f: F) -> LocalFutureObj<'a, T> {
        LocalFutureObj {
            ptr: f.into_raw(),
            poll_fn: F::poll,
            drop_fn: F::drop,
            _marker: PhantomData,
        }
    }

    /// Converts the `LocalFutureObj` into a `FutureObj`
    /// To make this operation safe one has to ensure that the `UnsafeFutureObj`
    /// instance from which this `LocalFutureObj` was created actually
    /// implements `Send`.
    #[inline]
    pub unsafe fn into_future_obj(self) -> FutureObj<'a, T> {
        FutureObj(self)
    }
}

impl<'a, T> fmt::Debug for LocalFutureObj<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("LocalFutureObj")
            .finish()
    }
}

impl<'a, T> From<FutureObj<'a, T>> for LocalFutureObj<'a, T> {
    #[inline]
    fn from(f: FutureObj<'a, T>) -> LocalFutureObj<'a, T> {
        f.0
    }
}

impl<'a, T> Future for LocalFutureObj<'a, T> {
    type Output = T;

    #[inline]
    fn poll(self: PinMut<Self>, cx: &mut Context) -> Poll<T> {
        unsafe {
            (self.poll_fn)(self.ptr, cx)
        }
    }
}

impl<'a, T> Drop for LocalFutureObj<'a, T> {
    fn drop(&mut self) {
        unsafe {
            (self.drop_fn)(self.ptr)
        }
    }
}

/// A custom trait object for polling futures, roughly akin to
/// `Box<dyn Future<Output = T> + Send + 'a>`.
///
/// This custom trait object was introduced for two reasons:
/// - Currently it is not possible to take `dyn Trait` by value and
///   `Box<dyn Trait>` is not available in no_std contexts.
/// - The `Future` trait is currently not object safe: The `Future::poll`
///   method makes uses the arbitrary self types feature and traits in which
///   this feature is used are currently not object safe due to current compiler
///   limitations. (See tracking issue for arbitray self types for more
///   information #44874)
pub struct FutureObj<'a, T>(LocalFutureObj<'a, T>);

unsafe impl<'a, T> Send for FutureObj<'a, T> {}

impl<'a, T> FutureObj<'a, T> {
    /// Create a `FutureObj` from a custom trait object representation.
    #[inline]
    pub fn new<F: UnsafeFutureObj<'a, T> + Send>(f: F) -> FutureObj<'a, T> {
        FutureObj(LocalFutureObj::new(f))
    }
}

impl<'a, T> fmt::Debug for FutureObj<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("FutureObj")
            .finish()
    }
}

impl<'a, T> Future for FutureObj<'a, T> {
    type Output = T;

    #[inline]
    fn poll(self: PinMut<Self>, cx: &mut Context) -> Poll<T> {
        let pinned_field = unsafe { PinMut::map_unchecked(self, |x| &mut x.0) };
        pinned_field.poll(cx)
    }
}

/// A custom implementation of a future trait object for `FutureObj`, providing
/// a hand-rolled vtable.
///
/// This custom representation is typically used only in `no_std` contexts,
/// where the default `Box`-based implementation is not available.
///
/// The implementor must guarantee that it is safe to call `poll` repeatedly (in
/// a non-concurrent fashion) with the result of `into_raw` until `drop` is
/// called.
pub unsafe trait UnsafeFutureObj<'a, T>: 'a {
    /// Convert an owned instance into a (conceptually owned) void pointer.
    fn into_raw(self) -> *mut ();

    /// Poll the future represented by the given void pointer.
    ///
    /// # Safety
    ///
    /// The trait implementor must guarantee that it is safe to repeatedly call
    /// `poll` with the result of `into_raw` until `drop` is called; such calls
    /// are not, however, allowed to race with each other or with calls to
    /// `drop`.
    unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll<T>;

    /// Drops the future represented by the given void pointer.
    ///
    /// # Safety
    ///
    /// The trait implementor must guarantee that it is safe to call this
    /// function once per `into_raw` invocation; that call cannot race with
    /// other calls to `drop` or `poll`.
    unsafe fn drop(ptr: *mut ());
}

unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for &'a mut F
    where F: Future<Output = T> + Unpin + 'a
{
    fn into_raw(self) -> *mut () {
        self as *mut F as *mut ()
    }

    unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll<T> {
        PinMut::new_unchecked(&mut *(ptr as *mut F)).poll(cx)
    }

    unsafe fn drop(_ptr: *mut ()) {}
}
<commit_msg>Rollup merge of #52870 - cramertj:unpin-futureobj, r=aturon<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![unstable(feature = "futures_api",
            reason = "futures in libcore are unstable",
            issue = "50547")]

use fmt;
use future::Future;
use marker::{PhantomData, Unpin};
use mem::PinMut;
use task::{Context, Poll};

/// A custom trait object for polling futures, roughly akin to
/// `Box<dyn Future<Output = T> + 'a>`.
///
/// This custom trait object was introduced for two reasons:
/// - Currently it is not possible to take `dyn Trait` by value and
///   `Box<dyn Trait>` is not available in no_std contexts.
/// - The `Future` trait is currently not object safe: The `Future::poll`
///   method makes uses the arbitrary self types feature and traits in which
///   this feature is used are currently not object safe due to current compiler
///   limitations. (See tracking issue for arbitray self types for more
///   information #44874)
pub struct LocalFutureObj<'a, T> {
    ptr: *mut (),
    poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<T>,
    drop_fn: unsafe fn(*mut ()),
    _marker: PhantomData<&'a ()>,
}

impl<'a, T> Unpin for LocalFutureObj<'a, T> {}

impl<'a, T> LocalFutureObj<'a, T> {
    /// Create a `LocalFutureObj` from a custom trait object representation.
    #[inline]
    pub fn new<F: UnsafeFutureObj<'a, T> + 'a>(f: F) -> LocalFutureObj<'a, T> {
        LocalFutureObj {
            ptr: f.into_raw(),
            poll_fn: F::poll,
            drop_fn: F::drop,
            _marker: PhantomData,
        }
    }

    /// Converts the `LocalFutureObj` into a `FutureObj`
    /// To make this operation safe one has to ensure that the `UnsafeFutureObj`
    /// instance from which this `LocalFutureObj` was created actually
    /// implements `Send`.
    #[inline]
    pub unsafe fn into_future_obj(self) -> FutureObj<'a, T> {
        FutureObj(self)
    }
}

impl<'a, T> fmt::Debug for LocalFutureObj<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("LocalFutureObj")
            .finish()
    }
}

impl<'a, T> From<FutureObj<'a, T>> for LocalFutureObj<'a, T> {
    #[inline]
    fn from(f: FutureObj<'a, T>) -> LocalFutureObj<'a, T> {
        f.0
    }
}

impl<'a, T> Future for LocalFutureObj<'a, T> {
    type Output = T;

    #[inline]
    fn poll(self: PinMut<Self>, cx: &mut Context) -> Poll<T> {
        unsafe {
            (self.poll_fn)(self.ptr, cx)
        }
    }
}

impl<'a, T> Drop for LocalFutureObj<'a, T> {
    fn drop(&mut self) {
        unsafe {
            (self.drop_fn)(self.ptr)
        }
    }
}

/// A custom trait object for polling futures, roughly akin to
/// `Box<dyn Future<Output = T> + Send + 'a>`.
///
/// This custom trait object was introduced for two reasons:
/// - Currently it is not possible to take `dyn Trait` by value and
///   `Box<dyn Trait>` is not available in no_std contexts.
/// - The `Future` trait is currently not object safe: The `Future::poll`
///   method makes uses the arbitrary self types feature and traits in which
///   this feature is used are currently not object safe due to current compiler
///   limitations. (See tracking issue for arbitray self types for more
///   information #44874)
pub struct FutureObj<'a, T>(LocalFutureObj<'a, T>);

impl<'a, T> Unpin for FutureObj<'a, T> {}
unsafe impl<'a, T> Send for FutureObj<'a, T> {}

impl<'a, T> FutureObj<'a, T> {
    /// Create a `FutureObj` from a custom trait object representation.
    #[inline]
    pub fn new<F: UnsafeFutureObj<'a, T> + Send>(f: F) -> FutureObj<'a, T> {
        FutureObj(LocalFutureObj::new(f))
    }
}

impl<'a, T> fmt::Debug for FutureObj<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("FutureObj")
            .finish()
    }
}

impl<'a, T> Future for FutureObj<'a, T> {
    type Output = T;

    #[inline]
    fn poll(self: PinMut<Self>, cx: &mut Context) -> Poll<T> {
        let pinned_field = unsafe { PinMut::map_unchecked(self, |x| &mut x.0) };
        pinned_field.poll(cx)
    }
}

/// A custom implementation of a future trait object for `FutureObj`, providing
/// a hand-rolled vtable.
///
/// This custom representation is typically used only in `no_std` contexts,
/// where the default `Box`-based implementation is not available.
///
/// The implementor must guarantee that it is safe to call `poll` repeatedly (in
/// a non-concurrent fashion) with the result of `into_raw` until `drop` is
/// called.
pub unsafe trait UnsafeFutureObj<'a, T>: 'a {
    /// Convert an owned instance into a (conceptually owned) void pointer.
    fn into_raw(self) -> *mut ();

    /// Poll the future represented by the given void pointer.
    ///
    /// # Safety
    ///
    /// The trait implementor must guarantee that it is safe to repeatedly call
    /// `poll` with the result of `into_raw` until `drop` is called; such calls
    /// are not, however, allowed to race with each other or with calls to
    /// `drop`.
    unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll<T>;

    /// Drops the future represented by the given void pointer.
    ///
    /// # Safety
    ///
    /// The trait implementor must guarantee that it is safe to call this
    /// function once per `into_raw` invocation; that call cannot race with
    /// other calls to `drop` or `poll`.
    unsafe fn drop(ptr: *mut ());
}

unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for &'a mut F
    where F: Future<Output = T> + Unpin + 'a
{
    fn into_raw(self) -> *mut () {
        self as *mut F as *mut ()
    }

    unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll<T> {
        PinMut::new_unchecked(&mut *(ptr as *mut F)).poll(cx)
    }

    unsafe fn drop(_ptr: *mut ()) {}
}
<|endoftext|>


<commit_before>#![crate_type="lib"]
#![feature(associated_consts)]
#![feature(box_syntax)]
#![feature(core)]

extern crate core;

pub use bmp::BmpFile;
pub use color::Color;
pub use event::*;
pub use point::Point;
pub use size::Size;
pub use window::Window;

pub mod bmp;
pub mod color;
#[path="../kernel/common/event.rs"]
pub mod event;
pub mod point;
pub mod size;
pub mod window;
<commit_msg>Cleanup warnings, add close button<commit_after>#![crate_type="lib"]
#![feature(associated_consts)]
#![feature(box_syntax)]

extern crate core;

pub use bmp::BmpFile;
pub use color::Color;
pub use event::*;
pub use point::Point;
pub use size::Size;
pub use window::Window;

pub mod bmp;
pub mod color;
#[path="../kernel/common/event.rs"]
pub mod event;
pub mod point;
pub mod size;
pub mod window;
<|endoftext|>


<commit_before>use anyhow::*;
use diesel;
use diesel::prelude::*;
#[cfg(feature = "profile-index")]
use flame;
use log::{error, info};
use rayon::prelude::*;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::time;

use crate::config::MiscSettings;
use crate::db::{directories, misc_settings, songs, DB};
use crate::metadata;
use crate::vfs::VFSSource;

const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction
const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Insertions in each transaction

pub fn update(db: &DB) -> Result<()> {
	let start = time::Instant::now();
	info!("Beginning library index update");
	clean(db)?;
	populate(db)?;
	info!(
		"Library index update took {} seconds",
		start.elapsed().as_millis() as f32 / 1000.0
	);
	#[cfg(feature = "profile-index")]
	flame::dump_html(&mut fs::File::create("index-flame-graph.html").unwrap()).unwrap();
	Ok(())
}

#[derive(Debug, Insertable)]
#[table_name = "songs"]
struct NewSong {
	path: String,
	parent: String,
	track_number: Option<i32>,
	disc_number: Option<i32>,
	title: Option<String>,
	artist: Option<String>,
	album_artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	duration: Option<i32>,
}

#[derive(Debug, Insertable)]
#[table_name = "directories"]
struct NewDirectory {
	path: String,
	parent: Option<String>,
	artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	date_added: i32,
}

struct IndexBuilder {
	new_songs: Vec<NewSong>,
	new_directories: Vec<NewDirectory>,
	db: DB,
	album_art_pattern: Regex,
}

impl IndexBuilder {
	#[cfg_attr(feature = "profile-index", flame)]
	fn new(db: DB, album_art_pattern: Regex) -> Result<IndexBuilder> {
		let mut new_songs = Vec::new();
		let mut new_directories = Vec::new();
		new_songs.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		new_directories.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		Ok(IndexBuilder {
			new_songs,
			new_directories,
			db,
			album_art_pattern,
		})
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_songs(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(songs::table)
			.values(&self.new_songs)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_songs.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_directories(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(directories::table)
			.values(&self.new_directories)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_directories.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_song(&mut self, song: NewSong) -> Result<()> {
		if self.new_songs.len() >= self.new_songs.capacity() {
			self.flush_songs()?;
		}
		self.new_songs.push(song);
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_directory(&mut self, directory: NewDirectory) -> Result<()> {
		if self.new_directories.len() >= self.new_directories.capacity() {
			self.flush_directories()?;
		}
		self.new_directories.push(directory);
		Ok(())
	}

	fn get_artwork(&self, dir: &Path) -> Result<Option<String>> {
		for file in fs::read_dir(dir)? {
			let file = file?;
			if let Some(name_string) = file.file_name().to_str() {
				if self.album_art_pattern.is_match(name_string) {
					return Ok(file.path().to_str().map(|p| p.to_owned()));
				}
			}
		}
		Ok(None)
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn populate_directory(&mut self, parent: Option<&Path>, path: &Path) -> Result<()> {
		// Find artwork
		let artwork = self.get_artwork(path).unwrap_or(None);

		// Extract path and parent path
		let parent_string = parent.and_then(|p| p.to_str()).map(|s| s.to_owned());
		let path_string = path.to_str().ok_or(anyhow!("Invalid directory path"))?;

		// Find date added
		let metadata = fs::metadata(path_string)?;
		let created = metadata
			.created()
			.or_else(|_| metadata.modified())?
			.duration_since(time::UNIX_EPOCH)?
			.as_secs() as i32;

		let mut directory_album = None;
		let mut directory_year = None;
		let mut directory_artist = None;
		let mut inconsistent_directory_album = false;
		let mut inconsistent_directory_year = false;
		let mut inconsistent_directory_artist = false;

		// Sub directories
		let mut sub_directories = Vec::new();

		// Insert content
		for file in fs::read_dir(path)? {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("directory-entry");
			let file_path = match file {
				Ok(ref f) => f.path(),
				_ => {
					error!("File read error within {}", path_string);
					break;
				}
			};

			if file_path.is_dir() {
				sub_directories.push(file_path.to_path_buf());
				continue;
			}

			if let Some(file_path_string) = file_path.to_str() {
				if let Ok(tags) = metadata::read(file_path.as_path()) {
					if tags.year.is_some() {
						inconsistent_directory_year |=
							directory_year.is_some() && directory_year != tags.year;
						directory_year = tags.year;
					}

					if tags.album.is_some() {
						inconsistent_directory_album |=
							directory_album.is_some() && directory_album != tags.album;
						directory_album = tags.album.as_ref().cloned();
					}

					if tags.album_artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.album_artist;
						directory_artist = tags.album_artist.as_ref().cloned();
					} else if tags.artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.artist;
						directory_artist = tags.artist.as_ref().cloned();
					}

					let song = NewSong {
						path: file_path_string.to_owned(),
						parent: path_string.to_owned(),
						disc_number: tags.disc_number.map(|n| n as i32),
						track_number: tags.track_number.map(|n| n as i32),
						title: tags.title,
						duration: tags.duration.map(|n| n as i32),
						artist: tags.artist,
						album_artist: tags.album_artist,
						album: tags.album,
						year: tags.year,
						artwork: artwork.as_ref().cloned(),
					};

					self.push_song(song)?;
				}
			}
		}

		// Insert directory
		if inconsistent_directory_year {
			directory_year = None;
		}
		if inconsistent_directory_album {
			directory_album = None;
		}
		if inconsistent_directory_artist {
			directory_artist = None;
		}

		let directory = NewDirectory {
			path: path_string.to_owned(),
			parent: parent_string,
			artwork,
			album: directory_album,
			artist: directory_artist,
			year: directory_year,
			date_added: created,
		};
		self.push_directory(directory)?;

		// Populate subdirectories
		for sub_directory in sub_directories {
			self.populate_directory(Some(path), &sub_directory)?;
		}

		Ok(())
	}
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn clean(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;

	{
		let all_songs: Vec<String>;
		{
			let connection = db.connect()?;
			all_songs = songs::table.select(songs::path).load(&connection)?;
		}

		let missing_songs = all_songs
			.par_iter()
			.filter(|ref song_path| {
				let path = Path::new(&song_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(songs::table.filter(songs::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	{
		let all_directories: Vec<String>;
		{
			let connection = db.connect()?;
			all_directories = directories::table
				.select(directories::path)
				.load(&connection)?;
		}

		let missing_directories = all_directories
			.par_iter()
			.filter(|ref directory_path| {
				let path = Path::new(&directory_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(directories::table.filter(directories::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	Ok(())
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn populate(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;
	let mount_points = vfs.get_mount_points();

	let album_art_pattern;
	{
		let connection = db.connect()?;
		let settings: MiscSettings = misc_settings::table.get_result(&connection)?;
		album_art_pattern = Regex::new(&settings.index_album_art_pattern)?;
	}

	let mut builder = IndexBuilder::new(db.clone(), album_art_pattern)?;
	for target in mount_points.values() {
		builder.populate_directory(None, target.as_path())?;
	}
	builder.flush_songs()?;
	builder.flush_directories()?;
	Ok(())
}
<commit_msg>Renamed IndexBuilder<commit_after>use anyhow::*;
use diesel;
use diesel::prelude::*;
#[cfg(feature = "profile-index")]
use flame;
use log::{error, info};
use rayon::prelude::*;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::time;

use crate::config::MiscSettings;
use crate::db::{directories, misc_settings, songs, DB};
use crate::metadata;
use crate::vfs::VFSSource;

const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction
const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Insertions in each transaction

pub fn update(db: &DB) -> Result<()> {
	let start = time::Instant::now();
	info!("Beginning library index update");
	clean(db)?;
	populate(db)?;
	info!(
		"Library index update took {} seconds",
		start.elapsed().as_millis() as f32 / 1000.0
	);
	#[cfg(feature = "profile-index")]
	flame::dump_html(&mut fs::File::create("index-flame-graph.html").unwrap()).unwrap();
	Ok(())
}

#[derive(Debug, Insertable)]
#[table_name = "songs"]
struct NewSong {
	path: String,
	parent: String,
	track_number: Option<i32>,
	disc_number: Option<i32>,
	title: Option<String>,
	artist: Option<String>,
	album_artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	duration: Option<i32>,
}

#[derive(Debug, Insertable)]
#[table_name = "directories"]
struct NewDirectory {
	path: String,
	parent: Option<String>,
	artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	date_added: i32,
}

struct IndexUpdater {
	new_songs: Vec<NewSong>,
	new_directories: Vec<NewDirectory>,
	db: DB,
	album_art_pattern: Regex,
}

impl IndexUpdater {
	#[cfg_attr(feature = "profile-index", flame)]
	fn new(db: DB, album_art_pattern: Regex) -> Result<IndexUpdater> {
		let mut new_songs = Vec::new();
		let mut new_directories = Vec::new();
		new_songs.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		new_directories.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		Ok(IndexUpdater {
			new_songs,
			new_directories,
			db,
			album_art_pattern,
		})
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_songs(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(songs::table)
			.values(&self.new_songs)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_songs.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_directories(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(directories::table)
			.values(&self.new_directories)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_directories.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_song(&mut self, song: NewSong) -> Result<()> {
		if self.new_songs.len() >= self.new_songs.capacity() {
			self.flush_songs()?;
		}
		self.new_songs.push(song);
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_directory(&mut self, directory: NewDirectory) -> Result<()> {
		if self.new_directories.len() >= self.new_directories.capacity() {
			self.flush_directories()?;
		}
		self.new_directories.push(directory);
		Ok(())
	}

	fn get_artwork(&self, dir: &Path) -> Result<Option<String>> {
		for file in fs::read_dir(dir)? {
			let file = file?;
			if let Some(name_string) = file.file_name().to_str() {
				if self.album_art_pattern.is_match(name_string) {
					return Ok(file.path().to_str().map(|p| p.to_owned()));
				}
			}
		}
		Ok(None)
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn populate_directory(&mut self, parent: Option<&Path>, path: &Path) -> Result<()> {
		// Find artwork
		let artwork = self.get_artwork(path).unwrap_or(None);

		// Extract path and parent path
		let parent_string = parent.and_then(|p| p.to_str()).map(|s| s.to_owned());
		let path_string = path.to_str().ok_or(anyhow!("Invalid directory path"))?;

		// Find date added
		let metadata = fs::metadata(path_string)?;
		let created = metadata
			.created()
			.or_else(|_| metadata.modified())?
			.duration_since(time::UNIX_EPOCH)?
			.as_secs() as i32;

		let mut directory_album = None;
		let mut directory_year = None;
		let mut directory_artist = None;
		let mut inconsistent_directory_album = false;
		let mut inconsistent_directory_year = false;
		let mut inconsistent_directory_artist = false;

		// Sub directories
		let mut sub_directories = Vec::new();

		// Insert content
		for file in fs::read_dir(path)? {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("directory-entry");
			let file_path = match file {
				Ok(ref f) => f.path(),
				_ => {
					error!("File read error within {}", path_string);
					break;
				}
			};

			if file_path.is_dir() {
				sub_directories.push(file_path.to_path_buf());
				continue;
			}

			if let Some(file_path_string) = file_path.to_str() {
				if let Ok(tags) = metadata::read(file_path.as_path()) {
					if tags.year.is_some() {
						inconsistent_directory_year |=
							directory_year.is_some() && directory_year != tags.year;
						directory_year = tags.year;
					}

					if tags.album.is_some() {
						inconsistent_directory_album |=
							directory_album.is_some() && directory_album != tags.album;
						directory_album = tags.album.as_ref().cloned();
					}

					if tags.album_artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.album_artist;
						directory_artist = tags.album_artist.as_ref().cloned();
					} else if tags.artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.artist;
						directory_artist = tags.artist.as_ref().cloned();
					}

					let song = NewSong {
						path: file_path_string.to_owned(),
						parent: path_string.to_owned(),
						disc_number: tags.disc_number.map(|n| n as i32),
						track_number: tags.track_number.map(|n| n as i32),
						title: tags.title,
						duration: tags.duration.map(|n| n as i32),
						artist: tags.artist,
						album_artist: tags.album_artist,
						album: tags.album,
						year: tags.year,
						artwork: artwork.as_ref().cloned(),
					};

					self.push_song(song)?;
				}
			}
		}

		// Insert directory
		if inconsistent_directory_year {
			directory_year = None;
		}
		if inconsistent_directory_album {
			directory_album = None;
		}
		if inconsistent_directory_artist {
			directory_artist = None;
		}

		let directory = NewDirectory {
			path: path_string.to_owned(),
			parent: parent_string,
			artwork,
			album: directory_album,
			artist: directory_artist,
			year: directory_year,
			date_added: created,
		};
		self.push_directory(directory)?;

		// Populate subdirectories
		for sub_directory in sub_directories {
			self.populate_directory(Some(path), &sub_directory)?;
		}

		Ok(())
	}
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn clean(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;

	{
		let all_songs: Vec<String>;
		{
			let connection = db.connect()?;
			all_songs = songs::table.select(songs::path).load(&connection)?;
		}

		let missing_songs = all_songs
			.par_iter()
			.filter(|ref song_path| {
				let path = Path::new(&song_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(songs::table.filter(songs::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	{
		let all_directories: Vec<String>;
		{
			let connection = db.connect()?;
			all_directories = directories::table
				.select(directories::path)
				.load(&connection)?;
		}

		let missing_directories = all_directories
			.par_iter()
			.filter(|ref directory_path| {
				let path = Path::new(&directory_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(directories::table.filter(directories::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	Ok(())
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn populate(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;
	let mount_points = vfs.get_mount_points();

	let album_art_pattern;
	{
		let connection = db.connect()?;
		let settings: MiscSettings = misc_settings::table.get_result(&connection)?;
		album_art_pattern = Regex::new(&settings.index_album_art_pattern)?;
	}

	let mut builder = IndexUpdater::new(db.clone(), album_art_pattern)?;
	for target in mount_points.values() {
		builder.populate_directory(None, target.as_path())?;
	}
	builder.flush_songs()?;
	builder.flush_directories()?;
	Ok(())
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;
use std::io::{self, Cursor, Read};
use std::marker::PhantomData;
use std::mem;

use crate::color::ColorType;
use crate::image::ImageDecoder;
use crate::error::{ImageError, ImageResult};

/// JPEG decoder
pub struct JpegDecoder<R> {
    decoder: jpeg::Decoder<R>,
    metadata: jpeg::ImageInfo,
}

impl<R: Read> JpegDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
        let mut decoder = jpeg::Decoder::new(r);

        decoder.read_info().map_err(ImageError::from_jpeg)?;
        let mut metadata = decoder.info().unwrap();

        // We convert CMYK data to RGB before returning it to the user.
        if metadata.pixel_format == jpeg::PixelFormat::CMYK32 {
            metadata.pixel_format = jpeg::PixelFormat::RGB24;
        }

        Ok(JpegDecoder {
            decoder,
            metadata,
        })
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct JpegReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for JpegReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for JpegDecoder<R> {
    type Reader = JpegReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        (u32::from(self.metadata.width), u32::from(self.metadata.height))
    }

    fn color_type(&self) -> ColorType {
        ColorType::from_jpeg(self.metadata.pixel_format)
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        Ok(JpegReader(Cursor::new(data), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));

        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        buf.copy_from_slice(&data);
        Ok(())
    }
}

fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let size = input.len() - input.len() / 4;
    let mut output = Vec::with_capacity(size);

    for pixel in input.chunks(4) {
        let c = f32::from(pixel[0]);
        let m = f32::from(pixel[1]);
        let y = f32::from(pixel[2]);
        let k = 255.0 - f32::from(pixel[3]);

        let k_sub = k / 255.0;

        // CMY -> RGB
        let r = k - c * k_sub;
        let g = k - m * k_sub;
        let b = k - y * k_sub;

        output.push(r as u8);
        output.push(g as u8);
        output.push(b as u8);
    }

    output
}

impl ColorType {
    fn from_jpeg(pixel_format: jpeg::PixelFormat) -> ColorType {
        use jpeg::PixelFormat::*;
        match pixel_format {
            L8 => ColorType::L8,
            RGB24 => ColorType::Rgb8,
            CMYK32 => panic!(),
        }
    }
}

impl ImageError {
    fn from_jpeg(err: jpeg::Error) -> ImageError {
        use jpeg::Error::*;
        match err {
            Format(desc) => ImageError::FormatError(desc),
            Unsupported(desc) => ImageError::UnsupportedError(format!("{:?}", desc)),
            Io(err) => ImageError::IoError(err),
            Internal(err) => ImageError::FormatError(err.to_string()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::cmyk_to_rgb;

    #[test]
    fn cymk_to_rgb_correct() {
        // Based on R = 255 * (1-C/255) * (1-K/255)
        for c in 0..255 {
            for k in 0..255 {
                let r = (255.0 - f32::from(c)) * (255.0 - f32::from(k)) / 255.0;
                let r_u8 = r as u8;
                let convert_r = cmyk_to_rgb(&[c, 0, 0, k])[0];
                assert_eq!(convert_r, r_u8,
                           "c = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_r, r_u8);
            }
        }
    }
}

<commit_msg>Update decoder to be correct for all colors.<commit_after>use std::convert::TryFrom;
use std::io::{self, Cursor, Read};
use std::marker::PhantomData;
use std::mem;

use crate::color::ColorType;
use crate::image::ImageDecoder;
use crate::error::{ImageError, ImageResult};

/// JPEG decoder
pub struct JpegDecoder<R> {
    decoder: jpeg::Decoder<R>,
    metadata: jpeg::ImageInfo,
}

impl<R: Read> JpegDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
        let mut decoder = jpeg::Decoder::new(r);

        decoder.read_info().map_err(ImageError::from_jpeg)?;
        let mut metadata = decoder.info().unwrap();

        // We convert CMYK data to RGB before returning it to the user.
        if metadata.pixel_format == jpeg::PixelFormat::CMYK32 {
            metadata.pixel_format = jpeg::PixelFormat::RGB24;
        }

        Ok(JpegDecoder {
            decoder,
            metadata,
        })
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct JpegReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for JpegReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for JpegDecoder<R> {
    type Reader = JpegReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        (u32::from(self.metadata.width), u32::from(self.metadata.height))
    }

    fn color_type(&self) -> ColorType {
        ColorType::from_jpeg(self.metadata.pixel_format)
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        Ok(JpegReader(Cursor::new(data), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));

        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        buf.copy_from_slice(&data);
        Ok(())
    }
}

fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let size = input.len() - input.len() / 4;
    let mut output = Vec::with_capacity(size);

    let inv = 1.0 / 255.0;
    for pixel in input.chunks(4) {
        let c = f32::from(pixel[0]);
        let m = f32::from(pixel[1]);
        let y = f32::from(pixel[2]);
        let k = 255.0 - f32::from(pixel[3]);

        let k_sub = -k;
        let k = k * 255.0;
        // CMY -> RGB
        let r = (c * k_sub + k) * inv;
        let g = (y * k_sub + k) * inv;
        let b = (m * k_sub + k) * inv;

        output.push(r as u8);
        output.push(g as u8);
        output.push(b as u8);
    }

    output
}

impl ColorType {
    fn from_jpeg(pixel_format: jpeg::PixelFormat) -> ColorType {
        use jpeg::PixelFormat::*;
        match pixel_format {
            L8 => ColorType::L8,
            RGB24 => ColorType::Rgb8,
            CMYK32 => panic!(),
        }
    }
}

impl ImageError {
    fn from_jpeg(err: jpeg::Error) -> ImageError {
        use jpeg::Error::*;
        match err {
            Format(desc) => ImageError::FormatError(desc),
            Unsupported(desc) => ImageError::UnsupportedError(format!("{:?}", desc)),
            Io(err) => ImageError::IoError(err),
            Internal(err) => ImageError::FormatError(err.to_string()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::cmyk_to_rgb;

    #[test]
    fn cymk_to_rgb_correct() {
        // Based on R = 255 * (1-C/255) * (1-K/255)
        for c in 0..255 {
            for k in 0..255 {
                let r = (255.0 - f32::from(c)) * (255.0 - f32::from(k)) / 255.0;
                let r_u8 = r as u8;
                let convert_r = cmyk_to_rgb(&[c, 0, 0, k])[0];
                assert_eq!(convert_r, r_u8,
                           "c = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_r, r_u8);
            }
        }
    }
}

<|endoftext|>


<commit_before>// Build script.
//
// This program is run by Cargo when building lib/cretonne. It is used to generate Rust code from
// the language definitions in the lib/cretonne/meta directory.
//
// Environment:
//
// OUT_DIR
//     Directory where generated files should be placed.
//
// The build script expects to be run from the directory where this build.rs file lives. The
// current directory is used to find the sources.

use std::env;
use std::process;

fn main() {
    let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");

    println!("Build script generating files in {}", out_dir);

    let cur_dir = env::current_dir().expect("Can't access current working directory");
    let crate_dir = cur_dir.as_path();

    // Make sure we rebuild is this build script changes.
    // I guess that won't happen if you have non-UTF8 bytes in your path names.
    // The `build.py` script prints out its own dependencies.
    println!("cargo:rerun-if-changed={}",
             crate_dir.join("build.rs").to_string_lossy());

    // Scripts are in `$crate_dir/meta`.
    let meta_dir = crate_dir.join("meta");
    let build_script = meta_dir.join("build.py");

    // Launch build script with Python. We'll just find python in the path.
    let status = process::Command::new("python")
        .current_dir(crate_dir)
        .arg(build_script)
        .arg("--out-dir")
        .arg(out_dir)
        .status()
        .expect("Failed to launch second-level build script");
    if !status.success() {
        process::exit(status.code().unwrap());
    }
}
<commit_msg>Implement conditional compilation configuration in build.rs<commit_after>// Build script.
//
// This program is run by Cargo when building lib/cretonne. It is used to generate Rust code from
// the language definitions in the lib/cretonne/meta directory.
//
// Environment:
//
// OUT_DIR
//     Directory where generated files should be placed.
//
// TARGET
//     Target triple provided by Cargo.
//
// CRETONNE_TARGETS (Optional)
//     A setting for conditional compilation of isa targets. Possible values can be "native" or
//     known isa targets separated by ','.
//
// The build script expects to be run from the directory where this build.rs file lives. The
// current directory is used to find the sources.

use std::env;
use std::process;

fn main() {
    let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
    let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
    let cretonne_targets = env::var("CRETONNE_TARGETS").ok();
    let cretonne_targets = cretonne_targets.as_ref().map(|s| s.as_ref());

    // Configure isa targets cfg.
    match isa_targets(cretonne_targets, &target_triple) {
        Ok(isa_targets) => {
            for isa in &isa_targets {
                println!("cargo:rustc-cfg=build_{}", isa.name());
            }
        }
        Err(err) => {
            eprintln!("Error: {}", err);
            process::exit(1);
        }
    }

    println!("Build script generating files in {}", out_dir);

    let cur_dir = env::current_dir().expect("Can't access current working directory");
    let crate_dir = cur_dir.as_path();

    // Make sure we rebuild is this build script changes.
    // I guess that won't happen if you have non-UTF8 bytes in your path names.
    // The `build.py` script prints out its own dependencies.
    println!("cargo:rerun-if-changed={}",
             crate_dir.join("build.rs").to_string_lossy());

    // Scripts are in `$crate_dir/meta`.
    let meta_dir = crate_dir.join("meta");
    let build_script = meta_dir.join("build.py");

    // Launch build script with Python. We'll just find python in the path.
    let status = process::Command::new("python")
        .current_dir(crate_dir)
        .arg(build_script)
        .arg("--out-dir")
        .arg(out_dir)
        .status()
        .expect("Failed to launch second-level build script");
    if !status.success() {
        process::exit(status.code().unwrap());
    }
}

/// Represents known ISA target.
#[derive(Copy, Clone)]
enum Isa {
    Riscv,
    Intel,
    Arm32,
    Arm64,
}

impl Isa {
    /// Creates isa target using name.
    fn new(name: &str) -> Option<Self> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.name() == name)
            .next()
    }

    /// Creates isa target from arch.
    fn from_arch(arch: &str) -> Option<Isa> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.is_arch_applicable(arch))
            .next()
    }

    /// Returns all supported isa targets.
    fn all() -> [Isa; 4] {
        [Isa::Riscv, Isa::Intel, Isa::Arm32, Isa::Arm64]
    }

    /// Returns name of the isa target.
    fn name(&self) -> &'static str {
        match *self {
            Isa::Riscv => "riscv",
            Isa::Intel => "intel",
            Isa::Arm32 => "arm32",
            Isa::Arm64 => "arm64",
        }
    }

    /// Checks if arch is applicable for the isa target.
    fn is_arch_applicable(&self, arch: &str) -> bool {
        match *self {
            Isa::Riscv => arch == "riscv",
            Isa::Intel => ["x86_64", "i386", "i586", "i686"].contains(&arch),
            Isa::Arm32 => arch.starts_with("arm") || arch.starts_with("thumb"),
            Isa::Arm64 => arch == "aarch64",
        }
    }
}

/// Returns isa targets to configure conditional compilation.
fn isa_targets(cretonne_targets: Option<&str>, target_triple: &str) -> Result<Vec<Isa>, String> {
    match cretonne_targets {
        Some("native") => {
            Isa::from_arch(target_triple.split('-').next().unwrap())
                .map(|isa| vec![isa])
                .ok_or_else(|| {
                                format!("no supported isa found for target triple `{}`",
                                        target_triple)
                            })
        }
        Some(targets) => {
            let unknown_isa_targets = targets
                .split(',')
                .filter(|target| Isa::new(target).is_none())
                .collect::<Vec<_>>();
            let isa_targets = targets.split(',').flat_map(Isa::new).collect::<Vec<_>>();
            match (unknown_isa_targets.is_empty(), isa_targets.is_empty()) {
                (true, true) => Ok(Isa::all().to_vec()),
                (true, _) => Ok(isa_targets),
                (_, _) => Err(format!("unknown isa targets: `{}`", unknown_isa_targets.join(", "))),
            }
        }
        None => Ok(Isa::all().to_vec()),
    }
}
<|endoftext|>


<commit_before>//! Modeled after [the Scala solution]
//!
//! [the Scala solution]: http://rosettacode.org/wiki/24_game/Solve#Scala

#![feature(slice_patterns)]

extern crate num;
extern crate permutohedron;

use num::rational::{Ratio, Rational};
use num::traits::Zero;
use permutohedron::Heap;

/// convenience macro to create a fixed-sized vector of rationals by writing `rational![1, 2, ...]`
/// instead of `[Ratio::<isize>::from_integer(1), Ratio::<isize>::from_integer(2), ...]`
macro_rules! rationals(
    ($($e:expr),+) => ([$(Ratio::<isize>::from_integer($e)),+])
);

fn main() {
    let mut r = rationals![1, 3, 7, 9];
    let sol = solve(&mut r, 24).unwrap_or_else(|| "no solution found".to_string());
    println!("{}", sol);
}

/// for a vector of rationals r, find the combination of arithmetic operations that yield
/// `target_val` as a result (if such combination exists)
fn solve(r: &mut [Rational], target_val: isize) -> Option<String> {
    // need to sort because next_permutation()
    // returns permutations in lexicographic order
    r.sort();
    loop {
        let all_ops = compute_all_operations(r);
        for &(res, ref ops) in &all_ops {
            if res == Ratio::from_integer(target_val) {
                return Some(ops.to_string());
            }
        }
        let mut perm = Heap::new(r);
        if perm.next_permutation() == None {
            return None;
        }
    }
}

/// applies all the valid combinations of + - * and / to the numbers in l and for each combination
/// creates a tuple with the result and the expression in String form returns all (result,
/// expression in string form) results in a vector
fn compute_all_operations(l: &[Rational]) -> Vec<(Rational, String)> {
    match *l {
        [] => vec![],
        [x] => vec![(x, (format!("{}", x)))],
        [x, ref rest..] => {
            let mut rt = Vec::new();
            for &(y, ref exp) in &compute_all_operations(rest) {
                let mut sub = vec![(x * y, "*"), (x + y, "+"), (x - y, "-")];
                if y != Zero::zero() {
                    sub.push((x / y, "/"));
                }
                for &(z, op) in &sub {
                    let aux = (z, (format!("({} {} {})", x, op, exp)));
                    rt.push(aux);
                }
            }
            rt
        }
    }
}

#[test]
fn test_rationals_macro() {
    assert_eq!(
        // without the rationals! macro
        [
            Ratio::from_integer(1),
            Ratio::from_integer(2),
            Ratio::from_integer(3),
            Ratio::from_integer(4)
        ],
        // with the rationals! macro
        (rationals![1, 2, 3, 4])
    );
}

#[test]
#[ignore]
fn test_solve() {
    let mut r = rationals![1, 3, 7, 9];
    assert_eq!(solve(&mut r, 24), Some("(9 / (3 / (1 + 7)))".to_string()));
}
<commit_msg>use new slice pattern syntax<commit_after>//! Modeled after [the Scala solution]
//!
//! [the Scala solution]: http://rosettacode.org/wiki/24_game/Solve#Scala

#![feature(slice_patterns)]

extern crate num;
extern crate permutohedron;

use num::rational::{Ratio, Rational};
use num::traits::Zero;
use permutohedron::Heap;

/// convenience macro to create a fixed-sized vector of rationals by writing `rational![1, 2, ...]`
/// instead of `[Ratio::<isize>::from_integer(1), Ratio::<isize>::from_integer(2), ...]`
macro_rules! rationals(
    ($($e:expr),+) => ([$(Ratio::<isize>::from_integer($e)),+])
);

fn main() {
    let mut r = rationals![1, 3, 7, 9];
    let sol = solve(&mut r, 24).unwrap_or_else(|| "no solution found".to_string());
    println!("{}", sol);
}

/// for a vector of rationals r, find the combination of arithmetic operations that yield
/// `target_val` as a result (if such combination exists)
fn solve(r: &mut [Rational], target_val: isize) -> Option<String> {
    // need to sort because next_permutation()
    // returns permutations in lexicographic order
    r.sort();
    loop {
        let all_ops = compute_all_operations(r);
        for &(res, ref ops) in &all_ops {
            if res == Ratio::from_integer(target_val) {
                return Some(ops.to_string());
            }
        }
        let mut perm = Heap::new(r);
        if perm.next_permutation() == None {
            return None;
        }
    }
}

/// applies all the valid combinations of + - * and / to the numbers in l and for each combination
/// creates a tuple with the result and the expression in String form returns all (result,
/// expression in string form) results in a vector
fn compute_all_operations(l: &[Rational]) -> Vec<(Rational, String)> {
    match *l {
        [] => vec![],
        [x] => vec![(x, (format!("{}", x)))],
        [x, ref rest @ ..] => {
            let mut rt = Vec::new();
            for &(y, ref exp) in &compute_all_operations(rest) {
                let mut sub = vec![(x * y, "*"), (x + y, "+"), (x - y, "-")];
                if y != Zero::zero() {
                    sub.push((x / y, "/"));
                }
                for &(z, op) in &sub {
                    let aux = (z, (format!("({} {} {})", x, op, exp)));
                    rt.push(aux);
                }
            }
            rt
        }
    }
}

#[test]
fn test_rationals_macro() {
    assert_eq!(
        // without the rationals! macro
        [
            Ratio::from_integer(1),
            Ratio::from_integer(2),
            Ratio::from_integer(3),
            Ratio::from_integer(4)
        ],
        // with the rationals! macro
        (rationals![1, 2, 3, 4])
    );
}

#[test]
#[ignore]
fn test_solve() {
    let mut r = rationals![1, 3, 7, 9];
    assert_eq!(solve(&mut r, 24), Some("(9 / (3 / (1 + 7)))".to_string()));
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use renderbuffer::Renderbuffer;
use texture::{Texture, TextureType, ImageTargetType};
use types::{BufferBits, GLError};

pub struct Framebuffer {
    gl_id: GLuint
}

impl Framebuffer {
    pub unsafe fn from_gl(id: GLuint) -> Self {
        Framebuffer { gl_id: id }
    }

    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_framebuffer(&self) -> Framebuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenFramebuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Framebuffer {
                gl_id: id
            }
        }
    }
}

gl_enum! {
    pub gl_enum FramebufferAttachment {
        ColorAttachment0 as COLOR_ATTACHMENT0 = gl::COLOR_ATTACHMENT0,
        DepthAttachment as DEPTH_ATTACHMENT = gl::DEPTH_ATTACHMENT,
        StencilAttachment as STENCIL_ATTACHMENT = gl::STENCIL_ATTACHMENT
    }
}

pub struct FramebufferBinding<'a> {
    phantom: PhantomData<&'a mut Framebuffer>
}

impl<'a> FramebufferBinding<'a> {
    fn target(&self) -> GLenum {
        gl::FRAMEBUFFER
    }

    pub fn renderbuffer(&mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &mut Renderbuffer)
    {
        unsafe {
            gl::FramebufferRenderbuffer(self.target(),
                                        attachment.gl_enum(),
                                        gl::RENDERBUFFER,
                                        renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not a valid attachment point, or `renderbuffer` is not `GL_RENDERBUFFER` and `renderbuffer` is not 0",
                GLError::InvalidOperation => "Framebuffer 0 is bound, or `renderbuffer` is neither 0 nor the name of an existing renderbuffer object",
                _ => "Unknown error"
            }
        }
    }

    pub fn texture2d<T: TextureType>(&mut self,
                                     attachment: FramebufferAttachment,
                                     tex_target: T::ImageTargetType,
                                     texture: &mut Texture<T>,
                                     level: i32)
    {
        debug_assert!(level == 0);

        unsafe {
            gl::FramebufferTexture2D(self.target(),
                                     attachment.gl_enum(),
                                     tex_target.gl_enum(),
                                     texture.gl_id(),
                                     level as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not an accepted attachment point, or `textarget` is not an accepted texture target and texture is not 0",
                GLError::InvalidValue => "`level` is not 0 and `texture` is not 0",
                GLError::InvalidOperation => "Framebuffer object 0 is bound, `texture` is neither 0 nor the name of an existing texture object, or `textarget` is not a valid target for `texture`",
                _ => "Unknown error"
            }
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }
}

pub struct FramebufferBinder;
impl FramebufferBinder {
    pub fn bind<'a>(&'a mut self, fbo: &mut Framebuffer)
        -> FramebufferBinding<'a>
    {
        let binding = FramebufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindFramebuffer(binding.target(), fbo.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<commit_msg>Add `FramebufferBinder::current_binding()<commit_after>use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use renderbuffer::Renderbuffer;
use texture::{Texture, TextureType, ImageTargetType};
use types::{BufferBits, GLError};

pub struct Framebuffer {
    gl_id: GLuint
}

impl Framebuffer {
    pub unsafe fn from_gl(id: GLuint) -> Self {
        Framebuffer { gl_id: id }
    }

    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_framebuffer(&self) -> Framebuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenFramebuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Framebuffer {
                gl_id: id
            }
        }
    }
}

gl_enum! {
    pub gl_enum FramebufferAttachment {
        ColorAttachment0 as COLOR_ATTACHMENT0 = gl::COLOR_ATTACHMENT0,
        DepthAttachment as DEPTH_ATTACHMENT = gl::DEPTH_ATTACHMENT,
        StencilAttachment as STENCIL_ATTACHMENT = gl::STENCIL_ATTACHMENT
    }
}

pub struct FramebufferBinding<'a> {
    phantom: PhantomData<&'a mut Framebuffer>
}

impl<'a> FramebufferBinding<'a> {
    fn target(&self) -> GLenum {
        gl::FRAMEBUFFER
    }

    pub fn renderbuffer(&mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &mut Renderbuffer)
    {
        unsafe {
            gl::FramebufferRenderbuffer(self.target(),
                                        attachment.gl_enum(),
                                        gl::RENDERBUFFER,
                                        renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not a valid attachment point, or `renderbuffer` is not `GL_RENDERBUFFER` and `renderbuffer` is not 0",
                GLError::InvalidOperation => "Framebuffer 0 is bound, or `renderbuffer` is neither 0 nor the name of an existing renderbuffer object",
                _ => "Unknown error"
            }
        }
    }

    pub fn texture2d<T: TextureType>(&mut self,
                                     attachment: FramebufferAttachment,
                                     tex_target: T::ImageTargetType,
                                     texture: &mut Texture<T>,
                                     level: i32)
    {
        debug_assert!(level == 0);

        unsafe {
            gl::FramebufferTexture2D(self.target(),
                                     attachment.gl_enum(),
                                     tex_target.gl_enum(),
                                     texture.gl_id(),
                                     level as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not an accepted attachment point, or `textarget` is not an accepted texture target and texture is not 0",
                GLError::InvalidValue => "`level` is not 0 and `texture` is not 0",
                GLError::InvalidOperation => "Framebuffer object 0 is bound, `texture` is neither 0 nor the name of an existing texture object, or `textarget` is not a valid target for `texture`",
                _ => "Unknown error"
            }
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }
}

pub struct FramebufferBinder;
impl FramebufferBinder {
    pub unsafe fn current_binding<'a>(&'a mut self) -> FramebufferBinding<'a> {
        FramebufferBinding { phantom: PhantomData }
    }

    pub fn bind<'a>(&'a mut self, fbo: &mut Framebuffer)
        -> FramebufferBinding<'a>
    {
        let binding = FramebufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindFramebuffer(binding.target(), fbo.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::result::{Ok, Err};
use std::option::{Some};
use std::collections::HashMap;

use sdl2::surface::{Surface};
use sdl2::render::{Renderer, Texture};
use sdl2::rect::{Rect};
use sdl2::video::Window;

use p2d::sprite::SpriteTile;

pub struct TextureSheet {
    name: String,
    surface: Box<Surface>,
    texture: Box<Texture>
}

pub type TextureSheets = HashMap<String, TextureSheet>;

impl TextureSheet {
    pub fn new(renderer: &Renderer<Window>, path_str: &String, name: String) -> TextureSheet {
        let path = Path::new(path_str.as_slice());
        let surface = match Surface::from_bmp(&path) {
            Ok(s) => s,
            Err(msg) => fail!("new_sprite_from: Couldn't create surface from path '{}', msg: {}")
        };
        let surface = box surface;
        let texture = match renderer.create_texture_from_surface(&*surface) {
            Ok(t) => t,
            Err(msg) => fail!("new_sprite_from: Couldn't create texture from path '{}', msg: {}")
        };
        TextureSheet { name: name,
                     surface: surface, texture: box texture }
    }

    pub fn draw_tile(&self, renderer: &Renderer<Window>, st: &SpriteTile,
                     dst_coords: (int, int), dst_size: (uint, uint)) -> bool {
        //let (x, y) = dst;
        let (tile_x, tile_y) = st.coords;
        let (size_x, size_y) = st.size;
        let src = Some(Rect::new(tile_x as i32, tile_y as i32, size_x as i32, size_y as i32));
        let (dst_x, dst_y) = dst_coords;
        let (dst_size_x, dst_size_y) = dst_size;
        let dst = Some(Rect::new(dst_x as i32, dst_y as i32, dst_size_x as i32, dst_size_y as i32));
        // FIXME this is lame
        renderer.copy(&*self.texture, src, dst).is_ok()
    }
}
<commit_msg>another try<commit_after>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::result::{Ok, Err};
use std::option::{Some};
use std::collections::HashMap;

use sdl2::surface::{Surface};
use sdl2::render::{Renderer, Texture};
use sdl2::rect::{Rect};
use sdl2::video::Window;

use p2d::sprite::SpriteTile;

pub struct TextureSheet {
    name: String,
    surface: Box<Surface>,
    texture: Box<Texture>
}

pub type TextureSheets = HashMap<String, TextureSheet>;

impl TextureSheet {
    pub fn new(renderer: &Renderer<Window>, path_str: &String, name: String) -> TextureSheet {
        let path = Path::new(path_str.as_slice());
        let surface = match Surface::from_bmp(&path) {
            Ok(s) => s,
            Err(msg) => fail!("new_sprite_from: Couldn't create surface from path '{}', msg: {}")
        };
        let surface = box surface;
        let texture = match renderer.create_texture_from_surface(&*surface) {
            Ok(t) => t,
            Err(msg) => fail!(format!("new_sprite_from: Couldn't create texture from path '{}', msg: {}", path_str, msg))
        };
        TextureSheet { name: name,
                     surface: surface, texture: box texture }
    }

    pub fn draw_tile(&self, renderer: &Renderer<Window>, st: &SpriteTile,
                     dst_coords: (int, int), dst_size: (uint, uint)) -> bool {
        //let (x, y) = dst;
        let (tile_x, tile_y) = st.coords;
        let (size_x, size_y) = st.size;
        let src = Some(Rect::new(tile_x as i32, tile_y as i32, size_x as i32, size_y as i32));
        let (dst_x, dst_y) = dst_coords;
        let (dst_size_x, dst_size_y) = dst_size;
        let dst = Some(Rect::new(dst_x as i32, dst_y as i32, dst_size_x as i32, dst_size_y as i32));
        // FIXME this is lame
        renderer.copy(&*self.texture, src, dst).is_ok()
    }
}
<|endoftext|>