<commit_before>// CITA
// Copyright 2016-2019 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use cita_database::{Config, Database, RocksDB, NUM_COLUMNS};
use cita_directories::DataPath;
use cita_types::H256;
use libproto::blockchain::SignedTransaction;
use libproto::{TryFrom, TryInto};
use std::sync::Arc;

/// Wal means write ahead log
/// used to persist transaction pools message
#[derive(Clone)]
pub struct TxWal {
    db: Arc<Database>,
}

impl TxWal {
    pub fn new(path: &str) -> Self {
        let nosql_path = DataPath::root_node_path() + path;
        // TODO: Can remove NUM_COLUMNS(useless)
        let config = Config::with_category_num(NUM_COLUMNS);
        let db = RocksDB::open(&nosql_path, &config).unwrap();
        TxWal { db: Arc::new(db) }
    }

    pub fn regenerate(&mut self, path: &str) {
        let nosql_path = DataPath::root_node_path() + path;
        let _ = Arc::get_mut(&mut self.db).unwrap().restore(&nosql_path);
    }

    pub fn write(&self, tx: &SignedTransaction) {
        // TODO Fix the block_binary. tx_binary?
        let block_binary: Vec<u8> = tx.try_into().unwrap();
        self.db
            .insert(None, tx.get_tx_hash().to_vec(), block_binary)
            .expect("insert tx");
    }

    pub fn write_batch(&self, txs: &[SignedTransaction]) {
        let mut values: Vec<Vec<u8>> = Vec::new();
        let mut keys: Vec<Vec<u8>> = Vec::new();
        for tx in txs {
            let block_binary: Vec<u8> = tx.try_into().unwrap();
            values.push(block_binary);
            keys.push(tx.get_tx_hash().to_vec());
        }
        self.db
            .insert_batch(None, values, keys)
            .expect("insert batch txs");
    }

    pub fn delete_with_hash(&mut self, tx_hash: &H256) {
        self.db.remove(None, tx_hash).expect("delete with hash");
    }

    pub fn delete_with_hashes(&mut self, tx_hashes: &[H256]) {
        let mut keys: Vec<Vec<u8>> = Vec::new();
        for tx_hash in tx_hashes {
            keys.push(tx_hash.to_vec());
        }
        self.db
            .remove_batch(None, &keys)
            .expect("delete with hashes");
    }

    pub fn read_all(&self) -> Vec<SignedTransaction> {
        // TODO fix the unwrap
        let items = self.db.iterator(None).unwrap();

        items
            .map(|item| SignedTransaction::try_from(item.1.as_ref()).unwrap())
            .collect()
    }

    pub fn get(&self, tx_hash: &[u8]) -> Option<SignedTransaction> {
        // TODO fix the unwrap
        let result = self.db.get(None, tx_hash).unwrap();
        result.map(|item| SignedTransaction::try_from(&item).unwrap())
    }
}

// FIXME
#[cfg(test)]
mod tests {
    extern crate tempdir;
    use self::tempdir::TempDir;
    use super::*;
    use crypto::{CreateKey, KeyPair};
    use libproto::blockchain::Transaction;

    fn tx_wal() -> TxWal {
        let tempdir = TempDir::new("").unwrap().into_path();
        let config = DatabaseConfig::with_columns(None);
        let db = Database::open(&config, &tempdir.to_str().unwrap()).unwrap();
        TxWal { db: Arc::new(db) }
    }

    #[test]
    fn test_write_delete() {
        let mut wal = tx_wal();
        let keypair = KeyPair::gen_keypair();
        let privkey = keypair.privkey();
        let mut raw_tx = Transaction::new();
        raw_tx.quota = 1000;
        let tx = raw_tx.sign(*privkey);
        wal.write(&tx);

        let tx1 = wal.get(tx.get_tx_hash());

        assert_eq!(Some(tx.clone()), tx1);

        wal.delete_with_hash(&H256::from(tx.get_tx_hash()));
        let tx2 = wal.get(tx.get_tx_hash());

        assert_eq!(None, tx2);
    }

    #[test]
    fn test_batch() {
        let mut wal = tx_wal();

        let keypair = KeyPair::gen_keypair();
        let privkey = keypair.privkey();
        let mut raw_tx = Transaction::new();
        raw_tx.quota = 1000;
        let tx1 = raw_tx.sign(*privkey);
        let mut raw_tx2 = Transaction::new();
        raw_tx2.quota = 1000;
        let tx2 = raw_tx2.sign(*privkey);
        wal.write_batch(&vec![tx1.clone(), tx2.clone()]);

        let tx11 = wal.get(tx1.get_tx_hash());
        assert_eq!(Some(tx1.clone()), tx11);

        let tx11 = wal.get(tx2.get_tx_hash());
        assert_eq!(Some(tx2.clone()), tx11);

        wal.delete_with_hashes(&vec![
            H256::from(tx1.get_tx_hash()),
            H256::from(tx2.get_tx_hash()),
        ]);
        let tx12 = wal.get(tx1.get_tx_hash());
        let tx22 = wal.get(tx2.get_tx_hash());

        assert_eq!(None, tx12);
        assert_eq!(None, tx22);
    }
}
<commit_msg>Fix the unit test of cita-auth.<commit_after>// CITA
// Copyright 2016-2019 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use cita_database::{Config, Database, RocksDB, NUM_COLUMNS};
use cita_directories::DataPath;
use cita_types::H256;
use libproto::blockchain::SignedTransaction;
use libproto::{TryFrom, TryInto};
use std::sync::Arc;

/// Wal means write ahead log
/// used to persist transaction pools message
#[derive(Clone)]
pub struct TxWal {
    db: Arc<Database>,
}

impl TxWal {
    pub fn new(path: &str) -> Self {
        let nosql_path = DataPath::root_node_path() + path;
        // TODO: Can remove NUM_COLUMNS(useless)
        let config = Config::with_category_num(NUM_COLUMNS);
        let db = RocksDB::open(&nosql_path, &config).unwrap();
        TxWal { db: Arc::new(db) }
    }

    pub fn regenerate(&mut self, path: &str) {
        let nosql_path = DataPath::root_node_path() + path;
        let _ = Arc::get_mut(&mut self.db).unwrap().restore(&nosql_path);
    }

    pub fn write(&self, tx: &SignedTransaction) {
        // TODO Fix the block_binary. tx_binary?
        let block_binary: Vec<u8> = tx.try_into().unwrap();
        self.db
            .insert(None, tx.get_tx_hash().to_vec(), block_binary)
            .expect("insert tx");
    }

    pub fn write_batch(&self, txs: &[SignedTransaction]) {
        let mut values: Vec<Vec<u8>> = Vec::new();
        let mut keys: Vec<Vec<u8>> = Vec::new();
        for tx in txs {
            let block_binary: Vec<u8> = tx.try_into().unwrap();
            values.push(block_binary);
            keys.push(tx.get_tx_hash().to_vec());
        }
        self.db
            .insert_batch(None, keys, values)
            .expect("insert batch txs");
    }

    pub fn delete_with_hash(&mut self, tx_hash: &H256) {
        self.db.remove(None, tx_hash).expect("delete with hash");
    }

    pub fn delete_with_hashes(&mut self, tx_hashes: &[H256]) {
        let mut keys: Vec<Vec<u8>> = Vec::new();
        for tx_hash in tx_hashes {
            keys.push(tx_hash.to_vec());
        }
        self.db
            .remove_batch(None, &keys)
            .expect("delete with hashes");
    }

    pub fn read_all(&self) -> Vec<SignedTransaction> {
        // TODO fix the unwrap
        let items = self.db.iterator(None).unwrap();

        items
            .map(|item| SignedTransaction::try_from(item.1.as_ref()).unwrap())
            .collect()
    }

    pub fn get(&self, tx_hash: &[u8]) -> Option<SignedTransaction> {
        // TODO fix the unwrap
        let result = self.db.get(None, tx_hash).unwrap();
        result.map(|item| SignedTransaction::try_from(&item).unwrap())
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use self::tempdir::TempDir;
    use super::*;
    use crypto::{CreateKey, KeyPair};
    use libproto::blockchain::Transaction;

    fn tx_wal() -> TxWal {
        let tempdir = TempDir::new("").unwrap().into_path();
        let config = Config::with_category_num(None);
        let db = RocksDB::open(&tempdir.to_str().unwrap(), &config).unwrap();
        TxWal { db: Arc::new(db) }
    }

    #[test]
    fn test_write_delete() {
        let mut wal = tx_wal();
        let keypair = KeyPair::gen_keypair();
        let privkey = keypair.privkey();
        let mut raw_tx = Transaction::new();
        raw_tx.quota = 1000;
        let tx = raw_tx.sign(*privkey);
        wal.write(&tx);

        let tx1 = wal.get(tx.get_tx_hash());

        assert_eq!(Some(tx.clone()), tx1);

        wal.delete_with_hash(&H256::from(tx.get_tx_hash()));
        let tx2 = wal.get(tx.get_tx_hash());

        assert_eq!(None, tx2);
    }

    #[test]
    fn test_batch() {
        let mut wal = tx_wal();

        let keypair = KeyPair::gen_keypair();
        let privkey = keypair.privkey();
        let mut raw_tx = Transaction::new();
        raw_tx.quota = 1000;
        let tx1 = raw_tx.sign(*privkey);
        let mut raw_tx2 = Transaction::new();
        raw_tx2.quota = 1000;
        let tx2 = raw_tx2.sign(*privkey);
        wal.write_batch(&vec![tx1.clone(), tx2.clone()]);

        let tx11 = wal.get(tx1.get_tx_hash());
        assert_eq!(Some(tx1.clone()), tx11);

        let tx11 = wal.get(tx2.get_tx_hash());
        assert_eq!(Some(tx2.clone()), tx11);

        wal.delete_with_hashes(&vec![
            H256::from(tx1.get_tx_hash()),
            H256::from(tx2.get_tx_hash()),
        ]);
        let tx12 = wal.get(tx1.get_tx_hash());
        let tx22 = wal.get(tx2.get_tx_hash());

        assert_eq!(None, tx12);
        assert_eq!(None, tx22);
    }
}
<|endoftext|>


<commit_before>//To use this, please install zfs-fuse
use redox::*;

pub struct ZFS {
    disk: File,
}

impl ZFS {
    pub fn new(disk: File) -> ZFS {
        ZFS {
            disk: disk,
        }
    }

    //TODO: Error handling
    pub fn read(&mut self, block: usize) -> [u8; 512] {
        let mut data: [u8; 512] = [0; 512];
        self.disk.seek(Seek::Start(block * 512));
        self.disk.read(&mut data);
        return data;
    }

    pub fn write(&mut self, block: usize, data: &[u8; 512]) {
        self.disk.seek(Seek::Start(block * 512));
        self.disk.write(data);
    }
}

//TODO: Find a way to remove all the to_string's
pub fn main() {
    console_title(&"ZFS".to_string());

    println!("Opening ZFS Image".to_string());
    let mut zfs = ZFS::new(File::open(&"zfs.img".to_string()));

    while let Option::Some(line) = readln!() {
        let mut args: Vec<String> = Vec::new();
        for arg in line.split(" ".to_string()) {
            args.push(arg);
        }

        if let Option::Some(command) = args.get(0) {
            println!("# ".to_string() + line);

            if *command == "list".to_string() {
                print_color!("List volumes\n".to_string(), Color::new(127, 255, 127));
            } else if *command == "dump".to_string() || *command == "d".to_string() {
                match args.get(1) {
                    Option::Some(arg) => {
                        let block = arg.to_num();
                        print_color!("Dump block:".to_string() + block, Color::new(127, 255, 127));

                        let data = zfs.read(block);
                        for i in 0..data.len() {
                            if i % 32 == 0 {
                                print!("\n".to_string() + String::from_num_radix(i, 16) + ":");
                            }
                            print!(" ".to_string() + String::from_num_radix(data[i] as usize, 16));
                        }
                        print!("\n".to_string());
                    },
                    Option::None => print_color!("No block specified!\n".to_string(), Color::new(255, 127, 127))
                }
            } else {
                print_color!("Commands: list\n".to_string(), Color::new(127, 127, 255));
            }
        }
    }

    println!("Closing ZFS Image".to_string());
    drop(zfs);
}
<commit_msg>Add ability to open and close<commit_after>//To use this, please install zfs-fuse
use redox::*;

pub struct ZFS {
    disk: File,
}

impl ZFS {
    pub fn new(disk: File) -> ZFS {
        ZFS {
            disk: disk,
        }
    }

    //TODO: Error handling
    pub fn read(&mut self, block: usize) -> [u8; 512] {
        let mut data: [u8; 512] = [0; 512];
        self.disk.seek(Seek::Start(block * 512));
        self.disk.read(&mut data);
        return data;
    }

    pub fn write(&mut self, block: usize, data: &[u8; 512]) {
        self.disk.seek(Seek::Start(block * 512));
        self.disk.write(data);
    }
}

//TODO: Find a way to remove all the to_string's
pub fn main() {
    console_title(&"ZFS".to_string());

    println!("Type open zfs.img to open the image file".to_string());
    println!("This may take up to 30 seconds".to_string());

    let mut zfs_option: Option<ZFS> = Option::None;

    while let Option::Some(line) = readln!() {
        let mut args: Vec<String> = Vec::new();
        for arg in line.split(" ".to_string()) {
            args.push(arg);
        }

        if let Option::Some(command) = args.get(0) {
            println!("# ".to_string() + line);

            let mut close = false;
            match zfs_option {
                Option::Some(ref mut zfs) => {
                    if *command == "list".to_string() {
                        print_color!("List volumes\n".to_string(), Color::new(127, 255, 127));
                    } else if *command == "dump".to_string() {
                        match args.get(1) {
                            Option::Some(arg) => {
                                let block = arg.to_num();
                                print_color!("Dump block:".to_string() + block, Color::new(127, 255, 127));

                                let data = zfs.read(block);
                                for i in 0..data.len() {
                                    if i % 32 == 0 {
                                        print!("\n".to_string() + String::from_num_radix(i, 16) + ":");
                                    }
                                    print!(" ".to_string() + String::from_num_radix(data[i] as usize, 16));
                                }
                                print!("\n".to_string());
                            },
                            Option::None => print_color!("No block specified!\n".to_string(), Color::new(255, 127, 127))
                        }
                    }else if *command == "close".to_string() {
                        print_color!("Closing\n".to_string(), Color::new(255, 127, 127));
                        close = true;
                    } else {
                        print_color!("Commands: list dump close\n".to_string(), Color::new(127, 127, 255));
                    }
                },
                Option::None => {
                    if *command == "open".to_string() {
                        match args.get(1) {
                            Option::Some(arg) => {
                                print_color!("Open: ".to_string() + arg.clone() + "\n", Color::new(127, 255, 127));
                                zfs_option = Option::Some(ZFS::new(File::open(arg)));
                            },
                            Option::None => print_color!("No file specified!\n".to_string(), Color::new(255, 127, 127))
                        }
                    }else{
                        print_color!("Commands: open\n".to_string(), Color::new(127, 127, 255));
                    }
                }
            }
            if close {
                zfs_option = Option::None;
            }
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * TODO: allow pattern matching by creating a function update(&mut self, Quit: Msg, model: &mut Model) so that we can separate
 * the update function in multiple functions.
 * TODO: provide default parameter for constructor (like Toplevel). Is it still necessary?
 * Perhaps for construct-only properties (if they don't have a default value, does this happen?).
 * TODO: think about conditions and loops (widget-list).
 */

#![feature(proc_macro)]

#[macro_use]
extern crate lazy_static;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

mod adder;
mod gen;
mod parser;
mod walker;

use std::collections::{HashMap, HashSet};

use proc_macro::TokenStream;
use quote::{Tokens, ToTokens};
use syn::{Delimited, FunctionRetTy, Ident, ImplItem, Mac, Path, TokenTree, parse_expr, parse_item};
use syn::fold::Folder;
use syn::ItemKind::Impl;
use syn::ImplItemKind::{Const, Macro, Method, Type};
use syn::Ty;
use syn::visit::Visitor;

use adder::{Adder, Property};
use gen::gen;
use parser::{Widget, parse};
use walker::ModelVariableVisitor;

type PropertyModelMap = HashMap<Ident, HashSet<Property>>;

struct State {
    container_method: Option<ImplItem>,
    container_type: Option<ImplItem>,
    model_type: Option<ImplItem>,
    properties_model_map: Option<PropertyModelMap>,
    root_widget: Option<Ident>,
    root_widget_type: Option<Ident>,
    update_method: Option<ImplItem>,
    widget_model_type: Option<Path>,
    widgets: HashMap<Ident, Ident>, // Map widget ident to widget type.
}

impl State {
    fn new() -> Self {
        State {
            container_method: None,
            root_widget: None,
            root_widget_type: None,
            widget_model_type: None,
            update_method: None,
            properties_model_map: None,
            container_type: None,
            model_type: None,
            widgets: HashMap::new(),
        }
    }
}

#[proc_macro_attribute]
pub fn widget(_attributes: TokenStream, input: TokenStream) -> TokenStream {
    let source = input.to_string();
    let mut ast = parse_item(&source).unwrap();
    if let Impl(unsafety, polarity, generics, path, typ, items) = ast.node {
        let name = get_name(&typ);
        let mut new_items = vec![];
        let mut state = State::new();
        for item in items {
            let i = item.clone();
            let new_item =
                match item.node {
                    Const(_, _) => panic!("Unexpected const item"),
                    Macro(mac) => {
                        let (view, map) = get_view(mac, &name, &mut state.root_widget, &mut state.root_widget_type, &mut state.widgets);
                        state.properties_model_map = Some(map);
                        view
                    },
                    Method(sig, _) => {
                        match item.ident.to_string().as_ref() {
                            "container" => {
                                state.container_method = Some(i);
                                continue;
                            },
                            "model" => {
                                if let FunctionRetTy::Ty(Ty::Path(_, path)) = sig.decl.output {
                                    state.widget_model_type = Some(path);
                                }
                                i
                            },
                            "subscriptions" => i,
                            "update" => {
                                state.update_method = Some(i);
                                continue
                            },
                            "update_command" => i, // TODO: automatically create this function from the events present in the view (or by splitting the update() fucntion).
                            method_name => panic!("Unexpected method {}", method_name),
                        }
                    },
                    Type(_) => {
                        if item.ident == Ident::new("Container") {
                            state.container_type = Some(i);
                        }
                        else if item.ident == Ident::new("Model") {
                            state.model_type = Some(i);
                        }
                        else {
                            panic!("Unexpected type item {:?}", item.ident);
                        }
                        continue;
                    },
                };
            new_items.push(new_item);
        }
        state.widgets.insert(state.root_widget.clone().unwrap(), state.root_widget_type.clone().unwrap());
        new_items.push(get_model_type(state.model_type, state.widget_model_type));
        new_items.push(get_container_type(state.container_type, state.root_widget_type));
        new_items.push(get_update(state.update_method.unwrap(), state.properties_model_map.unwrap()));
        new_items.push(get_container(state.container_method, state.root_widget));
        let item = Impl(unsafety, polarity, generics, path, typ, new_items);
        ast.node = item;
        let widget_struct = create_struct(&name, state.widgets);
        let expanded = quote! {
            #widget_struct
            #ast
        };
        expanded.parse().unwrap()
    }
    else {
        panic!("Expected impl");
    }
}

fn add_widgets(widget: &Widget, widgets: &mut HashMap<Ident, Ident>, map: &PropertyModelMap) {
    // Only add widgets that are needed by the update() function.
    let mut to_add = false;
    for values in map.values() {
        for value in values {
            if value.widget_name == widget.name {
                to_add = true;
            }
        }
    }
    if to_add {
        widgets.insert(widget.name.clone(), Ident::new(widget.gtk_type.as_ref()));
    }
    for child in &widget.children {
        add_widgets(child, widgets, map);
    }
}

fn block_to_impl_item(tokens: Tokens) -> ImplItem {
    let implementation = quote! {
        impl Test {
            #tokens
        }
    };
    let implementation = parse_item(implementation.as_str()).unwrap();
    match implementation.node {
        Impl(_, _, _, _, _, items) => items[0].clone(),
        _ => unreachable!(),
    }
}

fn create_struct(name: &Ident, widgets: HashMap<Ident, Ident>) -> Tokens {
    let idents = widgets.keys();
    let types = widgets.values();
    quote! {
        struct #name {
            #(#idents: #types,)*
        }
    }
}

fn impl_view(name: &Ident, tokens: Vec<TokenTree>, root_widget: &mut Option<Ident>, root_widget_type: &mut Option<Ident>, widgets: &mut HashMap<Ident, Ident>) -> (ImplItem, PropertyModelMap) {
    if let TokenTree::Delimited(Delimited { ref tts, .. }) = tokens[0] {
        let widget = parse(tts);
        let mut properties_model_map = HashMap::new();
        get_properties_model_map(&widget, &mut properties_model_map);
        add_widgets(&widget, widgets, &properties_model_map);
        let view = gen(name, widget, root_widget, root_widget_type, widgets.keys().collect());
        let item = block_to_impl_item(quote! {
            #[allow(unused_variables)] // Necessary to avoid warnings in case the parameters are unused.
            fn view(relm: RemoteRelm<Msg>, model: &Self::Model) -> Self {
                #view
            }
        });
        (item, properties_model_map)
    }
    else {
        panic!("Expected `{{` but found `{:?}` in view! macro", tokens[0]);
    }
}

fn get_container(container_method: Option<ImplItem>, root_widget: Option<Ident>) -> ImplItem {
    container_method.unwrap_or_else(|| {
        let root_widget = root_widget.unwrap();
        block_to_impl_item(quote! {
            fn container(&self) -> &Self::Container {
                &self.#root_widget
            }
        })
    })
}

fn get_container_type(container_type: Option<ImplItem>, root_widget_type: Option<Ident>) -> ImplItem {
    container_type.unwrap_or_else(|| {
        let root_widget_type = root_widget_type.unwrap();
        block_to_impl_item(quote! {
            type Container = #root_widget_type;
        })
    })
}

fn get_model_type(model_type: Option<ImplItem>, widget_model_type: Option<Path>) -> ImplItem {
    model_type.unwrap_or_else(|| {
        let widget_model_type = widget_model_type.unwrap();
        block_to_impl_item(quote! {
            type Model = #widget_model_type;
        })
    })
}

fn get_name(typ: &Ty) -> Ident {
    if let Ty::Path(_, ref path) = *typ {
        let mut tokens = Tokens::new();
        path.to_tokens(&mut tokens);
        Ident::new(tokens.to_string())
    }
    else {
        panic!("Expected Path")
    }
}

/*
 * TODO: Create a control flow graph for each variable of the model.
 * Add the set_property() calls in every leaf of every graphs.
 */
fn get_update(mut func: ImplItem, map: PropertyModelMap) -> ImplItem {
    if let Method(_, ref mut block) = func.node {
        let mut adder = Adder::new(&map);
        *block = adder.fold_block(block.clone());
    }
    // TODO: consider gtk::main_quit() as return.
    func
}

fn get_view(mac: Mac, name: &Ident, root_widget: &mut Option<Ident>, root_widget_type: &mut Option<Ident>, widgets: &mut HashMap<Ident, Ident>) -> (ImplItem, PropertyModelMap) {
    let segments = mac.path.segments;
    if segments.len() == 1 && segments[0].ident.to_string() == "view" {
        impl_view(name, mac.tts, root_widget, root_widget_type, widgets)
    }
    else {
        panic!("Unexpected macro item")
    }
}

/*
 * The map maps model variable name to a vector of tuples (widget name, property name).
 */
fn get_properties_model_map(widget: &Widget, map: &mut PropertyModelMap) {
    for (name, value) in &widget.properties {
        let string = value.parse::<String>().unwrap();
        let expr = parse_expr(&string).unwrap();
        let mut visitor = ModelVariableVisitor::new();
        visitor.visit_expr(&expr);
        let model_variables = visitor.idents;
        for var in model_variables {
            let set = map.entry(var).or_insert_with(HashSet::new);
            set.insert(Property {
                expr: string.clone(),
                name: name.clone(),
                widget_name: widget.name.clone(),
            });
        }
    }
    for child in &widget.children {
        get_properties_model_map(child, map);
    }
}
<commit_msg>Fix return type bug<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * TODO: allow pattern matching by creating a function update(&mut self, Quit: Msg, model: &mut Model) so that we can separate
 * the update function in multiple functions.
 * TODO: provide default parameter for constructor (like Toplevel). Is it still necessary?
 * Perhaps for construct-only properties (if they don't have a default value, does this happen?).
 * TODO: think about conditions and loops (widget-list).
 */

#![feature(proc_macro)]

#[macro_use]
extern crate lazy_static;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

mod adder;
mod gen;
mod parser;
mod walker;

use std::collections::{HashMap, HashSet};

use proc_macro::TokenStream;
use quote::{Tokens, ToTokens};
use syn::{Delimited, FunctionRetTy, Ident, ImplItem, Mac, TokenTree, parse_expr, parse_item};
use syn::fold::Folder;
use syn::ItemKind::Impl;
use syn::ImplItemKind::{Const, Macro, Method, Type};
use syn::Ty;
use syn::visit::Visitor;

use adder::{Adder, Property};
use gen::gen;
use parser::{Widget, parse};
use walker::ModelVariableVisitor;

type PropertyModelMap = HashMap<Ident, HashSet<Property>>;

struct State {
    container_method: Option<ImplItem>,
    container_type: Option<ImplItem>,
    model_type: Option<ImplItem>,
    properties_model_map: Option<PropertyModelMap>,
    root_widget: Option<Ident>,
    root_widget_type: Option<Ident>,
    update_method: Option<ImplItem>,
    widget_model_type: Option<Ty>,
    widgets: HashMap<Ident, Ident>, // Map widget ident to widget type.
}

impl State {
    fn new() -> Self {
        State {
            container_method: None,
            root_widget: None,
            root_widget_type: None,
            widget_model_type: None,
            update_method: None,
            properties_model_map: None,
            container_type: None,
            model_type: None,
            widgets: HashMap::new(),
        }
    }
}

#[proc_macro_attribute]
pub fn widget(_attributes: TokenStream, input: TokenStream) -> TokenStream {
    let source = input.to_string();
    let mut ast = parse_item(&source).unwrap();
    if let Impl(unsafety, polarity, generics, path, typ, items) = ast.node {
        let name = get_name(&typ);
        let mut new_items = vec![];
        let mut state = State::new();
        for item in items {
            let i = item.clone();
            let new_item =
                match item.node {
                    Const(_, _) => panic!("Unexpected const item"),
                    Macro(mac) => {
                        let (view, map) = get_view(mac, &name, &mut state.root_widget, &mut state.root_widget_type, &mut state.widgets);
                        state.properties_model_map = Some(map);
                        view
                    },
                    Method(sig, _) => {
                        match item.ident.to_string().as_ref() {
                            "container" => {
                                state.container_method = Some(i);
                                continue;
                            },
                            "model" => {
                                if let FunctionRetTy::Ty(ty) = sig.decl.output {
                                    state.widget_model_type = Some(ty);
                                }
                                else {
                                    panic!("Unexpected default, expecting Ty");
                                }
                                i
                            },
                            "subscriptions" => i,
                            "update" => {
                                state.update_method = Some(i);
                                continue
                            },
                            "update_command" => i, // TODO: automatically create this function from the events present in the view (or by splitting the update() fucntion).
                            method_name => panic!("Unexpected method {}", method_name),
                        }
                    },
                    Type(_) => {
                        if item.ident == Ident::new("Container") {
                            state.container_type = Some(i);
                        }
                        else if item.ident == Ident::new("Model") {
                            state.model_type = Some(i);
                        }
                        else {
                            panic!("Unexpected type item {:?}", item.ident);
                        }
                        continue;
                    },
                };
            new_items.push(new_item);
        }
        state.widgets.insert(state.root_widget.clone().expect("root widget"),
            state.root_widget_type.clone().expect("root widget type"));
        new_items.push(get_model_type(state.model_type, state.widget_model_type));
        new_items.push(get_container_type(state.container_type, state.root_widget_type));
        new_items.push(get_update(state.update_method.expect("update method"),
            state.properties_model_map.expect("properties model map")));
        new_items.push(get_container(state.container_method, state.root_widget));
        let item = Impl(unsafety, polarity, generics, path, typ, new_items);
        ast.node = item;
        let widget_struct = create_struct(&name, state.widgets);
        let expanded = quote! {
            #widget_struct
            #ast
        };
        expanded.parse().unwrap()
    }
    else {
        panic!("Expected impl");
    }
}

fn add_widgets(widget: &Widget, widgets: &mut HashMap<Ident, Ident>, map: &PropertyModelMap) {
    // Only add widgets that are needed by the update() function.
    let mut to_add = false;
    for values in map.values() {
        for value in values {
            if value.widget_name == widget.name {
                to_add = true;
            }
        }
    }
    if to_add {
        widgets.insert(widget.name.clone(), Ident::new(widget.gtk_type.as_ref()));
    }
    for child in &widget.children {
        add_widgets(child, widgets, map);
    }
}

fn block_to_impl_item(tokens: Tokens) -> ImplItem {
    let implementation = quote! {
        impl Test {
            #tokens
        }
    };
    let implementation = parse_item(implementation.as_str()).unwrap();
    match implementation.node {
        Impl(_, _, _, _, _, items) => items[0].clone(),
        _ => unreachable!(),
    }
}

fn create_struct(name: &Ident, widgets: HashMap<Ident, Ident>) -> Tokens {
    let idents = widgets.keys();
    let types = widgets.values();
    quote! {
        struct #name {
            #(#idents: #types,)*
        }
    }
}

fn impl_view(name: &Ident, tokens: Vec<TokenTree>, root_widget: &mut Option<Ident>, root_widget_type: &mut Option<Ident>, widgets: &mut HashMap<Ident, Ident>) -> (ImplItem, PropertyModelMap) {
    if let TokenTree::Delimited(Delimited { ref tts, .. }) = tokens[0] {
        let widget = parse(tts);
        let mut properties_model_map = HashMap::new();
        get_properties_model_map(&widget, &mut properties_model_map);
        add_widgets(&widget, widgets, &properties_model_map);
        let view = gen(name, widget, root_widget, root_widget_type, widgets.keys().collect());
        let item = block_to_impl_item(quote! {
            #[allow(unused_variables)] // Necessary to avoid warnings in case the parameters are unused.
            fn view(relm: RemoteRelm<Msg>, model: &Self::Model) -> Self {
                #view
            }
        });
        (item, properties_model_map)
    }
    else {
        panic!("Expected `{{` but found `{:?}` in view! macro", tokens[0]);
    }
}

fn get_container(container_method: Option<ImplItem>, root_widget: Option<Ident>) -> ImplItem {
    container_method.unwrap_or_else(|| {
        let root_widget = root_widget.expect("root widget");
        block_to_impl_item(quote! {
            fn container(&self) -> &Self::Container {
                &self.#root_widget
            }
        })
    })
}

fn get_container_type(container_type: Option<ImplItem>, root_widget_type: Option<Ident>) -> ImplItem {
    container_type.unwrap_or_else(|| {
        let root_widget_type = root_widget_type.expect("root widget type");
        block_to_impl_item(quote! {
            type Container = #root_widget_type;
        })
    })
}

fn get_model_type(model_type: Option<ImplItem>, widget_model_type: Option<Ty>) -> ImplItem {
    model_type.unwrap_or_else(|| {
        let widget_model_type = widget_model_type.expect("widget model type");
        block_to_impl_item(quote! {
            type Model = #widget_model_type;
        })
    })
}

fn get_name(typ: &Ty) -> Ident {
    if let Ty::Path(_, ref path) = *typ {
        let mut tokens = Tokens::new();
        path.to_tokens(&mut tokens);
        Ident::new(tokens.to_string())
    }
    else {
        panic!("Expected Path")
    }
}

/*
 * TODO: Create a control flow graph for each variable of the model.
 * Add the set_property() calls in every leaf of every graphs.
 */
fn get_update(mut func: ImplItem, map: PropertyModelMap) -> ImplItem {
    if let Method(_, ref mut block) = func.node {
        let mut adder = Adder::new(&map);
        *block = adder.fold_block(block.clone());
    }
    // TODO: consider gtk::main_quit() as return.
    func
}

fn get_view(mac: Mac, name: &Ident, root_widget: &mut Option<Ident>, root_widget_type: &mut Option<Ident>, widgets: &mut HashMap<Ident, Ident>) -> (ImplItem, PropertyModelMap) {
    let segments = mac.path.segments;
    if segments.len() == 1 && segments[0].ident.to_string() == "view" {
        impl_view(name, mac.tts, root_widget, root_widget_type, widgets)
    }
    else {
        panic!("Unexpected macro item")
    }
}

/*
 * The map maps model variable name to a vector of tuples (widget name, property name).
 */
fn get_properties_model_map(widget: &Widget, map: &mut PropertyModelMap) {
    for (name, value) in &widget.properties {
        let string = value.parse::<String>().unwrap();
        let expr = parse_expr(&string).unwrap();
        let mut visitor = ModelVariableVisitor::new();
        visitor.visit_expr(&expr);
        let model_variables = visitor.idents;
        for var in model_variables {
            let set = map.entry(var).or_insert_with(HashSet::new);
            set.insert(Property {
                expr: string.clone(),
                name: name.clone(),
                widget_name: widget.name.clone(),
            });
        }
    }
    for child in &widget.children {
        get_properties_model_map(child, map);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;
extern crate dotenv;

use futures::future;
use futures::future::Future;
use std::io;

use actix::prelude::Addr;
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpResponse, HttpServer};
use dotenv::dotenv;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
pub struct CacheableSecret {
    content: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: u32,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
enum GetSecretResponse {
    Success(CacheableSecret),
    Error(String),
}

struct AppState {
    redis: Addr<RedisActor>,
    max_secret_length: usize,
}

fn create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();

    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();

    let validation = future::result(if body.content.len() > state.max_secret_length {
        Err("content too large")
    } else if body.expiration_seconds > 9000 {
        Err("expiration too long")
    } else {
        Ok(())
    })
    .map_err(actix_web::error::ErrorBadRequest);

    println!("Writing: {:?}: {:?}", id.to_string(), expiration);

    let redis_fetch = state
        .redis
        .send(Command(resp_array![
            "SETEX",
            id.to_string(),
            expiration,
            body.content
        ]))
        .map_err(actix_web::Error::from)
        .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }));

    validation.and_then(|_| redis_fetch)
}

fn get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.to_string();
    println!("Fetching: {:?}", secret_id);

    state
        .redis
        .send(Command(resp_array!["GET", secret_id]))
        .map_err(actix_web::Error::from)
        .map(|res: Result<RespValue, actix_redis::Error>| match res {
            Ok(RespValue::BulkString(bytes)) => {
                let str = String::from_utf8(bytes).expect("non-utf8 string in redis");
                HttpResponse::Ok()
                    .json(GetSecretResponse::Success(CacheableSecret { content: str }))
            }
            _ => HttpResponse::NotFound().json(GetSecretResponse::Error("not found".to_string())),
        })
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    HttpServer::new(move || {
        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
            max_secret_length: std::env::var("MAX_SECRET_LENGTH")
                .unwrap_or("4096".to_string())
                .parse()
                .expect("unvalid integer value"),
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(web::resource("/secret").route(web::post().to_async(create_secret)))
            .service(web::resource("/secret/{secretid}").route(web::get().to_async(get_secret)))
    })
    .bind("127.0.0.1:8080")?
    .run()
}
<commit_msg>Expire secret after read<commit_after>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;
extern crate dotenv;

use futures::future;
use futures::future::Future;
use std::io;

use actix::prelude::Addr;
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpResponse, HttpServer};
use dotenv::dotenv;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
pub struct CacheableSecret {
    content: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: usize,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
enum GetSecretResponse {
    Success(CacheableSecret),
    Error(String),
}

#[derive(Debug, Clone, Copy)]
struct AppConfig {
    max_secret_length: usize,
    max_secret_expiration: usize,
}

struct AppState {
    redis: Addr<RedisActor>,
    config: AppConfig,
}

fn create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();

    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();

    let validation = future::result(if body.content.len() > state.config.max_secret_length {
        Err("content too large")
    } else if body.expiration_seconds > state.config.max_secret_expiration {
        Err("expiration too long")
    } else {
        Ok(())
    })
    .map_err(actix_web::error::ErrorBadRequest);

    println!("Writing: {:?}: {:?}", id.to_string(), expiration);

    let write_secret = state
        .redis
        .send(Command(resp_array![
            "SETEX",
            id.to_string(),
            expiration,
            body.content
        ]))
        .map_err(actix_web::Error::from)
        .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }));

    validation.and_then(|_| write_secret)
}

fn get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.to_string();
    println!("Fetching: {:?}", secret_id);

    let fetch_secret = state
        .redis
        .send(Command(resp_array!["GET", secret_id.clone()]))
        .map_err(actix_web::Error::from);

    let expire_secret = state
        .redis
        .send(Command(resp_array!["DEL", secret_id]))
        .map_err(actix_web::Error::from);

    fetch_secret
        .and_then(|result| expire_secret.map(|_| result))
        .map(|res: Result<RespValue, actix_redis::Error>| match res {
            Ok(RespValue::BulkString(bytes)) => {
                let str = String::from_utf8(bytes).expect("non-utf8 string in redis");
                HttpResponse::Ok()
                    .json(GetSecretResponse::Success(CacheableSecret { content: str }))
            }

            Ok(RespValue::Nil) => {
                HttpResponse::NotFound().json(GetSecretResponse::Error("not found".to_string()))
            }

            err => {
                println!("Redis exception: {:?}", err);
                HttpResponse::InternalServerError()
                    .json(GetSecretResponse::Error("something went wrong".to_string()))
            }
        })
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    let config = AppConfig {
        max_secret_length: std::env::var("MAX_SECRET_LENGTH")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        max_secret_expiration: std::env::var("MAX_SECRET_EXPIRATION")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
    };
    println!("Booting with config: {:?}", config);

    HttpServer::new(move || {
        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
            config: config,
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(web::resource("/secret").route(web::post().to_async(create_secret)))
            .service(web::resource("/secret/{secretid}").route(web::get().to_async(get_secret)))
    })
    .bind("127.0.0.1:8080")?
    .run()
}
<|endoftext|>


<commit_before>use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions};

pub fn opts() -> TargetOptions {
    let pre_link_args_msvc = vec![
        "/NOLOGO".to_string(),
        "/NXCOMPAT".to_string(),
        "/APPCONTAINER".to_string(),
        "mincore.lib".to_string(),
    ];
    let mut pre_link_args = LinkArgs::new();
    pre_link_args.insert(LinkerFlavor::Msvc, pre_link_args_msvc.clone());
    pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), pre_link_args_msvc);

    TargetOptions {
        function_sections: true,
        dynamic_linking: true,
        executables: true,
        dll_prefix: String::new(),
        dll_suffix: ".dll".to_string(),
        exe_suffix: ".exe".to_string(),
        staticlib_prefix: String::new(),
        staticlib_suffix: ".lib".to_string(),
        target_family: Some("windows".to_string()),
        is_like_windows: true,
        is_like_msvc: true,
        pre_link_args,
        crt_static_allows_dylibs: true,
        crt_static_respected: true,
        abi_return_struct_as_int: true,
        emit_debug_gdb_scripts: false,
        requires_uwtable: true,
        lld_flavor: LldFlavor::Link,
        // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
        // as there's been trouble in the past of linking the C++ standard
        // library required by LLVM. This likely needs to happen one day, but
        // in general Windows is also a more controlled environment than
        // Unix, so it's not necessarily as critical that this be implemented.
        //
        // Note that there are also some licensing worries about statically
        // linking some libraries which require a specific agreement, so it may
        // not ever be possible for us to pass this flag.
        no_default_libraries: false,

        ..Default::default()
    }
}
<commit_msg>rustc_target: Inherit `windows_uwp_msvc_base` from `windows_msvc_base`<commit_after>use crate::spec::{LinkerFlavor, LldFlavor, TargetOptions};

pub fn opts() -> TargetOptions {
    let mut opts = super::windows_msvc_base::opts();

    let pre_link_args_msvc = vec!["/APPCONTAINER".to_string(), "mincore.lib".to_string()];
    opts.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone());
    opts.pre_link_args
        .get_mut(&LinkerFlavor::Lld(LldFlavor::Link))
        .unwrap()
        .extend(pre_link_args_msvc);

    opts
}
<|endoftext|>


<commit_before>// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::convert::TryFrom;

use anyhow::anyhow;
use base::error;
use base::warn;
use base::AsRawDescriptor;
use base::IntoRawDescriptor;
use libvda::decode::Event as LibvdaEvent;

use crate::virtio::video::decoder::backend::*;
use crate::virtio::video::decoder::Capability;
use crate::virtio::video::error::VideoError;
use crate::virtio::video::error::VideoResult;
use crate::virtio::video::format::*;

/// Since libvda only accepts 32-bit timestamps, we are going to truncate the frame 64-bit timestamp
/// (of nanosecond granularity) to only keep seconds granularity. This would result in information
/// being lost on a regular client, but the Android C2 decoder only sends timestamps with second
/// granularity, so this approach is going to work there. However, this means that this backend is
/// very unlikely to work with any other guest software. We accept this fact because it is
/// impossible to use outside of ChromeOS anyway.
const TIMESTAMP_TRUNCATE_FACTOR: u64 = 1_000_000_000;

impl TryFrom<Format> for libvda::Profile {
    type Error = VideoError;

    fn try_from(format: Format) -> Result<Self, Self::Error> {
        Ok(match format {
            Format::VP8 => libvda::Profile::VP8,
            Format::VP9 => libvda::Profile::VP9Profile0,
            Format::H264 => libvda::Profile::H264ProfileBaseline,
            Format::Hevc => libvda::Profile::HevcProfileMain,
            _ => {
                error!("specified format {} is not supported by VDA", format);
                return Err(VideoError::InvalidParameter);
            }
        })
    }
}

impl TryFrom<Format> for libvda::PixelFormat {
    type Error = VideoError;

    fn try_from(format: Format) -> Result<Self, Self::Error> {
        Ok(match format {
            Format::NV12 => libvda::PixelFormat::NV12,
            _ => {
                error!("specified format {} is not supported by VDA", format);
                return Err(VideoError::InvalidParameter);
            }
        })
    }
}

impl From<&FramePlane> for libvda::FramePlane {
    fn from(plane: &FramePlane) -> Self {
        libvda::FramePlane {
            offset: plane.offset as i32,
            stride: plane.stride as i32,
        }
    }
}

impl From<libvda::decode::Event> for DecoderEvent {
    fn from(event: libvda::decode::Event) -> Self {
        // We cannot use the From trait here since neither libvda::decode::Response
        // no std::result::Result are defined in the current crate.
        fn vda_response_to_result(resp: libvda::decode::Response) -> VideoResult<()> {
            match resp {
                libvda::decode::Response::Success => Ok(()),
                resp => Err(VideoError::BackendFailure(anyhow!("VDA failure: {}", resp))),
            }
        }

        match event {
            LibvdaEvent::ProvidePictureBuffers {
                min_num_buffers,
                width,
                height,
                visible_rect_left,
                visible_rect_top,
                visible_rect_right,
                visible_rect_bottom,
            } => DecoderEvent::ProvidePictureBuffers {
                min_num_buffers,
                width,
                height,
                visible_rect: Rect {
                    left: visible_rect_left,
                    top: visible_rect_top,
                    right: visible_rect_right,
                    bottom: visible_rect_bottom,
                },
            },
            LibvdaEvent::PictureReady {
                buffer_id,
                bitstream_id,
                left,
                top,
                right,
                bottom,
            } => DecoderEvent::PictureReady {
                picture_buffer_id: buffer_id,
                // Restore the truncated timestamp to its original value (hopefully).
                timestamp: bitstream_id as u64 * TIMESTAMP_TRUNCATE_FACTOR,
                visible_rect: Rect {
                    left,
                    top,
                    right,
                    bottom,
                },
            },
            LibvdaEvent::NotifyEndOfBitstreamBuffer { bitstream_id } => {
                // We will patch the timestamp to the actual bitstream ID in `read_event`.
                DecoderEvent::NotifyEndOfBitstreamBuffer(bitstream_id as u32)
            }
            LibvdaEvent::NotifyError(resp) => DecoderEvent::NotifyError(
                VideoError::BackendFailure(anyhow!("VDA failure: {}", resp)),
            ),
            LibvdaEvent::ResetResponse(resp) => {
                DecoderEvent::ResetCompleted(vda_response_to_result(resp))
            }
            LibvdaEvent::FlushResponse(resp) => {
                DecoderEvent::FlushCompleted(vda_response_to_result(resp))
            }
        }
    }
}

// Used by DecoderSession::get_capabilities().
fn from_pixel_format(
    fmt: &libvda::PixelFormat,
    mask: u64,
    width_range: FormatRange,
    height_range: FormatRange,
) -> FormatDesc {
    let format = match fmt {
        libvda::PixelFormat::NV12 => Format::NV12,
        libvda::PixelFormat::YV12 => Format::YUV420,
    };

    let frame_formats = vec![FrameFormat {
        width: width_range,
        height: height_range,
        bitrates: Vec::new(),
    }];

    FormatDesc {
        mask,
        format,
        frame_formats,
        plane_align: 1,
    }
}

pub struct VdaDecoderSession {
    vda_session: libvda::decode::Session,
    format: Option<libvda::PixelFormat>,
    /// libvda can only handle 32-bit timestamps, so we will give it the buffer ID as a timestamp
    /// and map it back to the actual timestamp using this table when a decoded frame is produced.
    timestamp_to_resource_id: BTreeMap<u32, u32>,
}

impl DecoderSession for VdaDecoderSession {
    fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()> {
        self.format = Some(libvda::PixelFormat::try_from(format)?);
        Ok(self.vda_session.set_output_buffer_count(buffer_count)?)
    }

    fn decode(
        &mut self,
        resource_id: u32,
        timestamp: u64,
        resource: GuestResourceHandle,
        offset: u32,
        bytes_used: u32,
    ) -> VideoResult<()> {
        let handle = match resource {
            GuestResourceHandle::VirtioObject(handle) => handle,
            _ => {
                return Err(VideoError::BackendFailure(anyhow!(
                    "VDA backend only supports virtio object resources"
                )))
            }
        };

        // While the virtio-video driver handles timestamps as nanoseconds, Chrome assumes
        // per-second timestamps coming. So, we need a conversion from nsec to sec. Note that this
        // value should not be an unix time stamp but a frame number that the Android V4L2 C2
        // decoder passes to the driver as a 32-bit integer in our implementation. So, overflow must
        // not happen in this conversion.
        let truncated_timestamp = (timestamp / TIMESTAMP_TRUNCATE_FACTOR) as u32;
        self.timestamp_to_resource_id
            .insert(truncated_timestamp, resource_id);

        if truncated_timestamp as u64 * TIMESTAMP_TRUNCATE_FACTOR != timestamp {
            warn!("truncation of timestamp {} resulted in precision loss. Only send timestamps with second granularity to this backend.", timestamp);
        }

        Ok(self.vda_session.decode(
            truncated_timestamp as i32, // bitstream_id
            // Steal the descriptor of the resource, as libvda will close it.
            handle.desc.into_raw_descriptor(),
            offset,
            bytes_used,
        )?)
    }

    fn flush(&mut self) -> VideoResult<()> {
        Ok(self.vda_session.flush()?)
    }

    fn reset(&mut self) -> VideoResult<()> {
        Ok(self.vda_session.reset()?)
    }

    fn clear_output_buffers(&mut self) -> VideoResult<()> {
        Ok(())
    }

    fn event_pipe(&self) -> &dyn AsRawDescriptor {
        self.vda_session.pipe()
    }

    fn use_output_buffer(
        &mut self,
        picture_buffer_id: i32,
        resource: GuestResource,
    ) -> VideoResult<()> {
        let handle = match resource.handle {
            GuestResourceHandle::VirtioObject(handle) => handle,
            _ => {
                return Err(VideoError::BackendFailure(anyhow!(
                    "VDA backend only supports virtio object resources"
                )))
            }
        };
        let vda_planes: Vec<libvda::FramePlane> = resource.planes.iter().map(Into::into).collect();

        Ok(self.vda_session.use_output_buffer(
            picture_buffer_id,
            self.format.ok_or(VideoError::BackendFailure(anyhow!(
                "set_output_parameters() must be called before use_output_buffer()"
            )))?,
            // Steal the descriptor of the resource, as libvda will close it.
            handle.desc.into_raw_descriptor(),
            &vda_planes,
            handle.modifier,
        )?)
    }

    fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()> {
        Ok(self.vda_session.reuse_output_buffer(picture_buffer_id)?)
    }

    fn read_event(&mut self) -> VideoResult<DecoderEvent> {
        self.vda_session
            .read_event()
            .map(Into::into)
            // Libvda returned the truncated timestamp that we gave it as the timestamp of this
            // buffer. Replace it with the bitstream ID that was passed to `decode` for this
            // resource.
            .map(|mut e| {
                if let DecoderEvent::NotifyEndOfBitstreamBuffer(timestamp) = &mut e {
                    let bitstream_id = self
                        .timestamp_to_resource_id
                        .remove(timestamp)
                        .unwrap_or_else(|| {
                            error!("timestamp {} not registered!", *timestamp);
                            0
                        });
                    *timestamp = bitstream_id;
                }
                e
            })
            .map_err(Into::into)
    }
}

/// A VDA decoder backend that can be passed to `Decoder::new` in order to create a working decoder.
pub struct LibvdaDecoder(libvda::decode::VdaInstance);

impl LibvdaDecoder {
    /// Create a decoder backend instance that can be used to instantiate an decoder.
    pub fn new(backend_type: libvda::decode::VdaImplType) -> VideoResult<Self> {
        Ok(Self(libvda::decode::VdaInstance::new(backend_type)?))
    }
}

impl DecoderBackend for LibvdaDecoder {
    type Session = VdaDecoderSession;

    fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> {
        let profile = libvda::Profile::try_from(format)?;

        Ok(VdaDecoderSession {
            vda_session: self.0.open_session(profile).map_err(|e| {
                error!("failed to open a session for {:?}: {}", format, e);
                VideoError::InvalidOperation
            })?,
            format: None,
            timestamp_to_resource_id: Default::default(),
        })
    }

    fn get_capabilities(&self) -> Capability {
        let caps = libvda::decode::VdaInstance::get_capabilities(&self.0);

        // Raise the first |# of supported raw formats|-th bits because we can assume that any
        // combination of (a coded format, a raw format) is valid in Chrome.
        let mask = !(u64::max_value() << caps.output_formats.len());

        let mut in_fmts = vec![];
        let mut profiles: BTreeMap<Format, Vec<Profile>> = Default::default();
        for fmt in caps.input_formats.iter() {
            match Profile::from_libvda_profile(fmt.profile) {
                Some(profile) => {
                    let format = profile.to_format();
                    in_fmts.push(FormatDesc {
                        mask,
                        format,
                        frame_formats: vec![FrameFormat {
                            width: FormatRange {
                                min: fmt.min_width,
                                max: fmt.max_width,
                                step: 1,
                            },
                            height: FormatRange {
                                min: fmt.min_height,
                                max: fmt.max_height,
                                step: 1,
                            },
                            bitrates: Vec::new(),
                        }],
                        plane_align: 1,
                    });
                    match profiles.entry(format) {
                        Entry::Occupied(mut e) => e.get_mut().push(profile),
                        Entry::Vacant(e) => {
                            e.insert(vec![profile]);
                        }
                    }
                }
                None => {
                    warn!(
                        "No virtio-video equivalent for libvda profile, skipping: {:?}",
                        fmt.profile
                    );
                }
            }
        }

        let levels: BTreeMap<Format, Vec<Level>> = if profiles.contains_key(&Format::H264) {
            // We only support Level 1.0 for H.264.
            vec![(Format::H264, vec![Level::H264_1_0])]
                .into_iter()
                .collect()
        } else {
            Default::default()
        };

        // Prepare {min, max} of {width, height}.
        // While these values are associated with each input format in libvda,
        // they are associated with each output format in virtio-video protocol.
        // Thus, we compute max of min values and min of max values here.
        let min_width = caps.input_formats.iter().map(|fmt| fmt.min_width).max();
        let max_width = caps.input_formats.iter().map(|fmt| fmt.max_width).min();
        let min_height = caps.input_formats.iter().map(|fmt| fmt.min_height).max();
        let max_height = caps.input_formats.iter().map(|fmt| fmt.max_height).min();
        let width_range = FormatRange {
            min: min_width.unwrap_or(0),
            max: max_width.unwrap_or(0),
            step: 1,
        };
        let height_range = FormatRange {
            min: min_height.unwrap_or(0),
            max: max_height.unwrap_or(0),
            step: 1,
        };

        // Raise the first |# of supported coded formats|-th bits because we can assume that any
        // combination of (a coded format, a raw format) is valid in Chrome.
        let mask = !(u64::max_value() << caps.input_formats.len());
        let out_fmts = caps
            .output_formats
            .iter()
            .map(|fmt| from_pixel_format(fmt, mask, width_range, height_range))
            .collect();

        Capability::new(in_fmts, out_fmts, profiles, levels)
    }
}
<commit_msg>video: decoder: vda: use wrapping multiplication for timestamp<commit_after>// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::convert::TryFrom;

use anyhow::anyhow;
use base::error;
use base::warn;
use base::AsRawDescriptor;
use base::IntoRawDescriptor;
use libvda::decode::Event as LibvdaEvent;

use crate::virtio::video::decoder::backend::*;
use crate::virtio::video::decoder::Capability;
use crate::virtio::video::error::VideoError;
use crate::virtio::video::error::VideoResult;
use crate::virtio::video::format::*;

/// Since libvda only accepts 32-bit timestamps, we are going to truncate the frame 64-bit timestamp
/// (of nanosecond granularity) to only keep seconds granularity. This would result in information
/// being lost on a regular client, but the Android C2 decoder only sends timestamps with second
/// granularity, so this approach is going to work there. However, this means that this backend is
/// very unlikely to work with any other guest software. We accept this fact because it is
/// impossible to use outside of ChromeOS anyway.
const TIMESTAMP_TRUNCATE_FACTOR: u64 = 1_000_000_000;

impl TryFrom<Format> for libvda::Profile {
    type Error = VideoError;

    fn try_from(format: Format) -> Result<Self, Self::Error> {
        Ok(match format {
            Format::VP8 => libvda::Profile::VP8,
            Format::VP9 => libvda::Profile::VP9Profile0,
            Format::H264 => libvda::Profile::H264ProfileBaseline,
            Format::Hevc => libvda::Profile::HevcProfileMain,
            _ => {
                error!("specified format {} is not supported by VDA", format);
                return Err(VideoError::InvalidParameter);
            }
        })
    }
}

impl TryFrom<Format> for libvda::PixelFormat {
    type Error = VideoError;

    fn try_from(format: Format) -> Result<Self, Self::Error> {
        Ok(match format {
            Format::NV12 => libvda::PixelFormat::NV12,
            _ => {
                error!("specified format {} is not supported by VDA", format);
                return Err(VideoError::InvalidParameter);
            }
        })
    }
}

impl From<&FramePlane> for libvda::FramePlane {
    fn from(plane: &FramePlane) -> Self {
        libvda::FramePlane {
            offset: plane.offset as i32,
            stride: plane.stride as i32,
        }
    }
}

impl From<libvda::decode::Event> for DecoderEvent {
    fn from(event: libvda::decode::Event) -> Self {
        // We cannot use the From trait here since neither libvda::decode::Response
        // no std::result::Result are defined in the current crate.
        fn vda_response_to_result(resp: libvda::decode::Response) -> VideoResult<()> {
            match resp {
                libvda::decode::Response::Success => Ok(()),
                resp => Err(VideoError::BackendFailure(anyhow!("VDA failure: {}", resp))),
            }
        }

        match event {
            LibvdaEvent::ProvidePictureBuffers {
                min_num_buffers,
                width,
                height,
                visible_rect_left,
                visible_rect_top,
                visible_rect_right,
                visible_rect_bottom,
            } => DecoderEvent::ProvidePictureBuffers {
                min_num_buffers,
                width,
                height,
                visible_rect: Rect {
                    left: visible_rect_left,
                    top: visible_rect_top,
                    right: visible_rect_right,
                    bottom: visible_rect_bottom,
                },
            },
            LibvdaEvent::PictureReady {
                buffer_id,
                bitstream_id,
                left,
                top,
                right,
                bottom,
            } => DecoderEvent::PictureReady {
                picture_buffer_id: buffer_id,
                // Restore the truncated timestamp to its original value (hopefully).
                timestamp: TIMESTAMP_TRUNCATE_FACTOR.wrapping_mul(bitstream_id as u64),
                visible_rect: Rect {
                    left,
                    top,
                    right,
                    bottom,
                },
            },
            LibvdaEvent::NotifyEndOfBitstreamBuffer { bitstream_id } => {
                // We will patch the timestamp to the actual bitstream ID in `read_event`.
                DecoderEvent::NotifyEndOfBitstreamBuffer(bitstream_id as u32)
            }
            LibvdaEvent::NotifyError(resp) => DecoderEvent::NotifyError(
                VideoError::BackendFailure(anyhow!("VDA failure: {}", resp)),
            ),
            LibvdaEvent::ResetResponse(resp) => {
                DecoderEvent::ResetCompleted(vda_response_to_result(resp))
            }
            LibvdaEvent::FlushResponse(resp) => {
                DecoderEvent::FlushCompleted(vda_response_to_result(resp))
            }
        }
    }
}

// Used by DecoderSession::get_capabilities().
fn from_pixel_format(
    fmt: &libvda::PixelFormat,
    mask: u64,
    width_range: FormatRange,
    height_range: FormatRange,
) -> FormatDesc {
    let format = match fmt {
        libvda::PixelFormat::NV12 => Format::NV12,
        libvda::PixelFormat::YV12 => Format::YUV420,
    };

    let frame_formats = vec![FrameFormat {
        width: width_range,
        height: height_range,
        bitrates: Vec::new(),
    }];

    FormatDesc {
        mask,
        format,
        frame_formats,
        plane_align: 1,
    }
}

pub struct VdaDecoderSession {
    vda_session: libvda::decode::Session,
    format: Option<libvda::PixelFormat>,
    /// libvda can only handle 32-bit timestamps, so we will give it the buffer ID as a timestamp
    /// and map it back to the actual timestamp using this table when a decoded frame is produced.
    timestamp_to_resource_id: BTreeMap<u32, u32>,
}

impl DecoderSession for VdaDecoderSession {
    fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()> {
        self.format = Some(libvda::PixelFormat::try_from(format)?);
        Ok(self.vda_session.set_output_buffer_count(buffer_count)?)
    }

    fn decode(
        &mut self,
        resource_id: u32,
        timestamp: u64,
        resource: GuestResourceHandle,
        offset: u32,
        bytes_used: u32,
    ) -> VideoResult<()> {
        let handle = match resource {
            GuestResourceHandle::VirtioObject(handle) => handle,
            _ => {
                return Err(VideoError::BackendFailure(anyhow!(
                    "VDA backend only supports virtio object resources"
                )))
            }
        };

        // While the virtio-video driver handles timestamps as nanoseconds, Chrome assumes
        // per-second timestamps coming. So, we need a conversion from nsec to sec. Note that this
        // value should not be an unix time stamp but a frame number that the Android V4L2 C2
        // decoder passes to the driver as a 32-bit integer in our implementation. So, overflow must
        // not happen in this conversion.
        let truncated_timestamp = (timestamp / TIMESTAMP_TRUNCATE_FACTOR) as u32;
        self.timestamp_to_resource_id
            .insert(truncated_timestamp, resource_id);

        if truncated_timestamp as u64 * TIMESTAMP_TRUNCATE_FACTOR != timestamp {
            warn!("truncation of timestamp {} resulted in precision loss. Only send timestamps with second granularity to this backend.", timestamp);
        }

        Ok(self.vda_session.decode(
            truncated_timestamp as i32, // bitstream_id
            // Steal the descriptor of the resource, as libvda will close it.
            handle.desc.into_raw_descriptor(),
            offset,
            bytes_used,
        )?)
    }

    fn flush(&mut self) -> VideoResult<()> {
        Ok(self.vda_session.flush()?)
    }

    fn reset(&mut self) -> VideoResult<()> {
        Ok(self.vda_session.reset()?)
    }

    fn clear_output_buffers(&mut self) -> VideoResult<()> {
        Ok(())
    }

    fn event_pipe(&self) -> &dyn AsRawDescriptor {
        self.vda_session.pipe()
    }

    fn use_output_buffer(
        &mut self,
        picture_buffer_id: i32,
        resource: GuestResource,
    ) -> VideoResult<()> {
        let handle = match resource.handle {
            GuestResourceHandle::VirtioObject(handle) => handle,
            _ => {
                return Err(VideoError::BackendFailure(anyhow!(
                    "VDA backend only supports virtio object resources"
                )))
            }
        };
        let vda_planes: Vec<libvda::FramePlane> = resource.planes.iter().map(Into::into).collect();

        Ok(self.vda_session.use_output_buffer(
            picture_buffer_id,
            self.format.ok_or(VideoError::BackendFailure(anyhow!(
                "set_output_parameters() must be called before use_output_buffer()"
            )))?,
            // Steal the descriptor of the resource, as libvda will close it.
            handle.desc.into_raw_descriptor(),
            &vda_planes,
            handle.modifier,
        )?)
    }

    fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()> {
        Ok(self.vda_session.reuse_output_buffer(picture_buffer_id)?)
    }

    fn read_event(&mut self) -> VideoResult<DecoderEvent> {
        self.vda_session
            .read_event()
            .map(Into::into)
            // Libvda returned the truncated timestamp that we gave it as the timestamp of this
            // buffer. Replace it with the bitstream ID that was passed to `decode` for this
            // resource.
            .map(|mut e| {
                if let DecoderEvent::NotifyEndOfBitstreamBuffer(timestamp) = &mut e {
                    let bitstream_id = self
                        .timestamp_to_resource_id
                        .remove(timestamp)
                        .unwrap_or_else(|| {
                            error!("timestamp {} not registered!", *timestamp);
                            0
                        });
                    *timestamp = bitstream_id;
                }
                e
            })
            .map_err(Into::into)
    }
}

/// A VDA decoder backend that can be passed to `Decoder::new` in order to create a working decoder.
pub struct LibvdaDecoder(libvda::decode::VdaInstance);

impl LibvdaDecoder {
    /// Create a decoder backend instance that can be used to instantiate an decoder.
    pub fn new(backend_type: libvda::decode::VdaImplType) -> VideoResult<Self> {
        Ok(Self(libvda::decode::VdaInstance::new(backend_type)?))
    }
}

impl DecoderBackend for LibvdaDecoder {
    type Session = VdaDecoderSession;

    fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> {
        let profile = libvda::Profile::try_from(format)?;

        Ok(VdaDecoderSession {
            vda_session: self.0.open_session(profile).map_err(|e| {
                error!("failed to open a session for {:?}: {}", format, e);
                VideoError::InvalidOperation
            })?,
            format: None,
            timestamp_to_resource_id: Default::default(),
        })
    }

    fn get_capabilities(&self) -> Capability {
        let caps = libvda::decode::VdaInstance::get_capabilities(&self.0);

        // Raise the first |# of supported raw formats|-th bits because we can assume that any
        // combination of (a coded format, a raw format) is valid in Chrome.
        let mask = !(u64::max_value() << caps.output_formats.len());

        let mut in_fmts = vec![];
        let mut profiles: BTreeMap<Format, Vec<Profile>> = Default::default();
        for fmt in caps.input_formats.iter() {
            match Profile::from_libvda_profile(fmt.profile) {
                Some(profile) => {
                    let format = profile.to_format();
                    in_fmts.push(FormatDesc {
                        mask,
                        format,
                        frame_formats: vec![FrameFormat {
                            width: FormatRange {
                                min: fmt.min_width,
                                max: fmt.max_width,
                                step: 1,
                            },
                            height: FormatRange {
                                min: fmt.min_height,
                                max: fmt.max_height,
                                step: 1,
                            },
                            bitrates: Vec::new(),
                        }],
                        plane_align: 1,
                    });
                    match profiles.entry(format) {
                        Entry::Occupied(mut e) => e.get_mut().push(profile),
                        Entry::Vacant(e) => {
                            e.insert(vec![profile]);
                        }
                    }
                }
                None => {
                    warn!(
                        "No virtio-video equivalent for libvda profile, skipping: {:?}",
                        fmt.profile
                    );
                }
            }
        }

        let levels: BTreeMap<Format, Vec<Level>> = if profiles.contains_key(&Format::H264) {
            // We only support Level 1.0 for H.264.
            vec![(Format::H264, vec![Level::H264_1_0])]
                .into_iter()
                .collect()
        } else {
            Default::default()
        };

        // Prepare {min, max} of {width, height}.
        // While these values are associated with each input format in libvda,
        // they are associated with each output format in virtio-video protocol.
        // Thus, we compute max of min values and min of max values here.
        let min_width = caps.input_formats.iter().map(|fmt| fmt.min_width).max();
        let max_width = caps.input_formats.iter().map(|fmt| fmt.max_width).min();
        let min_height = caps.input_formats.iter().map(|fmt| fmt.min_height).max();
        let max_height = caps.input_formats.iter().map(|fmt| fmt.max_height).min();
        let width_range = FormatRange {
            min: min_width.unwrap_or(0),
            max: max_width.unwrap_or(0),
            step: 1,
        };
        let height_range = FormatRange {
            min: min_height.unwrap_or(0),
            max: max_height.unwrap_or(0),
            step: 1,
        };

        // Raise the first |# of supported coded formats|-th bits because we can assume that any
        // combination of (a coded format, a raw format) is valid in Chrome.
        let mask = !(u64::max_value() << caps.input_formats.len());
        let out_fmts = caps
            .output_formats
            .iter()
            .map(|fmt| from_pixel_format(fmt, mask, width_range, height_range))
            .collect();

        Capability::new(in_fmts, out_fmts, profiles, levels)
    }
}
<|endoftext|>


<commit_before>//! Contains all `system` that are handeled in OrbTk.
//! Using the idiom `system` is ment as system objects inside OrbTks
//! Entity Component System.  These are used for event handling,
//! building layout and drawing.

pub use self::cleanup_system::*;
pub use self::event_state_system::*;
pub use self::init_system::*;
pub use self::layout_system::*;
pub use self::post_layout_state_system::*;
pub use self::render_system::*;

mod cleanup_system;
mod event_state_system;
mod init_system;
mod layout_system;
mod post_layout_state_system;
mod render_system;
<commit_msg>orbtk_core: update systems documentation<commit_after>//! Provides dedicated `system` pipelines inside OrbTk.
//!
//! System pipelines are modules, that handle specific tasks when
//! iteratively walking the widget tree. Because each widget
//! implements the `state` trait, all system modules are accessible.
//! Pipelines are connected in a logical order. E.g. the `InitSystem`
//! is triggered **before** the `LayoutSystem`. The `LayoutSystem` is
//! triggerd **before** the `RenderSystem`. Handling of widget objects
//! inside the pipelines rely on the Entity Component System
//! ([`DCES`]).
//!
//! [`DCES`]: https://gitlab.redox-os.org/redox-os/dces-rust

pub use self::cleanup_system::*;
pub use self::event_state_system::*;
pub use self::init_system::*;
pub use self::layout_system::*;
pub use self::post_layout_state_system::*;
pub use self::render_system::*;

mod cleanup_system;
mod event_state_system;
mod init_system;
mod layout_system;
mod post_layout_state_system;
mod render_system;
<|endoftext|>