<commit_before>use postgres;
use uuid::Uuid;
use error::Error;
use chrono::{NaiveDateTime, UTC};
use super::{conn, Model};
use model::enclosure::Enclosure;
use scraper;
use Track;
use Playlist;
use Album;
use model::PaginatedCollection;
use serde_json::Value;
use feed_rs;

static PROPS: [&'static str; 20]  = ["id",
                                     "url",
                                     "title",
                                     "description",
                                     "visual_url",
                                     "locale",

                                     "summary",
                                     "content",
                                     "author",
                                     "crawled",
                                     "published",
                                     "updated",
                                     "fingerprint",
                                     "origin_id",
                                     "alternate",
                                     "keywords",
                                     "enclosure",

                                     "feed_id",

                                     "created_at",
                                     "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Entry {
    pub id:          Uuid,
    pub url:         String,
    pub title:       Option<String>,
    pub description: Option<String>,
    pub visual_url:  Option<String>,
    pub locale:      Option<String>,
    pub summary:     Option<String>,
    pub content:     Option<String>,
    pub author:      Option<String>,
    pub crawled:     NaiveDateTime,
    pub published:   NaiveDateTime,
    pub updated:     Option<NaiveDateTime>,
    pub fingerprint: String,
    pub origin_id:   String,
    pub alternate:   Value,
    pub keywords:    Value,
    pub enclosure:   Value,
    pub feed_id:     Option<Uuid>,
    pub created_at:  NaiveDateTime,
    pub updated_at:  NaiveDateTime,
    pub tracks:      Vec<Track>,
    pub playlists:   Vec<Playlist>,
    pub albums:      Vec<Album>,
}

impl<'a> Model<'a> for Entry {
    fn table_name() -> String {
        "entries".to_string()
    }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Entry> {
        let mut entries = Vec::new();
        for row in rows.iter() {
            entries.push(Entry {
                id:          row.get(0),
                url:         row.get(1),
                title:       row.get(2),
                description: row.get(3),
                visual_url:  row.get(4),
                locale:      row.get(5),
                summary:     row.get(6),
                content:     row.get(7),
                author:      row.get(8),
                crawled:     row.get(9),
                published:   row.get(10),
                updated:     row.get(11),
                fingerprint: row.get(12),
                origin_id:   row.get(13),
                alternate:   row.get(14),
                keywords:    row.get(15),
                enclosure:   row.get(16),
                feed_id:     row.get(17),
                created_at:  row.get(18),
                updated_at:  row.get(19),
                tracks:      Track::find_by_entry_id(row.get(0)),
                playlists:   Playlist::find_by_entry_id(row.get(0)),
                albums:      Album::find_by_entry_id(row.get(0)),
            })
        }
        entries
    }
    fn create(&self) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&self.url]));
        let mut entry = self.clone();
        for row in rows.iter() {
            entry.id = row.get(0);
        }
        Ok(entry)
    }
    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = UTC::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE entries SET
                                   url         = $2,
                                   title       = $3,
                                   description = $4,
                                   visual_url  = $5,
                                   locale      = $6,
                                   summary     = $7,
                                   content     = $8,
                                   author      = $9,
                                   crawled     = $10,
                                   published   = $11,
                                   updated     = $12,
                                   fingerprint = $13,
                                   origin_id   = $14,
                                   alternate   = $15,
                                   keywords    = $16,
                                   enclosure   = $17,
                                   feed_id     = $18,
                                   created_at  = $19,
                                   updated_at  = $20
                                 WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.visual_url,
                                  &self.locale,
                                  &self.summary,
                                  &self.content,
                                  &self.author,
                                  &self.crawled,
                                  &self.published,
                                  &self.updated,
                                  &self.fingerprint,
                                  &self.origin_id,
                                  &self.alternate,
                                  &self.keywords,
                                  &self.enclosure,
                                  &self.feed_id,
                                  &self.created_at,
                                  &self.updated_at]);
        try!(result);
        Ok(())
    }
}

impl Entry {
    pub fn new(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&url]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     UTC::now().naive_utc(),
                published:   UTC::now().naive_utc(),
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn find_by_url(url: &str) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare(
            &format!("SELECT {} FROM entries
                        WHERE url = $1", Self::props_str(""))));
        let rows = try!(stmt.query(&[&url]));
        let entries = Entry::rows_to_items(rows);
        if entries.len() > 0 {
            return Ok(entries[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_or_create_by_url(url: String) -> Result<Entry, Error> {
        match Entry::find_by_url(&url) {
            Ok(entry) => Ok(entry),
            Err(_)    => Entry::create_by_url(url)
        }
    }

    pub fn create_by_url(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&url]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     UTC::now().naive_utc(),
                published:   UTC::now().naive_utc(),
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn add_track(&mut self, track: Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_entries (track_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        self.tracks.push(track);
        Ok(())
    }

    pub fn add_playlist(&mut self, playlist: Playlist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO playlist_entries (playlist_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&playlist.id, &self.id]));
        self.playlists.push(playlist);
        Ok(())
    }

    pub fn add_album(&mut self, album: Album) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_entries (album_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&album.id, &self.id]));
        self.albums.push(album);
        Ok(())
    }
}
<commit_msg>Implement Entry::find_by_feed_id<commit_after>use postgres;
use uuid::Uuid;
use error::Error;
use chrono::{NaiveDateTime, UTC};
use super::{conn, Model};
use model::enclosure::Enclosure;
use scraper;
use Track;
use Playlist;
use Album;
use model::PaginatedCollection;
use serde_json::Value;
use feed_rs;

static PROPS: [&'static str; 20]  = ["id",
                                     "url",
                                     "title",
                                     "description",
                                     "visual_url",
                                     "locale",

                                     "summary",
                                     "content",
                                     "author",
                                     "crawled",
                                     "published",
                                     "updated",
                                     "fingerprint",
                                     "origin_id",
                                     "alternate",
                                     "keywords",
                                     "enclosure",

                                     "feed_id",

                                     "created_at",
                                     "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Entry {
    pub id:          Uuid,
    pub url:         String,
    pub title:       Option<String>,
    pub description: Option<String>,
    pub visual_url:  Option<String>,
    pub locale:      Option<String>,
    pub summary:     Option<String>,
    pub content:     Option<String>,
    pub author:      Option<String>,
    pub crawled:     NaiveDateTime,
    pub published:   NaiveDateTime,
    pub updated:     Option<NaiveDateTime>,
    pub fingerprint: String,
    pub origin_id:   String,
    pub alternate:   Value,
    pub keywords:    Value,
    pub enclosure:   Value,
    pub feed_id:     Option<Uuid>,
    pub created_at:  NaiveDateTime,
    pub updated_at:  NaiveDateTime,
    pub tracks:      Vec<Track>,
    pub playlists:   Vec<Playlist>,
    pub albums:      Vec<Album>,
}

impl<'a> Model<'a> for Entry {
    fn table_name() -> String {
        "entries".to_string()
    }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Entry> {
        let mut entries = Vec::new();
        for row in rows.iter() {
            entries.push(Entry {
                id:          row.get(0),
                url:         row.get(1),
                title:       row.get(2),
                description: row.get(3),
                visual_url:  row.get(4),
                locale:      row.get(5),
                summary:     row.get(6),
                content:     row.get(7),
                author:      row.get(8),
                crawled:     row.get(9),
                published:   row.get(10),
                updated:     row.get(11),
                fingerprint: row.get(12),
                origin_id:   row.get(13),
                alternate:   row.get(14),
                keywords:    row.get(15),
                enclosure:   row.get(16),
                feed_id:     row.get(17),
                created_at:  row.get(18),
                updated_at:  row.get(19),
                tracks:      Track::find_by_entry_id(row.get(0)),
                playlists:   Playlist::find_by_entry_id(row.get(0)),
                albums:      Album::find_by_entry_id(row.get(0)),
            })
        }
        entries
    }
    fn create(&self) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&self.url]));
        let mut entry = self.clone();
        for row in rows.iter() {
            entry.id = row.get(0);
        }
        Ok(entry)
    }
    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = UTC::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE entries SET
                                   url         = $2,
                                   title       = $3,
                                   description = $4,
                                   visual_url  = $5,
                                   locale      = $6,
                                   summary     = $7,
                                   content     = $8,
                                   author      = $9,
                                   crawled     = $10,
                                   published   = $11,
                                   updated     = $12,
                                   fingerprint = $13,
                                   origin_id   = $14,
                                   alternate   = $15,
                                   keywords    = $16,
                                   enclosure   = $17,
                                   feed_id     = $18,
                                   created_at  = $19,
                                   updated_at  = $20
                                 WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.visual_url,
                                  &self.locale,
                                  &self.summary,
                                  &self.content,
                                  &self.author,
                                  &self.crawled,
                                  &self.published,
                                  &self.updated,
                                  &self.fingerprint,
                                  &self.origin_id,
                                  &self.alternate,
                                  &self.keywords,
                                  &self.enclosure,
                                  &self.feed_id,
                                  &self.created_at,
                                  &self.updated_at]);
        try!(result);
        Ok(())
    }
}

impl Entry {
    pub fn new(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&url]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     UTC::now().naive_utc(),
                published:   UTC::now().naive_utc(),
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn find_by_url(url: &str) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare(
            &format!("SELECT {} FROM entries
                        WHERE url = $1", Self::props_str(""))));
        let rows = try!(stmt.query(&[&url]));
        let entries = Entry::rows_to_items(rows);
        if entries.len() > 0 {
            return Ok(entries[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_or_create_by_url(url: String) -> Result<Entry, Error> {
        match Entry::find_by_url(&url) {
            Ok(entry) => Ok(entry),
            Err(Error::NotFound) => Entry::create_by_url(url),
            Err(e) => Err(e),
        }
    }

    pub fn find_by_feed_id(feed_id: Uuid, newer_than: Option<NaiveDateTime>, page: i64, per_page: i64) -> PaginatedCollection<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM entries
                        WHERE entries.feed_id = $1 AND entries.published >= $2
                        ORDER BY entries.published DESC
                        LIMIT $4 OFFSET $3",
                     Entry::props_str(""))).unwrap();
        let offset = page * per_page;
        let published = newer_than.unwrap_or(NaiveDateTime::from_timestamp(0, 0));
        let rows   = stmt.query(&[&feed_id, &published, &offset, &per_page]).unwrap();
        let items  = Self::rows_to_items(rows);
        let mut total: i64 = 0;
        let sql = "SELECT COUNT(*) FROM entries WHERE entries.feed_id = $1";
        for row in conn.query(&sql, &[&feed_id]).unwrap().iter() {
            total = row.get(0);
        }
        PaginatedCollection {
            page:     page,
            per_page: per_page,
            total:    total,
            items:    items,
        }
    }

    pub fn create_by_url(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&url]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     UTC::now().naive_utc(),
                published:   UTC::now().naive_utc(),
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn add_track(&mut self, track: Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_entries (track_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        self.tracks.push(track);
        Ok(())
    }

    pub fn add_playlist(&mut self, playlist: Playlist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO playlist_entries (playlist_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&playlist.id, &self.id]));
        self.playlists.push(playlist);
        Ok(())
    }

    pub fn add_album(&mut self, album: Album) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_entries (album_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&album.id, &self.id]));
        self.albums.push(album);
        Ok(())
    }
}
<|endoftext|>


<commit_before>
use Renderable;
use context::Context;
use token::Token;
use LiquidOptions;
use template::Template;
use parser;
use lexer;
use error::{Result, Error};

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

struct Include {
    partial: Template,
}

impl Renderable for Include {
    fn render(&self, mut context: &mut Context) -> Result<Option<String>> {
        self.partial.render(&mut context)
    }
}

fn parse_partial<P: AsRef<Path>>(path: P, options: &LiquidOptions) -> Result<Template> {
    let file_system = options.file_system.clone().unwrap_or_else(PathBuf::new);
    let path = file_system.join(path);

    // check if file exists
    if !path.exists() {
        return Err(Error::from(&*format!("{:?} does not exist", path)));
    }

    let mut file = try!(File::open(path));

    let mut content = String::new();
    try!(file.read_to_string(&mut content));

    let tokens = try!(lexer::tokenize(&content));
    parser::parse(&tokens, options).map(Template::new)
}

pub fn include_tag(_tag_name: &str,
                   arguments: &[Token],
                   options: &LiquidOptions)
                   -> Result<Box<Renderable>> {
    let mut args = arguments.iter();

    let path = match args.next() {
        Some(&Token::StringLiteral(ref path)) => path,
        arg => return Error::parser("String Literal", arg),
    };

    Ok(Box::new(Include { partial: try!(parse_partial(&path, &options)) }))
}

#[cfg(test)]
mod test {
    use context::Context;
    use Renderable;
    use parse;
    use error::Error;
    use LiquidOptions;
    use std::path::PathBuf;

    fn options() -> LiquidOptions {
        LiquidOptions {
            file_system: Some(PathBuf::from("tests/fixtures/input")),
            ..Default::default()
        }
    }

    #[test]
    fn include_tag() {
        let text = "{% include 'example.txt' %}";
        let template = parse(text, options()).unwrap();

        let mut context = Context::new();
        assert_eq!(template.render(&mut context).unwrap(),
                   Some("5 wot wot\n".to_owned()));
    }

    #[test]
    fn no_file() {
        let text = "{% include 'file_does_not_exist.liquid' %}";
        let output = parse(text, options());

        assert!(output.is_err());
        if let Err(Error::Other(val)) = output {
            assert_eq!(val,
                       "\"tests/fixtures/input/file_does_not_exist.liquid\" does not exist"
                           .to_owned());
        } else {
            assert!(false);
        }
    }
}
<commit_msg>Allow arguments to include tag to be non-quoted<commit_after>
use Renderable;
use context::Context;
use token::Token;
use LiquidOptions;
use template::Template;
use parser;
use lexer;
use error::{Result, Error};

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

struct Include {
    partial: Template,
}

impl Renderable for Include {
    fn render(&self, mut context: &mut Context) -> Result<Option<String>> {
        self.partial.render(&mut context)
    }
}

fn parse_partial<P: AsRef<Path>>(path: P, options: &LiquidOptions) -> Result<Template> {
    let file_system = options.file_system.clone().unwrap_or_else(PathBuf::new);
    let path = file_system.join(path);

    // check if file exists
    if !path.exists() {
        return Err(Error::from(&*format!("{:?} does not exist", path)));
    }

    let mut file = try!(File::open(path));

    let mut content = String::new();
    try!(file.read_to_string(&mut content));

    let tokens = try!(lexer::tokenize(&content));
    parser::parse(&tokens, options).map(Template::new)
}

pub fn include_tag(_tag_name: &str,
                   arguments: &[Token],
                   options: &LiquidOptions)
                   -> Result<Box<Renderable>> {
    let mut args = arguments.iter();

    let path = match args.next() {
        Some(&Token::StringLiteral(ref path)) => path,
        Some(&Token::Identifier(ref s)) => s,
        arg => return Error::parser("String Literal", arg),
    };

    Ok(Box::new(Include { partial: try!(parse_partial(&path, &options)) }))
}

#[cfg(test)]
mod test {
    use context::Context;
    use Renderable;
    use parse;
    use error::Error;
    use LiquidOptions;
    use std::path::PathBuf;

    fn options() -> LiquidOptions {
        LiquidOptions {
            file_system: Some(PathBuf::from("tests/fixtures/input")),
            ..Default::default()
        }
    }

    #[test]
    fn include_tag() {
        let text = "{% include 'example.txt' %}";
        let template = parse(text, options()).unwrap();

        let mut context = Context::new();
        assert_eq!(template.render(&mut context).unwrap(),
                   Some("5 wot wot\n".to_owned()));
    }

    #[test]
    fn include_non_string() {
        let text = "{% include example.txt %}";
        let template = parse(text, options()).unwrap();

        let mut context = Context::new();
        assert_eq!(template.render(&mut context).unwrap(),
                   Some("5 wot wot\n".to_owned()));
    }

    #[test]
    fn no_file() {
        let text = "{% include 'file_does_not_exist.liquid' %}";
        let output = parse(text, options());

        assert!(output.is_err());
        if let Err(Error::Other(val)) = output {
            assert_eq!(val,
                       "\"tests/fixtures/input/file_does_not_exist.liquid\" does not exist"
                           .to_owned());
        } else {
            assert!(false);
        }
    }
}
<|endoftext|>


<commit_before>use std::{fs, io, time};
use slog::Logger;
use std::io::Read;
use std::sync::atomic;
use {rpc, tracker, disk, listener, CONFIG, SHUTDOWN};
use util::{io_err, io_err_val};
use torrent::{self, peer, Torrent};
use std::collections::HashMap;
use throttle::Throttler;

pub mod cio;
pub mod acio;
mod job;

/// Tracker update job interval
const TRK_JOB_SECS: u64 = 60;
/// Unchoke rotation job interval
const UNCHK_JOB_SECS: u64 = 30;
/// Session serialization job interval
const SES_JOB_SECS: u64 = 10;
/// Interval to requery all jobs and execute if needed
const JOB_INT_MS: usize = 1000;

pub struct Control<T: cio::CIO> {
    throttler: Throttler,
    cio: T,
    tid_cnt: usize,
    job_timer: usize,
    jobs: job::JobManager<T>,
    torrents: HashMap<usize, Torrent<T>>,
    peers: HashMap<usize, usize>,
    hash_idx: HashMap<[u8; 20], usize>,
    l: Logger,
}

impl<T: cio::CIO> Control<T> {
    pub fn new(mut cio: T,
               throttler: Throttler,
               l: Logger) -> io::Result<Control<T>> {
        let torrents = HashMap::new();
        let peers = HashMap::new();
        let hash_idx = HashMap::new();
        // Every minute check to update trackers;
        let mut jobs = job::JobManager::new();
        jobs.add_job(job::TrackerUpdate, time::Duration::from_secs(TRK_JOB_SECS));
        jobs.add_job(job::UnchokeUpdate, time::Duration::from_secs(UNCHK_JOB_SECS));
        jobs.add_job(job::SessionUpdate, time::Duration::from_secs(SES_JOB_SECS));
        let job_timer = cio.set_timer(JOB_INT_MS).map_err(|_| io_err_val("timer failure!"))?;
        // 5 MiB max bucket
        Ok(Control {
            throttler,
            cio,
            tid_cnt: 0,
            job_timer,
            jobs,
            torrents,
            peers,
            hash_idx,
            l
        })
    }

    pub fn run(&mut self) {
        if self.deserialize().is_err() {
            warn!(self.l, "Session deserialization failed!");
        }
        debug!(self.l, "Initialized!");
        let mut events = Vec::with_capacity(20);
        loop {
            self.cio.poll(&mut events);
            for event in events.drain(..) {
                if self.handle_event(event) {
                    self.serialize();
                    return;
                }
            }
            if SHUTDOWN.load(atomic::Ordering::SeqCst) == true {
                break;
            }
        }
    }

    fn serialize(&mut self) {
        debug!(self.l, "Serializing torrents!");
        for (_, torrent) in self.torrents.iter_mut() {
            torrent.serialize();
        }
    }

    fn deserialize(&mut self) -> io::Result<()> {
        debug!(self.l, "Deserializing torrents!");
        let sd = &CONFIG.session;
        for entry in fs::read_dir(sd)? {
            if let Err(e) = self.deserialize_torrent(entry) {
                warn!(self.l, "Failed to deserialize torrent file: {:?}!", e);
            }
        }
        Ok(())
    }

    fn deserialize_torrent(&mut self, entry: io::Result<fs::DirEntry>) -> io::Result<()> {
        let dir = entry?;
        // TODO: We probably should improve this heuristic with and not rely
        // on directory entries, but this is good enough for now.
        if dir.file_name().len() != 40 {
            return Ok(())
        }
        trace!(self.l, "Attempting to deserialize file {:?}", dir);
        let mut f = fs::File::open(dir.path())?;
        let mut data = Vec::new();
        f.read_to_end(&mut data)?;
        trace!(self.l, "Succesfully read file");

        let tid = self.tid_cnt;
        let throttle = self.throttler.get_throttle(tid);
        let log = self.l.new(o!("torrent" => tid));
        if let Ok(t) = Torrent::deserialize(tid, &data, throttle, self.cio.new_handle(), log) {
            trace!(self.l, "Succesfully parsed torrent file {:?}", dir.path());
            self.hash_idx.insert(t.info().hash, tid);
            self.tid_cnt += 1;
            self.torrents.insert(tid, t);
        } else {
            return io_err("Torrent data invalid!");
        }
        Ok(())
    }

    fn handle_event(&mut self, event: cio::Event) -> bool {
        match event {
            cio::Event::Tracker(Ok(e)) => {
                self.handle_trk_ev(e);
            }
            cio::Event::Tracker(Err(e)) => {
                error!(self.l, "tracker error: {:?}", e.backtrace());
            }
            cio::Event::Disk(Ok(e)) => {
                self.handle_disk_ev(e);
            }
            cio::Event::Disk(Err(e)) => {
                error!(self.l, "disk error: {:?}", e.backtrace());
            }
            cio::Event::RPC(Ok(e)) => {
                return self.handle_rpc_ev(e);
            }
            cio::Event::RPC(Err(e)) => {
                error!(self.l, "rpc error: {:?}", e.backtrace());
            }
            cio::Event::Listener(Ok(e)) => {
                self.handle_lst_ev(e);
            }
            cio::Event::Listener(Err(e)) => {
                error!(self.l, "listener error: {:?}", e.backtrace());
            }
            cio::Event::Timer(t) => {
                if t == self.throttler.id() {
                    self.throttler.update();
                } else if t == self.throttler.fid() {
                    self.flush_blocked_peers();
                } else if t == self.job_timer {
                    self.update_jobs();
                } else {
                    error!(self.l, "unknown timer id {} reported", t);
                }
            }
            cio::Event::Peer { peer, event } => {
                self.handle_peer_ev(peer, event);
            }
        }
        false
    }

    fn handle_trk_ev(&mut self, tr: tracker::Response) {
        debug!(self.l, "Handling tracker response");
        let id = tr.0;
        let resp = tr.1;
        {
            if let Some(torrent) = self.torrents.get_mut(&id) {
                torrent.set_tracker_response(&resp);
            } else {
                return;
            }
        }
        trace!(self.l, "Adding peers!");
        if let Ok(r) = resp {
            for ip in r.peers.iter() {
                trace!(self.l, "Adding peer({:?})!", ip);
                if let Ok(peer) = peer::PeerConn::new_outgoing(ip) {
                    trace!(self.l, "Added peer({:?})!", ip);
                    self.add_peer(id, peer);
                }
            }
        }
    }

    fn update_jobs(&mut self) {
        debug!(self.l, "Handling job timer");
        self.jobs.update(&mut self.torrents);
    }

    fn handle_disk_ev(&mut self, resp: disk::Response) {
        trace!(self.l, "Got disk response {:?}!", resp);
        if let Some(torrent) = self.torrents.get_mut(&resp.tid()) {
            torrent.handle_disk_resp(resp);
        }
    }

    fn handle_lst_ev(&mut self, msg: listener::Message) {
        debug!(self.l, "Adding peer for torrent with hash {:?}!", msg.hash);
        if let Some(tid) = self.hash_idx.get(&msg.hash).cloned() {
            self.add_peer(tid, msg.peer);
        } else {
            warn!(self.l, "Couldn't add peer, torrent with hash {:?} doesn't exist", msg.hash);
        }
    }

    fn handle_peer_ev(&mut self, peer: cio::PID, ev: cio::Result<torrent::Message>) {
        let ref mut p = self.peers;
        let ref mut t = self.torrents;

        p.get(&peer).cloned()
            .and_then(|id| t.get_mut(&id))
            .map(|torrent| {
                if torrent.peer_ev(peer, ev).is_err() {
                    p.remove(&peer);
                }
            });
    }

    fn flush_blocked_peers(&mut self) {
        trace!(self.l, "Flushing blocked peers!");
        self.cio.flush_peers(self.throttler.flush_dl());
        self.cio.flush_peers(self.throttler.flush_ul());
    }

    fn add_torrent(&mut self, info: torrent::Info) {
        debug!(self.l, "Adding {:?}!", info);
        if self.hash_idx.contains_key(&info.hash) {
            warn!(self.l, "Torrent already exists!");
            return;
        }
        let tid = self.tid_cnt;
        let throttle = self.throttler.get_throttle(tid);
        let log = self.l.new(o!("torrent" => tid));
        let t = Torrent::new(tid, info, throttle, self.cio.new_handle(), log);
        self.hash_idx.insert(t.info().hash, tid);
        self.tid_cnt += 1;
        self.torrents.insert(tid, t);
    }

    fn send_rpc_msg(&mut self, resp: rpc::Response) {
        self.cio.msg_rpc(rpc::CMessage::Response(resp));
    }

    fn handle_rpc_ev(&mut self, req: rpc::Request) -> bool {
        debug!(self.l, "Handling rpc reqest!");
        match req {
            rpc::Request::ListTorrents => {
                let mut resp = Vec::new();
                for (id, _) in self.torrents.iter() {
                    resp.push(*id);
                }
                self.send_rpc_msg(rpc::Response::Torrents(resp));
            }
            rpc::Request::TorrentInfo(i) => {
                let resp = if let Some(torrent) = self.torrents.get(&i) {
                    rpc::Response::TorrentInfo(torrent.rpc_info())
                } else {
                    rpc::Response::Err("Torrent ID not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::AddTorrent(data) => {
                let resp = match torrent::Info::from_bencode(data) {
                    Ok(i) => {
                        self.add_torrent(i);
                        rpc::Response::Ack
                    }
                    Err(e) => {
                        rpc::Response::Err(e.to_owned())
                    }
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::PauseTorrent(id) => {
                let resp = if let Some(t) = self.torrents.get_mut(&id) {
                    t.pause();
                    rpc::Response::Ack
                } else {
                    rpc::Response::Err("Torrent not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::ResumeTorrent(id) => {
                let resp = if let Some(t) = self.torrents.get_mut(&id) {
                    t.resume();
                    rpc::Response::Ack
                } else {
                    rpc::Response::Err("Torrent not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::RemoveTorrent(id) => {
                let resp = if let Some(mut t) = self.torrents.remove(&id) {
                    self.hash_idx.remove(&t.info().hash);
                    t.delete();
                    rpc::Response::Ack
                } else {
                    rpc::Response::Err("Torrent not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::ThrottleUpload(amnt) => {
                self.throttler.set_ul_rate(amnt);
                self.send_rpc_msg(rpc::Response::Ack);
            }
            rpc::Request::ThrottleDownload(amnt) => {
                self.throttler.set_dl_rate(amnt);
                self.send_rpc_msg(rpc::Response::Ack);
            }
            rpc::Request::Shutdown => { return true; }
        }
        false
    }

    fn add_peer(&mut self, id: usize, peer: peer::PeerConn) {
        trace!(self.l, "Adding peer to torrent {:?}!", id);
        if let Some(torrent) = self.torrents.get_mut(&id) {
            if let Some(pid) = torrent.add_peer(peer) {
                self.peers.insert(pid, id);
            }
        }
    }
}

impl<T: cio::CIO> Drop for Control<T> {
    fn drop(&mut self) {
        debug!(self.l, "Triggering thread shutdown sequence!");
        self.cio.msg_disk(disk::Request::shutdown());
        self.cio.msg_rpc(rpc::CMessage::Shutdown);
        self.cio.msg_trk(tracker::Request::Shutdown);
        self.cio.msg_listener(listener::Request::Shutdown);
    }
}
<commit_msg>Move job timer logging to trace level<commit_after>use std::{fs, io, time};
use slog::Logger;
use std::io::Read;
use std::sync::atomic;
use {rpc, tracker, disk, listener, CONFIG, SHUTDOWN};
use util::{io_err, io_err_val};
use torrent::{self, peer, Torrent};
use std::collections::HashMap;
use throttle::Throttler;

pub mod cio;
pub mod acio;
mod job;

/// Tracker update job interval
const TRK_JOB_SECS: u64 = 60;
/// Unchoke rotation job interval
const UNCHK_JOB_SECS: u64 = 30;
/// Session serialization job interval
const SES_JOB_SECS: u64 = 10;
/// Interval to requery all jobs and execute if needed
const JOB_INT_MS: usize = 1000;

pub struct Control<T: cio::CIO> {
    throttler: Throttler,
    cio: T,
    tid_cnt: usize,
    job_timer: usize,
    jobs: job::JobManager<T>,
    torrents: HashMap<usize, Torrent<T>>,
    peers: HashMap<usize, usize>,
    hash_idx: HashMap<[u8; 20], usize>,
    l: Logger,
}

impl<T: cio::CIO> Control<T> {
    pub fn new(mut cio: T,
               throttler: Throttler,
               l: Logger) -> io::Result<Control<T>> {
        let torrents = HashMap::new();
        let peers = HashMap::new();
        let hash_idx = HashMap::new();
        // Every minute check to update trackers;
        let mut jobs = job::JobManager::new();
        jobs.add_job(job::TrackerUpdate, time::Duration::from_secs(TRK_JOB_SECS));
        jobs.add_job(job::UnchokeUpdate, time::Duration::from_secs(UNCHK_JOB_SECS));
        jobs.add_job(job::SessionUpdate, time::Duration::from_secs(SES_JOB_SECS));
        let job_timer = cio.set_timer(JOB_INT_MS).map_err(|_| io_err_val("timer failure!"))?;
        // 5 MiB max bucket
        Ok(Control {
            throttler,
            cio,
            tid_cnt: 0,
            job_timer,
            jobs,
            torrents,
            peers,
            hash_idx,
            l
        })
    }

    pub fn run(&mut self) {
        if self.deserialize().is_err() {
            warn!(self.l, "Session deserialization failed!");
        }
        debug!(self.l, "Initialized!");
        let mut events = Vec::with_capacity(20);
        loop {
            self.cio.poll(&mut events);
            for event in events.drain(..) {
                if self.handle_event(event) {
                    self.serialize();
                    return;
                }
            }
            if SHUTDOWN.load(atomic::Ordering::SeqCst) == true {
                break;
            }
        }
    }

    fn serialize(&mut self) {
        debug!(self.l, "Serializing torrents!");
        for (_, torrent) in self.torrents.iter_mut() {
            torrent.serialize();
        }
    }

    fn deserialize(&mut self) -> io::Result<()> {
        debug!(self.l, "Deserializing torrents!");
        let sd = &CONFIG.session;
        for entry in fs::read_dir(sd)? {
            if let Err(e) = self.deserialize_torrent(entry) {
                warn!(self.l, "Failed to deserialize torrent file: {:?}!", e);
            }
        }
        Ok(())
    }

    fn deserialize_torrent(&mut self, entry: io::Result<fs::DirEntry>) -> io::Result<()> {
        let dir = entry?;
        // TODO: We probably should improve this heuristic with and not rely
        // on directory entries, but this is good enough for now.
        if dir.file_name().len() != 40 {
            return Ok(())
        }
        trace!(self.l, "Attempting to deserialize file {:?}", dir);
        let mut f = fs::File::open(dir.path())?;
        let mut data = Vec::new();
        f.read_to_end(&mut data)?;
        trace!(self.l, "Succesfully read file");

        let tid = self.tid_cnt;
        let throttle = self.throttler.get_throttle(tid);
        let log = self.l.new(o!("torrent" => tid));
        if let Ok(t) = Torrent::deserialize(tid, &data, throttle, self.cio.new_handle(), log) {
            trace!(self.l, "Succesfully parsed torrent file {:?}", dir.path());
            self.hash_idx.insert(t.info().hash, tid);
            self.tid_cnt += 1;
            self.torrents.insert(tid, t);
        } else {
            return io_err("Torrent data invalid!");
        }
        Ok(())
    }

    fn handle_event(&mut self, event: cio::Event) -> bool {
        match event {
            cio::Event::Tracker(Ok(e)) => {
                self.handle_trk_ev(e);
            }
            cio::Event::Tracker(Err(e)) => {
                error!(self.l, "tracker error: {:?}", e.backtrace());
            }
            cio::Event::Disk(Ok(e)) => {
                self.handle_disk_ev(e);
            }
            cio::Event::Disk(Err(e)) => {
                error!(self.l, "disk error: {:?}", e.backtrace());
            }
            cio::Event::RPC(Ok(e)) => {
                return self.handle_rpc_ev(e);
            }
            cio::Event::RPC(Err(e)) => {
                error!(self.l, "rpc error: {:?}", e.backtrace());
            }
            cio::Event::Listener(Ok(e)) => {
                self.handle_lst_ev(e);
            }
            cio::Event::Listener(Err(e)) => {
                error!(self.l, "listener error: {:?}", e.backtrace());
            }
            cio::Event::Timer(t) => {
                if t == self.throttler.id() {
                    self.throttler.update();
                } else if t == self.throttler.fid() {
                    self.flush_blocked_peers();
                } else if t == self.job_timer {
                    self.update_jobs();
                } else {
                    error!(self.l, "unknown timer id {} reported", t);
                }
            }
            cio::Event::Peer { peer, event } => {
                self.handle_peer_ev(peer, event);
            }
        }
        false
    }

    fn handle_trk_ev(&mut self, tr: tracker::Response) {
        debug!(self.l, "Handling tracker response");
        let id = tr.0;
        let resp = tr.1;
        {
            if let Some(torrent) = self.torrents.get_mut(&id) {
                torrent.set_tracker_response(&resp);
            } else {
                return;
            }
        }
        trace!(self.l, "Adding peers!");
        if let Ok(r) = resp {
            for ip in r.peers.iter() {
                trace!(self.l, "Adding peer({:?})!", ip);
                if let Ok(peer) = peer::PeerConn::new_outgoing(ip) {
                    trace!(self.l, "Added peer({:?})!", ip);
                    self.add_peer(id, peer);
                }
            }
        }
    }

    fn update_jobs(&mut self) {
        trace!(self.l, "Handling job timer");
        self.jobs.update(&mut self.torrents);
    }

    fn handle_disk_ev(&mut self, resp: disk::Response) {
        trace!(self.l, "Got disk response {:?}!", resp);
        if let Some(torrent) = self.torrents.get_mut(&resp.tid()) {
            torrent.handle_disk_resp(resp);
        }
    }

    fn handle_lst_ev(&mut self, msg: listener::Message) {
        debug!(self.l, "Adding peer for torrent with hash {:?}!", msg.hash);
        if let Some(tid) = self.hash_idx.get(&msg.hash).cloned() {
            self.add_peer(tid, msg.peer);
        } else {
            warn!(self.l, "Couldn't add peer, torrent with hash {:?} doesn't exist", msg.hash);
        }
    }

    fn handle_peer_ev(&mut self, peer: cio::PID, ev: cio::Result<torrent::Message>) {
        let ref mut p = self.peers;
        let ref mut t = self.torrents;

        p.get(&peer).cloned()
            .and_then(|id| t.get_mut(&id))
            .map(|torrent| {
                if torrent.peer_ev(peer, ev).is_err() {
                    p.remove(&peer);
                }
            });
    }

    fn flush_blocked_peers(&mut self) {
        trace!(self.l, "Flushing blocked peers!");
        self.cio.flush_peers(self.throttler.flush_dl());
        self.cio.flush_peers(self.throttler.flush_ul());
    }

    fn add_torrent(&mut self, info: torrent::Info) {
        debug!(self.l, "Adding {:?}!", info);
        if self.hash_idx.contains_key(&info.hash) {
            warn!(self.l, "Torrent already exists!");
            return;
        }
        let tid = self.tid_cnt;
        let throttle = self.throttler.get_throttle(tid);
        let log = self.l.new(o!("torrent" => tid));
        let t = Torrent::new(tid, info, throttle, self.cio.new_handle(), log);
        self.hash_idx.insert(t.info().hash, tid);
        self.tid_cnt += 1;
        self.torrents.insert(tid, t);
    }

    fn send_rpc_msg(&mut self, resp: rpc::Response) {
        self.cio.msg_rpc(rpc::CMessage::Response(resp));
    }

    fn handle_rpc_ev(&mut self, req: rpc::Request) -> bool {
        debug!(self.l, "Handling rpc reqest!");
        match req {
            rpc::Request::ListTorrents => {
                let mut resp = Vec::new();
                for (id, _) in self.torrents.iter() {
                    resp.push(*id);
                }
                self.send_rpc_msg(rpc::Response::Torrents(resp));
            }
            rpc::Request::TorrentInfo(i) => {
                let resp = if let Some(torrent) = self.torrents.get(&i) {
                    rpc::Response::TorrentInfo(torrent.rpc_info())
                } else {
                    rpc::Response::Err("Torrent ID not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::AddTorrent(data) => {
                let resp = match torrent::Info::from_bencode(data) {
                    Ok(i) => {
                        self.add_torrent(i);
                        rpc::Response::Ack
                    }
                    Err(e) => {
                        rpc::Response::Err(e.to_owned())
                    }
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::PauseTorrent(id) => {
                let resp = if let Some(t) = self.torrents.get_mut(&id) {
                    t.pause();
                    rpc::Response::Ack
                } else {
                    rpc::Response::Err("Torrent not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::ResumeTorrent(id) => {
                let resp = if let Some(t) = self.torrents.get_mut(&id) {
                    t.resume();
                    rpc::Response::Ack
                } else {
                    rpc::Response::Err("Torrent not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::RemoveTorrent(id) => {
                let resp = if let Some(mut t) = self.torrents.remove(&id) {
                    self.hash_idx.remove(&t.info().hash);
                    t.delete();
                    rpc::Response::Ack
                } else {
                    rpc::Response::Err("Torrent not found!".to_owned())
                };
                self.send_rpc_msg(resp);
            }
            rpc::Request::ThrottleUpload(amnt) => {
                self.throttler.set_ul_rate(amnt);
                self.send_rpc_msg(rpc::Response::Ack);
            }
            rpc::Request::ThrottleDownload(amnt) => {
                self.throttler.set_dl_rate(amnt);
                self.send_rpc_msg(rpc::Response::Ack);
            }
            rpc::Request::Shutdown => { return true; }
        }
        false
    }

    fn add_peer(&mut self, id: usize, peer: peer::PeerConn) {
        trace!(self.l, "Adding peer to torrent {:?}!", id);
        if let Some(torrent) = self.torrents.get_mut(&id) {
            if let Some(pid) = torrent.add_peer(peer) {
                self.peers.insert(pid, id);
            }
        }
    }
}

impl<T: cio::CIO> Drop for Control<T> {
    fn drop(&mut self) {
        debug!(self.l, "Triggering thread shutdown sequence!");
        self.cio.msg_disk(disk::Request::shutdown());
        self.cio.msg_rpc(rpc::CMessage::Shutdown);
        self.cio.msg_trk(tracker::Request::Shutdown);
        self.cio.msg_listener(listener::Request::Shutdown);
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use common::types::{Size2, ZInt, PlayerId, MapPos};
use game_state::{GameState};
use map::{distance};
use pathfinder::{MapPath, Pathfinder};
use dir::{Dir};
use command::{Command};
use unit::{Unit};
use db::{Db};
use core::{CoreEvent, los};

pub struct Ai {
    id: PlayerId,
    state: GameState,
    pathfinder: Pathfinder,
}

impl Ai {
    pub fn new(id: &PlayerId, map_size: &Size2<ZInt>) -> Ai {
        Ai {
            id: id.clone(),
            state: GameState::new(map_size, id),
            pathfinder: Pathfinder::new(map_size),
        }
    }

    pub fn apply_event(&mut self, db: &Db, event: &CoreEvent) {
        self.state.apply_event(db, event);
    }

    // TODO: move fill_map here
    fn get_best_pos(&self) -> Option<MapPos> {
        let mut best_pos = None;
        let mut best_cost = None;
        for (_, enemy) in self.state.units().iter() {
            if enemy.player_id == self.id {
                continue;
            }
            for i in 0 .. 6 {
                let dir = Dir::from_int(i);
                let destination = Dir::get_neighbour_pos(&enemy.pos, &dir);
                if !self.state.map().is_inboard(&destination) {
                    continue;
                }
                if self.state.is_tile_occupied(&destination) {
                    continue;
                }
                let path = match self.pathfinder.get_path(&destination) {
                    Some(path) => path,
                    None => continue,
                };
                let cost = path.total_cost().n;
                if let Some(ref mut best_cost) = best_cost {
                    if *best_cost > cost {
                        *best_cost = cost;
                        best_pos = Some(destination.clone());
                    }
                } else {
                    best_cost = Some(cost);
                    best_pos = Some(destination.clone());
                }
            }
        }
        best_pos
    }

    fn truncate_path(&self, path: MapPath, move_points: ZInt) -> MapPath {
        if path.total_cost().n <= move_points {
            return path;
        }
        let len = path.nodes().len();
        for i in 1 .. len {
            let cost = &path.nodes()[i].cost;
            if cost.n > move_points {
                let mut new_nodes = path.nodes().clone();
                new_nodes.truncate(i);
                return MapPath::new(new_nodes);
            }
        }
        assert!(false); // TODO
        return path;
    }

    fn is_close_to_enemies(&self, db: &Db, unit: &Unit) -> bool {
        for (_, target) in self.state.units().iter() {
            if target.player_id == self.id {
                continue;
            }
            let max_distance = db.unit_max_attack_dist(unit);
            if distance(&unit.pos, &target.pos) <= max_distance {
                return true;
            }
        }
        false
    }

    pub fn get_command(&mut self, db: &Db) -> Command {
        // TODO: extract funcs
        {
            for (_, unit) in self.state.units().iter() {
                if unit.player_id != self.id {
                    continue;
                }
                // println!("id: {}, ap: {}", unit.id.id, unit.attack_points);
                if unit.attack_points <= 0 {
                    continue;
                }
                let unit_type = db.unit_type(&unit.type_id);
                for (_, target) in self.state.units().iter() {
                    if target.player_id == self.id {
                        continue;
                    }
                    let max_distance = db.unit_max_attack_dist(unit);
                    if distance(&unit.pos, &target.pos) > max_distance {
                        continue;
                    }
                    if !los(self.state.map(), unit_type, &unit.pos, &target.pos) {
                        continue;
                    }
                    return Command::AttackUnit {
                        attacker_id: unit.id.clone(),
                        defender_id: target.id.clone(),
                    };
                }
            }
        }
        {
            for (_, unit) in self.state.units().iter() {
                if unit.player_id != self.id {
                    continue;
                }
                if self.is_close_to_enemies(db, unit) {
                    continue;
                }
                self.pathfinder.fill_map(db, &self.state, unit);
                let destination = match self.get_best_pos() {
                    Some(destination) => destination,
                    None => continue,
                };
                let path = match self.pathfinder.get_path(&destination) {
                    Some(path) => path,
                    None => continue,
                };
                if unit.move_points == 0 {
                    continue;
                }
                let path = self.truncate_path(path, unit.move_points);
                return Command::Move{unit_id: unit.id.clone(), path: path};
            }
        }
        return Command::EndTurn;
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>core/ai.rs: Extructed 'try_get_{attak,move}_command' funcs<commit_after>// See LICENSE file for copyright and license details.

use common::types::{Size2, ZInt, PlayerId, MapPos};
use game_state::{GameState};
use map::{distance};
use pathfinder::{MapPath, Pathfinder};
use dir::{Dir};
use command::{Command};
use unit::{Unit};
use db::{Db};
use core::{CoreEvent, los};

pub struct Ai {
    id: PlayerId,
    state: GameState,
    pathfinder: Pathfinder,
}

impl Ai {
    pub fn new(id: &PlayerId, map_size: &Size2<ZInt>) -> Ai {
        Ai {
            id: id.clone(),
            state: GameState::new(map_size, id),
            pathfinder: Pathfinder::new(map_size),
        }
    }

    pub fn apply_event(&mut self, db: &Db, event: &CoreEvent) {
        self.state.apply_event(db, event);
    }

    // TODO: move fill_map here
    fn get_best_pos(&self) -> Option<MapPos> {
        let mut best_pos = None;
        let mut best_cost = None;
        for (_, enemy) in self.state.units().iter() {
            if enemy.player_id == self.id {
                continue;
            }
            for i in 0 .. 6 {
                let dir = Dir::from_int(i);
                let destination = Dir::get_neighbour_pos(&enemy.pos, &dir);
                if !self.state.map().is_inboard(&destination) {
                    continue;
                }
                if self.state.is_tile_occupied(&destination) {
                    continue;
                }
                let path = match self.pathfinder.get_path(&destination) {
                    Some(path) => path,
                    None => continue,
                };
                let cost = path.total_cost().n;
                if let Some(ref mut best_cost) = best_cost {
                    if *best_cost > cost {
                        *best_cost = cost;
                        best_pos = Some(destination.clone());
                    }
                } else {
                    best_cost = Some(cost);
                    best_pos = Some(destination.clone());
                }
            }
        }
        best_pos
    }

    fn truncate_path(&self, path: MapPath, move_points: ZInt) -> MapPath {
        if path.total_cost().n <= move_points {
            return path;
        }
        let len = path.nodes().len();
        for i in 1 .. len {
            let cost = &path.nodes()[i].cost;
            if cost.n > move_points {
                let mut new_nodes = path.nodes().clone();
                new_nodes.truncate(i);
                return MapPath::new(new_nodes);
            }
        }
        assert!(false); // TODO
        return path;
    }

    fn is_close_to_enemies(&self, db: &Db, unit: &Unit) -> bool {
        for (_, target) in self.state.units().iter() {
            if target.player_id == self.id {
                continue;
            }
            let max_distance = db.unit_max_attack_dist(unit);
            if distance(&unit.pos, &target.pos) <= max_distance {
                return true;
            }
        }
        false
    }

    pub fn try_get_move_command(&mut self, db: &Db) -> Option<Command> {
        for (_, unit) in self.state.units().iter() {
            if unit.player_id != self.id {
                continue;
            }
            // println!("id: {}, ap: {}", unit.id.id, unit.attack_points);
            if unit.attack_points <= 0 {
                continue;
            }
            let unit_type = db.unit_type(&unit.type_id);
            for (_, target) in self.state.units().iter() {
                if target.player_id == self.id {
                    continue;
                }
                let max_distance = db.unit_max_attack_dist(unit);
                if distance(&unit.pos, &target.pos) > max_distance {
                    continue;
                }
                if !los(self.state.map(), unit_type, &unit.pos, &target.pos) {
                    continue;
                }
                return Some(Command::AttackUnit {
                    attacker_id: unit.id.clone(),
                    defender_id: target.id.clone(),
                });
            }
        }
        None
    }

    pub fn try_get_attack_command(&mut self, db: &Db) -> Option<Command> {
        for (_, unit) in self.state.units().iter() {
            if unit.player_id != self.id {
                continue;
            }
            if self.is_close_to_enemies(db, unit) {
                continue;
            }
            self.pathfinder.fill_map(db, &self.state, unit);
            let destination = match self.get_best_pos() {
                Some(destination) => destination,
                None => continue,
            };
            let path = match self.pathfinder.get_path(&destination) {
                Some(path) => path,
                None => continue,
            };
            if unit.move_points == 0 {
                continue;
            }
            let path = self.truncate_path(path, unit.move_points);
            return Some(Command::Move{unit_id: unit.id.clone(), path: path});
        }
        None
    }

    pub fn get_command(&mut self, db: &Db) -> Command {
        if let Some(cmd) = self.try_get_move_command(db) {
            cmd
        } else if let Some(cmd) = self.try_get_attack_command(db) {
            cmd
        } else {
            Command::EndTurn
        }
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>use cudnn::ffi;
use cudnn::{Tensor, Filter4d, Convolution2d, Pooling};
use cudart::Memory;
use std::ptr;

pub struct Cudnn {
    handle: ffi::Handle
}

impl Drop for Cudnn {
    fn drop(&mut self) {
        unsafe { ffi::cudnnDestroy(self.handle) };
    }
}

impl Cudnn {
    pub fn new() -> Result<Cudnn, &'static str> {
        let mut handle: ffi::Handle = ptr::null_mut();
        match unsafe { ffi::cudnnCreate(&mut handle) } {
            ffi::Status::Success => Ok(Cudnn { handle : handle }),
            e => Err(e.to_str())
        }
    }

    pub fn sigmoid_forward(&self,
                           src_desc: Tensor,
                           src: &Memory<f32>,
                           dst_desc: Tensor,
                           dst: &mut Memory<f32>)
                           -> Result<(), &'static str> {
        match unsafe { ffi::cudnnActivationForward(self.handle,
                                                   ffi::ActivationDescriptor::Sigmoid,
                                                   *&[1.0f32].as_ptr() as *const ::libc::c_void,
                                                   src_desc.desc,
                                                   src.data,
                                                   *&[0.0f32].as_ptr() as *const ::libc::c_void,
                                                   dst_desc.desc,
                                                   dst.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn relu_forward_inplace(&self,
                                src_desc: &Tensor,
                                src: &mut Memory<f32>)
                                -> Result<(), &'static str> {
        match unsafe { ffi::cudnnActivationForward(self.handle,
                                                   ffi::ActivationDescriptor::ReLU,
                                                   *&[1.0f32].as_ptr() as *const ::libc::c_void,
                                                   src_desc.desc,
                                                   src.data,
                                                   *&[0.0f32].as_ptr() as *const ::libc::c_void,
                                                   src_desc.desc,
                                                   src.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn get_conv_forward_algo(&self,
                                 x_desc: &Tensor,
                                 w_desc: &Filter4d,
                                 conv_desc: &Convolution2d,
                                 y_desc: &Tensor)
                                 -> Result<ffi::ConvolutionFwdAlgo, &'static str> {
        let mut res = ffi::ConvolutionFwdAlgo::ImplicitGemm;
        match unsafe { ffi::cudnnGetConvolutionForwardAlgorithm(self.handle,
                                                                x_desc.desc,
                                                                w_desc.desc,
                                                                conv_desc.desc,
                                                                y_desc.desc,
                                                                ffi::ConvolutionFwdPreference::NoWorkspace,
                                                                0,
                                                                &mut res) } {
            ffi::Status::Success => Ok(res),
            e => Err(e.to_str())
        }
    }

    pub fn get_conv_forward_workspace_size(&self,
                                           x_desc: &Tensor,
                                           w_desc: &Filter4d,
                                           conv_desc: &Convolution2d,
                                           y_desc: &Tensor,
                                           algo: ffi::ConvolutionFwdAlgo)
                                           -> Result<usize, &'static str> {

        let mut res = 0usize;
        match unsafe { ffi::cudnnGetConvolutionForwardWorkspaceSize(self.handle,
                                                                    x_desc.desc,
                                                                    w_desc.desc,
                                                                    conv_desc.desc,
                                                                    y_desc.desc,
                                                                    algo,
                                                                    &mut res) } {
            ffi::Status::Success => Ok(res),
            e => Err(e.to_str())
        }
    }

    pub fn conv_forward(&self,
                        x_desc: &Tensor,
                        x: &Memory<f32>,
                        w_desc: &Filter4d,
                        w: &Memory<f32>,
                        conv_desc: &Convolution2d,
                        y_desc: &Tensor,
                        y: &Memory<f32>)
                        -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        let algo = try!(self.get_conv_forward_algo(&x_desc,
                                                   &w_desc,
                                                   &conv_desc,
                                                   &y_desc));
        let workspace_size = try!(self.get_conv_forward_workspace_size(&x_desc,
                                                                       &w_desc,
                                                                       &conv_desc,
                                                                       &y_desc,
                                                                       algo));
        let workspace = try!(Memory::<f32>::new(workspace_size / 4));
        match unsafe { ffi::cudnnConvolutionForward(self.handle,
                                                    &alpha as *const _ as *const ::libc::c_void,
                                                    x_desc.desc,
                                                    x.data,
                                                    w_desc.desc,
                                                    w.data,
                                                    conv_desc.desc,
                                                    algo,
                                                    workspace.data,
                                                    workspace_size,
                                                    &beta as *const _ as *const ::libc::c_void,
                                                    y_desc.desc,
                                                    y.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn conv_forward_src(&self,
                            x_desc: &Tensor,
                            x: &Memory<f32>,
                            w_desc: &Filter4d,
                            w: &Memory<f32>,
                            conv_desc: &Convolution2d,
                            y_desc: &Tensor)
                            -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        let algo = try!(self.get_conv_forward_algo(&x_desc,
                                                   &w_desc,
                                                   &conv_desc,
                                                   &y_desc));
        let workspace_size = try!(self.get_conv_forward_workspace_size(&x_desc,
                                                                       &w_desc,
                                                                       &conv_desc,
                                                                       &y_desc,
                                                                       algo));
        let workspace = try!(Memory::<f32>::new(workspace_size / 4));
        match unsafe { ffi::cudnnConvolutionForward(self.handle,
                                                    &alpha as *const _ as *const ::libc::c_void,
                                                    x_desc.desc,
                                                    x.data,
                                                    w_desc.desc,
                                                    w.data,
                                                    conv_desc.desc,
                                                    algo,
                                                    workspace.data,
                                                    workspace_size,
                                                    &beta as *const _ as *const ::libc::c_void,
                                                    y_desc.desc,
                                                    x.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn max_pooling_forward(&self,
                               pooling: &Pooling,
                               src_tensor: &Tensor,
                               src_memory: &Memory<f32>,
                               dst_tensor: &Tensor,
                               dst_memory: &Memory<f32>)
                               -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        match unsafe { ffi::cudnnPoolingForward(self.handle,
                                                pooling.desc,
                                                &alpha as *const _ as *const ::libc::c_void,
                                                src_tensor.desc,
                                                src_memory.data,
                                                &beta as *const _ as *const ::libc::c_void,
                                                dst_tensor.desc,
                                                dst_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn add_bias(&self,
                    bias_tensor: &Tensor,
                    bias_memory: &Memory<f32>,
                    dst_tensor: &Tensor,
                    dst_memory: &Memory<f32>)
                    -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 1f32;
        match unsafe { ffi::cudnnAddTensor(self.handle,
                                           &alpha as *const _ as *const ::libc::c_void,
                                           bias_tensor.desc,
                                           bias_memory.data,
                                           &beta as *const _ as *const ::libc::c_void,
                                           dst_tensor.desc,
                                           dst_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn softmax_forward(&self,
                           src_tensor: &Tensor,
                           src_memory: &Memory<f32>,
                           dst_tensor: &Tensor,
                           dst_memory: &Memory<f32>)
                           -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        match unsafe { ffi::cudnnSoftmaxForward(self.handle,
                                                ffi::SoftmaxAlgorithm::Fast,
                                                ffi::SoftmaxMode::Channel,
                                                &alpha as *const _ as *const ::libc::c_void,
                                                src_tensor.desc,
                                                src_memory.data,
                                                &beta as *const _ as *const ::libc::c_void,
                                                dst_tensor.desc,
                                                dst_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn softmax_backward(&self,
                            y_tensor: &Tensor,
                            y_memory: &Memory<f32>,
                            dy_tensor: &Tensor,
                            dy_memory: &Memory<f32>,
                            dx_tensor: &mut Tensor,
                            dx_memory: &Memory<f32>)
                            -> Result<(), &'static str>{
        let alpha = 1f32;
        let beta = 0f32;
        match unsafe { ffi::cudnnSoftmaxBackward(self.handle,
                                                 ffi::SoftmaxAlgorithm::Fast,
                                                 ffi::SoftmaxMode::Channel,
                                                 &alpha as *const _ as *const ::libc::c_void,
                                                 y_tensor.desc,
                                                 y_memory.data,
                                                 dy_tensor.desc,
                                                 dy_memory.data,
                                                 &beta as *const _ as *const ::libc::c_void,
                                                 dx_tensor.desc,
                                                 dx_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }
}
<commit_msg>implemented backward - bias<commit_after>use cudnn::ffi;
use cudnn::{Tensor, Filter4d, Convolution2d, Pooling};
use cudart::Memory;
use std::ptr;

pub struct Cudnn {
    handle: ffi::Handle
}

impl Drop for Cudnn {
    fn drop(&mut self) {
        unsafe { ffi::cudnnDestroy(self.handle) };
    }
}

impl Cudnn {
    pub fn new() -> Result<Cudnn, &'static str> {
        let mut handle: ffi::Handle = ptr::null_mut();
        match unsafe { ffi::cudnnCreate(&mut handle) } {
            ffi::Status::Success => Ok(Cudnn { handle : handle }),
            e => Err(e.to_str())
        }
    }

    pub fn sigmoid_forward(&self,
                           src_desc: Tensor,
                           src: &Memory<f32>,
                           dst_desc: Tensor,
                           dst: &mut Memory<f32>)
                           -> Result<(), &'static str> {
        match unsafe { ffi::cudnnActivationForward(self.handle,
                                                   ffi::ActivationDescriptor::Sigmoid,
                                                   *&[1.0f32].as_ptr() as *const ::libc::c_void,
                                                   src_desc.desc,
                                                   src.data,
                                                   *&[0.0f32].as_ptr() as *const ::libc::c_void,
                                                   dst_desc.desc,
                                                   dst.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn relu_forward_inplace(&self,
                                src_desc: &Tensor,
                                src: &mut Memory<f32>)
                                -> Result<(), &'static str> {
        match unsafe { ffi::cudnnActivationForward(self.handle,
                                                   ffi::ActivationDescriptor::ReLU,
                                                   *&[1.0f32].as_ptr() as *const ::libc::c_void,
                                                   src_desc.desc,
                                                   src.data,
                                                   *&[0.0f32].as_ptr() as *const ::libc::c_void,
                                                   src_desc.desc,
                                                   src.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn get_conv_forward_algo(&self,
                                 x_desc: &Tensor,
                                 w_desc: &Filter4d,
                                 conv_desc: &Convolution2d,
                                 y_desc: &Tensor)
                                 -> Result<ffi::ConvolutionFwdAlgo, &'static str> {
        let mut res = ffi::ConvolutionFwdAlgo::ImplicitGemm;
        match unsafe { ffi::cudnnGetConvolutionForwardAlgorithm(self.handle,
                                                                x_desc.desc,
                                                                w_desc.desc,
                                                                conv_desc.desc,
                                                                y_desc.desc,
                                                                ffi::ConvolutionFwdPreference::NoWorkspace,
                                                                0,
                                                                &mut res) } {
            ffi::Status::Success => Ok(res),
            e => Err(e.to_str())
        }
    }

    pub fn get_conv_forward_workspace_size(&self,
                                           x_desc: &Tensor,
                                           w_desc: &Filter4d,
                                           conv_desc: &Convolution2d,
                                           y_desc: &Tensor,
                                           algo: ffi::ConvolutionFwdAlgo)
                                           -> Result<usize, &'static str> {

        let mut res = 0usize;
        match unsafe { ffi::cudnnGetConvolutionForwardWorkspaceSize(self.handle,
                                                                    x_desc.desc,
                                                                    w_desc.desc,
                                                                    conv_desc.desc,
                                                                    y_desc.desc,
                                                                    algo,
                                                                    &mut res) } {
            ffi::Status::Success => Ok(res),
            e => Err(e.to_str())
        }
    }

    pub fn conv_forward(&self,
                        x_desc: &Tensor,
                        x: &Memory<f32>,
                        w_desc: &Filter4d,
                        w: &Memory<f32>,
                        conv_desc: &Convolution2d,
                        y_desc: &Tensor,
                        y: &Memory<f32>)
                        -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        let algo = try!(self.get_conv_forward_algo(&x_desc,
                                                   &w_desc,
                                                   &conv_desc,
                                                   &y_desc));
        let workspace_size = try!(self.get_conv_forward_workspace_size(&x_desc,
                                                                       &w_desc,
                                                                       &conv_desc,
                                                                       &y_desc,
                                                                       algo));
        let workspace = try!(Memory::<f32>::new(workspace_size / 4));
        match unsafe { ffi::cudnnConvolutionForward(self.handle,
                                                    &alpha as *const _ as *const ::libc::c_void,
                                                    x_desc.desc,
                                                    x.data,
                                                    w_desc.desc,
                                                    w.data,
                                                    conv_desc.desc,
                                                    algo,
                                                    workspace.data,
                                                    workspace_size,
                                                    &beta as *const _ as *const ::libc::c_void,
                                                    y_desc.desc,
                                                    y.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn conv_forward_src(&self,
                            x_desc: &Tensor,
                            x: &Memory<f32>,
                            w_desc: &Filter4d,
                            w: &Memory<f32>,
                            conv_desc: &Convolution2d,
                            y_desc: &Tensor)
                            -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        let algo = try!(self.get_conv_forward_algo(&x_desc,
                                                   &w_desc,
                                                   &conv_desc,
                                                   &y_desc));
        let workspace_size = try!(self.get_conv_forward_workspace_size(&x_desc,
                                                                       &w_desc,
                                                                       &conv_desc,
                                                                       &y_desc,
                                                                       algo));
        let workspace = try!(Memory::<f32>::new(workspace_size / 4));
        match unsafe { ffi::cudnnConvolutionForward(self.handle,
                                                    &alpha as *const _ as *const ::libc::c_void,
                                                    x_desc.desc,
                                                    x.data,
                                                    w_desc.desc,
                                                    w.data,
                                                    conv_desc.desc,
                                                    algo,
                                                    workspace.data,
                                                    workspace_size,
                                                    &beta as *const _ as *const ::libc::c_void,
                                                    y_desc.desc,
                                                    x.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn max_pooling_forward(&self,
                               pooling: &Pooling,
                               src_tensor: &Tensor,
                               src_memory: &Memory<f32>,
                               dst_tensor: &Tensor,
                               dst_memory: &Memory<f32>)
                               -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        match unsafe { ffi::cudnnPoolingForward(self.handle,
                                                pooling.desc,
                                                &alpha as *const _ as *const ::libc::c_void,
                                                src_tensor.desc,
                                                src_memory.data,
                                                &beta as *const _ as *const ::libc::c_void,
                                                dst_tensor.desc,
                                                dst_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn add_bias(&self,
                    bias_tensor: &Tensor,
                    bias_memory: &Memory<f32>,
                    dst_tensor: &Tensor,
                    dst_memory: &Memory<f32>)
                    -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 1f32;
        match unsafe { ffi::cudnnAddTensor(self.handle,
                                           &alpha as *const _ as *const ::libc::c_void,
                                           bias_tensor.desc,
                                           bias_memory.data,
                                           &beta as *const _ as *const ::libc::c_void,
                                           dst_tensor.desc,
                                           dst_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn bias_backward(&self,
                         scale: f32,
                         bias_tensor: &mut Tensor,
                         bias_memory: &Memory<f32>,
                         dy_tensor: &Tensor,
                         dy_memory: &Memory<f32>)
                         -> Result<(), &'static str> {
        let beta = 1f32;
        match unsafe { ffi::cudnnAddTensor(self.handle,
                                           &scale as *const _ as *const ::libc::c_void,
                                           dy_tensor.desc,
                                           dy_memory.data,
                                           &beta as *const _ as *const ::libc::c_void,
                                           bias_tensor.desc,
                                           bias_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn softmax_forward(&self,
                           src_tensor: &Tensor,
                           src_memory: &Memory<f32>,
                           dst_tensor: &Tensor,
                           dst_memory: &Memory<f32>)
                           -> Result<(), &'static str> {
        let alpha = 1f32;
        let beta = 0f32;
        match unsafe { ffi::cudnnSoftmaxForward(self.handle,
                                                ffi::SoftmaxAlgorithm::Fast,
                                                ffi::SoftmaxMode::Channel,
                                                &alpha as *const _ as *const ::libc::c_void,
                                                src_tensor.desc,
                                                src_memory.data,
                                                &beta as *const _ as *const ::libc::c_void,
                                                dst_tensor.desc,
                                                dst_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }

    pub fn softmax_backward(&self,
                            y_tensor: &Tensor,
                            y_memory: &Memory<f32>,
                            dy_tensor: &Tensor,
                            dy_memory: &Memory<f32>,
                            dx_tensor: &mut Tensor,
                            dx_memory: &Memory<f32>)
                            -> Result<(), &'static str>{
        let alpha = 1f32;
        let beta = 0f32;
        match unsafe { ffi::cudnnSoftmaxBackward(self.handle,
                                                 ffi::SoftmaxAlgorithm::Fast,
                                                 ffi::SoftmaxMode::Channel,
                                                 &alpha as *const _ as *const ::libc::c_void,
                                                 y_tensor.desc,
                                                 y_memory.data,
                                                 dy_tensor.desc,
                                                 dy_memory.data,
                                                 &beta as *const _ as *const ::libc::c_void,
                                                 dx_tensor.desc,
                                                 dx_memory.data) } {
            ffi::Status::Success => Ok(()),
            e => Err(e.to_str())
        }
    }
}
<|endoftext|>