<commit_before>#![feature(asm)]

//use std::dynamic_lib::DynamicLibrary;
use std::path::Path;
use std::mem::transmute;

use std::process::Command;
use std::thread;

extern crate gr;
use gr::*;

use std::sync::atomic::{AtomicBool, Ordering, AtomicPtr};
use std::option::Option;

use std::sync::{Arc, RwLock};
use std::fmt;
use std::io::Write;

extern crate dylib;
use dylib::DynamicLibrary;

#[derive(Clone)]
enum OpType {
    Func(String),
    Const(usize),
    Var(usize),
    UVar(usize),
    Op(String),
    Pow(i32)
}

impl fmt::Display for OpType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        let outstring = match self {
            &OpType::Func(ref s) => format!("f{}", s),
            &OpType::Const(i) => format!("c{}", i),
            &OpType::Var(i) => format!("i{}", i),
            &OpType::UVar(i) => format!("v{}", i),
            &OpType::Op(ref s) => format!("o{}", s),
            &OpType::Pow(i) => format!("p{}", i)
        };
        write!(f, "{}", outstring)
    }

}

#[derive(Clone)]
pub struct FuncObj {
    handle: Arc<RwLock<Option<DynamicLibrary>>>,
    user_vars: Vec<GI>,
    constants: Vec<GI>,
    instructions: Vec<OpType>,
    switched: Arc<AtomicBool>,
    function: Arc<AtomicPtr<fn(&Vec<GI>, &Vec<GI>) -> GI>>,

}

unsafe impl Sync for FuncObj {}
unsafe impl Send for FuncObj {}
fn dummy(_x: &Vec<GI>, _c: &Vec<GI>) -> GI {
    GI::new_c("1.0").unwrap()
}

impl FuncObj {
    pub fn call(&self, _x: &Vec<GI>) -> GI {
        if self.switched.load(Ordering::Acquire) {
            let result = unsafe{
                std::mem::transmute::<*mut fn(&Vec<GI>, &Vec<GI>)->GI,
                                               fn(&Vec<GI>, &Vec<GI>)->GI>(
                    (self.function.load(Ordering::Acquire)))(_x, &self.constants)};
            let mut interim: CInterval;
            // The compiler doesn't seem to be generating the code here to
            // retrieve the return value. This gets the result from the xmm0
            // register and returns it to the caller.
            unsafe{asm!("movapd %xmm0, $0"
                        : "=x"(interim)
                        : : :)};
            let test_var = GI{data:
                              gaol_int{data:
                                       interim
                              }};
            test_var
        }
        else {
            self.interpreted(_x, &self.constants)
        }
    }

    fn set(&self, f: fn(&Vec<GI>,&Vec<GI>) -> GI, handle: DynamicLibrary) {
        // This FuncObj owns the handle from the dynamic lib as the lifetime of
        // f is tied to the lifetime of the handle.
        self.function.store(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                         *mut fn(&Vec<GI>, &Vec<GI>)->GI>(f)}, Ordering::Release);
        let mut h = self.handle.write().unwrap();
        *h = Some(handle);
        self.switched.store(true, Ordering::Release);
    }

    fn interpreted(&self, _x: &Vec<GI>, _c: &Vec<GI>) -> GI {
        let mut stack: Vec<GI> = Vec::new();
        for inst in &self.instructions {
            match inst {
                &OpType::Func(ref s) => {
                    let op = stack.last_mut().unwrap();
                    match s.as_str() {
                        "abs" => op.abs(),
                        "sin" => op.sin(),
                        "cos" => op.cos(),
                        "tan" => op.tan(),
                        "exp" => op.exp(),
                        "log" => op.log(),
                        "neg" => op.neg(),
                        "sqrt" => op.sqrt(),
                        _     => unreachable!()
                    };
                },
                &OpType::Const(i) => {
                    stack.push(self.constants[i]);
                },
                &OpType::Var(i) => {
                    stack.push(_x[i]);
                },
                &OpType::UVar(i) => {
                    stack.push(self.user_vars[i]);
                },
                &OpType::Op(ref s) => {
                    let right = stack.pop().unwrap();
                    let left = stack.last_mut().unwrap();
                    match s.as_str() {
                        "+" => left.add(right),
                        "-" => left.sub(right),
                        "*" => left.mul(right),
                        "/" => left.div(right),
                        "p" => left.powi(right),
                        _   => unreachable!()
                    };
                },
                &OpType::Pow(exp) => {
                    let arg = stack.last_mut().unwrap();
                    arg.pow(exp);
                }
            }
        }
        stack[0]
    }

    pub fn new(consts: &Vec<GI>, instructions: &String, debug: bool, suffix: String) -> FuncObj {
        let mut insts = vec![];

        for inst in instructions.split(',') {
            let dummy = inst.trim().to_string();
            let (first, rest) = dummy.split_at(1);
            insts.push(match first {
                "c" => OpType::Const(rest.to_string().parse::<usize>().unwrap()),
                "i" => OpType::Var(rest.to_string().parse::<usize>().unwrap()),
                "v" => OpType::UVar(rest.to_string().parse::<usize>().unwrap()),
                "o" => OpType::Op(rest.to_string()),
                "f" => OpType::Func(rest.to_string()),
                "p" => OpType::Pow(rest.to_string().parse::<i32>().unwrap()),
                _   => panic!()
            });
        }
        let result =
            FuncObj{handle: Arc::new(RwLock::new(Option::None)),
                user_vars: vec![],
                constants: consts.clone(),
                instructions: insts,
                switched: Arc::new(AtomicBool::new(false)),
                function: Arc::new(AtomicPtr::new(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                                               *mut fn(&Vec<GI>, &Vec<GI>)->GI>(dummy)}))
        };

        {
            let fo_c = result.clone();
            thread::spawn(move || {
                &fo_c.compile(debug, &suffix);
            })
        };
        result
    }

    fn compile(&self, debug: bool, suffix: &String) {
        let mut process = Command::new("build_func.sh");
        // if debug {
        //     process.arg("debug");
        // }
        let ignore = process.arg(suffix.clone()).output()
            .unwrap_or_else(|e| {panic!("Could not compile: {}", e)});

        if !ignore.status.success() {
            panic!("Could not compile: {}\n----\n{}", String::from_utf8(ignore.stdout).unwrap(),
                   String::from_utf8(ignore.stderr).unwrap());
        }

        //DynamicLibrary::prepend_search_path(Path::new("./.compiled"));
        let dylib: String = "libfunc_".to_string() + suffix + ".so".into();

        let f = match DynamicLibrary::open(Some(Path::new(&dylib)))
        {
            Ok(lib) => lib,
            Err(err) => panic!("Could not load library: {}", err)
        };
        let g = unsafe{match f.symbol("gelpia_func") {
            Ok(func) => transmute::<*mut u32, fn(&Vec<GI>, &Vec<GI>)->GI>(func),
            Err(err) => panic!("Could not load function: {}", err),
        }};

        self.set(g, f);
    }
}

#[test]
fn test1() {
    let constants = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&constants,
                         &"c0,c1,o-,c2,o+".to_string());
    let expected = ((GI::new_c("1") - GI::new_c("2")) + GI::new_c("3")).to_string();
    assert!(f.call(&vec![]).to_string() == expected);
}

#[test]
fn test2() {
    let consts = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&consts,
                         &"c0,fsin,c1,o*,fcos,c2,o/".to_string());
    let expected = cos(sin(consts[0]) * consts[1])/consts[2];
    assert!(f.call(&vec![]).to_string() == expected.to_string());
}

#[test]
fn sum_inputs() {
    let consts = vec![];
    let v = vec![GI::new_d(1.0, 1.0), GI::new_d(1.0, 2.0), GI::new_d(1.0, 3.0),
                 GI::new_d(1.0, 4.0), GI::new_d(1.0, 5.0), GI::new_d(1.0, 6.0)];
    let f = FuncObj::new(&consts,
                         &"i0,i1, o+, i2, o+, i3, o+, i4, o+, i5, o+".to_string());
    let expected = (v[0] + v[1] + v[2] + v[3] + v[4] + v[5]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test3() {
    let consts = vec![GI::new_c("0.1")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fsin".to_string());
    let expected = sin(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test4() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fabs".to_string());
    let expected = abs(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test5() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fcos".to_string());
    let expected = cos(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test6() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, ftan".to_string());
    let expected = tan(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test7() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fexp".to_string());
    let expected = exp(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test8() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fneg".to_string());
    let expected = (-consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test9() {
    let consts = vec![GI::new_c("[0.1, .2]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, flog".to_string());
    let expected = log(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test10() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, c1, op".to_string());
    let expected = powi(consts[0], consts[1]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test_power() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, p-1, c1, p2, o+".to_string());
    let expected = (pow(consts[0], -1) + pow(consts[1], 2)).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
<commit_msg>Remove mysterious assembly instructions as Rust appears to be fixed<commit_after>#![feature(asm)]

//use std::dynamic_lib::DynamicLibrary;
use std::path::Path;
use std::mem::transmute;

use std::process::Command;
use std::thread;

extern crate gr;
use gr::*;

use std::sync::atomic::{AtomicBool, Ordering, AtomicPtr};
use std::option::Option;

use std::sync::{Arc, RwLock};
use std::fmt;
use std::io::Write;

extern crate dylib;
use dylib::DynamicLibrary;

#[derive(Clone)]
enum OpType {
    Func(String),
    Const(usize),
    Var(usize),
    UVar(usize),
    Op(String),
    Pow(i32)
}

impl fmt::Display for OpType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        let outstring = match self {
            &OpType::Func(ref s) => format!("f{}", s),
            &OpType::Const(i) => format!("c{}", i),
            &OpType::Var(i) => format!("i{}", i),
            &OpType::UVar(i) => format!("v{}", i),
            &OpType::Op(ref s) => format!("o{}", s),
            &OpType::Pow(i) => format!("p{}", i)
        };
        write!(f, "{}", outstring)
    }

}

#[derive(Clone)]
pub struct FuncObj {
    handle: Arc<RwLock<Option<DynamicLibrary>>>,
    user_vars: Vec<GI>,
    constants: Vec<GI>,
    instructions: Vec<OpType>,
    switched: Arc<AtomicBool>,
    function: Arc<AtomicPtr<fn(&Vec<GI>, &Vec<GI>) -> GI>>,

}

unsafe impl Sync for FuncObj {}
unsafe impl Send for FuncObj {}
fn dummy(_x: &Vec<GI>, _c: &Vec<GI>) -> GI {
    GI::new_c("1.0").unwrap()
}

impl FuncObj {
    pub fn call(&self, _x: &Vec<GI>) -> GI {
        if self.switched.load(Ordering::Acquire) {
            let real_func = unsafe{
                std::mem::transmute::<*mut fn(&Vec<GI>, &Vec<GI>)->GI,
                                      fn(&Vec<GI>, &Vec<GI>)->GI>(
                    (self.function.load(Ordering::Acquire)))};
            real_func(_x, &self.constants)
        }
        else {
            self.interpreted(_x, &self.constants)
        }
    }

    fn set(&self, f: fn(&Vec<GI>,&Vec<GI>) -> GI, handle: DynamicLibrary) {
        // This FuncObj owns the handle from the dynamic lib as the lifetime of
        // f is tied to the lifetime of the handle.
        self.function.store(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                         *mut fn(&Vec<GI>, &Vec<GI>)->GI>(f)}, Ordering::Release);
        let mut h = self.handle.write().unwrap();
        *h = Some(handle);
        self.switched.store(true, Ordering::Release);
    }

    fn interpreted(&self, _x: &Vec<GI>, _c: &Vec<GI>) -> GI {
        let mut stack: Vec<GI> = Vec::new();
        for inst in &self.instructions {
            match inst {
                &OpType::Func(ref s) => {
                    let op = stack.last_mut().unwrap();
                    match s.as_str() {
                        "abs" => op.abs(),
                        "sin" => op.sin(),
                        "cos" => op.cos(),
                        "tan" => op.tan(),
                        "exp" => op.exp(),
                        "log" => op.log(),
                        "neg" => op.neg(),
                        "sqrt" => op.sqrt(),
                        _     => unreachable!()
                    };
                },
                &OpType::Const(i) => {
                    stack.push(self.constants[i]);
                },
                &OpType::Var(i) => {
                    stack.push(_x[i]);
                },
                &OpType::UVar(i) => {
                    stack.push(self.user_vars[i]);
                },
                &OpType::Op(ref s) => {
                    let right = stack.pop().unwrap();
                    let left = stack.last_mut().unwrap();
                    match s.as_str() {
                        "+" => left.add(right),
                        "-" => left.sub(right),
                        "*" => left.mul(right),
                        "/" => left.div(right),
                        "p" => left.powi(right),
                        _   => unreachable!()
                    };
                },
                &OpType::Pow(exp) => {
                    let arg = stack.last_mut().unwrap();
                    arg.pow(exp);
                }
            }
        }
        stack[0]
    }

    pub fn new(consts: &Vec<GI>, instructions: &String, debug: bool, suffix: String) -> FuncObj {
        let mut insts = vec![];

        for inst in instructions.split(',') {
            let dummy = inst.trim().to_string();
            let (first, rest) = dummy.split_at(1);
            insts.push(match first {
                "c" => OpType::Const(rest.to_string().parse::<usize>().unwrap()),
                "i" => OpType::Var(rest.to_string().parse::<usize>().unwrap()),
                "v" => OpType::UVar(rest.to_string().parse::<usize>().unwrap()),
                "o" => OpType::Op(rest.to_string()),
                "f" => OpType::Func(rest.to_string()),
                "p" => OpType::Pow(rest.to_string().parse::<i32>().unwrap()),
                _   => panic!()
            });
        }
        let result =
            FuncObj{handle: Arc::new(RwLock::new(Option::None)),
                user_vars: vec![],
                constants: consts.clone(),
                instructions: insts,
                switched: Arc::new(AtomicBool::new(false)),
                function: Arc::new(AtomicPtr::new(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                                               *mut fn(&Vec<GI>, &Vec<GI>)->GI>(dummy)}))
        };

        {
            let fo_c = result.clone();
            thread::spawn(move || {
                &fo_c.compile(debug, &suffix);
            })
        };
        result
    }

    fn compile(&self, debug: bool, suffix: &String) {
        let mut process = Command::new("build_func.sh");
        if debug {
            println!("compile suffix: {}", suffix);
        }
        let ignore = process.arg(suffix.clone()).output()
            .unwrap_or_else(|e| {panic!("Could not compile: {}", e)});

        if !ignore.status.success() {
            panic!("Could not compile: {}\n----\n{}", String::from_utf8(ignore.stdout).unwrap(),
                   String::from_utf8(ignore.stderr).unwrap());
        }

        DynamicLibrary::prepend_search_path(Path::new("./.compiled"));
        let dylib: String = "libfunc_".to_string() + suffix + ".so".into();

        let f = match DynamicLibrary::open(Some(Path::new(&dylib)))
        {
            Ok(lib) => lib,
            Err(err) => panic!("Could not load library: {}", err)
        };
        let g = unsafe{match f.symbol("gelpia_func") {
            Ok(func) => transmute::<*mut u32, fn(&Vec<GI>, &Vec<GI>)->GI>(func),
            Err(err) => panic!("Could not load function: {}", err),
        }};

        self.set(g, f);
    }
}

#[test]
fn test1() {
    let constants = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&constants,
                         &"c0,c1,o-,c2,o+".to_string());
    let expected = ((GI::new_c("1") - GI::new_c("2")) + GI::new_c("3")).to_string();
    assert!(f.call(&vec![]).to_string() == expected);
}

#[test]
fn test2() {
    let consts = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&consts,
                         &"c0,fsin,c1,o*,fcos,c2,o/".to_string());
    let expected = cos(sin(consts[0]) * consts[1])/consts[2];
    assert!(f.call(&vec![]).to_string() == expected.to_string());
}

#[test]
fn sum_inputs() {
    let consts = vec![];
    let v = vec![GI::new_d(1.0, 1.0), GI::new_d(1.0, 2.0), GI::new_d(1.0, 3.0),
                 GI::new_d(1.0, 4.0), GI::new_d(1.0, 5.0), GI::new_d(1.0, 6.0)];
    let f = FuncObj::new(&consts,
                         &"i0,i1, o+, i2, o+, i3, o+, i4, o+, i5, o+".to_string());
    let expected = (v[0] + v[1] + v[2] + v[3] + v[4] + v[5]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test3() {
    let consts = vec![GI::new_c("0.1")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fsin".to_string());
    let expected = sin(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test4() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fabs".to_string());
    let expected = abs(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test5() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fcos".to_string());
    let expected = cos(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test6() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, ftan".to_string());
    let expected = tan(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test7() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fexp".to_string());
    let expected = exp(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test8() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fneg".to_string());
    let expected = (-consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test9() {
    let consts = vec![GI::new_c("[0.1, .2]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, flog".to_string());
    let expected = log(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test10() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, c1, op".to_string());
    let expected = powi(consts[0], consts[1]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test_power() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, p-1, c1, p2, o+".to_string());
    let expected = (pow(consts[0], -1) + pow(consts[1], 2)).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
<|endoftext|>


<commit_before>//! Rendering functionality and abstractions
//!
//! Collection of common traits and implementations
//! to facilitate (possible hardware-accelerated) rendering.
//!
//! Supported rendering apis:
//!
//! - Raw OpenGL ES 2

use std::collections::HashSet;
use std::error::Error;

#[cfg(feature = "wayland_frontend")]
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};

#[cfg(feature = "renderer_gl")]
pub mod gles2;
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
use crate::backend::egl::display::EGLBufferReader;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
    /// Identity transformation (plane is unaltered when applied)
    Normal,
    /// Plane is rotated by 90 degrees
    _90,
    /// Plane is rotated by 180 degrees
    _180,
    /// Plane is rotated by 270 degrees
    _270,
    /// Plane is flipped vertically
    Flipped,
    /// Plane is flipped vertically and rotated by 90 degrees
    Flipped90,
    /// Plane is flipped vertically and rotated by 180 degrees
    Flipped180,
    /// Plane is flipped vertically and rotated by 270 degrees
    Flipped270,
}

impl Transform {
    /// A projection matrix to apply this transformation
    pub fn matrix(&self) -> Matrix3<f32> {
        match self {
            Transform::Normal => Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_90 => Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
        }
    }

    /// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
    ///
    /// Flipping is preserved and 180/Normal transformation are uneffected.
    pub fn invert(&self) -> Transform {
        match self {
            Transform::Normal => Transform::Normal,
            Transform::Flipped => Transform::Flipped,
            Transform::_90 => Transform::_270,
            Transform::_180 => Transform::_180,
            Transform::_270 => Transform::_90,
            Transform::Flipped90 => Transform::Flipped270,
            Transform::Flipped180 => Transform::Flipped180,
            Transform::Flipped270 => Transform::Flipped90,
        }
    }

    /// Transformed size after applying this transformation.
    pub fn transform_size(&self, width: u32, height: u32) -> (u32, u32) {
        if *self == Transform::_90
            || *self == Transform::_270
            || *self == Transform::Flipped90
            || *self == Transform::Flipped270
        {
            (height, width)
        } else {
            (width, height)
        }
    }
}

#[cfg(feature = "wayland-frontend")]
impl From<wayland_server::protocol::wl_output::Transform> for Transform {
    fn from(transform: wayland_server::protocol::wl_output::Transform) -> Transform {
        use wayland_server::protocol::wl_output::Transform::*;
        match transform {
            Normal => Transform::Normal,
            _90 => Transform::_90,
            _180 => Transform::_180,
            _270 => Transform::_270,
            Flipped => Transform::Flipped,
            Flipped90 => Transform::Flipped90,
            Flipped180 => Transform::Flipped180,
            Flipped270 => Transform::Flipped270,
        }
    }
}

/// Abstraction for Renderers, that can render into different targets
pub trait Bind<Target>: Unbind {
    /// Bind a given rendering target, which will contain the rendering results until `unbind` is called.
    ///
    /// Binding to target, while another one is already bound, is rendering defined.
    /// Some renderers might happily replace the current target, while other might drop the call
    /// or throw an error.
    fn bind(&mut self, target: Target) -> Result<(), <Self as Renderer>::Error>;
    /// Supported pixel formats for given targets, if applicable.
    fn supported_formats(&self) -> Option<HashSet<crate::backend::allocator::Format>> {
        None
    }
}

/// Functionality to unbind the current rendering target
pub trait Unbind: Renderer {
    /// Unbind the current rendering target.
    ///
    /// May fall back to a default target, if defined by the implementation.
    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error>;
}

/// A two dimensional texture
pub trait Texture {
    /// Size of the texture plane (w x h)
    fn size(&self) -> (u32, u32) {
        (self.width(), self.height())
    }

    /// Width of the texture plane
    fn width(&self) -> u32;
    /// Height of the texture plane
    fn height(&self) -> u32;
}

pub trait Frame {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;

    /// Clear the complete current target with a single given color.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
    /// Render a texture to the current target using given projection matrix and alpha.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture(
        &mut self,
        texture: &Self::TextureId,
        matrix: Matrix3<f32>,
        alpha: f32,
    ) -> Result<(), Self::Error>;
    /// Render a texture to the current target as a flat 2d-plane at a given
    /// position, applying the given transformation with the given alpha value.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture_at(
        &mut self,
        texture: &Self::TextureId,
        pos: (i32, i32),
        transform: Transform,
        alpha: f32,
    ) -> Result<(), Self::Error> {
        let mut mat = Matrix3::<f32>::identity();

        // position and scale
        let size = texture.size();
        mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
        mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);

        //apply surface transformation
        mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
        if transform == Transform::Normal {
            assert_eq!(mat, mat * transform.invert().matrix());
            assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
        }
        mat = mat * transform.invert().matrix();
        mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));

        self.render_texture(texture, mat, alpha)
    }
}

/// Abstraction of commonly used rendering operations for compositors.
pub trait Renderer {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;

    type Frame: Frame<Error = Self::Error, TextureId = Self::TextureId>;

    /// Import a given bitmap into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
    /// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
    ///
    /// This operation needs no bound or default rendering target.
    #[cfg(feature = "image")]
    fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
        &mut self,
        image: &image::ImageBuffer<image::Rgba<u8>, C>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Returns supported formats for shared memory buffers.
    ///
    /// Will always contain At least `Argb8888` and `Xrgb8888`.
    #[cfg(feature = "wayland_frontend")]
    fn shm_formats(&self) -> &[wl_shm::Format] {
        // Mandatory
        &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
    }

    /// Import a given buffer into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
    ///
    /// This operation needs no bound or default rendering target.
    ///
    /// The implementation defines, if the id keeps being valid, if the buffer is released,
    /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
    ///
    /// If provided the `SurfaceAttributes` can be used to do caching of rendering resources and is generally recommended.
    ///
    /// The `damage` argument provides a list of rectangle locating parts of the buffer that need to be updated. When provided
    /// with an empty list `&[]`, the renderer is allowed to not update the texture at all.
    #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
    fn import_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&SurfaceAttributes>,
        damage: &[Rectangle],
        egl: Option<&EGLBufferReader>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
    ///
    /// This function *may* error, if:
    /// - The given dimensions are unsuppored (too large) for this renderer
    /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
    /// - There was a previous `begin`-call, which was not terminated by `finish`.
    /// - This renderer implements `Bind`, no target was bound *and* has no default target.
    /// - (Renderers not implementing `Bind` always have a default target.)
    fn render<F, R>(
        &mut self,
        width: u32,
        height: u32,
        transform: Transform,
        rendering: F,
    ) -> Result<R, Self::Error>
    where
        F: FnOnce(&mut Self, &mut Self::Frame) -> R;
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
pub fn buffer_dimensions(
    buffer: &wl_buffer::WlBuffer,
    egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<(i32, i32)> {
    if let Some((w, h)) = egl_buffer_reader
        .as_ref()
        .and_then(|x| x.egl_buffer_dimensions(&buffer))
    {
        Some((w, h))
    } else if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl")))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
    use crate::backend::allocator::Buffer;

    if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}
<commit_msg>renderer: Document `Frame` Associated Type and Trait<commit_after>//! Rendering functionality and abstractions
//!
//! Collection of common traits and implementations
//! to facilitate (possible hardware-accelerated) rendering.
//!
//! Supported rendering apis:
//!
//! - Raw OpenGL ES 2

use std::collections::HashSet;
use std::error::Error;

#[cfg(feature = "wayland_frontend")]
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};

#[cfg(feature = "renderer_gl")]
pub mod gles2;
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
use crate::backend::egl::display::EGLBufferReader;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
    /// Identity transformation (plane is unaltered when applied)
    Normal,
    /// Plane is rotated by 90 degrees
    _90,
    /// Plane is rotated by 180 degrees
    _180,
    /// Plane is rotated by 270 degrees
    _270,
    /// Plane is flipped vertically
    Flipped,
    /// Plane is flipped vertically and rotated by 90 degrees
    Flipped90,
    /// Plane is flipped vertically and rotated by 180 degrees
    Flipped180,
    /// Plane is flipped vertically and rotated by 270 degrees
    Flipped270,
}

impl Transform {
    /// A projection matrix to apply this transformation
    pub fn matrix(&self) -> Matrix3<f32> {
        match self {
            Transform::Normal => Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_90 => Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
        }
    }

    /// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
    ///
    /// Flipping is preserved and 180/Normal transformation are uneffected.
    pub fn invert(&self) -> Transform {
        match self {
            Transform::Normal => Transform::Normal,
            Transform::Flipped => Transform::Flipped,
            Transform::_90 => Transform::_270,
            Transform::_180 => Transform::_180,
            Transform::_270 => Transform::_90,
            Transform::Flipped90 => Transform::Flipped270,
            Transform::Flipped180 => Transform::Flipped180,
            Transform::Flipped270 => Transform::Flipped90,
        }
    }

    /// Transformed size after applying this transformation.
    pub fn transform_size(&self, width: u32, height: u32) -> (u32, u32) {
        if *self == Transform::_90
            || *self == Transform::_270
            || *self == Transform::Flipped90
            || *self == Transform::Flipped270
        {
            (height, width)
        } else {
            (width, height)
        }
    }
}

#[cfg(feature = "wayland-frontend")]
impl From<wayland_server::protocol::wl_output::Transform> for Transform {
    fn from(transform: wayland_server::protocol::wl_output::Transform) -> Transform {
        use wayland_server::protocol::wl_output::Transform::*;
        match transform {
            Normal => Transform::Normal,
            _90 => Transform::_90,
            _180 => Transform::_180,
            _270 => Transform::_270,
            Flipped => Transform::Flipped,
            Flipped90 => Transform::Flipped90,
            Flipped180 => Transform::Flipped180,
            Flipped270 => Transform::Flipped270,
        }
    }
}

/// Abstraction for Renderers, that can render into different targets
pub trait Bind<Target>: Unbind {
    /// Bind a given rendering target, which will contain the rendering results until `unbind` is called.
    ///
    /// Binding to target, while another one is already bound, is rendering defined.
    /// Some renderers might happily replace the current target, while other might drop the call
    /// or throw an error.
    fn bind(&mut self, target: Target) -> Result<(), <Self as Renderer>::Error>;
    /// Supported pixel formats for given targets, if applicable.
    fn supported_formats(&self) -> Option<HashSet<crate::backend::allocator::Format>> {
        None
    }
}

/// Functionality to unbind the current rendering target
pub trait Unbind: Renderer {
    /// Unbind the current rendering target.
    ///
    /// May fall back to a default target, if defined by the implementation.
    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error>;
}

/// A two dimensional texture
pub trait Texture {
    /// Size of the texture plane (w x h)
    fn size(&self) -> (u32, u32) {
        (self.width(), self.height())
    }

    /// Width of the texture plane
    fn width(&self) -> u32;
    /// Height of the texture plane
    fn height(&self) -> u32;
}

/// Helper trait for [`Renderer`], which defines a rendering api for a currently in-progress frame during [`Renderer::render`].
pub trait Frame {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;

    /// Clear the complete current target with a single given color.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
    /// Render a texture to the current target using given projection matrix and alpha.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture(
        &mut self,
        texture: &Self::TextureId,
        matrix: Matrix3<f32>,
        alpha: f32,
    ) -> Result<(), Self::Error>;
    /// Render a texture to the current target as a flat 2d-plane at a given
    /// position, applying the given transformation with the given alpha value.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture_at(
        &mut self,
        texture: &Self::TextureId,
        pos: (i32, i32),
        transform: Transform,
        alpha: f32,
    ) -> Result<(), Self::Error> {
        let mut mat = Matrix3::<f32>::identity();

        // position and scale
        let size = texture.size();
        mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
        mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);

        //apply surface transformation
        mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
        if transform == Transform::Normal {
            assert_eq!(mat, mat * transform.invert().matrix());
            assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
        }
        mat = mat * transform.invert().matrix();
        mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));

        self.render_texture(texture, mat, alpha)
    }
}

/// Abstraction of commonly used rendering operations for compositors.
pub trait Renderer {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;
    /// Type representing a currently in-progress frame during the [`Renderer::render`]-call
    type Frame: Frame<Error = Self::Error, TextureId = Self::TextureId>;

    /// Import a given bitmap into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
    /// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
    ///
    /// This operation needs no bound or default rendering target.
    #[cfg(feature = "image")]
    fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
        &mut self,
        image: &image::ImageBuffer<image::Rgba<u8>, C>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Returns supported formats for shared memory buffers.
    ///
    /// Will always contain At least `Argb8888` and `Xrgb8888`.
    #[cfg(feature = "wayland_frontend")]
    fn shm_formats(&self) -> &[wl_shm::Format] {
        // Mandatory
        &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
    }

    /// Import a given buffer into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
    ///
    /// This operation needs no bound or default rendering target.
    ///
    /// The implementation defines, if the id keeps being valid, if the buffer is released,
    /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
    ///
    /// If provided the `SurfaceAttributes` can be used to do caching of rendering resources and is generally recommended.
    ///
    /// The `damage` argument provides a list of rectangle locating parts of the buffer that need to be updated. When provided
    /// with an empty list `&[]`, the renderer is allowed to not update the texture at all.
    #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
    fn import_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&SurfaceAttributes>,
        damage: &[Rectangle],
        egl: Option<&EGLBufferReader>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
    ///
    /// This function *may* error, if:
    /// - The given dimensions are unsuppored (too large) for this renderer
    /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
    /// - There was a previous `begin`-call, which was not terminated by `finish`.
    /// - This renderer implements `Bind`, no target was bound *and* has no default target.
    /// - (Renderers not implementing `Bind` always have a default target.)
    fn render<F, R>(
        &mut self,
        width: u32,
        height: u32,
        transform: Transform,
        rendering: F,
    ) -> Result<R, Self::Error>
    where
        F: FnOnce(&mut Self, &mut Self::Frame) -> R;
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
pub fn buffer_dimensions(
    buffer: &wl_buffer::WlBuffer,
    egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<(i32, i32)> {
    if let Some((w, h)) = egl_buffer_reader
        .as_ref()
        .and_then(|x| x.egl_buffer_dimensions(&buffer))
    {
        Some((w, h))
    } else if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl")))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
    use crate::backend::allocator::Buffer;

    if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}
<|endoftext|>


<commit_before>// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc.
//
// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing
// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software
// is made available under an open source license such as the Apache 2.0 License.

use std::fmt;
use std::io::{Seek, SeekFrom};
use std::path::PathBuf;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat};
use regex::Regex;

use error::{BldrResult, BldrError, ErrorKind};
use package::PackageIdent;
use util::gpg;

static LOGKEY: &'static str = "PA";

#[derive(Debug)]
pub enum MetaFile {
    CFlags,
    Config,
    Deps,
    TDeps,
    Exposes,
    Ident,
    LdRunPath,
    LdFlags,
    Manifest,
    Path,
}

impl fmt::Display for MetaFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let id = match *self {
            MetaFile::CFlags => "CFLAGS",
            MetaFile::Config => "default.toml",
            MetaFile::Deps => "DEPS",
            MetaFile::TDeps => "TDEPS",
            MetaFile::Exposes => "EXPOSES",
            MetaFile::Ident => "IDENT",
            MetaFile::LdRunPath => "LD_RUN_PATH",
            MetaFile::LdFlags => "LDFLAGS",
            MetaFile::Manifest => "MANIFEST",
            MetaFile::Path => "PATH",
        };
        write!(f, "{}", id)
    }
}

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
}

impl PackageArchive {
    pub fn new(path: PathBuf) -> Self {
        PackageArchive { path: path }
    }

    pub fn cflags(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    pub fn config(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&self) -> BldrResult<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&self) -> BldrResult<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&self) -> BldrResult<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                                          .filter_map(|port| port.parse::<u16>().ok())
                                          .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&self) -> BldrResult<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(bldr_error!(ErrorKind::MetaFileMalformed(MetaFile::Ident))),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data),
            Err(BldrError {err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&self) -> BldrResult<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(bldr_error!(ErrorKind::MetaFileMalformed(MetaFile::Manifest))),
            Ok(Some(data)) => Ok(data),
            Err(e) => Err(e),
        }
    }

    pub fn path(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data),
            Err(BldrError {err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files gpg signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the GPG signature for any reason
    pub fn verify(&self) -> BldrResult<()> {
        match gpg::verify(self.path.to_str().unwrap()) {
            Ok(_) => Ok(()),
            Err(e) => Err(e),
        }
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked via gpg
    pub fn unpack(&self) -> BldrResult<()> {
        let file = self.path.to_str().unwrap().to_string();
        let mut out = try!(gpg::verify(&file));
        try!(out.seek(SeekFrom::Start(0)));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::All));
        try!(builder.support_filter(ReadFilter::All));
        let mut reader = try!(builder.open_stream(out));
        let writer = writer::Disk::new();
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some("/")));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&self, file: MetaFile) -> BldrResult<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                let ids: Vec<String> = body.split("\n").map(|d| d.to_string()).collect();
                for id in &ids {
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(bldr_error!(ErrorKind::InvalidPackageIdent(package.to_string())));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&self, file: MetaFile) -> BldrResult<Option<String>> {
        let f = self.path.to_str().unwrap().to_string();
        let mut out = try!(gpg::verify(&f));
        try!(out.seek(SeekFrom::Start(0)));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::All));
        try!(builder.support_filter(ReadFilter::All));
        let mut reader = try!(builder.open_stream(out));
        let re = try!(Regex::new(&format!("/{}$", file)));
        loop {
            {
                if let Some(entry) = reader.next_header() {
                    if re.is_match(entry.pathname()) {
                        break;
                    }
                } else {
                    return Err(bldr_error!(ErrorKind::MetaFileNotFound(file)));
                }
            }
        }
        match reader.read_block() {
            Ok(Some(bytes)) => {
                match str::from_utf8(bytes) {
                    Ok(content) => Ok(Some(content.trim().to_string())),
                    Err(_) => Err(bldr_error!(ErrorKind::MetaFileMalformed(file))),
                }
            }
            Ok(None) => Ok(None),
            Err(_) => Err(bldr_error!(ErrorKind::MetaFileMalformed(file))),
        }
    }
}
<commit_msg>match more explicitly when searching for metafiles<commit_after>// Copyright:: Copyright (c) 2015-2016 Chef Software, Inc.
//
// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing
// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software
// is made available under an open source license such as the Apache 2.0 License.

use std::fmt;
use std::io::{Seek, SeekFrom};
use std::path::PathBuf;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat};
use regex::Regex;

use error::{BldrResult, BldrError, ErrorKind};
use package::PackageIdent;
use util::gpg;

static LOGKEY: &'static str = "PA";

#[derive(Debug)]
pub enum MetaFile {
    CFlags,
    Config,
    Deps,
    TDeps,
    Exposes,
    Ident,
    LdRunPath,
    LdFlags,
    Manifest,
    Path,
}

impl fmt::Display for MetaFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let id = match *self {
            MetaFile::CFlags => "CFLAGS",
            MetaFile::Config => "default.toml",
            MetaFile::Deps => "DEPS",
            MetaFile::TDeps => "TDEPS",
            MetaFile::Exposes => "EXPOSES",
            MetaFile::Ident => "IDENT",
            MetaFile::LdRunPath => "LD_RUN_PATH",
            MetaFile::LdFlags => "LDFLAGS",
            MetaFile::Manifest => "MANIFEST",
            MetaFile::Path => "PATH",
        };
        write!(f, "{}", id)
    }
}

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
}

impl PackageArchive {
    pub fn new(path: PathBuf) -> Self {
        PackageArchive { path: path }
    }

    pub fn cflags(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    pub fn config(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&self) -> BldrResult<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&self) -> BldrResult<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&self) -> BldrResult<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                                          .filter_map(|port| port.parse::<u16>().ok())
                                          .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&self) -> BldrResult<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(bldr_error!(ErrorKind::MetaFileMalformed(MetaFile::Ident))),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data),
            Err(BldrError {err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&self) -> BldrResult<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(bldr_error!(ErrorKind::MetaFileMalformed(MetaFile::Manifest))),
            Ok(Some(data)) => Ok(data),
            Err(e) => Err(e),
        }
    }

    pub fn path(&self) -> BldrResult<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data),
            Err(BldrError {err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(None),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files gpg signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the GPG signature for any reason
    pub fn verify(&self) -> BldrResult<()> {
        match gpg::verify(self.path.to_str().unwrap()) {
            Ok(_) => Ok(()),
            Err(e) => Err(e),
        }
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked via gpg
    pub fn unpack(&self) -> BldrResult<()> {
        let file = self.path.to_str().unwrap().to_string();
        let mut out = try!(gpg::verify(&file));
        try!(out.seek(SeekFrom::Start(0)));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::All));
        try!(builder.support_filter(ReadFilter::All));
        let mut reader = try!(builder.open_stream(out));
        let writer = writer::Disk::new();
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some("/")));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&self, file: MetaFile) -> BldrResult<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                let ids: Vec<String> = body.split("\n").map(|d| d.to_string()).collect();
                for id in &ids {
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(bldr_error!(ErrorKind::InvalidPackageIdent(package.to_string())));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(BldrError{err: ErrorKind::MetaFileNotFound(_), ..}) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&self, file: MetaFile) -> BldrResult<Option<String>> {
        let f = self.path.to_str().unwrap().to_string();
        let mut out = try!(gpg::verify(&f));
        try!(out.seek(SeekFrom::Start(0)));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::All));
        try!(builder.support_filter(ReadFilter::All));
        let mut reader = try!(builder.open_stream(out));
        let re = try!(Regex::new(&format!(r"^opt/bldr/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$",
                                          file)));
        loop {
            {
                if let Some(entry) = reader.next_header() {
                    if re.is_match(entry.pathname()) {
                        break;
                    }
                } else {
                    return Err(bldr_error!(ErrorKind::MetaFileNotFound(file)));
                }
            }
        }
        match reader.read_block() {
            Ok(Some(bytes)) => {
                match str::from_utf8(bytes) {
                    Ok(content) => Ok(Some(content.trim().to_string())),
                    Err(_) => Err(bldr_error!(ErrorKind::MetaFileMalformed(file))),
                }
            }
            Ok(None) => Ok(None),
            Err(_) => Err(bldr_error!(ErrorKind::MetaFileMalformed(file))),
        }
    }
}
<|endoftext|>


<commit_before>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: state/game/map_renderer.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      A client-only state that depends on
      the shared game state. This state is
      used only to render the voxel map.
*/

use std::{ vec, ptr, sys, cast, cell };
use extra;
use state::State;
use gl2 = opengles::gl2;
use gl;
use ui;
use math;
use voxel;
use util::Log;

#[path = "../../gl/check.rs"]
mod check;

#[macro_escape]
#[path = "../../util/log_macros.rs"]
mod log_macros;

pub struct Map_Renderer
{
  map: @mut voxel::Map,

  vao: gl2::GLuint,
  vox_vbo: gl2::GLuint,
  offset_tex_vbo: gl2::GLuint,
  offset_tex: gl2::GLuint,
  ibos: ~[gl2::GLuint],
  curr_ibo: u32,
  visible_voxels: Option<~[u32]>,
  prev_visible_voxel_count: u32,

  /* states, visible */
  map_stream: extra::comm::DuplexStream<(cell::Cell<~[u32]>, cell::Cell<~[u32]>), (~[u32], ~[u32])>,

  wireframe: bool,

  shader: @gl::Shader,
  proj_loc: gl2::GLint,
  world_loc: gl2::GLint,
  voxel_size_loc: gl2::GLint,
  offsets_loc: gl2::GLint,
}

impl Map_Renderer
{
  pub fn new(map: @mut voxel::Map) -> @mut Map_Renderer
  {
    let (local_stream, _) = extra::comm::DuplexStream();

    let mr = @mut Map_Renderer
    {
      map: map,

      vao: 0,
      vox_vbo: 0,
      offset_tex_vbo: 0,
      offset_tex: 0,
      ibos: vec::from_elem(2, 2u32),
      curr_ibo: 0,
      visible_voxels: Some(vec::from_elem((map.resolution * map.resolution * map.resolution) as uint, 0u32)),
      prev_visible_voxel_count: 0,

      map_stream: local_stream,

      wireframe: false,

      shader: gl::Shader_Builder::new_with_files("data/shaders/voxel.vert", "data/shaders/voxel.frag"),
      proj_loc: 0,
      world_loc: 0,
      voxel_size_loc: 0,
      offsets_loc: 0,
    };

    /* Single voxel that will be instance-rendered. */
    let h: f32 = mr.map.voxel_size / 2.0;
    let voxel: ~[f32] = /* TRIANGLE_STRIP style. */
    ~[
      -h,-h,h,  h,-h,h,
      -h,h,h,   h,h,h,

      h,-h,h,   h,-h,-h,
      h,h,h,    h,h,-h,

      h,-h,-h,  -h,-h,-h,
      h,h,-h,   -h,h,-h,

      -h,-h,-h, -h,-h,h,
      -h,h,-h,  -h,h,h,

      -h,-h,-h, h,-h,-h,
      -h,-h,h,  h,-h,h,

      -h,h,h,   h,h,h,
      -h,h,-h,  h,h,-h,
    ];

    let names = check!(gl2::gen_vertex_arrays(1));
    assert!(names.len() == 1);
    mr.vao = names[0];

    let names = check!(gl2::gen_buffers(4));
    assert!(names.len() == 4);
    mr.vox_vbo = names[0];
    mr.offset_tex_vbo = names[1];
    mr.ibos[0] = names[2];
    mr.ibos[1] = names[3];

    check!(gl2::bind_vertex_array(mr.vao));
    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, mr.vox_vbo));
    check!(gl2::buffer_data(gl2::ARRAY_BUFFER, voxel, gl2::STATIC_DRAW));

    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, mr.ibos[0]));
    let ibo_buf = vec::from_elem((mr.map.resolution * mr.map.resolution * mr.map.resolution) as uint, 0);
    check!(gl2::buffer_data(gl2::ARRAY_BUFFER, ibo_buf, gl2::DYNAMIC_DRAW));

    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, mr.ibos[1]));
    check!(gl2::buffer_data(gl2::ARRAY_BUFFER, ibo_buf, gl2::DYNAMIC_DRAW));

    check!(gl2::bind_buffer(gl2::TEXTURE_BUFFER, mr.offset_tex_vbo));
    check!(gl2::buffer_data(gl2::TEXTURE_BUFFER, mr.map.voxels, gl2::STATIC_DRAW));

    let name = check!(gl2::gen_textures(1));
    assert!(name.len() == 1);
    mr.offset_tex = name[0];
    check!(gl2::bind_texture(gl2::TEXTURE_BUFFER, mr.offset_tex));
    check!(gl2::tex_buffer(gl2::TEXTURE_BUFFER, 0x8815 /* RGB32F */, mr.offset_tex_vbo));

    /* Console functions. */
    ui::Console_Activator::get().add_accessor("map.wireframe", |_|
    { mr.wireframe.to_str() });
    ui::Console_Activator::get().add_mutator("map.wireframe", |p, x|
    {
      let mut error = ~"";
      if x == "true"
      { mr.wireframe = true; }
      else if x == "false"
      { mr.wireframe = false; }
      else
      { error = fmt!("Invalid value for %s (use 'true' or 'false')", p); }

      if error.len() == 0
      { None }
      else
      { Some(error) }
    });

    mr
  }

  pub fn update_visibility(&mut self)
  {
    self.prev_visible_voxel_count = self.visible_voxels.get_ref().len() as u32;

    let cam = gl::Camera::get_active();
    let dist = (cam.near_far.y  / self.map.voxel_size) as i32; /* How far the camera can see. */
    let res = self.map.resolution as f32;
    let pos = math::Vec3f::new(cam.position.x / self.map.voxel_size,
                               cam.position.y / self.map.voxel_size,
                               cam.position.z / self.map.voxel_size)
                               + math::Vec3f::new(res / 2.0, res / 2.0, res / 2.0);
    let start = math::Vec3i::new
    (
      (pos.x - dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.y - dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.z - dist as f32).clamp(&0.0, &(res - 1.0)) as i32
    );
    let end = math::Vec3i::new
    (
      (pos.x + dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.y + dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.z + dist as f32).clamp(&0.0, &(res - 1.0)) as i32
    );

    self.visible_voxels.get_mut_ref().clear();

    /* Updating visible voxels is an expensive task. To remedy this,
     * the work is done on a background thread that has a shared OpenGL
     * context. While that work is being done, the map renderer will
     * not have visible voxels or voxel states, since they're moved
     * to the task. Once the task is finished, however, the fields are
     * sent back. */
    let (local_stream, remote_stream) = extra::comm::DuplexStream();
    self.map_stream = local_stream;

    /* Send out the voxel states and visible voxels. */
    self.map_stream.send((cell::Cell::new(self.map.states.take_unwrap()), cell::Cell::new(self.visible_voxels.take_unwrap())));

    /* Start the new background task of culling far-away voxels. */
    let resolution = self.map.resolution;
    let ibo = self.ibos[self.curr_ibo];
    do gl::Worker::new_task
    {
      let (cell_states, cell_visible_voxels) = remote_stream.recv();
      let states = cell_states.take();
      let mut visible_voxels = cell_visible_voxels.take();

      for z in range(start.z, end.z)
      {
        for y in range(start.y, end.y)
        {
          for x in range(start.x, end.x)
          {
            let index = (z * ((resolution * resolution) as i32)) + (y * (resolution as i32)) + x;
            if (states[index] & voxel::Visible) != 0
            { visible_voxels.push(states[index] & !voxel::Visible); }
          }
        }
      }

      /* Upload the data in an unsynchronized manner. This will prevent
       * OpenGL from trying to synchronize the mapping, which means it
       * can be mapped without effectively without the need to glFinish.
       * This is safe since an alternate set is being renderered while
       * this is being updated. */
      check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, ibo));
      unsafe
      {
        let size = visible_voxels.len() * sys::size_of::<u32>();
        let mem = check!(gl2::glMapBufferRange(gl2::ARRAY_BUFFER, 0, size as i64, 2 | 32));
        ptr::copy_nonoverlapping_memory(cast::transmute(mem), vec::raw::to_ptr(visible_voxels), size);
        check!(gl2::glUnmapBuffer(gl2::ARRAY_BUFFER));
      }

      /* Send the member data back. */
      remote_stream.send((states, visible_voxels));

      false /* Don't kill the GL worker. */
    }
  }
}

impl State for Map_Renderer
{
  pub fn load(&mut self)
  {
    log_debug!("Loading map renderer state");

    self.shader.bind();
    self.proj_loc = self.shader.get_uniform_location("proj");
    self.world_loc = self.shader.get_uniform_location("world");
    self.voxel_size_loc = self.shader.get_uniform_location("voxel_size");
    self.offsets_loc = self.shader.get_uniform_location("offsets");

    self.shader.update_uniform_i32(self.offsets_loc, 0);

    self.update_visibility();
  }

  pub fn unload(&mut self)
  { log_debug!("Unloading map renderer state"); }

  pub fn update(&mut self, _delta: f32) -> bool /* dt is in terms of seconds. */
  {
    /* Check if there is data available between the background
     * thread and us. The last thing it does is send back some
     * member data that we'll need to put back in place before
     * doing any more work. */
    if !self.map_stream.peek()
    { return false; }

    /* Extract the new data. */
    let (states, visible_voxels) = self.map_stream.recv();
    self.map.states = Some(states);
    self.visible_voxels = Some(visible_voxels);

    /* TODO: Work goes here. */

    /* Swap the current IBO and begin updating the old one. */
    if self.curr_ibo == 0
    { self.curr_ibo = 1; }
    else
    { self.curr_ibo = 0; }
    self.update_visibility();

    false
  }

  pub fn render(&mut self) -> bool
  {
    let camera = gl::Camera::get_active();

    self.shader.bind();
    self.shader.update_uniform_mat(self.proj_loc, &camera.projection);
    self.shader.update_uniform_mat(self.world_loc, &camera.view);
    self.shader.update_uniform_f32(self.voxel_size_loc, self.map.voxel_size);

    check!(gl2::bind_vertex_array(self.vao));

    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, self.vox_vbo));
    check!(gl2::vertex_attrib_pointer_f32(0, 3, false, 0, 0));
    check!(gl2::enable_vertex_attrib_array(0));

    if self.curr_ibo == 0
    { check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, self.ibos[1])); }
    else
    { check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, self.ibos[0])); }
    check!(gl2::vertex_attrib_i_pointer_i32(1, 1, 0, 0));
    check!(gl2::enable_vertex_attrib_array(1));
    check!(gl2::vertex_attrib_divisor(1, 1));

    check!(gl2::bind_texture(gl2::TEXTURE_BUFFER, self.offset_tex));

    if self.wireframe
    { check!(gl2::polygon_mode(gl2::FRONT_AND_BACK, gl2::LINE)); }

    check!(gl2::draw_arrays_instanced(gl2::TRIANGLE_STRIP, 0, 24, self.prev_visible_voxel_count as i32));

    if self.wireframe
    { check!(gl2::polygon_mode(gl2::FRONT_AND_BACK, gl2::FILL)); }

    check!(gl2::disable_vertex_attrib_array(0));
    check!(gl2::disable_vertex_attrib_array(1));
    check!(gl2::bind_vertex_array(0));
    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, 0));

    false
  }
}

<commit_msg> #30 Worker thread must finish on renderer unload<commit_after>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: state/game/map_renderer.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      A client-only state that depends on
      the shared game state. This state is
      used only to render the voxel map.
*/

use std::{ vec, ptr, sys, cast, cell };
use extra;
use state::State;
use gl2 = opengles::gl2;
use gl;
use ui;
use math;
use voxel;
use util::Log;

#[path = "../../gl/check.rs"]
mod check;

#[macro_escape]
#[path = "../../util/log_macros.rs"]
mod log_macros;

pub struct Map_Renderer
{
  map: @mut voxel::Map,

  vao: gl2::GLuint,
  vox_vbo: gl2::GLuint,
  offset_tex_vbo: gl2::GLuint,
  offset_tex: gl2::GLuint,
  ibos: ~[gl2::GLuint],
  curr_ibo: u32,
  visible_voxels: Option<~[u32]>,
  prev_visible_voxel_count: u32,

  /* states, visible */
  map_stream: extra::comm::DuplexStream<(cell::Cell<~[u32]>, cell::Cell<~[u32]>), (~[u32], ~[u32])>,

  wireframe: bool,

  shader: @gl::Shader,
  proj_loc: gl2::GLint,
  world_loc: gl2::GLint,
  voxel_size_loc: gl2::GLint,
  offsets_loc: gl2::GLint,
}

impl Map_Renderer
{
  pub fn new(map: @mut voxel::Map) -> @mut Map_Renderer
  {
    let (local_stream, _) = extra::comm::DuplexStream();

    let mr = @mut Map_Renderer
    {
      map: map,

      vao: 0,
      vox_vbo: 0,
      offset_tex_vbo: 0,
      offset_tex: 0,
      ibos: vec::from_elem(2, 2u32),
      curr_ibo: 0,
      visible_voxels: Some(vec::from_elem((map.resolution * map.resolution * map.resolution) as uint, 0u32)),
      prev_visible_voxel_count: 0,

      map_stream: local_stream,

      wireframe: false,

      shader: gl::Shader_Builder::new_with_files("data/shaders/voxel.vert", "data/shaders/voxel.frag"),
      proj_loc: 0,
      world_loc: 0,
      voxel_size_loc: 0,
      offsets_loc: 0,
    };

    /* Single voxel that will be instance-rendered. */
    let h: f32 = mr.map.voxel_size / 2.0;
    let voxel: ~[f32] = /* TRIANGLE_STRIP style. */
    ~[
      -h,-h,h,  h,-h,h,
      -h,h,h,   h,h,h,

      h,-h,h,   h,-h,-h,
      h,h,h,    h,h,-h,

      h,-h,-h,  -h,-h,-h,
      h,h,-h,   -h,h,-h,

      -h,-h,-h, -h,-h,h,
      -h,h,-h,  -h,h,h,

      -h,-h,-h, h,-h,-h,
      -h,-h,h,  h,-h,h,

      -h,h,h,   h,h,h,
      -h,h,-h,  h,h,-h,
    ];

    let names = check!(gl2::gen_vertex_arrays(1));
    assert!(names.len() == 1);
    mr.vao = names[0];

    let names = check!(gl2::gen_buffers(4));
    assert!(names.len() == 4);
    mr.vox_vbo = names[0];
    mr.offset_tex_vbo = names[1];
    mr.ibos[0] = names[2];
    mr.ibos[1] = names[3];

    check!(gl2::bind_vertex_array(mr.vao));
    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, mr.vox_vbo));
    check!(gl2::buffer_data(gl2::ARRAY_BUFFER, voxel, gl2::STATIC_DRAW));

    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, mr.ibos[0]));
    let ibo_buf = vec::from_elem((mr.map.resolution * mr.map.resolution * mr.map.resolution) as uint, 0);
    check!(gl2::buffer_data(gl2::ARRAY_BUFFER, ibo_buf, gl2::DYNAMIC_DRAW));

    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, mr.ibos[1]));
    check!(gl2::buffer_data(gl2::ARRAY_BUFFER, ibo_buf, gl2::DYNAMIC_DRAW));

    check!(gl2::bind_buffer(gl2::TEXTURE_BUFFER, mr.offset_tex_vbo));
    check!(gl2::buffer_data(gl2::TEXTURE_BUFFER, mr.map.voxels, gl2::STATIC_DRAW));

    let name = check!(gl2::gen_textures(1));
    assert!(name.len() == 1);
    mr.offset_tex = name[0];
    check!(gl2::bind_texture(gl2::TEXTURE_BUFFER, mr.offset_tex));
    check!(gl2::tex_buffer(gl2::TEXTURE_BUFFER, 0x8815 /* RGB32F */, mr.offset_tex_vbo));

    /* Console functions. */
    ui::Console_Activator::get().add_accessor("map.wireframe", |_|
    { mr.wireframe.to_str() });
    ui::Console_Activator::get().add_mutator("map.wireframe", |p, x|
    {
      let mut error = ~"";
      if x == "true"
      { mr.wireframe = true; }
      else if x == "false"
      { mr.wireframe = false; }
      else
      { error = fmt!("Invalid value for %s (use 'true' or 'false')", p); }

      if error.len() == 0
      { None }
      else
      { Some(error) }
    });

    mr
  }

  pub fn update_visibility(&mut self)
  {
    self.prev_visible_voxel_count = self.visible_voxels.get_ref().len() as u32;

    let cam = gl::Camera::get_active();
    let dist = (cam.near_far.y  / self.map.voxel_size) as i32; /* How far the camera can see. */
    let res = self.map.resolution as f32;
    let pos = math::Vec3f::new(cam.position.x / self.map.voxel_size,
                               cam.position.y / self.map.voxel_size,
                               cam.position.z / self.map.voxel_size)
                               + math::Vec3f::new(res / 2.0, res / 2.0, res / 2.0);
    let start = math::Vec3i::new
    (
      (pos.x - dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.y - dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.z - dist as f32).clamp(&0.0, &(res - 1.0)) as i32
    );
    let end = math::Vec3i::new
    (
      (pos.x + dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.y + dist as f32).clamp(&0.0, &(res - 1.0)) as i32,
      (pos.z + dist as f32).clamp(&0.0, &(res - 1.0)) as i32
    );

    self.visible_voxels.get_mut_ref().clear();

    /* Updating visible voxels is an expensive task. To remedy this,
     * the work is done on a background thread that has a shared OpenGL
     * context. While that work is being done, the map renderer will
     * not have visible voxels or voxel states, since they're moved
     * to the task. Once the task is finished, however, the fields are
     * sent back. */
    let (local_stream, remote_stream) = extra::comm::DuplexStream();
    self.map_stream = local_stream;

    /* Send out the voxel states and visible voxels. */
    self.map_stream.send((cell::Cell::new(self.map.states.take_unwrap()), cell::Cell::new(self.visible_voxels.take_unwrap())));

    /* Start the new background task of culling far-away voxels. */
    let resolution = self.map.resolution;
    let ibo = self.ibos[self.curr_ibo];
    do gl::Worker::new_task
    {
      let (cell_states, cell_visible_voxels) = remote_stream.recv();
      let states = cell_states.take();
      let mut visible_voxels = cell_visible_voxels.take();

      for z in range(start.z, end.z)
      {
        for y in range(start.y, end.y)
        {
          for x in range(start.x, end.x)
          {
            let index = (z * ((resolution * resolution) as i32)) + (y * (resolution as i32)) + x;
            if (states[index] & voxel::Visible) != 0
            { visible_voxels.push(states[index] & !voxel::Visible); }
          }
        }
      }

      /* Upload the data in an unsynchronized manner. This will prevent
       * OpenGL from trying to synchronize the mapping, which means it
       * can be mapped without effectively without the need to glFinish.
       * This is safe since an alternate set is being renderered while
       * this is being updated. */
      check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, ibo));
      unsafe
      {
        let size = visible_voxels.len() * sys::size_of::<u32>();
        let mem = check!(gl2::glMapBufferRange(gl2::ARRAY_BUFFER, 0, size as i64, 2 | 32));
        ptr::copy_nonoverlapping_memory(cast::transmute(mem), vec::raw::to_ptr(visible_voxels), size);
        check!(gl2::glUnmapBuffer(gl2::ARRAY_BUFFER));
      }

      /* Send the member data back. */
      remote_stream.send((states, visible_voxels));

      false /* Don't kill the GL worker. */
    }
  }
}

impl State for Map_Renderer
{
  pub fn load(&mut self)
  {
    log_debug!("Loading map renderer state");

    self.shader.bind();
    self.proj_loc = self.shader.get_uniform_location("proj");
    self.world_loc = self.shader.get_uniform_location("world");
    self.voxel_size_loc = self.shader.get_uniform_location("voxel_size");
    self.offsets_loc = self.shader.get_uniform_location("offsets");

    self.shader.update_uniform_i32(self.offsets_loc, 0);

    self.update_visibility();
  }

  pub fn unload(&mut self)
  {
    log_debug!("Unloading map renderer state");

    /* Since the background worker is doing its thing, we'll
     * need to wait for it to finish so that it doesn't try
     * to update us when we're dead. */
    let (states, visible_voxels) = self.map_stream.recv();
    self.map.states = Some(states);
    self.visible_voxels = Some(visible_voxels);
  }

  pub fn update(&mut self, _delta: f32) -> bool /* dt is in terms of seconds. */
  {
    /* Check if there is data available between the background
     * thread and us. The last thing it does is send back some
     * member data that we'll need to put back in place before
     * doing any more work. */
    if !self.map_stream.peek()
    { return false; }

    /* Extract the new data. */
    let (states, visible_voxels) = self.map_stream.recv();
    self.map.states = Some(states);
    self.visible_voxels = Some(visible_voxels);

    /* TODO: Work goes here. */

    /* Swap the current IBO and begin updating the old one. */
    if self.curr_ibo == 0
    { self.curr_ibo = 1; }
    else
    { self.curr_ibo = 0; }
    self.update_visibility();

    false
  }

  pub fn render(&mut self) -> bool
  {
    let camera = gl::Camera::get_active();

    self.shader.bind();
    self.shader.update_uniform_mat(self.proj_loc, &camera.projection);
    self.shader.update_uniform_mat(self.world_loc, &camera.view);
    self.shader.update_uniform_f32(self.voxel_size_loc, self.map.voxel_size);

    check!(gl2::bind_vertex_array(self.vao));

    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, self.vox_vbo));
    check!(gl2::vertex_attrib_pointer_f32(0, 3, false, 0, 0));
    check!(gl2::enable_vertex_attrib_array(0));

    if self.curr_ibo == 0
    { check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, self.ibos[1])); }
    else
    { check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, self.ibos[0])); }
    check!(gl2::vertex_attrib_i_pointer_i32(1, 1, 0, 0));
    check!(gl2::enable_vertex_attrib_array(1));
    check!(gl2::vertex_attrib_divisor(1, 1));

    check!(gl2::bind_texture(gl2::TEXTURE_BUFFER, self.offset_tex));

    if self.wireframe
    { check!(gl2::polygon_mode(gl2::FRONT_AND_BACK, gl2::LINE)); }

    check!(gl2::draw_arrays_instanced(gl2::TRIANGLE_STRIP, 0, 24, self.prev_visible_voxel_count as i32));

    if self.wireframe
    { check!(gl2::polygon_mode(gl2::FRONT_AND_BACK, gl2::FILL)); }

    check!(gl2::disable_vertex_attrib_array(0));
    check!(gl2::disable_vertex_attrib_array(1));
    check!(gl2::bind_vertex_array(0));
    check!(gl2::bind_buffer(gl2::ARRAY_BUFFER, 0));

    false
  }
}

<|endoftext|>


<commit_before>use super::{Invocation, get_system_dir, };
use util::{CommandQueue};

use clang_driver;

use std::error::Error;
use std::path::{Path};

const FILES: &'static [&'static str] = &[
  "dlmalloc.c",
];

impl Invocation {
  fn build_cc(&self,
              file: &'static str,
              queue: &mut &mut CommandQueue<Invocation>)
    -> Result<(), Box<Error>>
  {
    let full_file = get_system_dir().join(file);

    let mut clang = clang_driver::Invocation::default();
    clang.driver_mode = clang_driver::DriverMode::CC;

    let mut args = Vec::new();
    args.push(format!("-isystem{}", self.musl_include_dir().display()));
    args.push("-c".to_string());
    args.push(format!("{}", full_file.display()));

    let out_file = self.dlmalloc_obj_output()?;

    args.push("-O3".to_string());
    super::add_default_args(&mut args);

    let cmd = queue
      .enqueue_tool(Some("clang"),
                    clang, args, false,
                    None::<Vec<::tempdir::TempDir>>)
      .expect("internal error: bad clang arguments");
    cmd.prev_outputs = false;
    cmd.output_override = true;
    cmd.intermediate_name = Some(out_file);

    Ok(())
  }

  pub fn build_dlmalloc(&self, mut queue: &mut CommandQueue<Invocation>)
    -> Result<(), Box<Error>>
  {
    assert_eq!(FILES.len(), 1, "XXX dlmalloc will probably be a single file forever tho");
    for file in FILES.iter() {
      self.build_cc(file, &mut queue)?;
    }
    Ok(())
  }
}
<commit_msg>Some `-O2` optimization introduces a infinite loop in dlmalloc.<commit_after>use super::{Invocation, get_system_dir, };
use util::{CommandQueue};

use clang_driver;

use std::error::Error;
use std::path::{Path};

const FILES: &'static [&'static str] = &[
  "dlmalloc.c",
];

impl Invocation {
  fn build_cc(&self,
              file: &'static str,
              queue: &mut &mut CommandQueue<Invocation>)
    -> Result<(), Box<Error>>
  {
    let full_file = get_system_dir().join(file);

    let mut clang = clang_driver::Invocation::default();
    clang.driver_mode = clang_driver::DriverMode::CC;

    let mut args = Vec::new();
    args.push(format!("-isystem{}", self.musl_include_dir().display()));
    args.push("-c".to_string());
    args.push(format!("{}", full_file.display()));

    let out_file = self.dlmalloc_obj_output()?;

    args.push("-O1".to_string());
    super::add_default_args(&mut args);

    let cmd = queue
      .enqueue_tool(Some("clang"),
                    clang, args, false,
                    None::<Vec<::tempdir::TempDir>>)
      .expect("internal error: bad clang arguments");
    cmd.prev_outputs = false;
    cmd.output_override = true;
    cmd.intermediate_name = Some(out_file);

    Ok(())
  }

  pub fn build_dlmalloc(&self, mut queue: &mut CommandQueue<Invocation>)
    -> Result<(), Box<Error>>
  {
    assert_eq!(FILES.len(), 1, "XXX dlmalloc will probably be a single file forever tho");
    for file in FILES.iter() {
      self.build_cc(file, &mut queue)?;
    }
    Ok(())
  }
}
<|endoftext|>


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

//! Tidy check to ensure that unstable features are all in order
//!
//! This check will ensure properties like:
//!
//! * All stability attributes look reasonably well formed
//! * The set of library features is disjoint from the set of language features
//! * Library features have at most one stability level
//! * Library features have at most one `since` value

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[derive(PartialEq)]
enum Status {
    Stable,
    Unstable,
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let as_str = match *self {
            Status::Stable => "stable",
            Status::Unstable => "unstable",
        };
        fmt::Display::fmt(as_str, f)
    }
}

struct Feature {
    name: String,
    level: Status,
    since: String,
}

struct LibFeature {
    level: Status,
    since: String,
}

pub fn check(path: &Path, bad: &mut bool) {
    let features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
    assert!(!features.is_empty());
    let mut lib_features = HashMap::<String, LibFeature>::new();

    let mut contents = String::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        for (i, line) in contents.lines().enumerate() {
            let mut err = |msg: &str| {
                println!("{}:{}: {}", file.display(), i + 1, msg);
                *bad = true;
            };
            let level = if line.contains("[unstable(") {
                Status::Unstable
            } else if line.contains("[stable(") {
                Status::Stable
            } else {
                continue;
            };
            let feature_name = match find_attr_val(line, "feature") {
                Some(name) => name,
                None => {
                    err("malformed stability attribute");
                    continue;
                }
            };
            let since = match find_attr_val(line, "since") {
                Some(name) => name,
                None if level == Status::Stable => {
                    err("malformed stability attribute");
                    continue;
                }
                None => "None",
            };

            if features.iter().any(|f| f.name == feature_name) {
                err("duplicating a lang feature");
            }
            if let Some(ref s) = lib_features.get(feature_name) {
                if s.level != level {
                    err("different stability level than before");
                }
                if s.since != since {
                    err("different `since` than before");
                }
                continue;
            }
            lib_features.insert(feature_name.to_owned(),
                                LibFeature {
                                    level: level,
                                    since: since.to_owned(),
                                });
        }
    });

    if *bad {
        return;
    }

    let mut lines = Vec::new();
    for feature in features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           feature.name,
                           "lang",
                           feature.level,
                           feature.since));
    }
    for (name, feature) in lib_features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lib",
                           feature.level,
                           feature.since));
    }

    lines.sort();
    for line in lines {
        println!("* {}", line);
    }
}

fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
    line.find(attr)
        .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
        .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
        .map(|(i, j)| &line[i..j])
}

fn collect_lang_features(path: &Path) -> Vec<Feature> {
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    contents.lines()
        .filter_map(|line| {
            let mut parts = line.trim().split(",");
            let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
                Some("active") => Status::Unstable,
                Some("removed") => Status::Unstable,
                Some("accepted") => Status::Stable,
                _ => return None,
            };
            let name = parts.next().unwrap().trim();
            let since = parts.next().unwrap().trim().trim_matches('"');
            Some(Feature {
                name: name.to_owned(),
                level: level,
                since: since.to_owned(),
            })
        })
        .collect()
}
<commit_msg>Use only one feature struct, and use hash maps in both occasions<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Tidy check to ensure that unstable features are all in order
//!
//! This check will ensure properties like:
//!
//! * All stability attributes look reasonably well formed
//! * The set of library features is disjoint from the set of language features
//! * Library features have at most one stability level
//! * Library features have at most one `since` value

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[derive(PartialEq)]
enum Status {
    Stable,
    Unstable,
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let as_str = match *self {
            Status::Stable => "stable",
            Status::Unstable => "unstable",
        };
        fmt::Display::fmt(as_str, f)
    }
}

struct Feature {
    level: Status,
    since: String,
}

pub fn check(path: &Path, bad: &mut bool) {
    let features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
    assert!(!features.is_empty());
    let mut lib_features = HashMap::<String, Feature>::new();

    let mut contents = String::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        for (i, line) in contents.lines().enumerate() {
            let mut err = |msg: &str| {
                println!("{}:{}: {}", file.display(), i + 1, msg);
                *bad = true;
            };
            let level = if line.contains("[unstable(") {
                Status::Unstable
            } else if line.contains("[stable(") {
                Status::Stable
            } else {
                continue;
            };
            let feature_name = match find_attr_val(line, "feature") {
                Some(name) => name,
                None => {
                    err("malformed stability attribute");
                    continue;
                }
            };
            let since = match find_attr_val(line, "since") {
                Some(name) => name,
                None if level == Status::Stable => {
                    err("malformed stability attribute");
                    continue;
                }
                None => "None",
            };

            if features.contains_key(feature_name) {
                err("duplicating a lang feature");
            }
            if let Some(ref s) = lib_features.get(feature_name) {
                if s.level != level {
                    err("different stability level than before");
                }
                if s.since != since {
                    err("different `since` than before");
                }
                continue;
            }
            lib_features.insert(feature_name.to_owned(),
                                Feature {
                                    level: level,
                                    since: since.to_owned(),
                                });
        }
    });

    if *bad {
        return;
    }

    let mut lines = Vec::new();
    for (name, feature) in features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lang",
                           feature.level,
                           feature.since));
    }
    for (name, feature) in lib_features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lib",
                           feature.level,
                           feature.since));
    }

    lines.sort();
    for line in lines {
        println!("* {}", line);
    }
}

fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
    line.find(attr)
        .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
        .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
        .map(|(i, j)| &line[i..j])
}

fn collect_lang_features(path: &Path) -> HashMap<String, Feature> {
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    contents.lines()
        .filter_map(|line| {
            let mut parts = line.trim().split(",");
            let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
                Some("active") => Status::Unstable,
                Some("removed") => Status::Unstable,
                Some("accepted") => Status::Stable,
                _ => return None,
            };
            let name = parts.next().unwrap().trim();
            let since = parts.next().unwrap().trim().trim_matches('"');
            Some((name.to_owned(),
                Feature {
                    level: level,
                    since: since.to_owned(),
                }))
        })
        .collect()
}
<|endoftext|>