<commit_before>
extern crate base64;
extern crate clap;
extern crate openssl;

use openssl::symm::{Cipher, Crypter, Mode};
use std::io::{self, Read};
use clap::{App, Arg};

fn new_crypter_unpadded(
        cipher: Cipher,
        mode: Mode,
        key: &[u8],
        iv: Option<&[u8]>
        ) -> Crypter {
    let mut crypter = match Crypter::new(cipher, mode, key, iv) {
            Ok(val) => val,
            Err(err) => panic!("{}", err)
    };

    crypter.pad(false);

    crypter
}

fn main() {
    let args = App::new("aes_ecb")
                    .version("0.1")
                    .about("AES ECB/CBC tools")
                    .arg(Arg::with_name("key")
                            .short("k")
                            .long("key")
                            .value_name("KEY")
                            .takes_value(true)
                            .required(true))
                    .arg(Arg::with_name("mode")
                            .short("e")
                            .long("encrypt")
                            .help("encrypt (instead of decrypt)"))
                    .arg(Arg::with_name("iv")
                            .short("i")
                            .long("iv")
                            .value_name("INIT")
                            .takes_value(true)
                            .help("initial value"))
                    .get_matches();

    let mut buffer = String::new();

    io::stdin().read_to_string(&mut buffer).expect("aes_ecb");

    let decoded = match base64::decode(&buffer) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    let decoded_len = decoded.len();

    let cipher = Cipher::aes_128_ecb();

    let mode = match args.occurrences_of("mode") {
            0 => Mode::Decrypt,
            _ => Mode::Encrypt,
    };

    let key = match args.value_of("key") {
            Some(text) =>
                if text.len() == 16 {
                    text.as_bytes()
                } else {
                    panic!("invalid key length!");
                },
            None => panic!("no key provided.")
    };

    let iv = match args.value_of("iv") {
            Some(text) => Some(text.as_bytes()),
            None       => None,
    };

    let mut crypter = new_crypter_unpadded(cipher, mode, key, iv);
    let mut result  = vec![0u8; decoded_len + cipher.key_len()];

    let decrypted_len = match crypter.update(&decoded, result.as_mut_slice()) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    let output = &result[0..decrypted_len];

    println!("{}", String::from_utf8_lossy(output));
}

<commit_msg>Serialize to base64.<commit_after>
extern crate base64;
extern crate clap;
extern crate openssl;

use openssl::symm::{Cipher, Crypter, Mode};
use std::io::{self, Read};
use clap::{App, Arg};

fn new_crypter_unpadded(
        cipher: Cipher,
        mode: Mode,
        key: &[u8],
        iv: Option<&[u8]>
        ) -> Crypter {
    let mut crypter = match Crypter::new(cipher, mode, key, iv) {
            Ok(val) => val,
            Err(err) => panic!("{}", err)
    };

    crypter.pad(false);

    crypter
}

fn main() {
    let args = App::new("aes_ecb")
                    .version("0.1")
                    .about("AES ECB/CBC tools")
                    .arg(Arg::with_name("key")
                            .short("k")
                            .long("key")
                            .value_name("KEY")
                            .takes_value(true)
                            .required(true))
                    .arg(Arg::with_name("mode")
                            .short("e")
                            .long("encrypt")
                            .help("encrypt (instead of decrypt)"))
                    .arg(Arg::with_name("iv")
                            .short("i")
                            .long("iv")
                            .value_name("INIT")
                            .takes_value(true)
                            .help("initial value"))
                    .get_matches();

    let mut buffer = String::new();

    io::stdin().read_to_string(&mut buffer).expect("aes_ecb");

    let decoded = match base64::decode(&buffer) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    let decoded_len = decoded.len();

    let cipher = Cipher::aes_128_ecb();

    let mode = match args.occurrences_of("mode") {
            0 => Mode::Decrypt,
            _ => Mode::Encrypt,
    };

    let key = match args.value_of("key") {
            Some(text) =>
                if text.len() == 16 {
                    text.as_bytes()
                } else {
                    panic!("invalid key length!");
                },
            None => panic!("no key provided.")
    };

    let iv = match args.value_of("iv") {
            Some(text) => Some(text.as_bytes()),
            None       => None,
    };

    let mut crypter = new_crypter_unpadded(cipher, mode, key, iv);
    let mut result  = vec![0u8; decoded_len + cipher.key_len()];

    let decrypted_len = match crypter.update(&decoded, result.as_mut_slice()) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    let output = &result[0..decrypted_len];

    match mode {
        Mode::Decrypt => println!("{}", String::from_utf8_lossy(output)),
        Mode::Encrypt => println!("{}", base64::encode(output)),
    };
}

<|endoftext|>


<commit_before>use crate::config::Config;
use crate::layout::{Layout, Tall};
use crate::layout::LayoutMsg;
use crate::stack::Stack;
use crate::xlib_window_system::XlibWindowSystem;
use std::cmp;
use x11::xlib::Window;
use serde::{Serialize, Deserialize};

#[allow(dead_code)]
pub struct WorkspaceInfo {
    pub tags: String,
    pub layout: String,
    pub current: bool,
    pub visible: bool,
    pub urgent: bool,
}

pub struct WorkspaceConfig {
    pub tag: String,
    pub screen: usize,
    pub layout: Box<dyn Layout>,
}

pub enum MoveOp {
    Up,
    Down,
    Swap,
}

#[derive(Serialize, Deserialize)]
pub struct Workspace {
    pub(crate) managed: Stack,
    pub(crate) unmanaged: Stack,
    pub(crate) tag: String,
    pub screen: usize,
    pub visible: bool,
}

impl Default for Workspace {
    fn default() -> Self {
        Self {
            managed: Stack::new(Some(Tall::new(1, 0.5, 0.05))),
            unmanaged: Stack::new(None),
            tag: String::new(),
            screen: 0,
            visible: false,
        }
    }
}

impl Workspace {
    pub fn all(&self) -> Vec<Window> {
        self.unmanaged.all_windows().iter().chain(self.managed.all_windows().iter()).copied().collect()
    }

    fn all_urgent(&self) -> Vec<Window> {
        self.unmanaged.urgent.iter().chain(self.managed.urgent.iter()).copied().collect()
    }

    pub fn send_layout_message(&mut self, msg: LayoutMsg) {
        if let Some(layout) = self.managed.layout.as_mut() {
            layout.send_msg(msg);
        }
    }

    pub fn get_tag(&self) -> &str {
        &self.tag
    }

    pub fn get_screen(&self) -> usize {
        self.screen
    }

    pub fn is_unmanaged(&self, window: Window) -> bool {
        self.unmanaged.contains(window)
    }

    pub fn is_managed(&self, window: Window) -> bool {
        self.managed.contains(window)
    }

    pub fn is_urgent(&self) -> bool {
        self.managed.is_urgent() || self.unmanaged.is_urgent()
    }

    pub fn is_visible(&self) -> bool {
        self.visible
    }

    pub fn focused_window(&self) -> Option<Window> {
        if self.unmanaged.focused_window().is_none() {
            self.managed.focused_window()
        } else {
            self.unmanaged.focused_window()
        }
    }

    pub fn add_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if !xws.is_floating_window(window) {
            debug!("Add Managed: {}", window);
            self.managed.add_window(window);

            if self.unmanaged.len() > 0 {
                debug!("Restacking");
                xws.restack_windows(self.all());
            }
        } else {
            self.unmanaged.add_window(window);
            debug!("Add Unmanaged: {}", window);
        }

        if self.visible {
            self.redraw(xws, config);
            xws.show_window(window);
        }
    }

    pub fn nest_layout(&mut self, layout: Box<dyn Layout>) {
        if self.managed.len() > 1 {
            self.managed.add_container(layout);
        } else  if let Some(s) = self.managed.all_container_mut().first_mut() {
            if s.len() < 1 {
                s.layout = Some(layout);
            } else {
                self.managed.add_container(layout);
            }
        } else {
            self.managed.add_container(layout);
        }
    }

    pub fn set_urgency(&mut self, urgent: bool, xws: &XlibWindowSystem,  config: &Config, window: Window) {
        if !urgent {
            debug!("unset urgent {}", window);
            self.remove_urgent_window(window);
        } else {
            debug!("set urgent {}", window);
            if self.is_managed(window) {
                self.managed.urgent.push(window);
            } else {
                self.unmanaged.urgent.push(window);
            }
        }
        self.redraw(xws, config);
    }

    fn remove_urgent_window(&mut self, window: Window) {
        if !self.managed.remove_urgent(window) {
            self.unmanaged.remove_urgent(window);
        }
    }

    fn remove_managed(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove managed window: {}", window);
        xws.unmap_window(window);
        self.managed.remove(window);

        if self.visible {
            self.redraw(xws, config);
        }
    }

    fn remove_unmanaged(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove unmanaged window: {}", window);
        xws.unmap_window(window);
        self.unmanaged.remove(window);
        self.unmanaged.focus = if self.unmanaged.nodes.is_empty() {
            None
        } else {
            Some(self.unmanaged.nodes.len() - 1)
        };

        if self.visible {
            self.redraw(xws, config);
        }
    }

    pub fn remove_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if self.managed.contains(window) {
            trace!("Remove Managed: {}", window);
            self.remove_managed(xws, config, window);
        } else if self.unmanaged.contains(window) {
            trace!("Remove Unmanaged: {}", window);
            self.remove_unmanaged(xws, config, window);
        }
    }

    pub fn focus_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if window == 0 || self.unmanaged.focused_window() == Some(window) || self.managed.focused_window() == Some(window) {
            return;
        }

        self.remove_urgent_window(window);
        self.remove_window_highlight(xws, config);

        if self.unmanaged.contains(window) {
            self.unmanaged.focus_window(window);
        } else {
            self.managed.focus_window(window);
        }

        xws.focus_window(window, config.border_focus_color);
        self.redraw(xws, config);
    }

    pub fn remove_window_highlight(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn move_parent_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.managed.move_parent_focus(op).is_some() {
            self.redraw(xws, config);
        }
    }

    // TODO: impl managed and unmanaged focus mode incl switching
    pub fn move_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.managed.move_focus(op).is_some() {
            self.redraw(xws, config);
        }
    }

    pub fn move_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if let Some(window) = self.focused_window() {
            trace!("move window: {:?}", window);

            self.managed.move_window(op);
            self.redraw(xws, config);
        }
    }

    pub fn move_parent_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if let Some(window) = self.focused_window() {
            trace!("move parent window: {:?}", window);

            self.managed.move_parent_window(op);
            self.redraw(xws, config);
        }
    }

    pub fn contains(&self, window: Window) -> bool {
        self.all().iter().any(|&w| w == window)
    }

    pub fn unfocus(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("unfocus window: {}", window);
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn focus(&self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("focus window: {}", window);
            xws.focus_window(window, config.border_focus_color);
            xws.skip_enter_events();
        }
    }

    pub fn center_pointer(&self, xws: &XlibWindowSystem) {
        let screen = xws.get_screen_infos()[self.screen];
        xws.move_pointer((screen.x + (screen.width / 2)) as i32 - 1, (screen.y + (screen.height / 2)) as i32);
    }

    pub fn hide(&mut self, xws: &XlibWindowSystem) {
        self.visible = false;

        for &w in self.managed.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        for &w in self.unmanaged.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        if let Some(w) = self.focused_window() {
            xws.hide_window(w);
        }
    }

    pub fn show(&mut self, xws: &XlibWindowSystem, config: &Config) {
        self.visible = true;

        self.redraw(xws, config);
        for &w in self.managed.all_windows().iter() {
            xws.show_window(w);
        }

        for &w in self.unmanaged.all_windows().iter() {
            xws.show_window(w);
        }
    }

    pub fn redraw(&self, xws: &XlibWindowSystem, config: &Config) {
        trace!("Redraw...");

        let screen = xws.get_screen_infos()[self.screen];

        for (rect, window) in self.managed.apply_layout(screen, xws) {
            xws.setup_window(rect.x,
                rect.y,
                rect.width,
                rect.height,
                config.border_width,
                config.border_color,
                window);
        }

        for &window in self.unmanaged.all_windows().iter() {
            let mut rect = xws.get_geometry(window);
            rect.width = cmp::min(screen.width, rect.width + (2 * config.border_width));
            rect.height = cmp::min(screen.height, rect.height + (2 * config.border_width));

            xws.setup_window(screen.x + (screen.width - rect.width) / 2,
                            screen.y + (screen.height - rect.height) / 2,
                            rect.width,
                            rect.height,
                            config.border_width,
                            config.border_color,
                            window);
        }

        for &window in self.all_urgent().iter() {
            xws.set_window_border_color(window, config.border_urgent_color);
        }

        self.focus(xws, config);
    }
}
<commit_msg>Prevent unnecessary redraws<commit_after>use crate::config::Config;
use crate::layout::{Layout, Tall};
use crate::layout::LayoutMsg;
use crate::stack::Stack;
use crate::xlib_window_system::XlibWindowSystem;
use std::cmp;
use x11::xlib::Window;
use serde::{Serialize, Deserialize};

#[allow(dead_code)]
pub struct WorkspaceInfo {
    pub tags: String,
    pub layout: String,
    pub current: bool,
    pub visible: bool,
    pub urgent: bool,
}

pub struct WorkspaceConfig {
    pub tag: String,
    pub screen: usize,
    pub layout: Box<dyn Layout>,
}

pub enum MoveOp {
    Up,
    Down,
    Swap,
}

#[derive(Serialize, Deserialize)]
pub struct Workspace {
    pub(crate) managed: Stack,
    pub(crate) unmanaged: Stack,
    pub(crate) tag: String,
    pub screen: usize,
    pub visible: bool,
}

impl Default for Workspace {
    fn default() -> Self {
        Self {
            managed: Stack::new(Some(Tall::new(1, 0.5, 0.05))),
            unmanaged: Stack::new(None),
            tag: String::new(),
            screen: 0,
            visible: false,
        }
    }
}

impl Workspace {
    pub fn all(&self) -> Vec<Window> {
        self.unmanaged.all_windows().iter().chain(self.managed.all_windows().iter()).copied().collect()
    }

    fn all_urgent(&self) -> Vec<Window> {
        self.unmanaged.urgent.iter().chain(self.managed.urgent.iter()).copied().collect()
    }

    pub fn send_layout_message(&mut self, msg: LayoutMsg) {
        if let Some(layout) = self.managed.layout.as_mut() {
            layout.send_msg(msg);
        }
    }

    pub fn get_tag(&self) -> &str {
        &self.tag
    }

    pub fn get_screen(&self) -> usize {
        self.screen
    }

    pub fn is_unmanaged(&self, window: Window) -> bool {
        self.unmanaged.contains(window)
    }

    pub fn is_managed(&self, window: Window) -> bool {
        self.managed.contains(window)
    }

    pub fn is_urgent(&self) -> bool {
        self.managed.is_urgent() || self.unmanaged.is_urgent()
    }

    pub fn is_visible(&self) -> bool {
        self.visible
    }

    pub fn focused_window(&self) -> Option<Window> {
        if self.unmanaged.focused_window().is_none() {
            self.managed.focused_window()
        } else {
            self.unmanaged.focused_window()
        }
    }

    pub fn add_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if !xws.is_floating_window(window) {
            debug!("Add Managed: {}", window);
            self.managed.add_window(window);

            if self.unmanaged.len() > 0 {
                debug!("Restacking");
                xws.restack_windows(self.all());
            }
        } else {
            self.unmanaged.add_window(window);
            debug!("Add Unmanaged: {}", window);
        }

        if self.visible {
            self.redraw(xws, config);
            xws.show_window(window);
        }
    }

    pub fn nest_layout(&mut self, layout: Box<dyn Layout>) {
        if self.managed.len() > 1 {
            self.managed.add_container(layout);
        } else  if let Some(s) = self.managed.all_container_mut().first_mut() {
            if s.len() < 1 {
                s.layout = Some(layout);
            } else {
                self.managed.add_container(layout);
            }
        } else {
            self.managed.add_container(layout);
        }
    }

    pub fn set_urgency(&mut self, urgent: bool, xws: &XlibWindowSystem,  config: &Config, window: Window) {
        if !urgent {
            debug!("unset urgent {}", window);
            self.remove_urgent_window(window);
        } else {
            debug!("set urgent {}", window);
            if self.is_managed(window) {
                self.managed.urgent.push(window);
            } else {
                self.unmanaged.urgent.push(window);
            }
        }
        self.redraw(xws, config);
    }

    fn remove_urgent_window(&mut self, window: Window) {
        if !self.managed.remove_urgent(window) {
            self.unmanaged.remove_urgent(window);
        }
    }

    fn remove_managed(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove managed window: {}", window);
        xws.unmap_window(window);
        self.managed.remove(window);

        if self.visible {
            self.redraw(xws, config);
        }
    }

    fn remove_unmanaged(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove unmanaged window: {}", window);
        xws.unmap_window(window);
        self.unmanaged.remove(window);
        self.unmanaged.focus = if self.unmanaged.nodes.is_empty() {
            None
        } else {
            Some(self.unmanaged.nodes.len() - 1)
        };

        if self.visible {
            self.redraw(xws, config);
        }
    }

    pub fn remove_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if self.managed.contains(window) {
            trace!("Remove Managed: {}", window);
            self.remove_managed(xws, config, window);
        } else if self.unmanaged.contains(window) {
            trace!("Remove Unmanaged: {}", window);
            self.remove_unmanaged(xws, config, window);
        }
    }

    pub fn focus_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if window == 0 ||
           !self.is_visible() ||
           self.unmanaged.focused_window() == Some(window) ||
           self.managed.focused_window() == Some(window)
        {
            return;
        }

        self.remove_urgent_window(window);
        self.remove_window_highlight(xws, config);

        if self.unmanaged.contains(window) {
            self.unmanaged.focus_window(window);
        } else {
            self.managed.focus_window(window);
        }

        xws.focus_window(window, config.border_focus_color);
        self.redraw(xws, config);
    }

    pub fn remove_window_highlight(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn move_parent_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.managed.move_parent_focus(op).is_some() {
            self.redraw(xws, config);
        }
    }

    // TODO: impl managed and unmanaged focus mode incl switching
    pub fn move_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.focused_window() != self.managed.move_focus(op) {
            self.redraw(xws, config);
        }
    }

    pub fn move_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if let Some(window) = self.focused_window() {
            trace!("move window: {:?}", window);

            self.managed.move_window(op);
            self.redraw(xws, config);
        }
    }

    pub fn move_parent_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if let Some(window) = self.focused_window() {
            trace!("move parent window: {:?}", window);

            self.managed.move_parent_window(op);
            self.redraw(xws, config);
        }
    }

    pub fn contains(&self, window: Window) -> bool {
        self.all().iter().any(|&w| w == window)
    }

    pub fn unfocus(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("unfocus window: {}", window);
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn focus(&self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("focus window: {}", window);
            xws.focus_window(window, config.border_focus_color);
            xws.skip_enter_events();
        }
    }

    pub fn center_pointer(&self, xws: &XlibWindowSystem) {
        let screen = xws.get_screen_infos()[self.screen];
        xws.move_pointer((screen.x + (screen.width / 2)) as i32 - 1, (screen.y + (screen.height / 2)) as i32);
    }

    pub fn hide(&mut self, xws: &XlibWindowSystem) {
        self.visible = false;

        for &w in self.managed.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        for &w in self.unmanaged.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        if let Some(w) = self.focused_window() {
            xws.hide_window(w);
        }
    }

    pub fn show(&mut self, xws: &XlibWindowSystem, config: &Config) {
        self.visible = true;

        self.redraw(xws, config);
        for &w in self.managed.all_windows().iter() {
            xws.show_window(w);
        }

        for &w in self.unmanaged.all_windows().iter() {
            xws.show_window(w);
        }
    }

    pub fn redraw(&self, xws: &XlibWindowSystem, config: &Config) {
        trace!("Redraw...");

        if !self.is_visible() {
            return;
        }

        let screen = xws.get_screen_infos()[self.screen];

        for (rect, window) in self.managed.apply_layout(screen, xws) {
            xws.setup_window(rect.x,
                rect.y,
                rect.width,
                rect.height,
                config.border_width,
                config.border_color,
                window);
        }

        for &window in self.unmanaged.all_windows().iter() {
            let mut rect = xws.get_geometry(window);
            rect.width = cmp::min(screen.width, rect.width + (2 * config.border_width));
            rect.height = cmp::min(screen.height, rect.height + (2 * config.border_width));

            xws.setup_window(screen.x + (screen.width - rect.width) / 2,
                            screen.y + (screen.height - rect.height) / 2,
                            rect.width,
                            rect.height,
                            config.border_width,
                            config.border_color,
                            window);
        }

        for &window in self.all_urgent().iter() {
            xws.set_window_border_color(window, config.border_urgent_color);
        }

        self.focus(xws, config);
    }
}
<|endoftext|>


<commit_before>use crate::ansi::AnsiString;
use crate::event::{Event, EventArg, EventHandler, UpdateScreen};
use crate::item::Item;
use crate::spinlock::SpinLock;
use crate::util::inject_command;
use derive_builder::Builder;
use nix::libc;
use regex::Regex;
use std::cmp::{max, min};
use std::env;
use std::io::{BufRead, BufReader};
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use tuikit::prelude::*;

const TAB_STOP: usize = 8;
const DELIMITER_STR: &str = r"[\t\n ]+";

pub struct Previewer {
    tx_preview: Sender<(Event, PreviewInput)>,
    content_lines: Arc<SpinLock<Vec<AnsiString>>>,

    width: AtomicUsize,
    height: AtomicUsize,
    hscroll_offset: usize,
    vscroll_offset: usize,
    wrap: bool,

    prev_item: Option<Arc<Item>>,
    preview_cmd: Option<String>,
    delimiter: Regex,
    thread_previewer: Option<JoinHandle<()>>,
}

impl Previewer {
    pub fn new(preview_cmd: Option<String>) -> Self {
        let content_lines = Arc::new(SpinLock::new(Vec::new()));
        let (tx_preview, rx_preview) = channel();
        let content_clone = content_lines.clone();
        let thread_previewer = thread::spawn(move || run(rx_preview, content_clone));

        Self {
            tx_preview,
            content_lines,

            width: AtomicUsize::new(80),
            height: AtomicUsize::new(60),
            hscroll_offset: 0,
            vscroll_offset: 0,
            wrap: false,

            prev_item: None,
            preview_cmd,
            delimiter: Regex::new(DELIMITER_STR).unwrap(),
            thread_previewer: Some(thread_previewer),
        }
    }

    pub fn wrap(mut self, wrap: bool) -> Self {
        self.wrap = wrap;
        self
    }

    pub fn delimiter(mut self, delimiter: Regex) -> Self {
        self.delimiter = delimiter;
        self
    }

    pub fn on_item_change(&mut self, item: Arc<Item>) {
        if self
            .prev_item
            .as_ref()
            .map(|prev| prev.get_output_text() == item.get_output_text())
            .unwrap_or(false)
        {
            return;
        }

        let cmd = self.preview_cmd.as_ref().expect("previewer: invalid preview command");

        self.prev_item.replace(item);
        let text = self.prev_item.as_ref().map(|item| item.get_output_text()).unwrap();
        let cmd = inject_command(&cmd, &self.delimiter, &text).to_string();
        let columns = self.width.load(Ordering::Relaxed);
        let lines = self.height.load(Ordering::Relaxed);

        let request = PreviewInput { cmd, columns, lines };
        let _ = self.tx_preview.send((Event::EvPreviewRequest, request));

        self.hscroll_offset = 0;
        self.vscroll_offset = 0;
    }

    fn act_scroll_down(&mut self, diff: i32) {
        if diff > 0 {
            self.vscroll_offset += diff as usize;
        } else {
            self.vscroll_offset -= min((-diff) as usize, self.vscroll_offset);
        }

        self.vscroll_offset = min(self.vscroll_offset, max(self.content_lines.lock().len(), 1) - 1);
    }

    fn act_scroll_right(&mut self, diff: i32) {
        if diff > 0 {
            self.hscroll_offset += diff as usize;
        } else {
            self.hscroll_offset -= min((-diff) as usize, self.hscroll_offset);
        }
    }

    fn act_toggle_wrap(&mut self) {
        self.wrap = !self.wrap;
    }
}

impl Drop for Previewer {
    fn drop(&mut self) {
        let request = PreviewInput {
            cmd: "".to_string(),
            columns: 0,
            lines: 0,
        };
        let _ = self.tx_preview.send((Event::EvActAbort, request));
        self.thread_previewer.take().map(|handle| handle.join());
    }
}

impl EventHandler for Previewer {
    fn accept_event(&self, event: Event) -> bool {
        use crate::event::Event::*;
        match event {
            EvActTogglePreviewWrap
            | EvActPreviewUp
            | EvActPreviewDown
            | EvActPreviewLeft
            | EvActPreviewRight
            | EvActPreviewPageUp
            | EvActPreviewPageDown => true,
            _ => false,
        }
    }

    fn handle(&mut self, event: Event, _arg: &EventArg) -> UpdateScreen {
        use crate::event::Event::*;
        let height = self.height.load(Ordering::Relaxed);
        match event {
            EvActTogglePreviewWrap => self.act_toggle_wrap(),
            EvActPreviewUp => self.act_scroll_down(-1),
            EvActPreviewDown => self.act_scroll_down(1),
            EvActPreviewLeft => self.act_scroll_right(-1),
            EvActPreviewRight => self.act_scroll_right(1),
            EvActPreviewPageUp => self.act_scroll_down(-(height as i32)),
            EvActPreviewPageDown => self.act_scroll_down(height as i32),
            _ => {}
        }
        UpdateScreen::Redraw
    }
}

impl Draw for Previewer {
    fn draw(&self, canvas: &mut Canvas) -> Result<()> {
        canvas.clear()?;
        let (screen_width, screen_height) = canvas.size()?;

        if screen_width == 0 || screen_height == 0 {
            return Ok(());
        }

        self.width.store(screen_width, Ordering::Relaxed);
        self.height.store(screen_height, Ordering::Relaxed);

        let content = self.content_lines.lock();

        let mut printer = PrinterBuilder::default()
            .width(screen_width)
            .height(screen_height)
            .skip_rows(self.vscroll_offset)
            .skip_cols(self.hscroll_offset)
            .wrap(self.wrap)
            .build()
            .unwrap();
        printer.print_lines(canvas, &content);

        // print the vscroll info
        let status = format!("{}/{}", self.vscroll_offset + 1, content.len());
        let col = max(status.len() + 1, self.width.load(Ordering::Relaxed)) - status.len() - 1;
        canvas.print_with_attr(
            0,
            col,
            &status,
            Attr {
                effect: Effect::REVERSE,
                ..Attr::default()
            },
        )?;

        Ok(())
    }
}

#[derive(Debug)]
pub struct PreviewInput {
    pub cmd: String,
    pub lines: usize,
    pub columns: usize,
}

struct PreviewThread {
    pid: u32,
    thread: thread::JoinHandle<()>,
    stopped: Arc<AtomicBool>,
}

impl PreviewThread {
    fn kill(self) {
        if !self.stopped.load(Ordering::Relaxed) {
            unsafe { libc::kill(self.pid as i32, libc::SIGKILL) };
        }
        self.thread.join().expect("Failed to join Preview process");
    }
}

pub fn run(rx_preview: Receiver<(Event, PreviewInput)>, content: Arc<SpinLock<Vec<AnsiString>>>) {
    let mut preview_thread: Option<PreviewThread> = None;
    while let Ok((_ev, mut new_prv)) = rx_preview.recv() {
        if preview_thread.is_some() {
            preview_thread.unwrap().kill();
            preview_thread = None;
        }

        if _ev == Event::EvActAbort {
            return;
        }

        // Try to empty the channel. Happens when spamming up/down or typing fast.
        while let Ok((_ev, new_prv1)) = rx_preview.try_recv() {
            if _ev == Event::EvActAbort {
                return;
            }
            new_prv = new_prv1;
        }

        let cmd = &new_prv.cmd;
        if cmd == "" {
            continue;
        }

        let shell = env::var("SHELL").unwrap_or_else(|_| "sh".to_string());
        let spawned = Command::new(shell)
            .env("LINES", new_prv.lines.to_string())
            .env("COLUMNS", new_prv.columns.to_string())
            .arg("-c")
            .arg(&cmd)
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn();

        match spawned {
            Err(err) => {
                let astdout = AnsiString::from_str(format!("Failed to spawn: {} / {}", cmd, err).as_str());
                *content.lock() = vec![astdout];
                preview_thread = None;
            }
            Ok(spawned) => {
                let pid = spawned.id();
                let stopped = Arc::new(AtomicBool::new(false));
                let stopped_clone = stopped.clone();
                let content_clone = content.clone();
                let thread = thread::spawn(move || wait_and_update(spawned, content_clone, stopped_clone));
                preview_thread = Some(PreviewThread { pid, thread, stopped });
            }
        }
    }
}

fn wait_and_update(
    mut spawned: std::process::Child,
    content: Arc<SpinLock<Vec<AnsiString>>>,
    stopped: Arc<AtomicBool>,
) {
    let status = spawned.wait();
    stopped.store(true, Ordering::SeqCst);

    if status.is_err() {
        return;
    }
    let status = status.unwrap();

    // Capture stderr in case users want to debug ...
    let mut pipe: Box<dyn BufRead> = if status.success() {
        Box::new(BufReader::new(spawned.stdout.unwrap()))
    } else {
        Box::new(BufReader::new(spawned.stderr.unwrap()))
    };

    let mut lines = Vec::new();

    let mut res = String::new();
    while let Ok(n) = pipe.read_line(&mut res) {
        if n == 0 {
            break;
        }

        let astdout = AnsiString::from_str(&res);
        lines.push(astdout);
        res.clear();
    }

    *content.lock() = lines;
}

#[derive(Builder, Default, Debug)]
#[builder(default)]
struct Printer {
    #[builder(setter(skip))]
    row: usize,
    #[builder(setter(skip))]
    col: usize,
    skip_rows: usize,
    skip_cols: usize,
    wrap: bool,
    width: usize,
    height: usize,
}

impl Printer {
    pub fn print_lines(&mut self, canvas: &mut Canvas, content: &[AnsiString]) {
        for (line_no, line) in content.iter().enumerate() {
            if line_no < self.skip_rows {
                self.move_to_next_line();
                continue;
            } else if self.row >= self.skip_rows + self.height {
                break;
            }

            for (ch, attr) in line.iter() {
                let _ = self.print_char_with_attr(canvas, ch, attr);

                // skip if the content already exceeded the canvas
                if !self.wrap && self.col >= self.width + self.skip_cols {
                    break;
                }

                if self.row >= self.skip_rows + self.height {
                    break;
                }
            }

            self.move_to_next_line();
        }
    }

    fn move_to_next_line(&mut self) {
        self.row += 1;
        self.col = 0;
    }

    fn print_char_with_attr(&mut self, canvas: &mut Canvas, ch: char, attr: Attr) -> Result<()> {
        match ch {
            '\n' | '\r' | '\0' => {}
            '\t' => {
                // handle tabstop
                let rest = TAB_STOP - self.col % TAB_STOP;
                let rest = min(rest, max(self.col, self.width) - self.col);
                for _ in 0..rest {
                    self.print_char_raw(canvas, ' ', attr)?;
                }
            }

            ch => {
                self.print_char_raw(canvas, ch, attr)?;
            }
        }
        Ok(())
    }

    fn print_char_raw(&mut self, canvas: &mut Canvas, ch: char, attr: Attr) -> Result<()> {
        if self.row < self.skip_rows || self.row >= self.height + self.skip_rows {
            return Ok(());
        }

        if self.wrap {
            // if wrap is enabled, hscroll is discarded
            self.col += self.adjust_scroll_print(canvas, ch, attr)?;
            if self.col == self.width {
                let _ = self.move_to_next_line();
            } else if self.col > self.width {
                // re-print the wide character
                let _ = self.move_to_next_line();
                self.col += self.adjust_scroll_print(canvas, ch, attr)?;
            }
        } else {
            self.col += self.adjust_scroll_print(canvas, ch, attr)?;
        }

        Ok(())
    }

    fn adjust_scroll_print(&self, canvas: &mut Canvas, ch: char, attr: Attr) -> Result<usize> {
        if self.row < self.skip_rows || self.col < self.skip_cols {
            canvas.put_char_with_attr(usize::max_value(), usize::max_value(), ch, attr)
        } else {
            canvas.put_char_with_attr(self.row - self.skip_rows, self.col - self.skip_cols, ch, attr)
        }
    }
}
<commit_msg>fix #156 previewer dies on large chuck of input<commit_after>use crate::ansi::AnsiString;
use crate::event::{Event, EventArg, EventHandler, UpdateScreen};
use crate::item::Item;
use crate::spinlock::SpinLock;
use crate::util::inject_command;
use derive_builder::Builder;
use nix::libc;
use regex::Regex;
use std::cmp::{max, min};
use std::env;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use tuikit::prelude::*;

const TAB_STOP: usize = 8;
const DELIMITER_STR: &str = r"[\t\n ]+";

pub struct Previewer {
    tx_preview: Sender<(Event, PreviewInput)>,
    content_lines: Arc<SpinLock<Vec<AnsiString>>>,

    width: AtomicUsize,
    height: AtomicUsize,
    hscroll_offset: usize,
    vscroll_offset: usize,
    wrap: bool,

    prev_item: Option<Arc<Item>>,
    preview_cmd: Option<String>,
    delimiter: Regex,
    thread_previewer: Option<JoinHandle<()>>,
}

impl Previewer {
    pub fn new(preview_cmd: Option<String>) -> Self {
        let content_lines = Arc::new(SpinLock::new(Vec::new()));
        let (tx_preview, rx_preview) = channel();
        let content_clone = content_lines.clone();
        let thread_previewer = thread::spawn(move || run(rx_preview, content_clone));

        Self {
            tx_preview,
            content_lines,

            width: AtomicUsize::new(80),
            height: AtomicUsize::new(60),
            hscroll_offset: 0,
            vscroll_offset: 0,
            wrap: false,

            prev_item: None,
            preview_cmd,
            delimiter: Regex::new(DELIMITER_STR).unwrap(),
            thread_previewer: Some(thread_previewer),
        }
    }

    pub fn wrap(mut self, wrap: bool) -> Self {
        self.wrap = wrap;
        self
    }

    pub fn delimiter(mut self, delimiter: Regex) -> Self {
        self.delimiter = delimiter;
        self
    }

    pub fn on_item_change(&mut self, item: Arc<Item>) {
        if self
            .prev_item
            .as_ref()
            .map(|prev| prev.get_output_text() == item.get_output_text())
            .unwrap_or(false)
        {
            return;
        }

        let cmd = self.preview_cmd.as_ref().expect("previewer: invalid preview command");

        self.prev_item.replace(item);
        let text = self.prev_item.as_ref().map(|item| item.get_output_text()).unwrap();
        let cmd = inject_command(&cmd, &self.delimiter, &text).to_string();
        let columns = self.width.load(Ordering::Relaxed);
        let lines = self.height.load(Ordering::Relaxed);

        let request = PreviewInput { cmd, columns, lines };
        let _ = self.tx_preview.send((Event::EvPreviewRequest, request));

        self.hscroll_offset = 0;
        self.vscroll_offset = 0;
    }

    fn act_scroll_down(&mut self, diff: i32) {
        if diff > 0 {
            self.vscroll_offset += diff as usize;
        } else {
            self.vscroll_offset -= min((-diff) as usize, self.vscroll_offset);
        }

        self.vscroll_offset = min(self.vscroll_offset, max(self.content_lines.lock().len(), 1) - 1);
    }

    fn act_scroll_right(&mut self, diff: i32) {
        if diff > 0 {
            self.hscroll_offset += diff as usize;
        } else {
            self.hscroll_offset -= min((-diff) as usize, self.hscroll_offset);
        }
    }

    fn act_toggle_wrap(&mut self) {
        self.wrap = !self.wrap;
    }
}

impl Drop for Previewer {
    fn drop(&mut self) {
        let request = PreviewInput {
            cmd: "".to_string(),
            columns: 0,
            lines: 0,
        };
        let _ = self.tx_preview.send((Event::EvActAbort, request));
        self.thread_previewer.take().map(|handle| handle.join());
    }
}

impl EventHandler for Previewer {
    fn accept_event(&self, event: Event) -> bool {
        use crate::event::Event::*;
        match event {
            EvActTogglePreviewWrap
            | EvActPreviewUp
            | EvActPreviewDown
            | EvActPreviewLeft
            | EvActPreviewRight
            | EvActPreviewPageUp
            | EvActPreviewPageDown => true,
            _ => false,
        }
    }

    fn handle(&mut self, event: Event, _arg: &EventArg) -> UpdateScreen {
        use crate::event::Event::*;
        let height = self.height.load(Ordering::Relaxed);
        match event {
            EvActTogglePreviewWrap => self.act_toggle_wrap(),
            EvActPreviewUp => self.act_scroll_down(-1),
            EvActPreviewDown => self.act_scroll_down(1),
            EvActPreviewLeft => self.act_scroll_right(-1),
            EvActPreviewRight => self.act_scroll_right(1),
            EvActPreviewPageUp => self.act_scroll_down(-(height as i32)),
            EvActPreviewPageDown => self.act_scroll_down(height as i32),
            _ => {}
        }
        UpdateScreen::Redraw
    }
}

impl Draw for Previewer {
    fn draw(&self, canvas: &mut Canvas) -> Result<()> {
        canvas.clear()?;
        let (screen_width, screen_height) = canvas.size()?;

        if screen_width == 0 || screen_height == 0 {
            return Ok(());
        }

        self.width.store(screen_width, Ordering::Relaxed);
        self.height.store(screen_height, Ordering::Relaxed);

        let content = self.content_lines.lock();

        let mut printer = PrinterBuilder::default()
            .width(screen_width)
            .height(screen_height)
            .skip_rows(self.vscroll_offset)
            .skip_cols(self.hscroll_offset)
            .wrap(self.wrap)
            .build()
            .unwrap();
        printer.print_lines(canvas, &content);

        // print the vscroll info
        let status = format!("{}/{}", self.vscroll_offset + 1, content.len());
        let col = max(status.len() + 1, self.width.load(Ordering::Relaxed)) - status.len() - 1;
        canvas.print_with_attr(
            0,
            col,
            &status,
            Attr {
                effect: Effect::REVERSE,
                ..Attr::default()
            },
        )?;

        Ok(())
    }
}

#[derive(Debug)]
pub struct PreviewInput {
    pub cmd: String,
    pub lines: usize,
    pub columns: usize,
}

struct PreviewThread {
    pid: u32,
    thread: thread::JoinHandle<()>,
    stopped: Arc<AtomicBool>,
}

impl PreviewThread {
    fn kill(self) {
        if !self.stopped.load(Ordering::Relaxed) {
            unsafe { libc::kill(self.pid as i32, libc::SIGKILL) };
        }
        self.thread.join().expect("Failed to join Preview process");
    }
}

pub fn run(rx_preview: Receiver<(Event, PreviewInput)>, content: Arc<SpinLock<Vec<AnsiString>>>) {
    let mut preview_thread: Option<PreviewThread> = None;
    while let Ok((_ev, mut new_prv)) = rx_preview.recv() {
        if preview_thread.is_some() {
            preview_thread.unwrap().kill();
            preview_thread = None;
        }

        if _ev == Event::EvActAbort {
            return;
        }

        // Try to empty the channel. Happens when spamming up/down or typing fast.
        while let Ok((_ev, new_prv1)) = rx_preview.try_recv() {
            if _ev == Event::EvActAbort {
                return;
            }
            new_prv = new_prv1;
        }

        let cmd = &new_prv.cmd;
        if cmd == "" {
            continue;
        }

        let shell = env::var("SHELL").unwrap_or_else(|_| "sh".to_string());
        let spawned = Command::new(shell)
            .env("LINES", new_prv.lines.to_string())
            .env("COLUMNS", new_prv.columns.to_string())
            .arg("-c")
            .arg(&cmd)
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn();

        match spawned {
            Err(err) => {
                let astdout = AnsiString::from_str(format!("Failed to spawn: {} / {}", cmd, err).as_str());
                *content.lock() = vec![astdout];
                preview_thread = None;
            }
            Ok(spawned) => {
                let pid = spawned.id();
                let stopped = Arc::new(AtomicBool::new(false));
                let stopped_clone = stopped.clone();
                let content_clone = content.clone();
                let thread = thread::spawn(move || wait_and_update(spawned, content_clone, stopped_clone));
                preview_thread = Some(PreviewThread { pid, thread, stopped });
            }
        }
    }
}

fn wait_and_update(spawned: std::process::Child, content: Arc<SpinLock<Vec<AnsiString>>>, stopped: Arc<AtomicBool>) {
    let output = spawned.wait_with_output();
    stopped.store(true, Ordering::SeqCst);

    if output.is_err() {
        return;
    }

    let output = output.unwrap();

    // Capture stderr in case users want to debug ...
    let out_str = String::from_utf8_lossy(if output.status.success() {
        &output.stdout
    } else {
        &output.stderr
    });

    let lines = out_str.lines().map(AnsiString::from_str).collect();
    *content.lock() = lines;
}

#[derive(Builder, Default, Debug)]
#[builder(default)]
struct Printer {
    #[builder(setter(skip))]
    row: usize,
    #[builder(setter(skip))]
    col: usize,
    skip_rows: usize,
    skip_cols: usize,
    wrap: bool,
    width: usize,
    height: usize,
}

impl Printer {
    pub fn print_lines(&mut self, canvas: &mut Canvas, content: &[AnsiString]) {
        for (line_no, line) in content.iter().enumerate() {
            if line_no < self.skip_rows {
                self.move_to_next_line();
                continue;
            } else if self.row >= self.skip_rows + self.height {
                break;
            }

            for (ch, attr) in line.iter() {
                let _ = self.print_char_with_attr(canvas, ch, attr);

                // skip if the content already exceeded the canvas
                if !self.wrap && self.col >= self.width + self.skip_cols {
                    break;
                }

                if self.row >= self.skip_rows + self.height {
                    break;
                }
            }

            self.move_to_next_line();
        }
    }

    fn move_to_next_line(&mut self) {
        self.row += 1;
        self.col = 0;
    }

    fn print_char_with_attr(&mut self, canvas: &mut Canvas, ch: char, attr: Attr) -> Result<()> {
        match ch {
            '\n' | '\r' | '\0' => {}
            '\t' => {
                // handle tabstop
                let rest = TAB_STOP - self.col % TAB_STOP;
                let rest = min(rest, max(self.col, self.width) - self.col);
                for _ in 0..rest {
                    self.print_char_raw(canvas, ' ', attr)?;
                }
            }

            ch => {
                self.print_char_raw(canvas, ch, attr)?;
            }
        }
        Ok(())
    }

    fn print_char_raw(&mut self, canvas: &mut Canvas, ch: char, attr: Attr) -> Result<()> {
        if self.row < self.skip_rows || self.row >= self.height + self.skip_rows {
            return Ok(());
        }

        if self.wrap {
            // if wrap is enabled, hscroll is discarded
            self.col += self.adjust_scroll_print(canvas, ch, attr)?;
            if self.col == self.width {
                let _ = self.move_to_next_line();
            } else if self.col > self.width {
                // re-print the wide character
                let _ = self.move_to_next_line();
                self.col += self.adjust_scroll_print(canvas, ch, attr)?;
            }
        } else {
            self.col += self.adjust_scroll_print(canvas, ch, attr)?;
        }

        Ok(())
    }

    fn adjust_scroll_print(&self, canvas: &mut Canvas, ch: char, attr: Attr) -> Result<usize> {
        if self.row < self.skip_rows || self.col < self.skip_cols {
            canvas.put_char_with_attr(usize::max_value(), usize::max_value(), ch, attr)
        } else {
            canvas.put_char_with_attr(self.row - self.skip_rows, self.col - self.skip_cols, ch, attr)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Colin Finck, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Implementation of the Allocator for dynamically allocating heap memory
//! in the kernel.
//!
//! The data structures used to manage heap memory require dynamic memory allocations
//! themselves. To solve this chicken-egg problem, eduOS-rs first uses a
//! "Bootstrap Allocator". This is a simple single-threaded implementation using some
//! preallocated space, along with an index variable.
//! Freed memory is never reused, but this can be neglected for bootstrapping.

use crate::arch::{BasePageSize, PageSize};
use crate::consts::*;
use crate::logging::*;
use crate::mm;
use alloc::alloc::Layout;
use core::alloc::GlobalAlloc;

/// Size of the preallocated space for the Bootstrap Allocator.
const BOOTSTRAP_HEAP_SIZE: usize = 0x1000;

/// Alignment of pointers returned by the Bootstrap Allocator.
/// Note that you also have to align the HermitAllocatorInfo structure!
const BOOTSTRAP_HEAP_ALIGNMENT: usize = CACHE_LINE;

/// The Allocator structure is immutable, so we need this helper structure
/// for our allocator information.
#[repr(align(64))]
#[repr(C)]
struct AllocatorInfo {
	heap: [u8; BOOTSTRAP_HEAP_SIZE],
	index: usize,
	is_bootstrapping: bool,
}

impl AllocatorInfo {
	const fn new() -> AllocatorInfo {
		AllocatorInfo {
			heap: [0xCC; BOOTSTRAP_HEAP_SIZE],
			index: 0,
			is_bootstrapping: true,
		}
	}

	fn switch_to_system_allocator(&mut self) {
		info!("Switching to the System Allocator");
		self.is_bootstrapping = false;
	}
}

static mut ALLOCATOR_INFO: AllocatorInfo = AllocatorInfo::new();

pub struct Allocator;

unsafe impl<'a> GlobalAlloc for &'a Allocator {
	unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
		if ALLOCATOR_INFO.is_bootstrapping {
			alloc_bootstrap(layout)
		} else {
			alloc_system(layout)
		}
	}

	unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
		let address = ptr as usize;

		// We never deallocate memory of the Bootstrap Allocator.
		// It would only increase the management burden and we wouldn't save
		// any significant amounts of memory.
		// So check if this is a pointer allocated by the System Allocator.
		debug!("Deallocate {} bytes at {:#X}", layout.size(), ptr as usize);
		if address >= mm::kernel_end_address() {
			dealloc_system(address, layout);
		}
	}
}

/// An allocation using the always available Bootstrap Allocator.
unsafe fn alloc_bootstrap(layout: Layout) -> *mut u8 {
	let ptr = &mut ALLOCATOR_INFO.heap[ALLOCATOR_INFO.index] as *mut u8;
	debug!(
		"Allocating {:#X} bytes at {:#X}, index {}",
		layout.size(),
		ptr as usize,
		ALLOCATOR_INFO.index
	);

	if ALLOCATOR_INFO.index + layout.size() >= BOOTSTRAP_HEAP_SIZE {
		panic!("Bootstrap Allocator Overflow! Increase BOOTSTRAP_HEAP_SIZE.");
	}

	// Bump the heap index and align it up to the next BOOTSTRAP_HEAP_ALIGNMENT boundary.
	ALLOCATOR_INFO.index = align_up!(
		ALLOCATOR_INFO.index + layout.size(),
		BOOTSTRAP_HEAP_ALIGNMENT
	);

	ptr
}

/// An allocation using the initialized System Allocator.
fn alloc_system(layout: Layout) -> *mut u8 {
	debug!(
		"Allocating {} bytes using the System Allocator",
		layout.size()
	);

	let size = align_up!(layout.size(), BasePageSize::SIZE);
	mm::allocate(size, true) as *mut u8
}

/// A deallocation using the initialized System Allocator.
fn dealloc_system(virtual_address: usize, layout: Layout) {
	debug!(
		"Deallocating {} bytes at {:#X} using the System Allocator",
		layout.size(),
		virtual_address
	);

	let size = align_up!(layout.size(), BasePageSize::SIZE);
	mm::deallocate(virtual_address, size);
}

pub fn init() {
	unsafe {
		ALLOCATOR_INFO.switch_to_system_allocator();
	}
}
<commit_msg>disable temporary system allocator<commit_after>// Copyright (c) 2017 Colin Finck, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Implementation of the Allocator for dynamically allocating heap memory
//! in the kernel.
//!
//! The data structures used to manage heap memory require dynamic memory allocations
//! themselves. To solve this chicken-egg problem, eduOS-rs first uses a
//! "Bootstrap Allocator". This is a simple single-threaded implementation using some
//! preallocated space, along with an index variable.
//! Freed memory is never reused, but this can be neglected for bootstrapping.

use crate::arch::{BasePageSize, PageSize};
use crate::consts::*;
use crate::logging::*;
use crate::mm;
use alloc::alloc::Layout;
use core::alloc::GlobalAlloc;

/// Size of the preallocated space for the Bootstrap Allocator.
const BOOTSTRAP_HEAP_SIZE: usize = 0x140000;

/// Alignment of pointers returned by the Bootstrap Allocator.
/// Note that you also have to align the HermitAllocatorInfo structure!
const BOOTSTRAP_HEAP_ALIGNMENT: usize = CACHE_LINE;

/// The Allocator structure is immutable, so we need this helper structure
/// for our allocator information.
#[repr(align(64))]
#[repr(C)]
struct AllocatorInfo {
	heap: [u8; BOOTSTRAP_HEAP_SIZE],
	index: usize,
	is_bootstrapping: bool,
}

impl AllocatorInfo {
	const fn new() -> AllocatorInfo {
		AllocatorInfo {
			heap: [0x00; BOOTSTRAP_HEAP_SIZE],
			index: 0,
			is_bootstrapping: true,
		}
	}

	fn switch_to_system_allocator(&mut self) {
		info!("Switching to the System Allocator");
		self.is_bootstrapping = false;
	}
}

static mut ALLOCATOR_INFO: AllocatorInfo = AllocatorInfo::new();

pub struct Allocator;

unsafe impl<'a> GlobalAlloc for &'a Allocator {
	unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
		if ALLOCATOR_INFO.is_bootstrapping {
			alloc_bootstrap(layout)
		} else {
			alloc_system(layout)
		}
	}

	unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
		let address = ptr as usize;

		// We never deallocate memory of the Bootstrap Allocator.
		// It would only increase the management burden and we wouldn't save
		// any significant amounts of memory.
		// So check if this is a pointer allocated by the System Allocator.
		debug!("Deallocate {} bytes at {:#X}", layout.size(), ptr as usize);
		if address >= mm::kernel_end_address() {
			dealloc_system(address, layout);
		}
	}
}

/// An allocation using the always available Bootstrap Allocator.
unsafe fn alloc_bootstrap(layout: Layout) -> *mut u8 {
	let ptr = &mut ALLOCATOR_INFO.heap[ALLOCATOR_INFO.index] as *mut u8;
	debug!(
		"Allocating {:#X} bytes at {:#X}, index {}",
		layout.size(),
		ptr as usize,
		ALLOCATOR_INFO.index
	);

	if ALLOCATOR_INFO.index + layout.size() >= BOOTSTRAP_HEAP_SIZE {
		panic!("Bootstrap Allocator Overflow! Increase BOOTSTRAP_HEAP_SIZE.");
	}

	// Bump the heap index and align it up to the next BOOTSTRAP_HEAP_ALIGNMENT boundary.
	ALLOCATOR_INFO.index = align_up!(
		ALLOCATOR_INFO.index + layout.size(),
		BOOTSTRAP_HEAP_ALIGNMENT
	);

	ptr
}

/// An allocation using the initialized System Allocator.
fn alloc_system(layout: Layout) -> *mut u8 {
	debug!(
		"Allocating {} bytes using the System Allocator",
		layout.size()
	);

	let size = align_up!(layout.size(), BasePageSize::SIZE);
	mm::allocate(size, true) as *mut u8
}

/// A deallocation using the initialized System Allocator.
fn dealloc_system(virtual_address: usize, layout: Layout) {
	debug!(
		"Deallocating {} bytes at {:#X} using the System Allocator",
		layout.size(),
		virtual_address
	);

	let size = align_up!(layout.size(), BasePageSize::SIZE);
	mm::deallocate(virtual_address, size);
}

pub fn init() {
	/*unsafe {
		ALLOCATOR_INFO.switch_to_system_allocator();
	}*/
}
<|endoftext|>


<commit_before>//! This module reexports the OS-specific module that actually implements Platform.
pub mod common;
pub use self::common::*;

#[cfg(windows)]
pub mod windows;
#[cfg(windows)]
pub use self::windows::PlatformImpl;

#[cfg(unix)]
pub mod unix;

#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub mod bsd;

#[cfg(target_os = "freebsd")]
pub mod freebsd;
#[cfg(target_os = "freebsd")]
pub use self::freebsd::PlatformImpl;

#[cfg(target_os = "openbsd")]
pub mod openbsd;
#[cfg(target_os = "openbsd")]
pub use self::openbsd::PlatformImpl;

#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "macos")]
pub use self::macos::PlatformImpl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::PlatformImpl;

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::time::Duration;

    #[test]
    fn test_cpu_load() {
        let load = PlatformImpl::new().cpu_load().unwrap();
        thread::sleep(Duration::from_millis(300));
        let load = load.done().unwrap();
        assert!(load.len() >= 1);
        for cpu in load.iter() {
            let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
            assert!(sum > 0.95 && sum < 1.05);
        }
    }

    #[test]
    fn test_cpu_load_aggregate() {
        let cpu = PlatformImpl::new().cpu_load_aggregate().unwrap();
        thread::sleep(Duration::from_millis(300));
        let cpu = cpu.done().unwrap();
        let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
        assert!(sum > 0.95 && sum < 1.05);
    }

    #[test]
    fn test_load_average() {
        let load = PlatformImpl::new().load_average().unwrap();
        assert!(load.one > 0.00001 && load.five > 0.00001 && load.fifteen > 0.00001);
    }

    #[test]
    fn test_memory() {
        let mem = PlatformImpl::new().memory().unwrap();
        assert!(mem.free.as_usize() > 1024 && mem.total.as_usize() > 1024);
    }

    #[test]
    fn test_battery_life() {
        if let Ok(bat) = PlatformImpl::new().battery_life() {
            assert!(bat.remaining_capacity <= 100.0 && bat.remaining_capacity >= 0.0);
        }
    }

    #[test]
    fn test_on_ac_power() {
        PlatformImpl::new().on_ac_power().unwrap();
    }

    #[test]
    fn test_mounts() {
        let mounts = PlatformImpl::new().mounts().unwrap();
        assert!(mounts.len() > 0);
        assert!(mounts.iter().find(|m| m.fs_mounted_on == "/").unwrap().fs_mounted_on == "/");
    }

    #[test]
    fn test_mount_at() {
        let mount = PlatformImpl::new().mount_at("/").unwrap();
        assert!(mount.fs_mounted_on == "/");
    }

    #[test]
    fn test_networks() {
        let networks = PlatformImpl::new().networks().unwrap();
        assert!(networks.values().filter(|n| n.name == "lo" || n.name == "lo0").next().unwrap().addrs.len() > 0);
    }

    #[test]
    fn test_cpu_measurement_is_send() {
        use crate::{DelayedMeasurement, CPULoad};
        fn take_delayed(dm: DelayedMeasurement<Vec<CPULoad>>) {
            use std::thread;
            thread::spawn(move || dm);
        }
    }

}
<commit_msg>Use PathBuf in test_mount_at<commit_after>//! This module reexports the OS-specific module that actually implements Platform.
pub mod common;
pub use self::common::*;

#[cfg(windows)]
pub mod windows;
#[cfg(windows)]
pub use self::windows::PlatformImpl;

#[cfg(unix)]
pub mod unix;

#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub mod bsd;

#[cfg(target_os = "freebsd")]
pub mod freebsd;
#[cfg(target_os = "freebsd")]
pub use self::freebsd::PlatformImpl;

#[cfg(target_os = "openbsd")]
pub mod openbsd;
#[cfg(target_os = "openbsd")]
pub use self::openbsd::PlatformImpl;

#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "macos")]
pub use self::macos::PlatformImpl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::PlatformImpl;

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::time::Duration;

    #[test]
    fn test_cpu_load() {
        let load = PlatformImpl::new().cpu_load().unwrap();
        thread::sleep(Duration::from_millis(300));
        let load = load.done().unwrap();
        assert!(load.len() >= 1);
        for cpu in load.iter() {
            let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
            assert!(sum > 0.95 && sum < 1.05);
        }
    }

    #[test]
    fn test_cpu_load_aggregate() {
        let cpu = PlatformImpl::new().cpu_load_aggregate().unwrap();
        thread::sleep(Duration::from_millis(300));
        let cpu = cpu.done().unwrap();
        let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
        assert!(sum > 0.95 && sum < 1.05);
    }

    #[test]
    fn test_load_average() {
        let load = PlatformImpl::new().load_average().unwrap();
        assert!(load.one > 0.00001 && load.five > 0.00001 && load.fifteen > 0.00001);
    }

    #[test]
    fn test_memory() {
        let mem = PlatformImpl::new().memory().unwrap();
        assert!(mem.free.as_usize() > 1024 && mem.total.as_usize() > 1024);
    }

    #[test]
    fn test_battery_life() {
        if let Ok(bat) = PlatformImpl::new().battery_life() {
            assert!(bat.remaining_capacity <= 100.0 && bat.remaining_capacity >= 0.0);
        }
    }

    #[test]
    fn test_on_ac_power() {
        PlatformImpl::new().on_ac_power().unwrap();
    }

    #[test]
    fn test_mounts() {
        let mounts = PlatformImpl::new().mounts().unwrap();
        assert!(mounts.len() > 0);
        assert!(mounts.iter().find(|m| m.fs_mounted_on == "/").unwrap().fs_mounted_on == "/");
    }

    #[test]
    fn test_mount_at() {
        // XXX: PathBuf required instead of constant string at least on FreeBSD??
        let mount = PlatformImpl::new().mount_at(std::path::PathBuf::from("/")).unwrap();
        assert!(mount.fs_mounted_on == "/");
    }

    #[test]
    fn test_networks() {
        let networks = PlatformImpl::new().networks().unwrap();
        assert!(networks.values().filter(|n| n.name == "lo" || n.name == "lo0").next().unwrap().addrs.len() > 0);
    }

    #[test]
    fn test_cpu_measurement_is_send() {
        use crate::{DelayedMeasurement, CPULoad};
        fn take_delayed(dm: DelayedMeasurement<Vec<CPULoad>>) {
            use std::thread;
            thread::spawn(move || dm);
        }
    }

}
<|endoftext|>


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

use syntax::{abi, ast, codemap, ext};
use syntax::ext::base::ItemDecorator;
use syntax::ext::build::AstBuilder;
use syntax::owned_slice::OwnedSlice;
use syntax::parse::token;
use syntax::ptr::P;

#[derive(Copy, PartialEq, Debug)]
enum Param {
    Uniform,
    Block,
    Texture,
    Special,
}

#[derive(Copy, Debug)]
enum ParamError {
    DeprecatedTexture,
}

/// Classify variable types (`i32`, `TextureParam`, etc) into the `Param`
fn classify(node: &ast::Ty_) -> Result<Param, ParamError> {
    match *node {
        ast::TyPath(_,ref path) => match path.segments.last() {
            Some(segment) => match segment.identifier.name.as_str() {
                "RawBufferHandle" => Ok(Param::Block),
                "TextureParam"    => Ok(Param::Texture),
                "TextureHandle"   => Err(ParamError::DeprecatedTexture),
                "PhantomData"     => Ok(Param::Special),
                _ => Ok(Param::Uniform),
            },
            None => Ok(Param::Uniform),
        },
        _ => Ok(Param::Uniform),
    }
}

/// Generates the the method body for `gfx::shade::ShaderParam::create_link`
fn method_create(cx: &mut ext::base::ExtCtxt,
                 span: codemap::Span,
                 definition: &ast::StructDef,
                 input: ast::Ident,
                 link_ident: ast::Ident,
                 path_root: ast::Ident)
                 -> P<ast::Block> {
    let init_expr = cx.expr_struct_ident(
        span, link_ident,
        definition.fields.iter().scan((), |_, field| {
            field.node.ident().map(|name|
                cx.field_imm(field.span, name, cx.expr_none(field.span))
            )
        }).collect()
    );
    let class_info: Vec<(Param, P<ast::Expr>)> = definition.fields.iter().scan((), |_, field|
        match (field.node.ident(), classify(&field.node.ty.node)) {
            (None, _) => {
                cx.span_err(field.span, "Named fields are required for `ShaderParam`");
                None
            },
            (Some(fname), Ok(c)) => {
                let name = match super::find_name(cx, field.span, &field.node.attrs) {
                    Some(name) => name,
                    None => token::get_ident(fname),
                };
                Some((c, cx.expr_str(field.span, name)))
            },
            (Some(fname), Err(e)) => {
                cx.span_err(field.span, &format!(
                    "Unrecognized parameter ({:?}) type {:?}",
                    fname.as_str(), e
                ));
                None
            },
        }
    ).collect();
    let gen_arms = |ptype: Param, var: ast::Ident| -> Vec<ast::Arm> {
        class_info.iter().zip(definition.fields.iter())
                  .filter(|&(&(ref class, _), _)| *class == ptype)
                  .scan((), |_, (&(_, ref name_expr), field)|
            field.node.ident().map(|name| quote_arm!(cx,
                $name_expr => {out.$name = Some(i as $path_root::gfx::shade::$var)}
            ))
        ).collect()
    };
    let uniform_arms = gen_arms(Param::Uniform, cx.ident_of("VarUniform"));
    let block_arms = gen_arms(Param::Block, cx.ident_of("VarBlock"));
    let texture_arms = gen_arms(Param::Texture, cx.ident_of("VarTexture"));
    let expr = quote_expr!(cx, {
        let mut out = $init_expr;
        for (i, u) in $input.uniforms.iter().enumerate() {
            let _ = i; // suppress warning about unused i
            match &u.name[..] {
                $uniform_arms
                _ => return Err($path_root::gfx::shade::
                    ParameterError::MissingUniform(u.name.clone())),
            }
        }
        for (i, b) in $input.blocks.iter().enumerate() {
            let _ = i; // suppress warning about unused i
            match &b.name[..] {
                $block_arms
                _ => return Err($path_root::gfx::shade::
                    ParameterError::MissingBlock(b.name.clone())),
            }
        }
        for (i, t) in $input.textures.iter().enumerate() {
            let _ = i; // suppress warning about unused i
            match &t.name[..] {
                $texture_arms
                _ => return Err($path_root::gfx::shade::
                    ParameterError::MissingTexture(t.name.clone())),
            }
        }
        Ok(out)
    });
    cx.block_expr(expr)
}

/// Generates the the method body for `gfx::shade::ShaderParam::fill_params`
fn method_fill(cx: &mut ext::base::ExtCtxt,
               span: codemap::Span,
               definition: &ast::StructDef,
               path_root: ast::Ident)
               -> P<ast::Block> {
    let max_num = cx.expr_usize(span, definition.fields.len());
    let mut calls = vec![
        quote_stmt!(cx, use self::$path_root::gfx::shade::ToUniform;),
        quote_stmt!(cx, out.uniforms.reserve($max_num);),
        quote_stmt!(cx, out.blocks.reserve($max_num);),
        quote_stmt!(cx, out.textures.reserve($max_num);),
    ];
    calls.extend(definition.fields.iter().scan((), |_, field| {
        let name = match field.node.ident() {
            Some(n) => n,
            None => {
                cx.span_err(span, "Named fields are required for `ShaderParam`");
                return None
            }
        };
        classify(&field.node.ty.node).ok().map(|param| match param {
            Param::Uniform => quote_stmt!(cx,
                link.$name.map_or((), |id| {
                    out.uniforms[id as usize] = Some(self.$name.to_uniform());
                })
            ),
            Param::Block   => quote_stmt!(cx,
                link.$name.map_or((), |id| {
                    out.blocks[id as usize] = Some(self.$name.clone());
                })
            ),
            Param::Texture => quote_stmt!(cx,
                link.$name.map_or((), |id| {
                    out.textures[id as usize] = Some(self.$name.clone());
                })
            ),
            Param::Special => quote_stmt!(cx, ()),
        })
    }));
    cx.block_all(span, calls, None)
}

/// A helper function that translates variable type (`i32`, `TextureHandle`, etc)
/// into the corresponding shader var id type (`VarUniform`, `VarBlock`, or `VarTexture`)
fn node_to_var_type(cx: &mut ext::base::ExtCtxt,
                    span: codemap::Span, node: &ast::Ty_,
                    path_root: ast::Ident) -> P<ast::Ty> {
    let id = cx.ident_of(match classify(node) {
        Ok(Param::Uniform) => "VarUniform",
        Ok(Param::Block)   => "VarBlock",
        Ok(Param::Texture) => "VarTexture",
        Ok(Param::Special) => return quote_ty!(cx, Option<()>),
        Err(ParamError::DeprecatedTexture) => {
            cx.span_err(span, "Use gfx::shade::TextureParam for texture vars instead of gfx::shade::TextureHandle");
            ""
        },
    });
    quote_ty!(cx, Option<$path_root::gfx::shade::$id>)
}

fn impl_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span, name: &str, type_ident: ast::Ident) -> ast::ImplItem {
    ast::TypeImplItem(P(ast::Typedef {
        id: ast::DUMMY_NODE_ID,
        span: span,
        ident: cx.ident_of(name),
        vis: ast::Visibility::Inherited,
        attrs: Vec::new(),
        typ: cx.ty_ident(span, type_ident),
    }))
}

fn impl_method(cx: &mut ext::base::ExtCtxt, span: codemap::Span, name: &str,
               with_self: bool, declaration: P<ast::FnDecl>, body: P<ast::Block>)
               -> ast::ImplItem {
    ast::MethodImplItem(P(ast::Method {
        attrs: Vec::new(),
        id: ast::DUMMY_NODE_ID,
        span: span,
        node: ast::MethDecl(
            cx.ident_of(name),
            ast::Generics {
                lifetimes: Vec::new(),
                ty_params: OwnedSlice::empty(),
                where_clause: ast::WhereClause {
                    id: ast::DUMMY_NODE_ID,
                    predicates: Vec::new(),
                },
            },
            abi::Abi::Rust,
            codemap::Spanned {
                node: if with_self {
                        ast::SelfRegion(None, ast::MutImmutable, cx.ident_of("self"))
                    }else {
                        ast::SelfStatic
                },
                span: span,
            },
            ast::Unsafety::Normal,
            declaration,
            body,
            ast::Visibility::Inherited
        )
    }))
}

#[derive(Copy)]
pub struct ShaderParam;
impl ItemDecorator for ShaderParam {
    /// Decorator for `shader_param` attribute
    fn expand(&self, context: &mut ext::base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: &mut FnMut(P<ast::Item>)) {
        // Insert the `gfx` reexport module
        let extern_hack = context.ident_of(super::EXTERN_CRATE_HACK);
        let path_root = super::extern_crate_hack(context, span, |i| (*push)(i));

        // constructing the Link struct
        let (base_def, generics, link_def) = match item.node {
            ast::ItemStruct(ref definition, ref generics) => {
                (definition, generics.clone(), ast::StructDef {
                    fields: definition.fields.iter()
                        .map(|f| codemap::Spanned {
                            node: ast::StructField_ {
                                kind: f.node.kind,
                                id: f.node.id,
                                ty: node_to_var_type(context, f.span, &f.node.ty.node, path_root),
                                attrs: Vec::new(),
                            },
                            span: f.span,
                        }).collect(),
                    ctor_id: None,
                })
            },
            _ => {
                context.span_err(span, "Only free-standing named structs allowed to derive ShaderParam");
                return;
            }
        };

        // derive and push
        let link_name = format!("_{}Link", item.ident.as_str());
        let link_ident = context.ident_of(&link_name);
        let link_item = context.item_struct(span, link_ident, link_def)
                               .map(|mut item| {
            item.attrs.push(context.attribute(span,
                context.meta_list(span, token::InternedString::new("derive"), vec![
                        context.meta_word(span, token::InternedString::new("Copy")),
                        context.meta_word(span, token::InternedString::new("Debug")),
                ])
            ));
            item
        });
        (*push)(link_item);

        // process meta parameters (expecting none)
        match meta_item.node {
            ast::MetaWord(_) => (), //expected
            _ => {
                context.span_err(meta_item.span, "#[shader_param] needs no param");
            }
        }

        // find the generic `Resources` bound
        let resource_ident = match generics.ty_params.iter().find(|typ|
            typ.bounds.iter().find(|b| match **b {
                ast::TraitTyParamBound(ref poly_trait, _) =>
                    poly_trait.trait_ref.path.segments.last().unwrap().identifier.as_str() == "Resources",
                ast::RegionTyParamBound(_) => false,
            }).is_some()
        ){
            Some(typ) => typ.ident,
            None => {
                context.span_err(meta_item.span, "#[shader_param] unable to find generic `gfx::Resources` bound");
                context.ident_of("R")
            }
        };

        // construct `create_link()`
        let lifetimes = generics.lifetimes.iter().map(|ld| ld.lifetime).collect();
        let generic_parameters = generics.ty_params.iter().map(|ty|
            context.ty_ident(span, ty.ident)
        ).collect();
        let struct_ty = context.ty_path(context.path_all(
            span, false,
            vec![item.ident],
            lifetimes,
            generic_parameters,
            Vec::new(),
        ));
        let create_param = context.ident_of("params");
        let body_create = method_create(context, span, base_def, create_param, link_ident, path_root);
        let decl_create = context.fn_decl(
            vec![
                ast::Arg {
                    ty: quote_ty!(context, Option<&$struct_ty>),
                    pat: context.pat_wild(span),
                    id: ast::DUMMY_NODE_ID,
                },
                ast::Arg {
                    ty: quote_ty!(context, &$extern_hack::gfx::ProgramInfo),
                    pat: context.pat_ident(span, create_param),
                    id: ast::DUMMY_NODE_ID,
                },
            ],
            context.ty_path(context.path_all(
                span, false, vec![context.ident_of("Result")],
                Vec::new(), vec![
                    context.ty_ident(span, link_ident),
                    quote_ty!(context, $extern_hack::gfx::shade::ParameterError),
                ], Vec::new()
            )),
        );

        // construct `fill_params()`
        let body_fill = method_fill(context, span, base_def, path_root);
        let decl_fill = context.fn_decl(
             vec![
                ast::Arg::new_self(span, ast::MutImmutable, context.ident_of("self")),
                ast::Arg {
                    ty: quote_ty!(context, &$link_ident),
                    pat: context.pat_ident(span, context.ident_of("link")),
                    id: ast::DUMMY_NODE_ID,
                },
                ast::Arg {
                    ty: quote_ty!(context, &mut $extern_hack::gfx::ParamStorage<$resource_ident>),
                    pat: context.pat_ident(span, context.ident_of("out")),
                    id: ast::DUMMY_NODE_ID,
                },
            ],
            quote_ty!(context, ())
        );

        // construct implementations for types and methods
        let impls = vec![
            impl_type(context, span, "Resources", resource_ident),
            impl_type(context, span, "Link", link_ident),
            impl_method(context, span, "create_link", false, decl_create, body_create),
            impl_method(context, span, "fill_params", true, decl_fill, body_fill),
        ];

        // final implementation item
        let item = context.item(span, item.ident, Vec::new(), ast::Item_::ItemImpl(
                ast::Unsafety::Normal,
                ast::ImplPolarity::Positive,
                generics,
                Some(context.trait_ref(context.path(span, vec![
                    extern_hack,
                    context.ident_of("gfx"),
                    context.ident_of("shade"),
                    context.ident_of("ShaderParam"),
                ]))),
                struct_ty,
                impls
        ));
        (*push)(super::fixup_extern_crate_paths(item, path_root));
    }
}
<commit_msg>Upgrade to latest syntax<commit_after>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::{abi, ast, codemap, ext};
use syntax::ext::base::ItemDecorator;
use syntax::ext::build::AstBuilder;
use syntax::owned_slice::OwnedSlice;
use syntax::parse::token;
use syntax::ptr::P;

#[derive(Copy, PartialEq, Debug)]
enum Param {
    Uniform,
    Block,
    Texture,
    Special,
}

#[derive(Copy, Debug)]
enum ParamError {
    DeprecatedTexture,
}

/// Classify variable types (`i32`, `TextureParam`, etc) into the `Param`
fn classify(node: &ast::Ty_) -> Result<Param, ParamError> {
    match *node {
        ast::TyPath(_,ref path) => match path.segments.last() {
            Some(segment) => match segment.identifier.name.as_str() {
                "RawBufferHandle" => Ok(Param::Block),
                "TextureParam"    => Ok(Param::Texture),
                "TextureHandle"   => Err(ParamError::DeprecatedTexture),
                "PhantomData"     => Ok(Param::Special),
                _ => Ok(Param::Uniform),
            },
            None => Ok(Param::Uniform),
        },
        _ => Ok(Param::Uniform),
    }
}

/// Generates the the method body for `gfx::shade::ShaderParam::create_link`
fn method_create(cx: &mut ext::base::ExtCtxt,
                 span: codemap::Span,
                 definition: &ast::StructDef,
                 input: ast::Ident,
                 link_ident: ast::Ident,
                 path_root: ast::Ident)
                 -> P<ast::Block> {
    let init_expr = cx.expr_struct_ident(
        span, link_ident,
        definition.fields.iter().scan((), |_, field| {
            field.node.ident().map(|name|
                cx.field_imm(field.span, name, cx.expr_none(field.span))
            )
        }).collect()
    );
    let class_info: Vec<(Param, P<ast::Expr>)> = definition.fields.iter().scan((), |_, field|
        match (field.node.ident(), classify(&field.node.ty.node)) {
            (None, _) => {
                cx.span_err(field.span, "Named fields are required for `ShaderParam`");
                None
            },
            (Some(fname), Ok(c)) => {
                let name = match super::find_name(cx, field.span, &field.node.attrs) {
                    Some(name) => name,
                    None => token::get_ident(fname),
                };
                Some((c, cx.expr_str(field.span, name)))
            },
            (Some(fname), Err(e)) => {
                cx.span_err(field.span, &format!(
                    "Unrecognized parameter ({:?}) type {:?}",
                    fname.as_str(), e
                ));
                None
            },
        }
    ).collect();
    let gen_arms = |ptype: Param, var: ast::Ident| -> Vec<ast::Arm> {
        class_info.iter().zip(definition.fields.iter())
                  .filter(|&(&(ref class, _), _)| *class == ptype)
                  .scan((), |_, (&(_, ref name_expr), field)|
            field.node.ident().map(|name| quote_arm!(cx,
                $name_expr => {out.$name = Some(i as $path_root::gfx::shade::$var)}
            ))
        ).collect()
    };
    let uniform_arms = gen_arms(Param::Uniform, cx.ident_of("VarUniform"));
    let block_arms = gen_arms(Param::Block, cx.ident_of("VarBlock"));
    let texture_arms = gen_arms(Param::Texture, cx.ident_of("VarTexture"));
    let expr = quote_expr!(cx, {
        let mut out = $init_expr;
        for (i, u) in $input.uniforms.iter().enumerate() {
            let _ = i; // suppress warning about unused i
            match &u.name[..] {
                $uniform_arms
                _ => return Err($path_root::gfx::shade::
                    ParameterError::MissingUniform(u.name.clone())),
            }
        }
        for (i, b) in $input.blocks.iter().enumerate() {
            let _ = i; // suppress warning about unused i
            match &b.name[..] {
                $block_arms
                _ => return Err($path_root::gfx::shade::
                    ParameterError::MissingBlock(b.name.clone())),
            }
        }
        for (i, t) in $input.textures.iter().enumerate() {
            let _ = i; // suppress warning about unused i
            match &t.name[..] {
                $texture_arms
                _ => return Err($path_root::gfx::shade::
                    ParameterError::MissingTexture(t.name.clone())),
            }
        }
        Ok(out)
    });
    cx.block_expr(expr)
}

/// Generates the the method body for `gfx::shade::ShaderParam::fill_params`
fn method_fill(cx: &mut ext::base::ExtCtxt,
               span: codemap::Span,
               definition: &ast::StructDef,
               path_root: ast::Ident)
               -> P<ast::Block> {
    let max_num = cx.expr_usize(span, definition.fields.len());
    let mut calls = vec![
        quote_stmt!(cx, use self::$path_root::gfx::shade::ToUniform;),
        quote_stmt!(cx, out.uniforms.reserve($max_num);),
        quote_stmt!(cx, out.blocks.reserve($max_num);),
        quote_stmt!(cx, out.textures.reserve($max_num);),
    ];
    calls.extend(definition.fields.iter().scan((), |_, field| {
        let name = match field.node.ident() {
            Some(n) => n,
            None => {
                cx.span_err(span, "Named fields are required for `ShaderParam`");
                return None
            }
        };
        classify(&field.node.ty.node).ok().map(|param| match param {
            Param::Uniform => quote_stmt!(cx,
                link.$name.map_or((), |id| {
                    out.uniforms[id as usize] = Some(self.$name.to_uniform());
                })
            ),
            Param::Block   => quote_stmt!(cx,
                link.$name.map_or((), |id| {
                    out.blocks[id as usize] = Some(self.$name.clone());
                })
            ),
            Param::Texture => quote_stmt!(cx,
                link.$name.map_or((), |id| {
                    out.textures[id as usize] = Some(self.$name.clone());
                })
            ),
            Param::Special => quote_stmt!(cx, ()),
        })
    }));
    cx.block_all(span, calls, None)
}

/// A helper function that translates variable type (`i32`, `TextureHandle`, etc)
/// into the corresponding shader var id type (`VarUniform`, `VarBlock`, or `VarTexture`)
fn node_to_var_type(cx: &mut ext::base::ExtCtxt,
                    span: codemap::Span, node: &ast::Ty_,
                    path_root: ast::Ident) -> P<ast::Ty> {
    let id = cx.ident_of(match classify(node) {
        Ok(Param::Uniform) => "VarUniform",
        Ok(Param::Block)   => "VarBlock",
        Ok(Param::Texture) => "VarTexture",
        Ok(Param::Special) => return quote_ty!(cx, Option<()>),
        Err(ParamError::DeprecatedTexture) => {
            cx.span_err(span, "Use gfx::shade::TextureParam for texture vars instead of gfx::shade::TextureHandle");
            ""
        },
    });
    quote_ty!(cx, Option<$path_root::gfx::shade::$id>)
}

fn impl_type(cx: &mut ext::base::ExtCtxt, span: codemap::Span,
             name: &str, type_ident: ast::Ident) -> ast::ImplItem {
    ast::ImplItem {
        id: ast::DUMMY_NODE_ID,
        ident: cx.ident_of(name),
        vis: ast::Visibility::Inherited,
        attrs: Vec::new(),
        node: ast::TypeImplItem(cx.ty_ident(span, type_ident)),
        span: span
    }
}

fn impl_method(cx: &mut ext::base::ExtCtxt, span: codemap::Span, name: &str,
               with_self: bool, declaration: P<ast::FnDecl>, body: P<ast::Block>)
               -> ast::ImplItem {
    ast::ImplItem {
        id: ast::DUMMY_NODE_ID,
        ident: cx.ident_of(name),
        vis: ast::Visibility::Inherited,
        attrs: Vec::new(),
        node: ast::MethodImplItem(
            ast::MethodSig {
                unsafety: ast::Unsafety::Normal,
                abi: abi::Abi::Rust,
                decl: declaration,
                generics: ast::Generics {
                    lifetimes: Vec::new(),
                    ty_params: OwnedSlice::empty(),
                    where_clause: ast::WhereClause {
                        id: ast::DUMMY_NODE_ID,
                        predicates: Vec::new()
                    }
                },
                explicit_self: codemap::Spanned {
                    node: if with_self {
                        ast::SelfRegion(None, ast::MutImmutable, cx.ident_of("self"))
                    } else {
                        ast::SelfStatic
                    },
                    span: span
                }
            },
            body
        ),
        span: span
    }
}

#[derive(Copy)]
pub struct ShaderParam;
impl ItemDecorator for ShaderParam {
    /// Decorator for `shader_param` attribute
    fn expand(&self, context: &mut ext::base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: &mut FnMut(P<ast::Item>)) {
        // Insert the `gfx` reexport module
        let extern_hack = context.ident_of(super::EXTERN_CRATE_HACK);
        let path_root = super::extern_crate_hack(context, span, |i| (*push)(i));

        // constructing the Link struct
        let (base_def, generics, link_def) = match item.node {
            ast::ItemStruct(ref definition, ref generics) => {
                (definition, generics.clone(), ast::StructDef {
                    fields: definition.fields.iter()
                        .map(|f| codemap::Spanned {
                            node: ast::StructField_ {
                                kind: f.node.kind,
                                id: f.node.id,
                                ty: node_to_var_type(context, f.span, &f.node.ty.node, path_root),
                                attrs: Vec::new(),
                            },
                            span: f.span,
                        }).collect(),
                    ctor_id: None,
                })
            },
            _ => {
                context.span_err(span, "Only free-standing named structs allowed to derive ShaderParam");
                return;
            }
        };

        // derive and push
        let link_name = format!("_{}Link", item.ident.as_str());
        let link_ident = context.ident_of(&link_name);
        let link_item = context.item_struct(span, link_ident, link_def)
                               .map(|mut item| {
            item.attrs.push(context.attribute(span,
                context.meta_list(span, token::InternedString::new("derive"), vec![
                        context.meta_word(span, token::InternedString::new("Copy")),
                        context.meta_word(span, token::InternedString::new("Debug")),
                ])
            ));
            item
        });
        (*push)(link_item);

        // process meta parameters (expecting none)
        match meta_item.node {
            ast::MetaWord(_) => (), //expected
            _ => {
                context.span_err(meta_item.span, "#[shader_param] needs no param");
            }
        }

        // find the generic `Resources` bound
        let resource_ident = match generics.ty_params.iter().find(|typ|
            typ.bounds.iter().find(|b| match **b {
                ast::TraitTyParamBound(ref poly_trait, _) =>
                    poly_trait.trait_ref.path.segments.last().unwrap().identifier.as_str() == "Resources",
                ast::RegionTyParamBound(_) => false,
            }).is_some()
        ){
            Some(typ) => typ.ident,
            None => {
                context.span_err(meta_item.span, "#[shader_param] unable to find generic `gfx::Resources` bound");
                context.ident_of("R")
            }
        };

        // construct `create_link()`
        let lifetimes = generics.lifetimes.iter().map(|ld| ld.lifetime).collect();
        let generic_parameters = generics.ty_params.iter().map(|ty|
            context.ty_ident(span, ty.ident)
        ).collect();
        let struct_ty = context.ty_path(context.path_all(
            span, false,
            vec![item.ident],
            lifetimes,
            generic_parameters,
            Vec::new(),
        ));
        let create_param = context.ident_of("params");
        let body_create = method_create(context, span, base_def, create_param, link_ident, path_root);
        let decl_create = context.fn_decl(
            vec![
                ast::Arg {
                    ty: quote_ty!(context, Option<&$struct_ty>),
                    pat: context.pat_wild(span),
                    id: ast::DUMMY_NODE_ID,
                },
                ast::Arg {
                    ty: quote_ty!(context, &$extern_hack::gfx::ProgramInfo),
                    pat: context.pat_ident(span, create_param),
                    id: ast::DUMMY_NODE_ID,
                },
            ],
            context.ty_path(context.path_all(
                span, false, vec![context.ident_of("Result")],
                Vec::new(), vec![
                    context.ty_ident(span, link_ident),
                    quote_ty!(context, $extern_hack::gfx::shade::ParameterError),
                ], Vec::new()
            )),
        );

        // construct `fill_params()`
        let body_fill = method_fill(context, span, base_def, path_root);
        let decl_fill = context.fn_decl(
             vec![
                ast::Arg::new_self(span, ast::MutImmutable, context.ident_of("self")),
                ast::Arg {
                    ty: quote_ty!(context, &$link_ident),
                    pat: context.pat_ident(span, context.ident_of("link")),
                    id: ast::DUMMY_NODE_ID,
                },
                ast::Arg {
                    ty: quote_ty!(context, &mut $extern_hack::gfx::ParamStorage<$resource_ident>),
                    pat: context.pat_ident(span, context.ident_of("out")),
                    id: ast::DUMMY_NODE_ID,
                },
            ],
            quote_ty!(context, ())
        );

        // construct implementations for types and methods
        let impls = vec![
            P(impl_type(context, span, "Resources", resource_ident)),
            P(impl_type(context, span, "Link", link_ident)),
            P(impl_method(context, span, "create_link", false, decl_create, body_create)),
            P(impl_method(context, span, "fill_params", true, decl_fill, body_fill))
        ];

        // final implementation item
        let item = context.item(span, item.ident, Vec::new(), ast::Item_::ItemImpl(
                ast::Unsafety::Normal,
                ast::ImplPolarity::Positive,
                generics,
                Some(context.trait_ref(context.path(span, vec![
                    extern_hack,
                    context.ident_of("gfx"),
                    context.ident_of("shade"),
                    context.ident_of("ShaderParam"),
                ]))),
                struct_ty,
                impls
        ));
        (*push)(super::fixup_extern_crate_paths(item, path_root));
    }
}
<|endoftext|>