<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::Book;
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use zipper::Zipper;
use escape::escape_tex;
use resource_handler::ResourceHandler;

use std::iter::Iterator;
use std::fs::File;
use std::borrow::Cow;
use std::io::Read;

use mustache;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
        }
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        if let Ok(pdf_file) = self.book.options.get_path("output.pdf") {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::FileNotFound(self.source.clone(),
                                                                                "image".to_owned(),
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::Render("no output pdf file specified in book config".to_owned()))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("numbering").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("display_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(&self.book.filenames[i]);
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v, true)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.warning(format!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = mustache::compile_str(try!(self.book.get_template("tex.template")).as_ref());
        let mut data = self.book.get_mapbuilder("tex")
            .insert_str("content", content)
            .insert_str("tex_lang", tex_lang);
        if self.book.options.get_bool("tex.short") == Ok(true) {
            data = data.insert_bool("short", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }

    /// Transform a vector of `Token`s to LaTeX
    fn render_vec(&mut self, tokens: &[Token], escape: bool) -> Result<String> {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&try!(self.parse_token(&token, escape)));
        }
        Ok(res)
    }

    fn parse_token(&mut self, token: &Token, escape: bool) -> Result<String> {
        match *token {
            Token::Str(ref text) => if escape {
                Ok(self.book.clean(escape_tex(text), true))
            } else {
                Ok(text.clone())
            },
            Token::Paragraph(ref vec) => Ok(format!("{}\n\n",
                                                 try!(self.render_vec(vec, escape)))),
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 && self.current_chapter == Number::Hidden {
                    return Ok(String::new());
                }
                if n == 1 {
                    if let Number::Specified(n) = self.current_chapter {
                        content.push_str(r"\setcounter{chapter}{");
                        content.push_str(&format!("{}", n - 1));
                        content.push_str("}\n");
                    }
                }
                match n {
                    1 => content.push_str(r"\chapter"),
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec, true)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec, escape)))),
            Token::CodeBlock(_, ref vec) => Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", try!(self.render_vec(vec, false)))),
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec, escape)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec, escape)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec, escape)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec, escape));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url);
                    if content == url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          Cow::Borrowed(url)));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.warning(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        Cow::Borrowed(url)))))
                } else {
                    self.book.logger.warning(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\footnote{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec, escape))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.parse_token(v, escape)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec, escape),
        }
    }
}
<commit_msg>Move ignoring a non-local image in Latex/PDF output to error instead of warning<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::Book;
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use zipper::Zipper;
use escape::escape_tex;
use resource_handler::ResourceHandler;

use std::iter::Iterator;
use std::fs::File;
use std::borrow::Cow;
use std::io::Read;

use mustache;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
        }
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        if let Ok(pdf_file) = self.book.options.get_path("output.pdf") {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::FileNotFound(self.source.clone(),
                                                                                "image".to_owned(),
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::Render("no output pdf file specified in book config".to_owned()))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("numbering").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("display_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(&self.book.filenames[i]);
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v, true)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.error(format!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = mustache::compile_str(try!(self.book.get_template("tex.template")).as_ref());
        let mut data = self.book.get_mapbuilder("tex")
            .insert_str("content", content)
            .insert_str("tex_lang", tex_lang);
        if self.book.options.get_bool("tex.short") == Ok(true) {
            data = data.insert_bool("short", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }

    /// Transform a vector of `Token`s to LaTeX
    fn render_vec(&mut self, tokens: &[Token], escape: bool) -> Result<String> {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&try!(self.parse_token(&token, escape)));
        }
        Ok(res)
    }

    fn parse_token(&mut self, token: &Token, escape: bool) -> Result<String> {
        match *token {
            Token::Str(ref text) => if escape {
                Ok(self.book.clean(escape_tex(text), true))
            } else {
                Ok(text.clone())
            },
            Token::Paragraph(ref vec) => Ok(format!("{}\n\n",
                                                 try!(self.render_vec(vec, escape)))),
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 && self.current_chapter == Number::Hidden {
                    return Ok(String::new());
                }
                if n == 1 {
                    if let Number::Specified(n) = self.current_chapter {
                        content.push_str(r"\setcounter{chapter}{");
                        content.push_str(&format!("{}", n - 1));
                        content.push_str("}\n");
                    }
                }
                match n {
                    1 => content.push_str(r"\chapter"),
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec, true)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec, escape)))),
            Token::CodeBlock(_, ref vec) => Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", try!(self.render_vec(vec, false)))),
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec, escape)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec, escape)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec, escape)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec, escape));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url);
                    if content == url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          Cow::Borrowed(url)));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        Cow::Borrowed(url)))))
                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\footnote{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec, escape))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.parse_token(v, escape)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec, escape),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(core_str_ext,core_slice_ext,const_fn,no_std,raw,core_char_ext,unique,slice_bytes)]
#![no_main]
#![no_std]

extern crate common;
extern crate support;
extern crate hil;
extern crate process;
extern crate platform;

mod apps;
mod sched;

pub mod syscall;

#[no_mangle]
pub extern fn main() {
    use process::Process;
    use process::AppId;

    let mut platform = unsafe {
        platform::init()
    };

    let app1 = unsafe { Process::create(apps::app::_start).unwrap() };

    let processes = unsafe {
        process::process::PROCS = [Some(app1)];
        &mut process::process::PROCS
    };

    loop {
        unsafe {
            platform.service_pending_interrupts();

            for (i, p) in processes.iter_mut().enumerate() {
                p.as_mut().map(|process| {
                    sched::do_process(platform, process, AppId::new(i));
                });
            }

            support::atomic(|| {
                if !platform.has_pending_interrupts() {
                    support::wfi();
                }
            })
        };
    }
}

<commit_msg>Remove warnings.<commit_after>#![feature(const_fn,raw,unique,slice_bytes)]
#![no_main]
#![no_std]

extern crate common;
extern crate support;
extern crate hil;
extern crate process;
extern crate platform;

mod apps;
mod sched;

pub mod syscall;

#[no_mangle]
pub extern fn main() {
    use process::Process;
    use process::AppId;

    let mut platform = unsafe {
        platform::init()
    };

    let app1 = unsafe { Process::create(apps::app::_start).unwrap() };

    let processes = unsafe {
        process::process::PROCS = [Some(app1)];
        &mut process::process::PROCS
    };

    loop {
        unsafe {
            platform.service_pending_interrupts();

            for (i, p) in processes.iter_mut().enumerate() {
                p.as_mut().map(|process| {
                    sched::do_process(platform, process, AppId::new(i));
                });
            }

            support::atomic(|| {
                if !platform.has_pending_interrupts() {
                    support::wfi();
                }
            })
        };
    }
}

<|endoftext|>


<commit_before>    //   Copyright 2014 Colin Sherratt
//
//   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.

#![crate_name = "snowmew-render-gfx"]
#![crate_type = "lib"]

#![feature(phase)]
#![allow(dead_code)]

//extern crate debug;

extern crate sync;
extern crate time;
extern crate libc;

extern crate opencl;
extern crate cow;
extern crate glfw;

#[phase(plugin)]
extern crate gfx_macros;
extern crate gfx;
extern crate device;
extern crate render;

extern crate snowmew  = "snowmew-core";
extern crate position = "snowmew-position";
extern crate graphics = "snowmew-graphics";
extern crate render_data = "snowmew-render-data";

use std::collections::hashmap::HashMap;

use opencl::hl;
use sync::Arc;

use position::Positions;
use graphics::Graphics;
use snowmew::common::ObjectKey;
use snowmew::io::Window;

use graphics::geometry::{Geo, GeoTex, GeoNorm, GeoTexNorm, GeoTexNormTan};

use cow::join::{join_set_to_map, join_maps};
use render_data::RenderData;

use gfx::{Device, DeviceHelper};

static VERTEX_SRC: gfx::ShaderSource = shaders! {
GLSL_150: b"
    #version 150 core
    uniform mat4 proj_mat;
    uniform mat4 view_mat;
    uniform mat4 model_mat;

    in vec3 position;
    in vec2 texture;

    out vec2 o_texture;

    void main() {
        gl_Position = proj_mat * view_mat * model_mat * vec4(position, 1.0);
        o_texture = texture;
    }
"
};

static FRAGMENT_SRC: gfx::ShaderSource = shaders! {
GLSL_150: b"
    #version 150 core
    uniform vec4 ka_color;
    uniform int ka_use_texture;
    uniform sampler2D ka_texture;

    in vec2 o_texture;

    out vec4 o_Color;

    void main() {
        if (1 == ka_use_texture) {
            o_Color = texture(ka_texture, o_texture);
        } else {
            o_Color = ka_color;
        }
    }
"
};

#[shader_param(MyProgram)]
struct Params {
    proj_mat: [[f32, ..4], ..4],
    view_mat: [[f32, ..4], ..4],
    model_mat: [[f32, ..4], ..4],
    ka_color: [f32, ..4],
    ka_use_texture: i32,
    ka_texture: gfx::shade::TextureParam,
}

struct Mesh {
    mesh: render::mesh::Mesh,
    index: device::BufferHandle<u32>
}

pub struct RenderManager {
    data: Params,
    graphics: render::Graphics<device::gl_device::GlDevice,
                               device::gl_device::GlCommandBuffer>,
    frame: render::target::Frame,
    state: render::state::DrawState,
    prog: device::Handle<u32,device::shade::ProgramInfo>,
    meshes: HashMap<ObjectKey, Mesh>,
    textures: HashMap<ObjectKey, device::TextureHandle>,
    sampler: device::SamplerHandle,
    window: Window
}

impl RenderManager {

    fn _new(mut device: gfx::GlDevice,
            window: Window,
            size: (i32, i32),
            _: Option<Arc<hl::Device>>) -> RenderManager {

        let (width, height) = size;
        let frame = gfx::Frame::new(width as u16, height as u16);
        let state = gfx::DrawState::new().depth(gfx::state::LessEqual, true);

        let sampler = device.create_sampler(
            gfx::tex::SamplerInfo::new(
                    gfx::tex::Bilinear, gfx::tex::Tile
            )
        );

        let (prog, data) = {
            let tinfo = gfx::tex::TextureInfo {
                width: 1,
                height: 1,
                depth: 1,
                levels: 1,
                kind: gfx::tex::Texture2D,
                format: gfx::tex::RGBA8,
            };

            let img_info = tinfo.to_image_info();
            let dummy_texture = device.create_texture(tinfo)
                                      .ok().expect("Failed to create texture");
            device.update_texture(&dummy_texture,
                                  &img_info,
                                  &vec![0u8, 0, 0, 0].as_slice());

            let data = Params {
                proj_mat: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ],
                view_mat: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ],
                model_mat: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ],
                ka_use_texture: 0,
                ka_color: [1., 1., 1., 1.],
                ka_texture: (dummy_texture, Some(sampler))
            };
            (device.link_program(VERTEX_SRC.clone(), FRAGMENT_SRC.clone())
                  .ok().expect("Failed to link program"),
             data)
        };

        RenderManager {
            data: data,
            graphics: gfx::Graphics::new(device),
            frame: frame,
            state: state,
            prog: prog,
            meshes: HashMap::new(),
            textures: HashMap::new(),
            sampler: sampler,
            window: window
        }
    }

    fn load_meshes<RD: RenderData>(&mut self, db: &RD) {
        for (oid, vb) in db.vertex_buffer_iter() {
            if self.meshes.find(oid).is_none() {
                let mesh = match vb.vertex {
                    Geo(ref d) => {
                        self.graphics.device.create_mesh(d.clone())
                    },
                    GeoTex(ref d) => {
                        self.graphics.device.create_mesh(d.clone())
                    },
                    GeoNorm(ref d) => {
                        self.graphics.device.create_mesh(d.clone())
                    },
                    GeoTexNorm(ref d) => {
                        self.graphics.device.create_mesh(d.clone())
                    },
                    GeoTexNormTan(ref d) => {
                        self.graphics.device.create_mesh(d.clone())
                    }
                };

                let vb: Vec<u32> = vb.index.iter().map(|&x| x as u32).collect();

                let index = self.graphics.device.create_buffer_static(&vb);

                self.meshes.insert(*oid, Mesh {
                    index: index,
                    mesh: mesh
                });
            }
        }
    }

    fn load_textures<RD: RenderData>(&mut self, db: &RD) {
        for (oid, text) in db.texture_iter() {
            if self.textures.find(oid).is_none() {
                let tinfo = gfx::tex::TextureInfo {
                    width: text.width() as u16,
                    height: text.height() as u16,
                    depth: 1 as u16,
                    levels: 1,
                    kind: gfx::tex::Texture2D,
                    format: match text.depth() {
                        4 => gfx::tex::Unsigned(gfx::tex::RGBA, 8, gfx::attrib::IntNormalized),
                        3 => gfx::tex::Unsigned(gfx::tex::RGB, 8, gfx::attrib::IntNormalized),
                        _ => fail!("Unsupported color depth")
                    }
                };

                let img_info = tinfo.to_image_info();
                let texture = self.graphics.device.create_texture(tinfo)
                                         .ok().expect("Failed to create texture");
                self.graphics.device.update_texture(&texture, &img_info, &text.data().to_vec());
                self.textures.insert(*oid, texture);
            }
        }
    }

    fn draw<RD: RenderData>(&mut self, db: &RD, scene: ObjectKey, camera: ObjectKey) {
        let cdata = gfx::ClearData {
            color: Some([0.3, 0.3, 0.3, 1.0]),
            depth: Some(1.0),
            stencil: None,
        };
        let start = time::precise_time_s();
        self.graphics.clear(cdata, &self.frame);

        let camera_trans = db.position(camera);
        let camera = snowmew::camera::Camera::new(camera_trans);

        let proj = camera.projection_matrix(16. / 9.);
        self.data.proj_mat =
            [[proj.x.x, proj.x.y, proj.x.z, proj.x.w],
             [proj.y.x, proj.y.y, proj.y.z, proj.y.w],
             [proj.z.x, proj.z.y, proj.z.z, proj.z.w],
             [proj.w.x, proj.w.y, proj.w.z, proj.w.w]];

        let view = camera.view_matrix();
        self.data.view_mat =
            [[view.x.x, view.x.y, view.x.z, view.x.w],
             [view.y.x, view.y.y, view.y.z, view.y.w],
             [view.z.x, view.z.y, view.z.z, view.z.w],
             [view.w.x, view.w.y, view.w.z, view.w.w]];

        for (id, (draw, _)) in join_set_to_map(db.scene_iter(scene),
                                               join_maps(db.drawable_iter(),
                                                         db.location_iter())) {

            let geo = db.geometry(draw.geometry).expect("failed to find geometry");
            let mat = db.material(draw.material).expect("Could not find material");
            let vb = self.meshes.find(&geo.vb).expect("Could not get vertex buffer");

            let model = db.position(*id);

            self.data.model_mat =
                [[model.x.x, model.x.y, model.x.z, model.x.w],
                 [model.y.x, model.y.y, model.y.z, model.y.w],
                 [model.z.x, model.z.y, model.z.z, model.z.w],
                 [model.w.x, model.w.y, model.w.z, model.w.w]];

            self.data.ka_use_texture = match mat.map_ka() {
                Some(tid) => {
                    let &texture = self.textures.find(&tid).expect("Texture not loaded");
                    self.data.ka_texture = (texture, Some(self.sampler));
                    1
                }
                None => {
                    let [r, g, b] = mat.ka();
                    self.data.ka_color = [r, g, b, 1.];
                    0
                }
            };

            let batch: MyProgram = self.graphics.make_batch(
                &self.prog,
                &vb.mesh,
                gfx::IndexSlice32(gfx::TriangleList,
                                  vb.index,
                                  geo.offset as u32,
                                  geo.count as u32),
                &self.state
            ).unwrap();

            self.graphics.draw(&batch, &self.data, &self.frame);
        }

        self.graphics.end_frame();
        self.window.swap_buffers();
        let end = time::precise_time_s();
        println!("{0:4.3}ms", (end - start) * 1000.);
    }
}

impl<RD: RenderData+Send> snowmew::Render<RD> for RenderManager {
    fn update(&mut self, db: RD, scene: ObjectKey, camera: ObjectKey) {
        self.load_meshes(&db);
        self.load_textures(&db);
        self.draw(&db, scene, camera);
    }
}

impl<RD: RenderData+Send> snowmew::RenderFactory<RD, RenderManager> for RenderFactory {
    fn init(self: Box<RenderFactory>,
            io: &snowmew::IOManager,
            window: Window,
            size: (i32, i32),
            cl: Option<Arc<hl::Device>>) -> RenderManager {

        window.make_context_current();

        let mut device = gfx::GlDevice::new(|s| io.get_proc_address(s));
        RenderManager::_new(device, window, size, cl)
    }
}

pub struct RenderFactory;

impl RenderFactory {
    pub fn new() -> RenderFactory { RenderFactory }
}<commit_msg>convert verts to include a texture coord<commit_after>    //   Copyright 2014 Colin Sherratt
//
//   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.

#![crate_name = "snowmew-render-gfx"]
#![crate_type = "lib"]

#![feature(phase)]
#![allow(dead_code)]

//extern crate debug;

extern crate sync;
extern crate time;
extern crate libc;

extern crate opencl;
extern crate cow;
extern crate glfw;

#[phase(plugin)]
extern crate gfx_macros;
extern crate gfx;
extern crate device;
extern crate render;
extern crate genmesh;

extern crate snowmew  = "snowmew-core";
extern crate position = "snowmew-position";
extern crate graphics = "snowmew-graphics";
extern crate render_data = "snowmew-render-data";

use std::collections::hashmap::HashMap;

use opencl::hl;
use sync::Arc;

use position::Positions;
use graphics::Graphics;
use snowmew::common::ObjectKey;
use snowmew::io::Window;

use graphics::geometry::{Geo, GeoTex, GeoNorm, GeoTexNorm, GeoTexNormTan};
use graphics::geometry::{VertexGeoTex, VertexGeoTexNorm};

use cow::join::{join_set_to_map, join_maps};
use render_data::RenderData;

use gfx::{Device, DeviceHelper};

static VERTEX_SRC: gfx::ShaderSource = shaders! {
GLSL_150: b"
    #version 150 core
    uniform mat4 proj_mat;
    uniform mat4 view_mat;
    uniform mat4 model_mat;

    in vec3 position;
    in vec2 texture;

    out vec2 o_texture;

    void main() {
        gl_Position = proj_mat * view_mat * model_mat * vec4(position, 1.0);
        o_texture = texture;
    }
"
};

static FRAGMENT_SRC: gfx::ShaderSource = shaders! {
GLSL_150: b"
    #version 150 core
    uniform vec4 ka_color;
    uniform int ka_use_texture;
    uniform sampler2D ka_texture;

    in vec2 o_texture;

    out vec4 o_Color;

    void main() {
        if (1 == ka_use_texture) {
            o_Color = texture(ka_texture, o_texture);
        } else {
            o_Color = ka_color;
        }
    }
"
};

#[shader_param(MyProgram)]
struct Params {
    proj_mat: [[f32, ..4], ..4],
    view_mat: [[f32, ..4], ..4],
    model_mat: [[f32, ..4], ..4],
    ka_color: [f32, ..4],
    ka_use_texture: i32,
    ka_texture: gfx::shade::TextureParam,
}

struct Mesh {
    mesh: render::mesh::Mesh,
    index: device::BufferHandle<u32>
}

pub struct RenderManager {
    data: Params,
    graphics: render::Graphics<device::gl_device::GlDevice,
                               device::gl_device::GlCommandBuffer>,
    frame: render::target::Frame,
    state: render::state::DrawState,
    prog: device::Handle<u32,device::shade::ProgramInfo>,
    meshes: HashMap<ObjectKey, Mesh>,
    textures: HashMap<ObjectKey, device::TextureHandle>,
    sampler: device::SamplerHandle,
    window: Window
}

impl RenderManager {

    fn _new(mut device: gfx::GlDevice,
            window: Window,
            size: (i32, i32),
            _: Option<Arc<hl::Device>>) -> RenderManager {

        let (width, height) = size;
        let frame = gfx::Frame::new(width as u16, height as u16);
        let state = gfx::DrawState::new().depth(gfx::state::LessEqual, true);

        let sampler = device.create_sampler(
            gfx::tex::SamplerInfo::new(
                    gfx::tex::Bilinear, gfx::tex::Tile
            )
        );

        let (prog, data) = {
            let tinfo = gfx::tex::TextureInfo {
                width: 1,
                height: 1,
                depth: 1,
                levels: 1,
                kind: gfx::tex::Texture2D,
                format: gfx::tex::RGBA8,
            };

            let img_info = tinfo.to_image_info();
            let dummy_texture = device.create_texture(tinfo)
                                      .ok().expect("Failed to create texture");
            device.update_texture(&dummy_texture,
                                  &img_info,
                                  &vec![0u8, 0, 0, 0].as_slice());

            let data = Params {
                proj_mat: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ],
                view_mat: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ],
                model_mat: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ],
                ka_use_texture: 0,
                ka_color: [1., 1., 1., 1.],
                ka_texture: (dummy_texture, Some(sampler))
            };
            (device.link_program(VERTEX_SRC.clone(), FRAGMENT_SRC.clone())
                  .ok().expect("Failed to link program"),
             data)
        };

        RenderManager {
            data: data,
            graphics: gfx::Graphics::new(device),
            frame: frame,
            state: state,
            prog: prog,
            meshes: HashMap::new(),
            textures: HashMap::new(),
            sampler: sampler,
            window: window
        }
    }

    fn load_meshes<RD: RenderData>(&mut self, db: &RD) {
        for (oid, vb) in db.vertex_buffer_iter() {
            if self.meshes.find(oid).is_none() {
                let mesh = match vb.vertex {
                    Geo(ref d) => {
                        println!("Geo");
                        let data: Vec<VertexGeoTex> = d.iter()
                            .map(|v| {
                                VertexGeoTex {
                                    position: v.position,
                                    texture: [0., 0.]
                                }
                            })
                            .collect();
                        self.graphics.device.create_mesh(data)
                    },
                    GeoTex(ref d) => {
                        println!("GeoTex");
                        self.graphics.device.create_mesh(d.clone())
                    },
                    GeoNorm(ref d) => {
                        let data: Vec<VertexGeoTexNorm> = d.iter()
                            .map(|v| {
                                VertexGeoTexNorm {
                                    position: v.position,
                                    texture: [0., 0.],
                                    normal: v.normal
                                }
                            })
                            .collect();
                        self.graphics.device.create_mesh(data)
                    },
                    GeoTexNorm(ref d) => {
                        println!("GeoTexNorm");
                        self.graphics.device.create_mesh(d.clone())
                    },
                    GeoTexNormTan(ref d) => {
                        println!("GeoTexNormTan");
                        self.graphics.device.create_mesh(d.clone())
                    }
                };

                let vb: Vec<u32> = vb.index.iter().map(|&x| x as u32).collect();

                let index = self.graphics.device.create_buffer_static(&vb);

                self.meshes.insert(*oid, Mesh {
                    index: index,
                    mesh: mesh
                });
            }
        }
    }

    fn load_textures<RD: RenderData>(&mut self, db: &RD) {
        for (oid, text) in db.texture_iter() {
            if self.textures.find(oid).is_none() {
                let tinfo = gfx::tex::TextureInfo {
                    width: text.width() as u16,
                    height: text.height() as u16,
                    depth: 1 as u16,
                    levels: 1,
                    kind: gfx::tex::Texture2D,
                    format: match text.depth() {
                        4 => gfx::tex::Unsigned(gfx::tex::RGBA, 8, gfx::attrib::IntNormalized),
                        3 => gfx::tex::Unsigned(gfx::tex::RGB, 8, gfx::attrib::IntNormalized),
                        _ => fail!("Unsupported color depth")
                    }
                };

                let img_info = tinfo.to_image_info();
                let texture = self.graphics.device.create_texture(tinfo)
                                         .ok().expect("Failed to create texture");
                self.graphics.device.update_texture(&texture, &img_info, &text.data().to_vec());
                self.textures.insert(*oid, texture);
            }
        }
    }

    fn draw<RD: RenderData>(&mut self, db: &RD, scene: ObjectKey, camera: ObjectKey) {
        let cdata = gfx::ClearData {
            color: Some([0.3, 0.3, 0.3, 1.0]),
            depth: Some(1.0),
            stencil: None,
        };
        let start = time::precise_time_s();
        self.graphics.clear(cdata, &self.frame);

        let camera_trans = db.position(camera);
        let camera = snowmew::camera::Camera::new(camera_trans);

        let proj = camera.projection_matrix(16. / 9.);
        self.data.proj_mat =
            [[proj.x.x, proj.x.y, proj.x.z, proj.x.w],
             [proj.y.x, proj.y.y, proj.y.z, proj.y.w],
             [proj.z.x, proj.z.y, proj.z.z, proj.z.w],
             [proj.w.x, proj.w.y, proj.w.z, proj.w.w]];

        let view = camera.view_matrix();
        self.data.view_mat =
            [[view.x.x, view.x.y, view.x.z, view.x.w],
             [view.y.x, view.y.y, view.y.z, view.y.w],
             [view.z.x, view.z.y, view.z.z, view.z.w],
             [view.w.x, view.w.y, view.w.z, view.w.w]];

        for (id, (draw, _)) in join_set_to_map(db.scene_iter(scene),
                                               join_maps(db.drawable_iter(),
                                                         db.location_iter())) {

            let geo = db.geometry(draw.geometry).expect("failed to find geometry");
            let mat = db.material(draw.material).expect("Could not find material");
            let vb = self.meshes.find(&geo.vb).expect("Could not get vertex buffer");

            let model = db.position(*id);

            self.data.model_mat =
                [[model.x.x, model.x.y, model.x.z, model.x.w],
                 [model.y.x, model.y.y, model.y.z, model.y.w],
                 [model.z.x, model.z.y, model.z.z, model.z.w],
                 [model.w.x, model.w.y, model.w.z, model.w.w]];

            self.data.ka_use_texture = match mat.map_ka() {
                Some(tid) => {
                    let &texture = self.textures.find(&tid).expect("Texture not loaded");
                    self.data.ka_texture = (texture, Some(self.sampler));
                    1
                }
                None => {
                    let [r, g, b] = mat.ka();
                    self.data.ka_color = [r, g, b, 1.];
                    0
                }
            };

            let batch: MyProgram = self.graphics.make_batch(
                &self.prog,
                &vb.mesh,
                gfx::IndexSlice32(gfx::TriangleList,
                                  vb.index,
                                  geo.offset as u32,
                                  geo.count as u32),
                &self.state
            ).unwrap();

            self.graphics.draw(&batch, &self.data, &self.frame);
        }

        self.graphics.end_frame();
        self.window.swap_buffers();
        let end = time::precise_time_s();
        println!("{0:4.3}ms", (end - start) * 1000.);
    }
}

impl<RD: RenderData+Send> snowmew::Render<RD> for RenderManager {
    fn update(&mut self, db: RD, scene: ObjectKey, camera: ObjectKey) {
        self.load_meshes(&db);
        self.load_textures(&db);
        self.draw(&db, scene, camera);
    }
}

impl<RD: RenderData+Send> snowmew::RenderFactory<RD, RenderManager> for RenderFactory {
    fn init(self: Box<RenderFactory>,
            io: &snowmew::IOManager,
            window: Window,
            size: (i32, i32),
            cl: Option<Arc<hl::Device>>) -> RenderManager {

        window.make_context_current();

        let mut device = gfx::GlDevice::new(|s| io.get_proc_address(s));
        RenderManager::_new(device, window, size, cl)
    }
}

pub struct RenderFactory;

impl RenderFactory {
    pub fn new() -> RenderFactory { RenderFactory }
}<|endoftext|>


<commit_before>use super::active_state::ActiveState;
use super::crystallized_state::CrystallizedState;
use super::validator_record::ValidatorRecord;
use super::utils::types::{ Bitfield, U256, Sha256Digest };
use super::utils::logging::Logger;
use super::config::Config;

use super::deposits::process_ffg_deposits;
use super::crosslinks::process_crosslinks;
use super::attestors::process_recent_attesters;
use super::proposers::process_recent_proposers;
use super::validators::get_incremented_validator_sets;
use super::shuffling::get_shuffling;

pub fn initialize_new_epoch(
    cry_state: &CrystallizedState,
    act_state: &ActiveState,
    config: &Config,
    log: &Logger)
    -> (CrystallizedState, ActiveState)
{
    let mut new_validator_records: Vec<ValidatorRecord> =
        cry_state.active_validators.to_vec();
    let ffg_voter_bitfield: Bitfield =
        act_state.ffg_voter_bitfield.clone();

    let (ffg_deltas, _, _,
         should_justify, should_finalize) = process_ffg_deposits (
             &cry_state,
             &ffg_voter_bitfield,
             &log);

    info!(log, "processed ffg deposits";
            "should_justify" => should_justify,
            "should_finalize" => should_finalize);

    let (crosslink_notaries_deltas, new_crosslinks) =
        process_crosslinks(
            &cry_state,
            &act_state.partial_crosslinks,
            &config);

    info!(log, "processed crosslinks";
            "new_crosslinks_count" => new_crosslinks.len());

    let recent_attesters_deltas = process_recent_attesters(
        &cry_state,
        &act_state.recent_attesters,
        &config);

    let recent_proposers_deltas = process_recent_proposers(
        &cry_state,
        &act_state.recent_proposers);

    for (i, validator) in new_validator_records.iter_mut().enumerate() {
        let balance: i64 =
            validator.balance.low_u64() as i64 +
            ffg_deltas[i] +
            crosslink_notaries_deltas[i] +
            recent_attesters_deltas[i] +
            recent_proposers_deltas[i];
        if balance > 0 {
            validator.balance = U256::from(balance as u64);
        } else {
            validator.balance = U256::zero();
        }
    }

    let deposit_sum: i64 =
        ffg_deltas.iter().sum::<i64>() +
        crosslink_notaries_deltas.iter().sum::<i64>() +
        recent_attesters_deltas.iter().sum::<i64>() +
        recent_proposers_deltas.iter().sum::<i64>();

    info!(log, "processed validator deltas";
            "new_total_deposits" => deposit_sum);

    let total_deposits: U256 = match deposit_sum > 0 {
        true => U256::from(deposit_sum as u64),
        false => U256::zero()
    };

    let last_justified_epoch = match should_justify {
        true => cry_state.current_epoch,
        false => cry_state.last_justified_epoch
    };

    let (last_finalized_epoch, dynasty) = match should_finalize {
        true => (cry_state.current_epoch - 1, cry_state.dynasty + 1),
        false => (cry_state.last_finalized_epoch, cry_state.dynasty)
    };

    let (new_queued_validators, new_active_validators, new_exited_validators) =
        match should_finalize
    {
        true => get_incremented_validator_sets(
            &cry_state,
            &new_validator_records,
            &config,
            &log),
        false => (cry_state.queued_validators.to_vec(),
                  cry_state.active_validators.to_vec(),
                  cry_state.exited_validators.to_vec())
    };

    let shuffling = get_shuffling(
        &act_state.randao,
        &new_active_validators.len(),
        &config);

    let new_cry_state = CrystallizedState {
        active_validators: new_active_validators,
        queued_validators: new_queued_validators,
        exited_validators: new_exited_validators,
        current_shuffling: shuffling,
        current_epoch: cry_state.current_epoch + 1,
        last_justified_epoch,
        last_finalized_epoch,
        dynasty,
        // TODO: why is this zero?
        next_shard: 0,
        // TODO: currenct checkpoint wasnt in reference implementation
        current_checkpoint: Sha256Digest::zero(),
        crosslink_records: new_crosslinks,
        total_deposits
    };

    info!(log, "created new crystallized state";
            "epoch" => new_cry_state.current_epoch,
            "last_justified_epoch" => new_cry_state.last_justified_epoch,
            "last_finalized_epoch" => new_cry_state.last_finalized_epoch);

    let new_act_state = ActiveState {
        height: act_state.height,
        randao: act_state.randao,
        ffg_voter_bitfield: Bitfield::new(),
        recent_attesters: vec![],
        partial_crosslinks: vec![],
        total_skip_count: act_state.total_skip_count,
        recent_proposers: vec![]
    };

    info!(log, "created new active state";
            "height" => new_act_state.height);

    (new_cry_state, new_act_state)
}

<commit_msg>Ensure active state height is incrememented<commit_after>use super::active_state::ActiveState;
use super::crystallized_state::CrystallizedState;
use super::validator_record::ValidatorRecord;
use super::utils::types::{ Bitfield, U256, Sha256Digest };
use super::utils::logging::Logger;
use super::config::Config;

use super::deposits::process_ffg_deposits;
use super::crosslinks::process_crosslinks;
use super::attestors::process_recent_attesters;
use super::proposers::process_recent_proposers;
use super::validators::get_incremented_validator_sets;
use super::shuffling::get_shuffling;

pub fn initialize_new_epoch(
    cry_state: &CrystallizedState,
    act_state: &ActiveState,
    config: &Config,
    log: &Logger)
    -> (CrystallizedState, ActiveState)
{
    let mut new_validator_records: Vec<ValidatorRecord> =
        cry_state.active_validators.to_vec();
    let ffg_voter_bitfield: Bitfield =
        act_state.ffg_voter_bitfield.clone();

    let (ffg_deltas, _, _,
         should_justify, should_finalize) = process_ffg_deposits (
             &cry_state,
             &ffg_voter_bitfield,
             &log);

    info!(log, "processed ffg deposits";
            "should_justify" => should_justify,
            "should_finalize" => should_finalize);

    let (crosslink_notaries_deltas, new_crosslinks) =
        process_crosslinks(
            &cry_state,
            &act_state.partial_crosslinks,
            &config);

    info!(log, "processed crosslinks";
            "new_crosslinks_count" => new_crosslinks.len());

    let recent_attesters_deltas = process_recent_attesters(
        &cry_state,
        &act_state.recent_attesters,
        &config);

    let recent_proposers_deltas = process_recent_proposers(
        &cry_state,
        &act_state.recent_proposers);

    for (i, validator) in new_validator_records.iter_mut().enumerate() {
        let balance: i64 =
            validator.balance.low_u64() as i64 +
            ffg_deltas[i] +
            crosslink_notaries_deltas[i] +
            recent_attesters_deltas[i] +
            recent_proposers_deltas[i];
        if balance > 0 {
            validator.balance = U256::from(balance as u64);
        } else {
            validator.balance = U256::zero();
        }
    }

    let deposit_sum: i64 =
        ffg_deltas.iter().sum::<i64>() +
        crosslink_notaries_deltas.iter().sum::<i64>() +
        recent_attesters_deltas.iter().sum::<i64>() +
        recent_proposers_deltas.iter().sum::<i64>();

    info!(log, "processed validator deltas";
            "new_total_deposits" => deposit_sum);

    let total_deposits: U256 = match deposit_sum > 0 {
        true => U256::from(deposit_sum as u64),
        false => U256::zero()
    };

    let last_justified_epoch = match should_justify {
        true => cry_state.current_epoch,
        false => cry_state.last_justified_epoch
    };

    let (last_finalized_epoch, dynasty) = match should_finalize {
        true => (cry_state.current_epoch - 1, cry_state.dynasty + 1),
        false => (cry_state.last_finalized_epoch, cry_state.dynasty)
    };

    let (new_queued_validators, new_active_validators, new_exited_validators) =
        match should_finalize
    {
        true => get_incremented_validator_sets(
            &cry_state,
            &new_validator_records,
            &config,
            &log),
        false => (cry_state.queued_validators.to_vec(),
                  cry_state.active_validators.to_vec(),
                  cry_state.exited_validators.to_vec())
    };

    let shuffling = get_shuffling(
        &act_state.randao,
        &new_active_validators.len(),
        &config);

    let new_cry_state = CrystallizedState {
        active_validators: new_active_validators,
        queued_validators: new_queued_validators,
        exited_validators: new_exited_validators,
        current_shuffling: shuffling,
        current_epoch: cry_state.current_epoch + 1,
        last_justified_epoch,
        last_finalized_epoch,
        dynasty,
        // TODO: why is this zero?
        next_shard: 0,
        // TODO: currenct checkpoint wasnt in reference implementation
        current_checkpoint: Sha256Digest::zero(),
        crosslink_records: new_crosslinks,
        total_deposits
    };

    info!(log, "created new crystallized state";
            "epoch" => new_cry_state.current_epoch,
            "last_justified_epoch" => new_cry_state.last_justified_epoch,
            "last_finalized_epoch" => new_cry_state.last_finalized_epoch);

    let new_act_state = ActiveState {
        height: act_state.height + 1,
        // TODO: update to new randao
        randao: act_state.randao,
        ffg_voter_bitfield: Bitfield::new(),
        recent_attesters: vec![],
        partial_crosslinks: vec![],
        total_skip_count: act_state.total_skip_count,
        recent_proposers: vec![]
    };

    info!(log, "created new active state";
            "height" => new_act_state.height);

    (new_cry_state, new_act_state)
}

<|endoftext|>


<commit_before>use maud::{PreEscaped, Markup, html};
use rand::random;
use rocket::http::RawStr;

use cache::Cache;
use config::Config;
use metadata::ClusterId;
use web_server::pages;
use web_server::view::layout;

use rocket::State;

fn topic_table(cluster_id: &ClusterId, topic_name: &str) -> PreEscaped<String> {
    let api_url = format!("/api/clusters/{}/topics/{}/topology", cluster_id, topic_name);
    layout::datatable_ajax("topology-ajax", &api_url, cluster_id.name(),
        html! { tr { th "Id" th "Size" th "Leader" th "Replicas" th "ISR" th "Status" } }
    )
}

fn consumer_groups_table(cluster_id: &ClusterId, topic_name: &str) -> PreEscaped<String> {
    let api_url = format!("/api/clusters/{}/topics/{}/groups", cluster_id, topic_name);
    layout::datatable_ajax("groups-ajax", &api_url, cluster_id.name(),
           html! { tr { th "Group name" th "Status" th "Registered members" th "Stored topic offsets" } },
    )
}

fn graph_link(graph_url: &str, topic: &str) -> PreEscaped<String> {
    let url = graph_url.replace("{%s}", topic);
    html! {
        a href=(url) "link"
    }
}

fn topic_tailer_panel(cluster_id: &ClusterId, topic: &str, tailer_id: u64) -> PreEscaped<String> {
    let panel_head = html! {
        i class="fa fa-align-left fa-fw" {} "Messages"
    };
    let panel_body = html! {
        div class="topic_tailer" data-cluster=(cluster_id) data-topic=(topic) data-tailer=(tailer_id) {
            "Tailing recent messages..."
        }
    };
    layout::panel(panel_head, panel_body)
}

#[get("/clusters/<cluster_id>/topics/<topic_name>")]
pub fn topic_page(cluster_id: ClusterId, topic_name: &RawStr, cache: State<Cache>, config: State<Config>) -> Markup {
    let partitions = match cache.topics.get(&(cluster_id.clone(), topic_name.to_string())) {
        Some(partitions) => partitions,
        None => {
            return pages::warning_page(
                &format!("Topic: {}", cluster_id),
                "The specified cluster doesn't exist.")
        }
    };

    let cluster_config = config.clusters.get(&cluster_id).unwrap();
    let _ = cache.brokers.get(&cluster_id).expect("Cluster should exist");  // TODO: handle better

    let metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.to_string()))
        .unwrap_or_default()
        .aggregate_broker_metrics();

    let cluster_link = format!("/clusters/{}/", cluster_id.name());
    let content = html! {
        h3 style="margin-top: 0px" "General information"
        dl class="dl-horizontal" {
            dt "Cluster name " dd { a href=(cluster_link) (cluster_id) }
            dt "Topic name " dd (topic_name)
            dt "Number of partitions " dd (partitions.len())
            dt "Number of replicas " dd (partitions[0].replicas.len())
            dt "Traffic last 15 minutes"
            dd (format!("{:.1}   KB/s {:.0} msg/s", metrics.b_rate_15 / 1000f64, metrics.m_rate_15))
            @if cluster_config.graph_url.is_some() {
                dt "Traffic chart" dd (graph_link(cluster_config.graph_url.as_ref().unwrap(), topic_name))
            }
        }
        h3 "Topology"
        (topic_table(&cluster_id, topic_name))
        h3 "Consumer groups"
        (consumer_groups_table(&cluster_id, topic_name))
        h3 "Tailer"
        @if cluster_config.enable_tailing {
            (topic_tailer_panel(&cluster_id, topic_name, random::<u64>()))
        } else {
            p "Topic tailing is disabled in this cluster."
        }
    };

    layout::page(&format!("Topic: {}", topic_name), content)
}

<commit_msg>Fix topic page<commit_after>use maud::{PreEscaped, Markup, html};
use rand::random;
use rocket::http::RawStr;

use cache::Cache;
use config::Config;
use metadata::ClusterId;
use web_server::pages;
use web_server::view::layout;

use rocket::State;

fn topic_table(cluster_id: &ClusterId, topic_name: &str) -> PreEscaped<String> {
    let api_url = format!("/api/clusters/{}/topics/{}/topology", cluster_id, topic_name);
    layout::datatable_ajax("topology-ajax", &api_url, cluster_id.name(),
        html! { tr { th "Id" th "Size" th "Leader" th "Replicas" th "ISR" th "Status" } }
    )
}

fn consumer_groups_table(cluster_id: &ClusterId, topic_name: &str) -> PreEscaped<String> {
    let api_url = format!("/api/clusters/{}/topics/{}/groups", cluster_id, topic_name);
    layout::datatable_ajax("groups-ajax", &api_url, cluster_id.name(),
           html! { tr { th "Group name" th "Status" th "Registered members" th "Stored topic offsets" } },
    )
}

fn graph_link(graph_url: &str, topic: &str) -> PreEscaped<String> {
    let url = graph_url.replace("{%s}", topic);
    html! {
        a href=(url) "link"
    }
}

fn topic_tailer_panel(cluster_id: &ClusterId, topic: &str, tailer_id: u64) -> PreEscaped<String> {
    let panel_head = html! {
        i class="fa fa-align-left fa-fw" {} "Messages"
    };
    let panel_body = html! {
        div class="topic_tailer" data-cluster=(cluster_id) data-topic=(topic) data-tailer=(tailer_id) {
            "Tailing recent messages..."
        }
    };
    layout::panel(panel_head, panel_body)
}

#[get("/clusters/<cluster_id>/topics/<topic_name>")]
pub fn topic_page(cluster_id: ClusterId, topic_name: &RawStr, cache: State<Cache>, config: State<Config>) -> Markup {
    let partitions = match cache.topics.get(&(cluster_id.clone(), topic_name.to_string())) {
        Some(partitions) => partitions,
        None => {
            return pages::warning_page(
                &format!("Topic: {}", cluster_id),
                "The specified cluster doesn't exist.")
        }
    };

    let cluster_config = config.clusters.get(&cluster_id).unwrap();
    let _ = cache.brokers.get(&cluster_id).expect("Cluster should exist");  // TODO: handle better

    let metrics = cache.metrics.get(&(cluster_id.clone(), topic_name.to_string()))
        .unwrap_or_default()
        .aggregate_broker_metrics();

    let cluster_link = format!("/clusters/{}/", cluster_id.name());
    let content = html! {
        h3 style="margin-top: 0px" "General information"
        dl class="dl-horizontal" {
            dt "Cluster name " dd { a href=(cluster_link) (cluster_id) }
            dt "Topic name " dd (topic_name)
            dt "Number of partitions " dd (partitions.len())
            dt "Number of replicas " dd (partitions[0].replicas.len())
            dt "Traffic last 15 minutes"
            dd (format!("{:.1}   KB/s {:.0} msg/s", metrics.b_rate_15 / 1000f64, metrics.m_rate_15))
            @if cluster_config.graph_url.is_some() {
                dt "Traffic chart" dd (graph_link(cluster_config.graph_url.as_ref().unwrap(), topic_name))
            }
        }
        h3 "Topology"
        (topic_table(&cluster_id, topic_name))
        h3 "Consumer groups"
        (consumer_groups_table(&cluster_id, topic_name))
        h3 "Tailer"
        @if cluster_config.enable_tailing {
            (topic_tailer_panel(&cluster_id, topic_name, random::<u64>()))
        } @else {
            p "Topic tailing is disabled in this cluster."
        }
    };

    layout::page(&format!("Topic: {}", topic_name), content)
}

<|endoftext|>


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

//! Represents an address in the guest's memory space.

use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use std::fmt::{self, Display};
use std::ops::{BitAnd, BitOr};

/// Represents an Address in the guest's memory.
#[derive(Clone, Copy, Debug)]
pub struct GuestAddress(pub u64);

impl GuestAddress {
    /// Returns the offset from this address to the given base address.
    ///
    /// # Examples
    ///
    /// ```
    /// # use sys_util::GuestAddress;
    ///   let base = GuestAddress(0x100);
    ///   let addr = GuestAddress(0x150);
    ///   assert_eq!(addr.offset_from(base), 0x50u64);
    /// ```
    pub fn offset_from(&self, base: GuestAddress) -> u64 {
        self.0 - base.0
    }

    /// Returns the address as a u64 offset from 0x0.
    /// Use this when a raw number is needed to pass to the kernel.
    pub fn offset(&self) -> u64 {
        self.0
    }

    /// Returns the result of the add or None if there is overflow.
    pub fn checked_add(&self, other: u64) -> Option<GuestAddress> {
        self.0.checked_add(other).map(GuestAddress)
    }

    /// Returns the result of the base address + the size.
    /// Only use this when `offset` is guaranteed not to overflow.
    pub fn unchecked_add(&self, offset: u64) -> GuestAddress {
        GuestAddress(self.0 + offset)
    }

    /// Returns the result of the subtraction of None if there is underflow.
    pub fn checked_sub(&self, other: u64) -> Option<GuestAddress> {
        self.0.checked_sub(other).map(GuestAddress)
    }

    /// Returns the bitwise and of the address with the given mask.
    pub fn mask(&self, mask: u64) -> GuestAddress {
        GuestAddress(self.0 & mask as u64)
    }
}

impl BitAnd<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitand(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 & other as u64)
    }
}

impl BitOr<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitor(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 | other as u64)
    }
}

impl PartialEq for GuestAddress {
    fn eq(&self, other: &GuestAddress) -> bool {
        self.0 == other.0
    }
}
impl Eq for GuestAddress {}

impl Ord for GuestAddress {
    fn cmp(&self, other: &GuestAddress) -> Ordering {
        self.0.cmp(&other.0)
    }
}

impl PartialOrd for GuestAddress {
    fn partial_cmp(&self, other: &GuestAddress) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Display for GuestAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

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

    #[test]
    fn equals() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x300);
        let c = GuestAddress(0x301);
        assert_eq!(a, b);
        assert_eq!(b, a);
        assert_ne!(a, c);
        assert_ne!(c, a);
    }

    #[test]
    fn cmp() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x301);
        assert!(a < b);
        assert!(b > a);
        assert!(!(a < a));
    }

    #[test]
    fn mask() {
        let a = GuestAddress(0x5050);
        assert_eq!(GuestAddress(0x5000), a & 0xff00u64);
        assert_eq!(GuestAddress(0x5055), a | 0x0005u64);
    }

    #[test]
    fn add_sub() {
        let a = GuestAddress(0x50);
        let b = GuestAddress(0x60);
        assert_eq!(Some(GuestAddress(0xb0)), a.checked_add(0x60));
        assert_eq!(0x10, b.offset_from(a));
    }

    #[test]
    fn checked_add_overflow() {
        let a = GuestAddress(0xffffffffffffff55);
        assert_eq!(Some(GuestAddress(0xffffffffffffff57)), a.checked_add(2));
        assert!(a.checked_add(0xf0).is_none());
    }
}
<commit_msg>sys_util: guest_address: take self by value in methods<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Represents an address in the guest's memory space.

use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use std::fmt::{self, Display};
use std::ops::{BitAnd, BitOr};

/// Represents an Address in the guest's memory.
#[derive(Clone, Copy, Debug)]
pub struct GuestAddress(pub u64);

impl GuestAddress {
    /// Returns the offset from this address to the given base address.
    ///
    /// # Examples
    ///
    /// ```
    /// # use sys_util::GuestAddress;
    ///   let base = GuestAddress(0x100);
    ///   let addr = GuestAddress(0x150);
    ///   assert_eq!(addr.offset_from(base), 0x50u64);
    /// ```
    pub fn offset_from(self, base: GuestAddress) -> u64 {
        self.0 - base.0
    }

    /// Returns the address as a u64 offset from 0x0.
    /// Use this when a raw number is needed to pass to the kernel.
    pub fn offset(self) -> u64 {
        self.0
    }

    /// Returns the result of the add or None if there is overflow.
    pub fn checked_add(self, other: u64) -> Option<GuestAddress> {
        self.0.checked_add(other).map(GuestAddress)
    }

    /// Returns the result of the base address + the size.
    /// Only use this when `offset` is guaranteed not to overflow.
    pub fn unchecked_add(self, offset: u64) -> GuestAddress {
        GuestAddress(self.0 + offset)
    }

    /// Returns the result of the subtraction of None if there is underflow.
    pub fn checked_sub(self, other: u64) -> Option<GuestAddress> {
        self.0.checked_sub(other).map(GuestAddress)
    }

    /// Returns the bitwise and of the address with the given mask.
    pub fn mask(self, mask: u64) -> GuestAddress {
        GuestAddress(self.0 & mask as u64)
    }
}

impl BitAnd<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitand(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 & other as u64)
    }
}

impl BitOr<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitor(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 | other as u64)
    }
}

impl PartialEq for GuestAddress {
    fn eq(&self, other: &GuestAddress) -> bool {
        self.0 == other.0
    }
}
impl Eq for GuestAddress {}

impl Ord for GuestAddress {
    fn cmp(&self, other: &GuestAddress) -> Ordering {
        self.0.cmp(&other.0)
    }
}

impl PartialOrd for GuestAddress {
    fn partial_cmp(&self, other: &GuestAddress) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Display for GuestAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

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

    #[test]
    fn equals() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x300);
        let c = GuestAddress(0x301);
        assert_eq!(a, b);
        assert_eq!(b, a);
        assert_ne!(a, c);
        assert_ne!(c, a);
    }

    #[test]
    fn cmp() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x301);
        assert!(a < b);
        assert!(b > a);
        assert!(!(a < a));
    }

    #[test]
    fn mask() {
        let a = GuestAddress(0x5050);
        assert_eq!(GuestAddress(0x5000), a & 0xff00u64);
        assert_eq!(GuestAddress(0x5055), a | 0x0005u64);
    }

    #[test]
    fn add_sub() {
        let a = GuestAddress(0x50);
        let b = GuestAddress(0x60);
        assert_eq!(Some(GuestAddress(0xb0)), a.checked_add(0x60));
        assert_eq!(0x10, b.offset_from(a));
    }

    #[test]
    fn checked_add_overflow() {
        let a = GuestAddress(0xffffffffffffff55);
        assert_eq!(Some(GuestAddress(0xffffffffffffff57)), a.checked_add(2));
        assert!(a.checked_add(0xf0).is_none());
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::cmp;
use std::time::Duration;

use manager::builder::PeerManagerBuilder;
use manager::peer_info::PeerInfo;
use manager::error::{PeerManagerError, PeerManagerErrorKind};

use futures::{StartSend, Poll, AsyncSink, Async};
use futures::sink::Sink;
use futures::stream::Stream;
use futures::sync::mpsc::{self, Sender, Receiver};
use futures::task::{self as futures_task, Task};
use tokio_core::reactor::Handle;
use tokio_timer::{self, Timer};

pub mod builder;
pub mod peer_info;
pub mod error;

mod future;
mod task;

// We configure our tick duration based on this, could let users configure this in the future...
const DEFAULT_TIMER_SLOTS: usize = 2048;

/// Manages a set of peers with heartbeating heartbeating.
pub struct PeerManager
 where P: Sink + Stream {
    handle:   Handle,
    timer:    Timer,
    build:    PeerManagerBuilder,
    send:     Sender<OPeerManagerMessage
>,
    peers:    HashMap<PeerInfo, Sender<IPeerManagerMessage
>>,
    recv:     Receiver<OPeerManagerMessage
>,
    opt_task: Option<Task>
}

impl
 PeerManager

    where P: Sink<SinkError=io::Error> +
             Stream<Error=io::Error>,
          P::SinkItem: ManagedMessage,
          P::Item:     ManagedMessage {
    /// Create a new `PeerManager` from the given `PeerManagerBuilder`.
    pub fn from_builder(builder: PeerManagerBuilder, handle: Handle) -> PeerManager
 {
        // We use one timer for manager heartbeat intervals, and one for peer heartbeat timeouts
        let maximum_timers = builder.peer_capacity() * 2;
        let pow_maximum_timers = if maximum_timers & (maximum_timers - 1) == 0 {
            maximum_timers
        } else {
            maximum_timers.next_power_of_two()
        };

        // Figure out the right tick duration to get num slots of 2048.
        // TODO: We could probably let users change this in the future...
        let max_duration = cmp::max(builder.heartbeat_interval(), builder.heartbeat_timeout());
        let tick_duration = Duration::from_millis(max_duration.as_secs() * 1000 / DEFAULT_TIMER_SLOTS + 1);
        let timer = tokio_timer::wheel()
            .tick_duration(tick_duration)
            .max_capacity(pow_maximum_timers + 1)
            .channel_capacity(pow_maximum_timers)
            .num_slots(DEFAULT_TIMER_SLOTS)
            .build();

        let (res_send, res_recv) = mpsc::channel(builder.stream_buffer_capacity());

        PeerManager{ handle: handle, timer: timer, build: builder, send: res_send, peers: HashMap::new(), recv: res_recv, opt_task: None }
    }
}

impl
 Sink for PeerManager

    where P: Sink<SinkError=io::Error> +
             Stream<Error=io::Error> +
             'static,
          P::SinkItem: ManagedMessage,
          P::Item:     ManagedMessage {
    type SinkItem = IPeerManagerMessage
;
    type SinkError = PeerManagerError;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        match item {
            IPeerManagerMessage::AddPeer(info, peer) => {
                if self.peers.len() >= self.build.peer_capacity() {
                    self.opt_task = Some(futures_task::current());

                    return Ok(AsyncSink::NotReady(IPeerManagerMessage::AddPeer(info, peer)))
                }

                match self.peers.entry(info) {
                    Entry::Occupied(_) => Err(PeerManagerError::from_kind(PeerManagerErrorKind::PeerNotFound{ info: info })),
                    Entry::Vacant(vac) => {
                        vac.insert(task::run_peer(peer, info, self.send.clone(), self.timer.clone(), &self.build, &self.handle));

                        Ok(AsyncSink::Ready)
                    }
                }
            },
            IPeerManagerMessage::RemovePeer(info) => {
                self.peers.get_mut(&info)
                    .ok_or_else(|| PeerManagerError::from_kind(PeerManagerErrorKind::PeerNotFound{ info: info }))
                    .and_then(|send| send.start_send(IPeerManagerMessage::RemovePeer(info))
                                         .map_err(|_| panic!("bip_peer: PeerManager Failed To Send RemovePeer"))
                    )
            },
            IPeerManagerMessage::SendMessage(info, mid, peer_message) => {
                self.peers.get_mut(&info)
                    .ok_or_else(|| PeerManagerError::from_kind(PeerManagerErrorKind::PeerNotFound{ info: info }))
                    .and_then(|send| send.start_send(IPeerManagerMessage::SendMessage(info, mid, peer_message))
                                         .map_err(|_| panic!("bip_peer: PeerManager Failed to Send SendMessage"))
                    )
            }
        }
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        for peer_mut in self.peers.values_mut() {
            // Needs type hint in case poll fails (so that error type matches)
            let result: Poll<(), Self::SinkError> = peer_mut
                .poll_complete()
                .map_err(|_| panic!("bip_peer: PeerManaged Failed To Poll Peer"));

            try!(result);
        }

        Ok(Async::Ready(()))
    }
}

impl
 Stream for PeerManager

    where P: Sink +
             Stream {
    type Item = OPeerManagerMessage
;
    type Error = ();

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        // Intercept and propogate any messages indicating the peer shutdown so we can remove them from our peer map
        self.recv.poll()
            .map(|result| {
                match result {
                    Async::Ready(Some(OPeerManagerMessage::PeerRemoved(info))) => {
                        self.peers.remove(&info).unwrap_or_else(|| panic!("bip_peer: Received PeerRemoved Message With No Matching Peer In Map"));
                        self.opt_task.take().map(|task| task.notify());

                        Async::Ready(Some(OPeerManagerMessage::PeerRemoved(info)))
                    },
                    Async::Ready(Some(OPeerManagerMessage::PeerDisconnect(info))) => {
                        self.peers.remove(&info).unwrap_or_else(|| panic!("bip_peer: Received PeerDisconnect Message With No Matching Peer In Map"));
                        self.opt_task.take().map(|task| task.notify());

                        Async::Ready(Some(OPeerManagerMessage::PeerDisconnect(info)))
                    },
                    Async::Ready(Some(OPeerManagerMessage::PeerError(info, error))) => {
                        self.peers.remove(&info).unwrap_or_else(|| panic!("bip_peer: Received PeerError Message With No Matching Peer In Map"));
                        self.opt_task.take().map(|task| task.notify());

                        Async::Ready(Some(OPeerManagerMessage::PeerError(info, error)))
                    },
                    other => other
                }
            })
    }
}

//----------------------------------------------------------------------------//

/// Trait for giving `PeerManager` message information it needs.
///
/// For any `PeerProtocol` (or plain `Codec`), that wants to be managed
/// by `PeerManager`, it must ensure that it's message type implements
/// this trait so that we have the hooks necessary to manage the peer.
pub trait ManagedMessage {
    /// Retrieve a keep alive message variant.
    fn keep_alive() -> Self;

    /// Whether or not this message is a keep alive message.
    fn is_keep_alive(&self) -> bool;
}

//----------------------------------------------------------------------------//

/// Identifier for matching sent messages with received messages.
pub type MessageId = u64;

/// Message that can be sent to the `PeerManager`.
pub enum IPeerManagerMessage

    where P: Sink {
    /// Add a peer to the peer manager.
    AddPeer(PeerInfo, P),
    /// Remove a peer from the peer manager.
    RemovePeer(PeerInfo),
    /// Send a message to a peer.
    SendMessage(PeerInfo, MessageId, P::SinkItem)
    // TODO: Support querying for statistics
}

/// Message that can be received from the `PeerManager`.
pub enum OPeerManagerMessage<M> {
    /// Message indicating a peer has been added to the peer manager.
    PeerAdded(PeerInfo),
    /// Message indicating a peer has been removed from the peer manager.
    PeerRemoved(PeerInfo),
    /// Message indicating a message has been sent to the given peer.
    SentMessage(PeerInfo, MessageId),
    /// Message indicating we have received a message from a peer.
    ReceivedMessage(PeerInfo, M),
    /// Message indicating a peer has disconnected from us.
    ///
    /// Same semantics as `PeerRemoved`, but the peer is not returned.
    PeerDisconnect(PeerInfo),
    /// Message indicating a peer errored out.
    ///
    /// Same semantics as `PeerRemoved`, but the peer is not returned.
    PeerError(PeerInfo, io::Error)
}<commit_msg>Add Cast<commit_after>use std::io;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::cmp;
use std::time::Duration;

use manager::builder::PeerManagerBuilder;
use manager::peer_info::PeerInfo;
use manager::error::{PeerManagerError, PeerManagerErrorKind};

use futures::{StartSend, Poll, AsyncSink, Async};
use futures::sink::Sink;
use futures::stream::Stream;
use futures::sync::mpsc::{self, Sender, Receiver};
use futures::task::{self as futures_task, Task};
use tokio_core::reactor::Handle;
use tokio_timer::{self, Timer};

pub mod builder;
pub mod peer_info;
pub mod error;

mod future;
mod task;

// We configure our tick duration based on this, could let users configure this in the future...
const DEFAULT_TIMER_SLOTS: usize = 2048;

/// Manages a set of peers with heartbeating heartbeating.
pub struct PeerManager
 where P: Sink + Stream {
    handle:   Handle,
    timer:    Timer,
    build:    PeerManagerBuilder,
    send:     Sender<OPeerManagerMessage
>,
    peers:    HashMap<PeerInfo, Sender<IPeerManagerMessage
>>,
    recv:     Receiver<OPeerManagerMessage
>,
    opt_task: Option<Task>
}

impl
 PeerManager

    where P: Sink<SinkError=io::Error> +
             Stream<Error=io::Error>,
          P::SinkItem: ManagedMessage,
          P::Item:     ManagedMessage {
    /// Create a new `PeerManager` from the given `PeerManagerBuilder`.
    pub fn from_builder(builder: PeerManagerBuilder, handle: Handle) -> PeerManager
 {
        // We use one timer for manager heartbeat intervals, and one for peer heartbeat timeouts
        let maximum_timers = builder.peer_capacity() * 2;
        let pow_maximum_timers = if maximum_timers & (maximum_timers - 1) == 0 {
            maximum_timers
        } else {
            maximum_timers.next_power_of_two()
        };

        // Figure out the right tick duration to get num slots of 2048.
        // TODO: We could probably let users change this in the future...
        let max_duration = cmp::max(builder.heartbeat_interval(), builder.heartbeat_timeout());
        let tick_duration = Duration::from_millis(max_duration.as_secs() * 1000 / (DEFAULT_TIMER_SLOTS as u64) + 1);
        let timer = tokio_timer::wheel()
            .tick_duration(tick_duration)
            .max_capacity(pow_maximum_timers + 1)
            .channel_capacity(pow_maximum_timers)
            .num_slots(DEFAULT_TIMER_SLOTS)
            .build();

        let (res_send, res_recv) = mpsc::channel(builder.stream_buffer_capacity());

        PeerManager{ handle: handle, timer: timer, build: builder, send: res_send, peers: HashMap::new(), recv: res_recv, opt_task: None }
    }
}

impl
 Sink for PeerManager

    where P: Sink<SinkError=io::Error> +
             Stream<Error=io::Error> +
             'static,
          P::SinkItem: ManagedMessage,
          P::Item:     ManagedMessage {
    type SinkItem = IPeerManagerMessage
;
    type SinkError = PeerManagerError;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        match item {
            IPeerManagerMessage::AddPeer(info, peer) => {
                if self.peers.len() >= self.build.peer_capacity() {
                    self.opt_task = Some(futures_task::current());

                    return Ok(AsyncSink::NotReady(IPeerManagerMessage::AddPeer(info, peer)))
                }

                match self.peers.entry(info) {
                    Entry::Occupied(_) => Err(PeerManagerError::from_kind(PeerManagerErrorKind::PeerNotFound{ info: info })),
                    Entry::Vacant(vac) => {
                        vac.insert(task::run_peer(peer, info, self.send.clone(), self.timer.clone(), &self.build, &self.handle));

                        Ok(AsyncSink::Ready)
                    }
                }
            },
            IPeerManagerMessage::RemovePeer(info) => {
                self.peers.get_mut(&info)
                    .ok_or_else(|| PeerManagerError::from_kind(PeerManagerErrorKind::PeerNotFound{ info: info }))
                    .and_then(|send| send.start_send(IPeerManagerMessage::RemovePeer(info))
                                         .map_err(|_| panic!("bip_peer: PeerManager Failed To Send RemovePeer"))
                    )
            },
            IPeerManagerMessage::SendMessage(info, mid, peer_message) => {
                self.peers.get_mut(&info)
                    .ok_or_else(|| PeerManagerError::from_kind(PeerManagerErrorKind::PeerNotFound{ info: info }))
                    .and_then(|send| send.start_send(IPeerManagerMessage::SendMessage(info, mid, peer_message))
                                         .map_err(|_| panic!("bip_peer: PeerManager Failed to Send SendMessage"))
                    )
            }
        }
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        for peer_mut in self.peers.values_mut() {
            // Needs type hint in case poll fails (so that error type matches)
            let result: Poll<(), Self::SinkError> = peer_mut
                .poll_complete()
                .map_err(|_| panic!("bip_peer: PeerManaged Failed To Poll Peer"));

            try!(result);
        }

        Ok(Async::Ready(()))
    }
}

impl
 Stream for PeerManager

    where P: Sink +
             Stream {
    type Item = OPeerManagerMessage
;
    type Error = ();

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        // Intercept and propogate any messages indicating the peer shutdown so we can remove them from our peer map
        self.recv.poll()
            .map(|result| {
                match result {
                    Async::Ready(Some(OPeerManagerMessage::PeerRemoved(info))) => {
                        self.peers.remove(&info).unwrap_or_else(|| panic!("bip_peer: Received PeerRemoved Message With No Matching Peer In Map"));
                        self.opt_task.take().map(|task| task.notify());

                        Async::Ready(Some(OPeerManagerMessage::PeerRemoved(info)))
                    },
                    Async::Ready(Some(OPeerManagerMessage::PeerDisconnect(info))) => {
                        self.peers.remove(&info).unwrap_or_else(|| panic!("bip_peer: Received PeerDisconnect Message With No Matching Peer In Map"));
                        self.opt_task.take().map(|task| task.notify());

                        Async::Ready(Some(OPeerManagerMessage::PeerDisconnect(info)))
                    },
                    Async::Ready(Some(OPeerManagerMessage::PeerError(info, error))) => {
                        self.peers.remove(&info).unwrap_or_else(|| panic!("bip_peer: Received PeerError Message With No Matching Peer In Map"));
                        self.opt_task.take().map(|task| task.notify());

                        Async::Ready(Some(OPeerManagerMessage::PeerError(info, error)))
                    },
                    other => other
                }
            })
    }
}

//----------------------------------------------------------------------------//

/// Trait for giving `PeerManager` message information it needs.
///
/// For any `PeerProtocol` (or plain `Codec`), that wants to be managed
/// by `PeerManager`, it must ensure that it's message type implements
/// this trait so that we have the hooks necessary to manage the peer.
pub trait ManagedMessage {
    /// Retrieve a keep alive message variant.
    fn keep_alive() -> Self;

    /// Whether or not this message is a keep alive message.
    fn is_keep_alive(&self) -> bool;
}

//----------------------------------------------------------------------------//

/// Identifier for matching sent messages with received messages.
pub type MessageId = u64;

/// Message that can be sent to the `PeerManager`.
pub enum IPeerManagerMessage

    where P: Sink {
    /// Add a peer to the peer manager.
    AddPeer(PeerInfo, P),
    /// Remove a peer from the peer manager.
    RemovePeer(PeerInfo),
    /// Send a message to a peer.
    SendMessage(PeerInfo, MessageId, P::SinkItem)
    // TODO: Support querying for statistics
}

/// Message that can be received from the `PeerManager`.
pub enum OPeerManagerMessage<M> {
    /// Message indicating a peer has been added to the peer manager.
    PeerAdded(PeerInfo),
    /// Message indicating a peer has been removed from the peer manager.
    PeerRemoved(PeerInfo),
    /// Message indicating a message has been sent to the given peer.
    SentMessage(PeerInfo, MessageId),
    /// Message indicating we have received a message from a peer.
    ReceivedMessage(PeerInfo, M),
    /// Message indicating a peer has disconnected from us.
    ///
    /// Same semantics as `PeerRemoved`, but the peer is not returned.
    PeerDisconnect(PeerInfo),
    /// Message indicating a peer errored out.
    ///
    /// Same semantics as `PeerRemoved`, but the peer is not returned.
    PeerError(PeerInfo, io::Error)
}<|endoftext|>