<commit_before>use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Doc, Enum, ForeignName, Pair, Variant};
use proc_macro2::Ident;
use serde::Deserialize;
use std::env;
use std::fs;
use std::path::PathBuf;

const CXX_CLANG_AST: &str = "CXX_CLANG_AST";

type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    Unknown,
}

#[derive(Deserialize)]
struct NamespaceDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumConstantDecl {
    name: String,
}

pub fn load(cx: &mut Errors, apis: &mut [Api]) {
    let ref mut variants_from_header = Vec::new();
    for api in apis {
        if let Api::Enum(enm) = api {
            if enm.variants_from_header {
                variants_from_header.push(enm);
            }
        }
    }

    let span = match variants_from_header.get(0) {
        None => return,
        Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
    };

    let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
        Some(ast_dump_path) => PathBuf::from(ast_dump_path),
        None => {
            let msg = format!(
                "environment variable ${} has not been provided",
                CXX_CLANG_AST,
            );
            return cx.error(span, msg);
        }
    };

    let ast_dump_bytes = match fs::read(&ast_dump_path) {
        Ok(ast_dump_bytes) => ast_dump_bytes,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref root: Node = match serde_json::from_slice(&ast_dump_bytes) {
        Ok(root) => root,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref mut namespace = Vec::new();
    traverse(root, namespace, variants_from_header, None);
}

fn traverse<'a>(
    node: &'a Node,
    namespace: &mut Vec<&'a str>,
    variants_from_header: &mut [&mut Enum],
    mut idx: Option<usize>,
) {
    match &node.kind {
        Clang::NamespaceDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                // Can ignore enums inside an anonymous namespace.
                None => return,
            };
            namespace.push(name);
            idx = None;
        }
        Clang::EnumDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                None => return,
            };
            idx = None;
            for (i, enm) in variants_from_header.iter().enumerate() {
                if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
                    idx = Some(i);
                    break;
                }
            }
            if idx.is_none() {
                return;
            }
        }
        Clang::EnumConstantDecl(decl) => {
            if let Some(idx) = idx {
                let enm = &mut *variants_from_header[idx];
                let span = enm
                    .variants_from_header_attr
                    .as_ref()
                    .unwrap()
                    .path
                    .get_ident()
                    .unwrap()
                    .span();
                let cxx_name = match ForeignName::parse(&decl.name, span) {
                    Ok(foreign_name) => foreign_name,
                    Err(_) => return,
                };
                let rust_name: Ident = match syn::parse_str(&decl.name) {
                    Ok(ident) => ident,
                    Err(_) => return,
                };
                enm.variants.push(Variant {
                    doc: Doc::new(),
                    attrs: OtherAttrs::none(),
                    name: Pair {
                        namespace: Namespace::ROOT,
                        cxx: cxx_name,
                        rust: rust_name,
                    },
                    discriminant: unimplemented!(),
                    expr: None,
                });
            }
        }
        _ => {}
    }
    for inner in &node.inner {
        traverse(inner, namespace, variants_from_header, idx);
    }
    if let Clang::NamespaceDecl(_) = &node.kind {
        let _ = namespace.pop().unwrap();
    }
}
<commit_msg>Check whether variants have been found for every enum<commit_after>use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Doc, Enum, ForeignName, Pair, Variant};
use proc_macro2::Ident;
use serde::Deserialize;
use std::env;
use std::fs;
use std::path::PathBuf;

const CXX_CLANG_AST: &str = "CXX_CLANG_AST";

type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    Unknown,
}

#[derive(Deserialize)]
struct NamespaceDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumConstantDecl {
    name: String,
}

pub fn load(cx: &mut Errors, apis: &mut [Api]) {
    let ref mut variants_from_header = Vec::new();
    for api in apis {
        if let Api::Enum(enm) = api {
            if enm.variants_from_header {
                variants_from_header.push(enm);
            }
        }
    }

    let span = match variants_from_header.get(0) {
        None => return,
        Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
    };

    let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
        Some(ast_dump_path) => PathBuf::from(ast_dump_path),
        None => {
            let msg = format!(
                "environment variable ${} has not been provided",
                CXX_CLANG_AST,
            );
            return cx.error(span, msg);
        }
    };

    let ast_dump_bytes = match fs::read(&ast_dump_path) {
        Ok(ast_dump_bytes) => ast_dump_bytes,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref root: Node = match serde_json::from_slice(&ast_dump_bytes) {
        Ok(root) => root,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref mut namespace = Vec::new();
    traverse(root, namespace, variants_from_header, None);

    for enm in variants_from_header {
        if enm.variants.is_empty() {
            let span = &enm.variants_from_header_attr;
            let mut msg = "failed to find any C++ definition of enum ".to_owned();
            for name in &enm.name.namespace {
                msg += &name.to_string();
                msg += "::";
            }
            msg += &enm.name.cxx.to_string();
            cx.error(span, msg);
        }
    }
}

fn traverse<'a>(
    node: &'a Node,
    namespace: &mut Vec<&'a str>,
    variants_from_header: &mut [&mut Enum],
    mut idx: Option<usize>,
) {
    match &node.kind {
        Clang::NamespaceDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                // Can ignore enums inside an anonymous namespace.
                None => return,
            };
            namespace.push(name);
            idx = None;
        }
        Clang::EnumDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                None => return,
            };
            idx = None;
            for (i, enm) in variants_from_header.iter().enumerate() {
                if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
                    idx = Some(i);
                    break;
                }
            }
            if idx.is_none() {
                return;
            }
        }
        Clang::EnumConstantDecl(decl) => {
            if let Some(idx) = idx {
                let enm = &mut *variants_from_header[idx];
                let span = enm
                    .variants_from_header_attr
                    .as_ref()
                    .unwrap()
                    .path
                    .get_ident()
                    .unwrap()
                    .span();
                let cxx_name = match ForeignName::parse(&decl.name, span) {
                    Ok(foreign_name) => foreign_name,
                    Err(_) => return,
                };
                let rust_name: Ident = match syn::parse_str(&decl.name) {
                    Ok(ident) => ident,
                    Err(_) => return,
                };
                enm.variants.push(Variant {
                    doc: Doc::new(),
                    attrs: OtherAttrs::none(),
                    name: Pair {
                        namespace: Namespace::ROOT,
                        cxx: cxx_name,
                        rust: rust_name,
                    },
                    discriminant: unimplemented!(),
                    expr: None,
                });
            }
        }
        _ => {}
    }
    for inner in &node.inner {
        traverse(inner, namespace, variants_from_header, idx);
    }
    if let Clang::NamespaceDecl(_) = &node.kind {
        let _ = namespace.pop().unwrap();
    }
}
<|endoftext|>


<commit_before>use cfg;
use std::fmt::Debug;

pub trait Lang : Debug + Send + Sync {
    fn eval(&self, code: &str) -> Result<String, String>;
    fn restart(&self) {}
    fn terminate(&self) {}
}

pub fn new(cfg: cfg::LangCfg,
           playpen_args: Vec<String>,
           sandbox_path: String,
           timeout: usize)
           -> Box<Lang> {
    if cfg.persistent {
        Box::new(compiler::CompilerLang::new(cfg, playpen_args, sandbox_path, timeout))
    } else {
        Box::new(persistent::ReplLang::new(cfg, playpen_args, sandbox_path, timeout))
    }
}

fn wrap_code(raw: &str, cfg: &cfg::LangCfg) -> String {
    let mut code = String::with_capacity(raw.len());

    if let Some(ref prefix) = cfg.code_before {
        code.push_str(prefix);
    }

    code.push_str(raw);

    if let Some(ref postfix) = cfg.code_after {
        code.push_str(postfix);
    }

    code
}

mod compiler;
mod persistent;
<commit_msg>Fix wrong evaluator struct created<commit_after>use cfg;
use std::fmt::Debug;

pub trait Lang : Debug + Send + Sync {
    fn eval(&self, code: &str) -> Result<String, String>;
    fn restart(&self) {}
    fn terminate(&self) {}
}

pub fn new(cfg: cfg::LangCfg,
           playpen_args: Vec<String>,
           sandbox_path: String,
           timeout: usize)
           -> Box<Lang> {
    if cfg.persistent {
        Box::new(persistent::ReplLang::new(cfg, playpen_args, sandbox_path, timeout))
    } else {
        Box::new(compiler::CompilerLang::new(cfg, playpen_args, sandbox_path, timeout))
    }
}

fn wrap_code(raw: &str, cfg: &cfg::LangCfg) -> String {
    let mut code = String::with_capacity(raw.len());

    if let Some(ref prefix) = cfg.code_before {
        code.push_str(prefix);
    }

    code.push_str(raw);

    if let Some(ref postfix) = cfg.code_after {
        code.push_str(postfix);
    }

    code
}

mod compiler;
mod persistent;
<|endoftext|>


<commit_before>use std::cast;
use std::logging;
use std::ptr;
use std::str::raw::from_c_str;
use std::task;

use gtk::*;
use gtk::ffi::*;

use gui;

struct Player {
    initialized: bool,

    playing: bool,

    playbin: *mut GstElement,
    clock_id: Option<GstClockID>,
}

impl Player {
    pub fn new() -> Player {
        Player {
            initialized: false,
            playing: false,
            playbin: ptr::mut_null(),
            clock_id: None,
        }
    }

    // It's important that the `gui` pointer be constant for the entire duration
    // of the program, as it's sent into the gstreamer lib.
    // I know, this is quite very hacky.
    pub fn init(&mut self, args: ~[~str], gui: &gui::Gui) -> ~[~str] {
        let args2 = unsafe {
            gst_init_with_args(args)
        };
        unsafe {
            "playbin".with_c_str(|c_str| {
                "rusttracks-playbin".with_c_str(|rtpb| {
                    self.playbin = gst_element_factory_make(c_str, rtpb);
                });
            });
            if self.playbin.is_null() {
                fail!("failed to create playbin");
            }

            let bus = gst_pipeline_get_bus(cast::transmute(self.playbin));
            gst_bus_add_watch(bus, bus_callback,
                              cast::transmute::<&gui::Gui, gpointer>(gui));
        }
        self.initialized = true;
        args2
    }

    pub fn set_uri(&mut self, uri: &str, gui: &gui::Gui) {
        self.stop();
        unsafe {
            "uri".with_c_str(|property_c_str| {
                uri.with_c_str(|uri_c_str| {
                    g_object_set(cast::transmute(self.playbin),
                        property_c_str, uri_c_str, ptr::null::<gchar>());
                });
            });

            let chan = gui.get_chan().clone();

            let clock = gst_pipeline_get_clock(cast::transmute(self.playbin));

            // in nanoseconds
            let timeout: guint64 = 30 * 1000 * 1000 * 1000;
            let target_time = gst_clock_get_time(clock) + timeout;

            let ci = gst_clock_new_single_shot_id(clock, target_time);
            self.clock_id = Some(ci);

            do task::spawn_sched(task::SingleThreaded) {
                let res = gst_clock_id_wait(ci, ptr::mut_null());
                match res {
                    GST_CLOCK_UNSCHEDULED => { } // Ignore, nothing to do
                    GST_CLOCK_OK => {
                        debug!("30s are up! sending ReportCurrentTrack to gui");
                        chan.send(gui::ReportCurrentTrack);
                    }
                    _ => unreachable!()
                }
            }

            gst_object_unref(cast::transmute(clock));
        }
    }

    pub fn play(&mut self) {
        if !self.initialized {
            fail!("player is not initialized");
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PLAYING);
        }
        self.playing = true;
    }

    pub fn pause(&mut self) {
        if !self.initialized {
            fail!("player is not initialized");
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PAUSED);
        }
        self.playing = false;
    }

    pub fn stop(&mut self) {
        if !self.initialized {
            fail!("player is not initialized");
        }
        unsafe {
            let maybe_ci = self.clock_id.take();
            for ci in maybe_ci.move_iter() {
                gst_clock_id_unschedule(ci);
                gst_clock_id_unref(ci);
            }
            gst_element_set_state(self.playbin, GST_STATE_READY);
        }
        self.playing = false;
    }

    pub fn toggle(&mut self) {
        if self.playing {
            self.pause()
        } else {
            self.play()
        }
    }
}

impl Drop for Player {
    fn drop(&mut self) {
        if self.initialized {
            unsafe {
                if !self.playbin.is_null() {
                    gst_element_set_state(self.playbin, GST_STATE_NULL);
                    gst_object_unref(cast::transmute(self.playbin));
                }
                gst_deinit();
            }
        }
    }
}

extern "C" fn bus_callback(_bus: *mut GstBus, msg: *mut GstMessage, data: gpointer) -> gboolean {
    unsafe {
    let gui: &gui::Gui = cast::transmute(data);

    let name = {
        let gst_obj = (*msg).src;
        if gst_obj.is_null() {
            ~"null-source"
        } else {
            let name_ptr = gst_object_get_name(gst_obj);
            if name_ptr.is_null() {
                ~"null-name"
            } else {
                let name = from_c_str(cast::transmute_immut_unsafe(name_ptr));
                g_free(cast::transmute(name_ptr));
                name
            }
        }
    };

    match (*msg)._type {
        GST_MESSAGE_ERROR => {
            let mut err = ptr::mut_null();
            let mut dbg_info = ptr::mut_null();

            gst_message_parse_error(msg, &mut err, &mut dbg_info);

            error!("ERROR from element {}: {}", name,
                from_c_str(cast::transmute_immut_unsafe((*err).message)));
            error!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

            g_error_free(err);
            g_free(cast::transmute(dbg_info));
        }
        GST_MESSAGE_WARNING => {
            if log_enabled!(logging::WARN) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                warn!("WARNING from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                warn!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_INFO => {
            if log_enabled!(logging::INFO) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                info!("INFO from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                info!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_EOS => {
            debug!("EOS from element {}", name);
            gui.get_chan().send(gui::NextTrack);
        }
        _ => {
            if log_enabled!(logging::DEBUG) {
                let msg_type_cstr = gst_message_type_get_name((*msg)._type);
                let msg_type_name = ::std::str::raw::from_c_str(msg_type_cstr);
                debug!("message of type `{}` from element `{}`", msg_type_name, name);
            }
        }
    }

    // Returning 0 removes this callback
    return 1;
    }
}
<commit_msg>player: don't start playing when no uri is set<commit_after>use std::cast;
use std::logging;
use std::ptr;
use std::str::raw::from_c_str;
use std::task;

use gtk::*;
use gtk::ffi::*;

use gui;

struct Player {
    initialized: bool,

    uri_set: bool,
    playing: bool,

    playbin: *mut GstElement,
    clock_id: Option<GstClockID>,
}

impl Player {
    pub fn new() -> Player {
        Player {
            initialized: false,
            uri_set: false,
            playing: false,
            playbin: ptr::mut_null(),
            clock_id: None,
        }
    }

    // It's important that the `gui` pointer be constant for the entire duration
    // of the program, as it's sent into the gstreamer lib.
    // I know, this is quite very hacky.
    pub fn init(&mut self, args: ~[~str], gui: &gui::Gui) -> ~[~str] {
        let args2 = unsafe {
            gst_init_with_args(args)
        };
        unsafe {
            "playbin".with_c_str(|c_str| {
                "rusttracks-playbin".with_c_str(|rtpb| {
                    self.playbin = gst_element_factory_make(c_str, rtpb);
                });
            });
            if self.playbin.is_null() {
                fail!("failed to create playbin");
            }

            let bus = gst_pipeline_get_bus(cast::transmute(self.playbin));
            gst_bus_add_watch(bus, bus_callback,
                              cast::transmute::<&gui::Gui, gpointer>(gui));
        }
        self.initialized = true;
        args2
    }

    pub fn set_uri(&mut self, uri: &str, gui: &gui::Gui) {
        self.stop();
        unsafe {
            "uri".with_c_str(|property_c_str| {
                uri.with_c_str(|uri_c_str| {
                    g_object_set(cast::transmute(self.playbin),
                        property_c_str, uri_c_str, ptr::null::<gchar>());
                });
            });

            let chan = gui.get_chan().clone();

            let clock = gst_pipeline_get_clock(cast::transmute(self.playbin));

            // in nanoseconds
            let timeout: guint64 = 30 * 1000 * 1000 * 1000;
            let target_time = gst_clock_get_time(clock) + timeout;

            let ci = gst_clock_new_single_shot_id(clock, target_time);
            self.clock_id = Some(ci);

            do task::spawn_sched(task::SingleThreaded) {
                let res = gst_clock_id_wait(ci, ptr::mut_null());
                match res {
                    GST_CLOCK_UNSCHEDULED => { } // Ignore, nothing to do
                    GST_CLOCK_OK => {
                        debug!("30s are up! sending ReportCurrentTrack to gui");
                        chan.send(gui::ReportCurrentTrack);
                    }
                    _ => unreachable!()
                }
            }

            gst_object_unref(cast::transmute(clock));
        }
        self.uri_set = true;
    }

    pub fn play(&mut self) {
        if !self.initialized {
            fail!("player is not initialized");
        }
        if !self.uri_set {
            debug!("uri not set, not playing");
            return;
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PLAYING);
        }
        self.playing = true;
    }

    pub fn pause(&mut self) {
        if !self.initialized {
            fail!("player is not initialized");
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PAUSED);
        }
        self.playing = false;
    }

    pub fn stop(&mut self) {
        if !self.initialized {
            fail!("player is not initialized");
        }
        unsafe {
            let maybe_ci = self.clock_id.take();
            for ci in maybe_ci.move_iter() {
                gst_clock_id_unschedule(ci);
                gst_clock_id_unref(ci);
            }
            gst_element_set_state(self.playbin, GST_STATE_READY);
        }
        self.uri_set = false;
        self.playing = false;
    }

    pub fn toggle(&mut self) {
        if self.playing {
            self.pause()
        } else {
            self.play()
        }
    }

    pub fn is_playing(&self) -> bool {
        self.playing
    }

    pub fn can_play(&self) -> bool {
        self.uri_set
    }
}

impl Drop for Player {
    fn drop(&mut self) {
        if self.initialized {
            unsafe {
                if !self.playbin.is_null() {
                    gst_element_set_state(self.playbin, GST_STATE_NULL);
                    gst_object_unref(cast::transmute(self.playbin));
                }
                gst_deinit();
            }
        }
    }
}

extern "C" fn bus_callback(_bus: *mut GstBus, msg: *mut GstMessage, data: gpointer) -> gboolean {
    unsafe {
    let gui: &gui::Gui = cast::transmute(data);

    let name = {
        let gst_obj = (*msg).src;
        if gst_obj.is_null() {
            ~"null-source"
        } else {
            let name_ptr = gst_object_get_name(gst_obj);
            if name_ptr.is_null() {
                ~"null-name"
            } else {
                let name = from_c_str(cast::transmute_immut_unsafe(name_ptr));
                g_free(cast::transmute(name_ptr));
                name
            }
        }
    };

    match (*msg)._type {
        GST_MESSAGE_ERROR => {
            let mut err = ptr::mut_null();
            let mut dbg_info = ptr::mut_null();

            gst_message_parse_error(msg, &mut err, &mut dbg_info);

            error!("ERROR from element {}: {}", name,
                from_c_str(cast::transmute_immut_unsafe((*err).message)));
            error!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

            g_error_free(err);
            g_free(cast::transmute(dbg_info));
        }
        GST_MESSAGE_WARNING => {
            if log_enabled!(logging::WARN) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                warn!("WARNING from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                warn!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_INFO => {
            if log_enabled!(logging::INFO) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                info!("INFO from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                info!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_EOS => {
            debug!("EOS from element {}", name);
            gui.get_chan().send(gui::NextTrack);
        }
        _ => {
            if log_enabled!(logging::DEBUG) {
                let msg_type_cstr = gst_message_type_get_name((*msg)._type);
                let msg_type_name = ::std::str::raw::from_c_str(msg_type_cstr);
                debug!("message of type `{}` from element `{}`", msg_type_name, name);
            }
        }
    }

    // Returning 0 removes this callback
    return 1;
    }
}
<|endoftext|>


<commit_before>//! # Paging
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html)

use core::{mem, ptr};
use core::ops::{Deref, DerefMut};
use spin::Mutex;

use crate::device::cpu::registers::{control_regs, tlb};
use crate::memory::{allocate_frames, Frame};

use self::mapper::{Mapper, PageFlushAll};
use self::temporary_page::TemporaryPage;

pub use rmm::{
    AArch64Arch as RmmA,
    Arch as RmmArch,
    PageFlags,
    PhysicalAddress,
    TableKind,
    VirtualAddress,
};

pub mod entry;
pub mod mapper;
pub mod table;
pub mod temporary_page;

/// Number of entries per page table
pub const ENTRY_COUNT: usize = 512;

/// Size of pages
pub const PAGE_SIZE: usize = 4096;

//TODO: This is a rudimentary recursive mutex used to naively fix multi_core issues, replace it!
pub struct PageTableLock {
    cpu_id: usize,
    count: usize,
}

pub static PAGE_TABLE_LOCK: Mutex<PageTableLock> = Mutex::new(PageTableLock {
    cpu_id: 0,
    count: 0,
});

fn page_table_lock() {
    let cpu_id = crate::cpu_id();
    loop {
        {
            let mut lock = PAGE_TABLE_LOCK.lock();
            if lock.count == 0 || lock.cpu_id == cpu_id {
                lock.cpu_id = cpu_id;
                lock.count += 1;
                return;
            }
        }
        crate::arch::interrupt::pause();
    }
}

fn page_table_unlock() {
    let mut lock = PAGE_TABLE_LOCK.lock();
    lock.count -= 1;
}

/// Setup Memory Access Indirection Register
unsafe fn init_mair() {
    let mut val: control_regs::MairEl1 = control_regs::mair_el1();

    val.insert(control_regs::MairEl1::DEVICE_MEMORY);
    val.insert(control_regs::MairEl1::NORMAL_UNCACHED_MEMORY);
    val.insert(control_regs::MairEl1::NORMAL_WRITEBACK_MEMORY);

    control_regs::mair_el1_write(val);
}

/// Map TSS
unsafe fn map_tss(cpu_id: usize, mapper: &mut Mapper) -> PageFlushAll<RmmA> {
    extern "C" {
        /// The starting byte of the thread data segment
        static mut __tdata_start: u8;
        /// The ending byte of the thread data segment
        static mut __tdata_end: u8;
        /// The starting byte of the thread BSS segment
        static mut __tbss_start: u8;
        /// The ending byte of the thread BSS segment
        static mut __tbss_end: u8;
    }

    let size = &__tbss_end as *const _ as usize - &__tdata_start as *const _ as usize;
    let start = crate::KERNEL_PERCPU_OFFSET + crate::KERNEL_PERCPU_SIZE * cpu_id;
    let end = start + size;

    let flush_all = PageFlushAll::new();
    let start_page = Page::containing_address(VirtualAddress::new(start));
    let end_page = Page::containing_address(VirtualAddress::new(end - 1));
    for page in Page::range_inclusive(start_page, end_page) {
        let result = mapper.map(page, PageFlags::new().write(true));
        flush_all.consume(result);
    }
    flush_all
}

/// Copy tdata, clear tbss, set TCB self pointer
unsafe fn init_tcb(cpu_id: usize) -> usize {
    extern "C" {
        /// The starting byte of the thread data segment
        static mut __tdata_start: u8;
        /// The ending byte of the thread data segment
        static mut __tdata_end: u8;
        /// The starting byte of the thread BSS segment
        static mut __tbss_start: u8;
        /// The ending byte of the thread BSS segment
        static mut __tbss_end: u8;
    }

    let tcb_offset;
    {
        let size = &__tbss_end as *const _ as usize - &__tdata_start as *const _ as usize;
        let tbss_offset = &__tbss_start as *const _ as usize - &__tdata_start as *const _ as usize;

        let start = crate::KERNEL_PERCPU_OFFSET + crate::KERNEL_PERCPU_SIZE * cpu_id;
        println!("SET TPIDR_EL1 TO {:X}", start - 0x10);
        // FIXME: Empirically initializing tpidr to 16 bytes below start works. I do not know
        // whether this is the correct way to handle TLS. Will need to revisit.
        control_regs::tpidr_el1_write((start - 0x10) as u64);
        println!("SET TPIDR_EL1 DONE");

        let end = start + size;
        tcb_offset = end - mem::size_of::<usize>();

        ptr::copy(&__tdata_start as *const u8, start as *mut u8, tbss_offset);
        ptr::write_bytes((start + tbss_offset) as *mut u8, 0, size - tbss_offset);

        *(tcb_offset as *mut usize) = end;
    }
    tcb_offset
}

/// Initialize paging
///
/// Returns page table and thread control block offset
pub unsafe fn init(
    cpu_id: usize,
) -> (ActivePageTable, usize) {
    extern "C" {
        /// The starting byte of the text (code) data segment.
        static mut __text_start: u8;
        /// The ending byte of the text (code) data segment.
        static mut __text_end: u8;
        /// The starting byte of the _.rodata_ (read-only data) segment.
        static mut __rodata_start: u8;
        /// The ending byte of the _.rodata_ (read-only data) segment.
        static mut __rodata_end: u8;
        /// The starting byte of the _.data_ segment.
        static mut __data_start: u8;
        /// The ending byte of the _.data_ segment.
        static mut __data_end: u8;
        /// The starting byte of the thread data segment
        static mut __tdata_start: u8;
        /// The ending byte of the thread data segment
        static mut __tdata_end: u8;
        /// The starting byte of the thread BSS segment
        static mut __tbss_start: u8;
        /// The ending byte of the thread BSS segment
        static mut __tbss_end: u8;
        /// The starting byte of the _.bss_ (uninitialized data) segment.
        static mut __bss_start: u8;
        /// The ending byte of the _.bss_ (uninitialized data) segment.
        static mut __bss_end: u8;
    }

    init_mair();

    let mut active_table = ActivePageTable::new_unlocked(TableKind::Kernel);

    let flush_all = map_tss(cpu_id, &mut active_table);
    flush_all.flush();

    return (active_table, init_tcb(cpu_id));
}

pub unsafe fn init_ap(
    cpu_id: usize,
    bsp_table: usize,
) -> usize {
    init_mair();

    let mut active_table = ActivePageTable::new_unlocked(TableKind::Kernel);

    let mut new_table = InactivePageTable::from_address(bsp_table);

    let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(
        crate::KERNEL_TMP_MISC_OFFSET,
    )));

    active_table.with(&mut new_table, &mut temporary_page, |mapper| {
        let flush_all = map_tss(cpu_id, mapper);
        // The flush can be ignored as this is not the active table. See later active_table.switch
        flush_all.ignore();
    });

    // This switches the active table, which is setup by the bootloader, to a correct table
    // setup by the lambda above. This will also flush the TLB
    active_table.switch(new_table);

    init_tcb(cpu_id)
}

#[derive(Debug)]
pub struct ActivePageTable {
    mapper: Mapper,
    locked: bool,
}

impl Deref for ActivePageTable {
    type Target = Mapper;

    fn deref(&self) -> &Mapper {
        &self.mapper
    }
}

impl DerefMut for ActivePageTable {
    fn deref_mut(&mut self) -> &mut Mapper {
        &mut self.mapper
    }
}

impl ActivePageTable {
    pub unsafe fn new(table_kind: TableKind) -> ActivePageTable {
        page_table_lock();
        ActivePageTable {
            mapper: Mapper::new(table_kind),
            locked: true,
        }
    }

    pub unsafe fn new_unlocked(table_kind: TableKind) -> ActivePageTable {
        ActivePageTable {
            mapper: Mapper::new(table_kind),
            locked: false,
        }
    }

    pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
        let old_table: InactivePageTable;

        match self.mapper.table_kind {
            TableKind::User => {
                old_table = InactivePageTable { frame: Frame::containing_address(PhysicalAddress::new(unsafe { control_regs::ttbr0_el1() } as usize)) };
                unsafe { control_regs::ttbr0_el1_write(new_table.frame.start_address().data() as u64) };
            },
            TableKind::Kernel =>  {
                old_table = InactivePageTable { frame: Frame::containing_address(PhysicalAddress::new(unsafe { control_regs::ttbr1_el1() } as usize)) };
                unsafe { control_regs::ttbr1_el1_write(new_table.frame.start_address().data() as u64) };
            }
        }

        unsafe { tlb::flush_all() };
        old_table
    }

    pub fn flush(&mut self, page: Page) {
        unsafe {
            tlb::flush(page.start_address().data());
        }
    }

    pub fn flush_all(&mut self) {
        unsafe {
            tlb::flush_all();
        }
    }

    pub fn with<F>(&mut self, table: &mut InactivePageTable, temporary_page: &mut TemporaryPage, f: F)
        where F: FnOnce(&mut Mapper)
    {
        {
            let backup = Frame::containing_address(PhysicalAddress::new(unsafe {
                match self.mapper.table_kind {
                    TableKind::User => control_regs::ttbr0_el1() as usize,
                    TableKind::Kernel => control_regs::ttbr1_el1() as usize,
                }
            }));

            // map temporary_page to current p4 table
            let p4_table = temporary_page.map_table_frame(
                backup.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
                self,
            );

            // overwrite recursive mapping
            self.p4_mut()[crate::RECURSIVE_PAGE_PML4].set(
                table.frame.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
            );
            self.flush_all();

            // execute f in the new context
            f(self);

            // restore recursive mapping to original p4 table
            p4_table[crate::RECURSIVE_PAGE_PML4].set(
                backup,
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
            );
            self.flush_all();
        }

        temporary_page.unmap(self);
    }

    pub unsafe fn address(&self) -> usize {
        match self.mapper.table_kind {
            TableKind::User => control_regs::ttbr0_el1() as usize,
            TableKind::Kernel => control_regs::ttbr1_el1() as usize,
        }
    }
}

impl Drop for ActivePageTable {
    fn drop(&mut self) {
        if self.locked {
            page_table_unlock();
            self.locked = false;
        }
    }
}

pub struct InactivePageTable {
    frame: Frame,
}

impl InactivePageTable {
    pub fn new(
        frame: Frame,
        active_table: &mut ActivePageTable,
        temporary_page: &mut TemporaryPage,
    ) -> InactivePageTable {
        {
            let table = temporary_page.map_table_frame(
                frame.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
                active_table,
            );
            // now we are able to zero the table
            table.zero();
            // set up recursive mapping for the table
            table[crate::RECURSIVE_PAGE_PML4].set(
                frame.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
            );
        }
        temporary_page.unmap(active_table);

        InactivePageTable { frame: frame }
    }

    pub unsafe fn from_address(address: usize) -> InactivePageTable {
        InactivePageTable {
            frame: Frame::containing_address(PhysicalAddress::new(address)),
        }
    }

    pub unsafe fn address(&self) -> usize {
        self.frame.start_address().data()
    }
}

/// Page
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Page {
    number: usize,
}

impl Page {
    pub fn start_address(self) -> VirtualAddress {
        VirtualAddress::new(self.number * PAGE_SIZE)
    }

    pub fn p4_index(self) -> usize {
        (self.number >> 27) & 0o777
    }

    pub fn p3_index(self) -> usize {
        (self.number >> 18) & 0o777
    }

    pub fn p2_index(self) -> usize {
        (self.number >> 9) & 0o777
    }

    pub fn p1_index(self) -> usize {
        self.number & 0o777
    }

    pub fn containing_address(address: VirtualAddress) -> Page {
        //TODO assert!(address.data() < 0x0000_8000_0000_0000 || address.data() >= 0xffff_8000_0000_0000,
        //    "invalid address: 0x{:x}", address.data());
        Page {
            number: address.data() / PAGE_SIZE,
        }
    }

    pub fn range_inclusive(start: Page, end: Page) -> PageIter {
        PageIter { start, end }
    }

    pub fn next(self) -> Page {
        Self {
            number: self.number + 1,
        }
    }
}

pub struct PageIter {
    start: Page,
    end: Page,
}

impl Iterator for PageIter {
    type Item = Page;

    fn next(&mut self) -> Option<Page> {
        if self.start <= self.end {
            let page = self.start;
            self.start = self.start.next();
            Some(page)
        } else {
            None
        }
    }
}
<commit_msg>Map TSS using RMM<commit_after>//! # Paging
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html)

use core::{mem, ptr};
use core::ops::{Deref, DerefMut};
use spin::Mutex;

use crate::device::cpu::registers::{control_regs, tlb};
use crate::memory::{allocate_frames, Frame};

use self::mapper::{Mapper, PageFlushAll};
use self::temporary_page::TemporaryPage;

pub use rmm::{
    AArch64Arch as RmmA,
    Arch as RmmArch,
    PageFlags,
    PhysicalAddress,
    TableKind,
    VirtualAddress,
};

pub mod entry;
pub mod mapper;
pub mod table;
pub mod temporary_page;

/// Number of entries per page table
pub const ENTRY_COUNT: usize = 512;

/// Size of pages
pub const PAGE_SIZE: usize = 4096;

//TODO: This is a rudimentary recursive mutex used to naively fix multi_core issues, replace it!
pub struct PageTableLock {
    cpu_id: usize,
    count: usize,
}

pub static PAGE_TABLE_LOCK: Mutex<PageTableLock> = Mutex::new(PageTableLock {
    cpu_id: 0,
    count: 0,
});

fn page_table_lock() {
    let cpu_id = crate::cpu_id();
    loop {
        {
            let mut lock = PAGE_TABLE_LOCK.lock();
            if lock.count == 0 || lock.cpu_id == cpu_id {
                lock.cpu_id = cpu_id;
                lock.count += 1;
                return;
            }
        }
        crate::arch::interrupt::pause();
    }
}

fn page_table_unlock() {
    let mut lock = PAGE_TABLE_LOCK.lock();
    lock.count -= 1;
}

/// Setup Memory Access Indirection Register
unsafe fn init_mair() {
    let mut val: control_regs::MairEl1 = control_regs::mair_el1();

    val.insert(control_regs::MairEl1::DEVICE_MEMORY);
    val.insert(control_regs::MairEl1::NORMAL_UNCACHED_MEMORY);
    val.insert(control_regs::MairEl1::NORMAL_WRITEBACK_MEMORY);

    control_regs::mair_el1_write(val);
}

/// Map TSS
unsafe fn map_tss(cpu_id: usize) {
    extern "C" {
        /// The starting byte of the thread data segment
        static mut __tdata_start: u8;
        /// The ending byte of the thread data segment
        static mut __tdata_end: u8;
        /// The starting byte of the thread BSS segment
        static mut __tbss_start: u8;
        /// The ending byte of the thread BSS segment
        static mut __tbss_end: u8;
    }

    let size = &__tbss_end as *const _ as usize - &__tdata_start as *const _ as usize;
    let start = crate::KERNEL_PERCPU_OFFSET + crate::KERNEL_PERCPU_SIZE * cpu_id;
    let end = start + size;

    let mut mapper = crate::rmm::mapper_current();
    let flush_all = PageFlushAll::new();
    let start_page = Page::containing_address(VirtualAddress::new(start));
    let end_page = Page::containing_address(VirtualAddress::new(end - 1));
    for page in Page::range_inclusive(start_page, end_page) {
        let result = mapper.map(
            page.start_address(),
            PageFlags::new().write(true)
        ).expect("Failed to map TSS page");
        flush_all.consume(result);
    }
    flush_all.flush();
}

/// Copy tdata, clear tbss, set TCB self pointer
unsafe fn init_tcb(cpu_id: usize) -> usize {
    extern "C" {
        /// The starting byte of the thread data segment
        static mut __tdata_start: u8;
        /// The ending byte of the thread data segment
        static mut __tdata_end: u8;
        /// The starting byte of the thread BSS segment
        static mut __tbss_start: u8;
        /// The ending byte of the thread BSS segment
        static mut __tbss_end: u8;
    }

    let tcb_offset;
    {
        let size = &__tbss_end as *const _ as usize - &__tdata_start as *const _ as usize;
        let tbss_offset = &__tbss_start as *const _ as usize - &__tdata_start as *const _ as usize;

        let start = crate::KERNEL_PERCPU_OFFSET + crate::KERNEL_PERCPU_SIZE * cpu_id;
        println!("SET TPIDR_EL1 TO {:X}", start - 0x10);
        // FIXME: Empirically initializing tpidr to 16 bytes below start works. I do not know
        // whether this is the correct way to handle TLS. Will need to revisit.
        control_regs::tpidr_el1_write((start - 0x10) as u64);
        println!("SET TPIDR_EL1 DONE");

        let end = start + size;
        tcb_offset = end - mem::size_of::<usize>();

        ptr::copy(&__tdata_start as *const u8, start as *mut u8, tbss_offset);
        ptr::write_bytes((start + tbss_offset) as *mut u8, 0, size - tbss_offset);

        *(tcb_offset as *mut usize) = end;
    }
    tcb_offset
}

/// Initialize paging
///
/// Returns page table and thread control block offset
pub unsafe fn init(
    cpu_id: usize,
) -> (ActivePageTable, usize) {
    extern "C" {
        /// The starting byte of the text (code) data segment.
        static mut __text_start: u8;
        /// The ending byte of the text (code) data segment.
        static mut __text_end: u8;
        /// The starting byte of the _.rodata_ (read-only data) segment.
        static mut __rodata_start: u8;
        /// The ending byte of the _.rodata_ (read-only data) segment.
        static mut __rodata_end: u8;
        /// The starting byte of the _.data_ segment.
        static mut __data_start: u8;
        /// The ending byte of the _.data_ segment.
        static mut __data_end: u8;
        /// The starting byte of the thread data segment
        static mut __tdata_start: u8;
        /// The ending byte of the thread data segment
        static mut __tdata_end: u8;
        /// The starting byte of the thread BSS segment
        static mut __tbss_start: u8;
        /// The ending byte of the thread BSS segment
        static mut __tbss_end: u8;
        /// The starting byte of the _.bss_ (uninitialized data) segment.
        static mut __bss_start: u8;
        /// The ending byte of the _.bss_ (uninitialized data) segment.
        static mut __bss_end: u8;
    }

    println!("INIT MAIR START");
    init_mair();
    println!("INIT MAIR COMPLETE");

    println!("ACTIVE TABLE START");
    let active_table = ActivePageTable::new_unlocked(TableKind::Kernel);
    println!("ACTIVE TABLE COMPLETE");

    println!("MAP TSS START");
    map_tss(cpu_id);
    println!("MAP TSS COMPLETE");

    println!("INIT TCB START");
    let tcb = init_tcb(cpu_id);
    println!("INIT_TCB COMPLETE");

    return (active_table, tcb);
}

pub unsafe fn init_ap(
    cpu_id: usize,
    bsp_table: usize,
) -> usize {
    init_mair();

    let mut active_table = ActivePageTable::new_unlocked(TableKind::Kernel);

    let mut new_table = InactivePageTable::from_address(bsp_table);

    // This switches the active table, which is setup by the bootloader, to a correct table
    // setup by the lambda above. This will also flush the TLB
    active_table.switch(new_table);

    map_tss(cpu_id);

    init_tcb(cpu_id)
}

#[derive(Debug)]
pub struct ActivePageTable {
    mapper: Mapper,
    locked: bool,
}

impl Deref for ActivePageTable {
    type Target = Mapper;

    fn deref(&self) -> &Mapper {
        &self.mapper
    }
}

impl DerefMut for ActivePageTable {
    fn deref_mut(&mut self) -> &mut Mapper {
        &mut self.mapper
    }
}

impl ActivePageTable {
    pub unsafe fn new(table_kind: TableKind) -> ActivePageTable {
        page_table_lock();
        ActivePageTable {
            mapper: Mapper::new(table_kind),
            locked: true,
        }
    }

    pub unsafe fn new_unlocked(table_kind: TableKind) -> ActivePageTable {
        ActivePageTable {
            mapper: Mapper::new(table_kind),
            locked: false,
        }
    }

    pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
        let old_table: InactivePageTable;

        match self.mapper.table_kind {
            TableKind::User => {
                old_table = InactivePageTable { frame: Frame::containing_address(PhysicalAddress::new(unsafe { control_regs::ttbr0_el1() } as usize)) };
                unsafe { control_regs::ttbr0_el1_write(new_table.frame.start_address().data() as u64) };
            },
            TableKind::Kernel =>  {
                old_table = InactivePageTable { frame: Frame::containing_address(PhysicalAddress::new(unsafe { control_regs::ttbr1_el1() } as usize)) };
                unsafe { control_regs::ttbr1_el1_write(new_table.frame.start_address().data() as u64) };
            }
        }

        unsafe { tlb::flush_all() };
        old_table
    }

    pub fn flush(&mut self, page: Page) {
        unsafe {
            tlb::flush(page.start_address().data());
        }
    }

    pub fn flush_all(&mut self) {
        unsafe {
            tlb::flush_all();
        }
    }

    pub fn with<F>(&mut self, table: &mut InactivePageTable, temporary_page: &mut TemporaryPage, f: F)
        where F: FnOnce(&mut Mapper)
    {
        {
            let backup = Frame::containing_address(PhysicalAddress::new(unsafe {
                match self.mapper.table_kind {
                    TableKind::User => control_regs::ttbr0_el1() as usize,
                    TableKind::Kernel => control_regs::ttbr1_el1() as usize,
                }
            }));

            // map temporary_page to current p4 table
            let p4_table = temporary_page.map_table_frame(
                backup.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
                self,
            );

            // overwrite recursive mapping
            self.p4_mut()[crate::RECURSIVE_PAGE_PML4].set(
                table.frame.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
            );
            self.flush_all();

            // execute f in the new context
            f(self);

            // restore recursive mapping to original p4 table
            p4_table[crate::RECURSIVE_PAGE_PML4].set(
                backup,
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
            );
            self.flush_all();
        }

        temporary_page.unmap(self);
    }

    pub unsafe fn address(&self) -> usize {
        match self.mapper.table_kind {
            TableKind::User => control_regs::ttbr0_el1() as usize,
            TableKind::Kernel => control_regs::ttbr1_el1() as usize,
        }
    }
}

impl Drop for ActivePageTable {
    fn drop(&mut self) {
        if self.locked {
            page_table_unlock();
            self.locked = false;
        }
    }
}

pub struct InactivePageTable {
    frame: Frame,
}

impl InactivePageTable {
    pub fn new(
        frame: Frame,
        active_table: &mut ActivePageTable,
        temporary_page: &mut TemporaryPage,
    ) -> InactivePageTable {
        {
            let table = temporary_page.map_table_frame(
                frame.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
                active_table,
            );
            // now we are able to zero the table
            table.zero();
            // set up recursive mapping for the table
            table[crate::RECURSIVE_PAGE_PML4].set(
                frame.clone(),
                PageFlags::new_table().write(true), //TODO: RISC-V will not like this
            );
        }
        temporary_page.unmap(active_table);

        InactivePageTable { frame: frame }
    }

    pub unsafe fn from_address(address: usize) -> InactivePageTable {
        InactivePageTable {
            frame: Frame::containing_address(PhysicalAddress::new(address)),
        }
    }

    pub unsafe fn address(&self) -> usize {
        self.frame.start_address().data()
    }
}

/// Page
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Page {
    number: usize,
}

impl Page {
    pub fn start_address(self) -> VirtualAddress {
        VirtualAddress::new(self.number * PAGE_SIZE)
    }

    pub fn p4_index(self) -> usize {
        (self.number >> 27) & 0o777
    }

    pub fn p3_index(self) -> usize {
        (self.number >> 18) & 0o777
    }

    pub fn p2_index(self) -> usize {
        (self.number >> 9) & 0o777
    }

    pub fn p1_index(self) -> usize {
        self.number & 0o777
    }

    pub fn containing_address(address: VirtualAddress) -> Page {
        //TODO assert!(address.data() < 0x0000_8000_0000_0000 || address.data() >= 0xffff_8000_0000_0000,
        //    "invalid address: 0x{:x}", address.data());
        Page {
            number: address.data() / PAGE_SIZE,
        }
    }

    pub fn range_inclusive(start: Page, end: Page) -> PageIter {
        PageIter { start, end }
    }

    pub fn next(self) -> Page {
        Self {
            number: self.number + 1,
        }
    }
}

pub struct PageIter {
    start: Page,
    end: Page,
}

impl Iterator for PageIter {
    type Item = Page;

    fn next(&mut self) -> Option<Page> {
        if self.start <= self.end {
            let page = self.start;
            self.start = self.start.next();
            Some(page)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for reading & extracting data from Multiboot 2 boot information.
//!
//! Consult the [Multiboot Specification](http://nongnu.askapache.com/grub/phcoder/multiboot.pdf)
//! for more information.
use memory::{PAddr, PhysicalPage, FrameRange};
use elf::section::{Sections, HeaderRepr as SectionHeader};

use core::iter::IntoIterator;
use core::fmt;

const END_TAG_LEN: u32 = 8;

const HEADER_LEN: u32 = 24;

pub const MAGIC: u32 = 0xe85250d6;

#[repr(u32)]
pub enum HeaderArch {
      I386 = 0
    , Mips = 4
}

#[repr(C)]
pub struct Header {
      pub magic: u32
    , pub arch: HeaderArch
    , pub header_length: u32
    , pub checksum: u32
    , pub end_tag: Tag
}

#[linkage = "external"]
#[link_section = ".multiboot_header"]
pub static HEADER: Header = Header {
    magic: MAGIC
  , arch: HeaderArch::I386
  , header_length: HEADER_LEN
  , checksum: -((MAGIC + 0 + HEADER_LEN) as i32) as u32
  , end_tag: Tag { ty: TagType::End
                 , length: END_TAG_LEN
                 }
};

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    #[inline]
    pub fn start_addr(&self) -> PAddr {
        PAddr::from(self as *const _ as u64)
    }

    pub fn end_addr(&self) -> PAddr {
        PAddr::from(self.start_addr() + self.length as u64)
    }

    /// TODO: rewrite this as a `TryFrom` implementation (see issue #85)
    //          - eliza, 03/09/2017
    pub unsafe fn from(addr: PAddr) -> Result<&'static Self, &'static str> {
        let info: &Info = &*(addr.into(): u64 as *const Info);
        // TODO: check if the multiboot tag *exists* at this location as well?
        //       since if we pass in the wrong address, we'll still make the
        //       "no end tag" error.
        //
        //       which, i suppose is *technically* correct, but not very
        //       helpful...
        //          - eliza, 03/04/2017
        if info.has_end() {
            Ok(info)
        } else {
            Err( "Multiboot info structure at {:?} had no end tag!")
        }
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    ///
    /// # Returns
    ///  - `Some(tag)` if a tag of the given type could be found.
    ///  - `None` if no tag of the given type could be found.
    pub fn get_tag(&'static self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    /// Finds the memory map tag.
    ///
    ///  # Returns
    ///  - `Some(MemMapTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn mem_map(&'static self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*((tag as *const Tag) as *const MemMapTag) })
    }

    /// Finds the ELF sections tag.
    ///
    ///  # Returns
    ///  - `Some(ElfSectionsTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn elf_sections(&'static self) -> Option<&'static ElfSectionsTag> {
        self.get_tag(TagType::ELFSections)
            .map(|tag| unsafe {
                &*((tag as *const Tag) as *const ElfSectionsTag)
            })
    }

    /// Returns an iterator over all Multiboot tags.
    #[inline]
    fn tags(&'static self) -> Tags { Tags(&self.tag_start as *const Tag) }

    /// Returns true if the multiboot structure has a valid end tag.
    fn has_end(&self) -> bool {
        // TODO: we should be able to use ptr::offset() here?
        //          - eliza, 03/05/2017
        let end_tag_addr
            = self as *const _ as usize +
              (self.length as usize - END_TAG_LEN as usize);
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }

    /// Returns the kernel frame range from the Multiboot 2 ELF Sections
    pub fn kernel_frames(&self) -> Result<FrameRange, &'static str> {
        let sections_tag = self.elf_sections()
                               .ok_or("ELF sections tag required!")?;

        let kernel_start = sections_tag.sections()
                              .map(|s| s.address())
                              .min()
                              .ok_or("Couldn't find kernel start section!")?;
        let kernel_end = sections_tag.sections()
                              .map(|s| s.address())
                              .max()
                              .ok_or("Couldn't find kernel end section!")?;

        Ok(PhysicalPage::from(kernel_start) .. PhysicalPage::from(kernel_end))
    }
}

impl IntoIterator for &'static Info {
    type IntoIter = Tags;
    type Item = &'static Tag;
    #[inline]  fn into_iter(self) -> Self::IntoIter { self.tags() }

}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
///
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
pub struct Tag { /// the type of this tag.
                 pub ty: TagType
               , length: u32
               }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType { /// Tag that indicates the end of multiboot tags
                   End              = 0
                 , /// Command line passed to the bootloader
                   CommandLine      = 1
                 , BootloaderName   = 2
                 , Modules          = 3
                 , BasicMemInfo     = 4
                 , BIOSBootDev      = 5
                 , MemoryMap        = 6
                 , VBEInfo          = 7
                 , FramebufferInfo  = 8
                 , ELFSections      = 9
                 , APMTable         = 10
                 }

/// An iterator over Multiboot 2 tags.
pub struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let next_addr = self.0 as usize + size as usize;
        self.0 = (((next_addr-1) & !0x7) + 0x8) as *const _;
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag { ty: TagType::End, length: END_TAG_LEN } => None
          , tag => {
              self.advance(tag.length);
              Some(tag)
            }
        }
    }
}

/// A Memory Map tag
#[repr(C)]
pub struct MemMapTag { tag: Tag
                     , pub entry_size: u32
                     , pub entry_version: u32
                     , first_entry: MemArea
                     }

impl MemMapTag {

    /// Returns an iterator over all the memory areas in this tag.
    #[inline] pub fn areas(&'static self) -> MemAreas {
        MemAreas { curr: (&self.first_entry) as *const MemArea
                    // TODO: we should be able to use ptr::offset() here?
                    //          - eliza, 03/05/2017
                 , last: ((self as *const MemMapTag as u32) +
                         self.tag.length - self.entry_size)
                         as *const MemArea
                 , size: self.entry_size
                 }
    }
}

impl IntoIterator for &'static MemMapTag {
    type Item = &'static MemArea;
    type IntoIter = MemAreas;

    #[inline] fn into_iter(self) -> Self::IntoIter { self.areas() }

}

/// A tag that stores the boot command line.
#[repr(C)]
pub struct CommandLineTag { tag: Tag
                          , /// The boot command line.
                            ///
                            /// The command line is a normal C-style zero-
                            /// terminated UTF-8 string.
                            pub command_line: [u8]
                          }

#[repr(C)]
pub struct ModulesTag { tag: Tag
                      , /// The address at which the module begins.
                        pub mod_begin: PAddr
                      , /// The address at which the module ends.
                        pub mod_end: PAddr
                      , /// A string (typically a command line)
                        pub string: [u8]
                      }

#[repr(u32)]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum MemAreaType { Available = 1
                     , Acpi      = 3
                     , Preserve  = 4
                     }

/// A multiboot 2 memory area
#[repr(C)]
#[derive(Debug)]
pub struct MemArea { /// the starting address of the memory area
                     pub base: PAddr
                   , /// the length of the memory area
                     pub length: PAddr
                   , /// the type of the memory area
                     pub ty: MemAreaType
                   , _pad: u32
                   }

impl MemArea {
    #[inline] pub fn address(&self) -> PAddr {
        self.base + self.length - 1
    }
}

impl fmt::Display for MemArea {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!( f, "{:?} from {:#08x} to {:#08x}"
              , self.ty, self.base, self.address())
    }
}

/// An iterator over memory areas
#[derive(Clone)]
pub struct MemAreas { curr: *const MemArea
                    , last: *const MemArea
                    , size: u32
                    }

impl Iterator for MemAreas {
    type Item = &'static MemArea;

    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = unsafe { &*self.curr };
            // TODO: we should be able to use ptr::offset() here.
            //          - eliza, 03/05/2017
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            // if current.ty == MemAreaType::Available {
            // NOTE: this used to skip over unavailable or ACPI memory areas,
            //       but i've disabled that as we may want to iterate over thsoe
            //       memory areas. we can use `filter` on this iterator to get
            //       only available memory areas.
            //          - eliza, 03/05/2017
            Some(current)
            // } else {
            //      self.next()
            // }
        }
    }
}

#[cfg(target_pointer_width = "32")]
pub type Word = u32;
#[cfg(target_pointer_width = "64")]
pub type Word = u64;

/// A Multiboot 2 ELF sections tag
#[derive(Debug)]
#[repr(packed)]
pub struct ElfSectionsTag { tag: Tag
                          , /// the number of sections pointed to by this tag
                            pub n_sections: u32
                          , /// the size of each ELF section
                            pub section_size: u32
                          , stringtable_idx: u32
                          , first_section: SectionHeader<Word>
                          }

impl ElfSectionsTag {
    /// Returns an iterator over the ELF sections pointed to by this tag.
    //  TODO: can the &'static bound be reduced to &'a? is there any reason to?
    //          - eliza, 03/04/2017
    #[inline] pub fn sections(&'static self) -> Sections<'static, Word> {
        Sections::new( &self.first_section
                     , self.n_sections - 1
                     , self.section_size
                     )
    }
}

impl IntoIterator for &'static ElfSectionsTag {
    //  TODO: can the &'static bound be reduced to &'a? is there any reason to?
    //          - eliza, 03/04/2017
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = Sections<'static, Word>;

    #[inline] fn into_iter(self) -> Self::IntoIter { self.sections() }

}
<commit_msg>fix(multiboot2): ensure that the ELF tag lives for the static lifetime<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for reading & extracting data from Multiboot 2 boot information.
//!
//! Consult the [Multiboot Specification](http://nongnu.askapache.com/grub/phcoder/multiboot.pdf)
//! for more information.
use memory::{PAddr, PhysicalPage, FrameRange};
use elf::section::{Sections, HeaderRepr as SectionHeader};

use core::iter::IntoIterator;
use core::fmt;

const END_TAG_LEN: u32 = 8;

const HEADER_LEN: u32 = 24;

pub const MAGIC: u32 = 0xe85250d6;

#[repr(u32)]
pub enum HeaderArch {
      I386 = 0
    , Mips = 4
}

#[repr(C)]
pub struct Header {
      pub magic: u32
    , pub arch: HeaderArch
    , pub header_length: u32
    , pub checksum: u32
    , pub end_tag: Tag
}

#[linkage = "external"]
#[link_section = ".multiboot_header"]
pub static HEADER: Header = Header {
    magic: MAGIC
  , arch: HeaderArch::I386
  , header_length: HEADER_LEN
  , checksum: -((MAGIC + 0 + HEADER_LEN) as i32) as u32
  , end_tag: Tag { ty: TagType::End
                 , length: END_TAG_LEN
                 }
};

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    #[inline]
    pub fn start_addr(&self) -> PAddr {
        PAddr::from(self as *const _ as u64)
    }

    pub fn end_addr(&self) -> PAddr {
        PAddr::from(self.start_addr() + self.length as u64)
    }

    /// TODO: rewrite this as a `TryFrom` implementation (see issue #85)
    //          - eliza, 03/09/2017
    pub unsafe fn from(addr: PAddr) -> Result<&'static Self, &'static str> {
        let info: &Info = &*(addr.into(): u64 as *const Info);
        // TODO: check if the multiboot tag *exists* at this location as well?
        //       since if we pass in the wrong address, we'll still make the
        //       "no end tag" error.
        //
        //       which, i suppose is *technically* correct, but not very
        //       helpful...
        //          - eliza, 03/04/2017
        if info.has_end() {
            Ok(info)
        } else {
            Err( "Multiboot info structure at {:?} had no end tag!")
        }
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    ///
    /// # Returns
    ///  - `Some(tag)` if a tag of the given type could be found.
    ///  - `None` if no tag of the given type could be found.
    pub fn get_tag(&'static self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    /// Finds the memory map tag.
    ///
    ///  # Returns
    ///  - `Some(MemMapTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn mem_map(&'static self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*((tag as *const Tag) as *const MemMapTag) })
    }

    /// Finds the ELF sections tag.
    ///
    ///  # Returns
    ///  - `Some(ElfSectionsTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn elf_sections(&'static self) -> Option<&'static ElfSectionsTag> {
        self.get_tag(TagType::ELFSections)
            .map(|tag| unsafe {
                &*((tag as *const Tag) as *const ElfSectionsTag)
            })
    }

    /// Returns an iterator over all Multiboot tags.
    #[inline]
    fn tags(&'static self) -> Tags { Tags(&self.tag_start as *const Tag) }

    /// Returns true if the multiboot structure has a valid end tag.
    fn has_end(&self) -> bool {
        // TODO: we should be able to use ptr::offset() here?
        //          - eliza, 03/05/2017
        let end_tag_addr
            = self as *const _ as usize +
              (self.length as usize - END_TAG_LEN as usize);
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }

    /// Returns the kernel frame range from the Multiboot 2 ELF Sections
    pub fn kernel_frames(&'static self) -> Result<FrameRange, &'static str> {
        let sections_tag = self.elf_sections()
                               .ok_or("ELF sections tag required!")?;

        let kernel_start = sections_tag.sections()
                              .map(|s| s.address())
                              .min()
                              .ok_or("Couldn't find kernel start section!")?;
        let kernel_end = sections_tag.sections()
                              .map(|s| s.address())
                              .max()
                              .ok_or("Couldn't find kernel end section!")?;

        Ok(PhysicalPage::from(kernel_start) .. PhysicalPage::from(kernel_end))
    }
}

impl IntoIterator for &'static Info {
    type IntoIter = Tags;
    type Item = &'static Tag;
    #[inline]  fn into_iter(self) -> Self::IntoIter { self.tags() }

}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
///
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
pub struct Tag { /// the type of this tag.
                 pub ty: TagType
               , length: u32
               }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType { /// Tag that indicates the end of multiboot tags
                   End              = 0
                 , /// Command line passed to the bootloader
                   CommandLine      = 1
                 , BootloaderName   = 2
                 , Modules          = 3
                 , BasicMemInfo     = 4
                 , BIOSBootDev      = 5
                 , MemoryMap        = 6
                 , VBEInfo          = 7
                 , FramebufferInfo  = 8
                 , ELFSections      = 9
                 , APMTable         = 10
                 }

/// An iterator over Multiboot 2 tags.
pub struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let next_addr = self.0 as usize + size as usize;
        self.0 = (((next_addr-1) & !0x7) + 0x8) as *const _;
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag { ty: TagType::End, length: END_TAG_LEN } => None
          , tag => {
              self.advance(tag.length);
              Some(tag)
            }
        }
    }
}

/// A Memory Map tag
#[repr(C)]
pub struct MemMapTag { tag: Tag
                     , pub entry_size: u32
                     , pub entry_version: u32
                     , first_entry: MemArea
                     }

impl MemMapTag {

    /// Returns an iterator over all the memory areas in this tag.
    #[inline] pub fn areas(&'static self) -> MemAreas {
        MemAreas { curr: (&self.first_entry) as *const MemArea
                    // TODO: we should be able to use ptr::offset() here?
                    //          - eliza, 03/05/2017
                 , last: ((self as *const MemMapTag as u32) +
                         self.tag.length - self.entry_size)
                         as *const MemArea
                 , size: self.entry_size
                 }
    }
}

impl IntoIterator for &'static MemMapTag {
    type Item = &'static MemArea;
    type IntoIter = MemAreas;

    #[inline] fn into_iter(self) -> Self::IntoIter { self.areas() }

}

/// A tag that stores the boot command line.
#[repr(C)]
pub struct CommandLineTag { tag: Tag
                          , /// The boot command line.
                            ///
                            /// The command line is a normal C-style zero-
                            /// terminated UTF-8 string.
                            pub command_line: [u8]
                          }

#[repr(C)]
pub struct ModulesTag { tag: Tag
                      , /// The address at which the module begins.
                        pub mod_begin: PAddr
                      , /// The address at which the module ends.
                        pub mod_end: PAddr
                      , /// A string (typically a command line)
                        pub string: [u8]
                      }

#[repr(u32)]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum MemAreaType { Available = 1
                     , Acpi      = 3
                     , Preserve  = 4
                     }

/// A multiboot 2 memory area
#[repr(C)]
#[derive(Debug)]
pub struct MemArea { /// the starting address of the memory area
                     pub base: PAddr
                   , /// the length of the memory area
                     pub length: PAddr
                   , /// the type of the memory area
                     pub ty: MemAreaType
                   , _pad: u32
                   }

impl MemArea {
    #[inline] pub fn address(&self) -> PAddr {
        self.base + self.length - 1
    }
}

impl fmt::Display for MemArea {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!( f, "{:?} from {:#08x} to {:#08x}"
              , self.ty, self.base, self.address())
    }
}

/// An iterator over memory areas
#[derive(Clone)]
pub struct MemAreas { curr: *const MemArea
                    , last: *const MemArea
                    , size: u32
                    }

impl Iterator for MemAreas {
    type Item = &'static MemArea;

    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = unsafe { &*self.curr };
            // TODO: we should be able to use ptr::offset() here.
            //          - eliza, 03/05/2017
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            // if current.ty == MemAreaType::Available {
            // NOTE: this used to skip over unavailable or ACPI memory areas,
            //       but i've disabled that as we may want to iterate over thsoe
            //       memory areas. we can use `filter` on this iterator to get
            //       only available memory areas.
            //          - eliza, 03/05/2017
            Some(current)
            // } else {
            //      self.next()
            // }
        }
    }
}

#[cfg(target_pointer_width = "32")]
pub type Word = u32;
#[cfg(target_pointer_width = "64")]
pub type Word = u64;

/// A Multiboot 2 ELF sections tag
#[derive(Debug)]
#[repr(packed)]
pub struct ElfSectionsTag { tag: Tag
                          , /// the number of sections pointed to by this tag
                            pub n_sections: u32
                          , /// the size of each ELF section
                            pub section_size: u32
                          , stringtable_idx: u32
                          , first_section: SectionHeader<Word>
                          }

impl ElfSectionsTag {
    /// Returns an iterator over the ELF sections pointed to by this tag.
    //  TODO: can the &'static bound be reduced to &'a? is there any reason to?
    //          - eliza, 03/04/2017
    #[inline] pub fn sections(&'static self) -> Sections<'static, Word> {
        Sections::new( &self.first_section
                     , self.n_sections - 1
                     , self.section_size
                     )
    }
}

impl IntoIterator for &'static ElfSectionsTag {
    //  TODO: can the &'static bound be reduced to &'a? is there any reason to?
    //          - eliza, 03/04/2017
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = Sections<'static, Word>;

    #[inline] fn into_iter(self) -> Self::IntoIter { self.sections() }

}
<|endoftext|>


<commit_before>//! An RPC library for Rust.
//!
//! Example usage:
//!
//! ```
//! # #![feature(custom_derive, plugin)]
//! # #![plugin(serde_macros)]
//! # #[macro_use] extern crate tarpc;
//! # extern crate serde;
//! rpc! {
//!     mod my_server {
//!         service {
//!             rpc hello(name: String) -> String;
//!             rpc add(x: i32, y: i32) -> i32;
//!         }
//!     }
//! }
//!
//! use self::my_server::*;
//! use std::time::Duration;
//!
//! impl my_server::Service for () {
//!     fn hello(&self, s: String) -> String {
//!         format!("Hello, {}!", s)
//!     }
//!     fn add(&self, x: i32, y: i32) -> i32 {
//!         x + y
//!     }
//! }
//!
//! fn main() {
//!     let addr = "127.0.0.1:9000";
//! let shutdown = my_server::serve(addr, (),
//! Some(Duration::from_secs(30))).unwrap();
//!     let client = Client::new(addr, None).unwrap();
//!     assert_eq!(3, client.add(1, 2).unwrap());
//!     assert_eq!("Hello, Mom!".to_string(),
//!                client.hello("Mom".to_string()).unwrap());
//!     drop(client);
//!     shutdown.shutdown();
//! }
//! ```

#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
#![deny(missing_docs)]

extern crate serde;
extern crate bincode;
#[macro_use]
extern crate log;

/// Provides the tarpc client and server, which implements the tarpc protocol.
/// The protocol is defined by the implementation.
pub mod protocol;

/// Provides the macro used for constructing rpc services and client stubs.
pub mod macros;

pub use protocol::{Error, Result};
<commit_msg>Put deny(missing_docs) at the top. I think it looks better there.<commit_after>//! An RPC library for Rust.
//!
//! Example usage:
//!
//! ```
//! # #![feature(custom_derive, plugin)]
//! # #![plugin(serde_macros)]
//! # #[macro_use] extern crate tarpc;
//! # extern crate serde;
//! rpc! {
//!     mod my_server {
//!         service {
//!             rpc hello(name: String) -> String;
//!             rpc add(x: i32, y: i32) -> i32;
//!         }
//!     }
//! }
//!
//! use self::my_server::*;
//! use std::time::Duration;
//!
//! impl my_server::Service for () {
//!     fn hello(&self, s: String) -> String {
//!         format!("Hello, {}!", s)
//!     }
//!     fn add(&self, x: i32, y: i32) -> i32 {
//!         x + y
//!     }
//! }
//!
//! fn main() {
//!     let addr = "127.0.0.1:9000";
//! let shutdown = my_server::serve(addr, (),
//! Some(Duration::from_secs(30))).unwrap();
//!     let client = Client::new(addr, None).unwrap();
//!     assert_eq!(3, client.add(1, 2).unwrap());
//!     assert_eq!("Hello, Mom!".to_string(),
//!                client.hello("Mom".to_string()).unwrap());
//!     drop(client);
//!     shutdown.shutdown();
//! }
//! ```

#![deny(missing_docs)]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate bincode;
#[macro_use]
extern crate log;

/// Provides the tarpc client and server, which implements the tarpc protocol.
/// The protocol is defined by the implementation.
pub mod protocol;

/// Provides the macro used for constructing rpc services and client stubs.
pub mod macros;

pub use protocol::{Error, Result};
<|endoftext|>


<commit_before>extern crate pkg_config;

use std::fs::{self,File};
use std::path::Path;
use std::process::Command;

use std::io::prelude::*;

fn check_func(function_name: &str) -> bool {
    let test_file_path = format!("target/build/check_{}.rs", function_name);
    let test_file_name = Path::new(&test_file_path[..]);

    fs::create_dir_all("target/build").unwrap();

    {
        let mut test_file = File::create(test_file_name).unwrap();

        writeln!(&mut test_file, "extern \"C\" {{").unwrap();
        writeln!(&mut test_file, "    fn {}();", function_name).unwrap();
        writeln!(&mut test_file, "}}").unwrap();
        writeln!(&mut test_file, "").unwrap();
        writeln!(&mut test_file, "fn main() {{").unwrap();
        writeln!(&mut test_file, "    unsafe {{").unwrap();
        writeln!(&mut test_file, "        {}();", function_name).unwrap();
        writeln!(&mut test_file, "    }}").unwrap();
        writeln!(&mut test_file, "}}").unwrap();
    }

    let output = Command::new("rustc").
        arg(test_file_name).
        arg("--out-dir").arg("target/build/").
        arg("-l").arg("udev").
        output().unwrap();

    output.status.success()
}

fn main() {
    pkg_config::find_library("libudev").unwrap();

    if check_func("udev_hwdb_new") {
        println!("cargo:rustc-cfg=hwdb");
        println!("cargo:hwdb=true");
    }
    else {
        println!("cargo:hwdb=false");
    }
}
<commit_msg>[fix #2] write temp files to OUT_DIR<commit_after>extern crate pkg_config;

use std::env;
use std::fs::File;
use std::path::Path;
use std::process::Command;

use std::io::prelude::*;

fn check_func(function_name: &str) -> bool {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let test_file_name = Path::new(&out_dir).join(format!("check_{}.rs", function_name));

    {
        let mut test_file = File::create(&test_file_name).unwrap();

        writeln!(&mut test_file, "extern \"C\" {{").unwrap();
        writeln!(&mut test_file, "    fn {}();", function_name).unwrap();
        writeln!(&mut test_file, "}}").unwrap();
        writeln!(&mut test_file, "").unwrap();
        writeln!(&mut test_file, "fn main() {{").unwrap();
        writeln!(&mut test_file, "    unsafe {{").unwrap();
        writeln!(&mut test_file, "        {}();", function_name).unwrap();
        writeln!(&mut test_file, "    }}").unwrap();
        writeln!(&mut test_file, "}}").unwrap();
    }

    let output = Command::new("rustc").
        arg(&test_file_name).
        arg("--out-dir").arg(&out_dir).
        arg("-l").arg("udev").
        output().unwrap();

    output.status.success()
}

fn main() {
    pkg_config::find_library("libudev").unwrap();

    if check_func("udev_hwdb_new") {
        println!("cargo:rustc-cfg=hwdb");
        println!("cargo:hwdb=true");
    }
    else {
        println!("cargo:hwdb=false");
    }
}
<|endoftext|>


<commit_before>extern crate semver;

use semver::{Version, VersionReq};
use semver::ParseError::IncorrectParse;
use std::borrow::Cow;
use std::path::PathBuf;
use std::str::from_utf8_unchecked;
use std::process::Command;

fn is_whitespace(x: &u8) -> bool {
    ['\n', '\t', ' '].contains(&(*x as char))
}

fn build_wrappers() -> (String, &'static str) {
    // Get Cargo directories
    let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
    let mut src_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
    src_dir.push("wrappers");

    // Run cmake generator
    match Command::new("cmake").arg(&src_dir).current_dir(&out_dir).status() {
        Err(e) => panic!("Failed to invoke CMake: {}", e),
        Ok(s) => {
            if !s.success() {
                panic!("CMake configuration of wrappers failed with status {}", s)
            }
        }
    }

    // Do the actual build
    match Command::new("cmake").arg("--build").arg(&out_dir).status() {
        Err(e) => panic!("Failed to invoke CMake: {}", e),
        Ok(s) => {
            if !s.success() {
                panic!("CMake build of wrappers failed with status {}", s)
            }
        }
    }

    (out_dir.into_os_string().into_string().unwrap(),
     "targetwrappers")
}

fn main() {
    let (wrappers_out_dir, wrappers_lib_name) = build_wrappers();
    println!("cargo:rustc-link-search=native={}", wrappers_out_dir);
    println!("cargo:rustc-link-lib=static={}", wrappers_lib_name);

    let minimum_llvm_version = VersionReq::parse(">=3.6").unwrap();
    let (llvm_config, version) = get_llvm_config();
    if minimum_llvm_version.matches(&version) {
        println!("Found LLVM version {}", version);
    } else {
        panic!("LLVM version 3.6 or higher is required. (Found {})", version);
    };

    // Are we using LLVM as a shared object or static library?
    let llvm_libtype = match std::env::var("CARGO_FEATURE_LLVM_DYLIB") {
        Ok(_) => "dylib",
        Err(_) => "static"
    };

    // llvm-config --ldflags: extract -L<dir> options
    let output = Command::new(&*llvm_config).arg("--ldflags").output().unwrap().stdout;
    for arg in output.split(is_whitespace) {
        if arg.starts_with(b"-L") {
            println!("cargo:rustc-link-search=native={}", unsafe {
                from_utf8_unchecked(&arg[2..])
            });
        }
    }

    // llvm-config --libs --system-libs: extract -l<lib> options
    let output = Command::new(&*llvm_config).args(&["--libs", "--system-libs"]).output().unwrap().stdout;
    for arg in output.split(is_whitespace) {
        if arg.starts_with(b"-l") {
            let arg = &arg[2..];
            let libtype = if arg.starts_with(b"LLVM") {
                llvm_libtype
            } else {
                "dylib"
            };
            println!("cargo:rustc-link-lib={}={}", libtype, unsafe {
                from_utf8_unchecked(arg)
            });
        }
    }

    // llvm-config --cxxflags: determine which libc++ to use: LLVM's or GCC's
    let output = String::from_utf8(
        Command::new(&*llvm_config).arg("--cxxflags").output().unwrap().stdout
    ).unwrap();
    let libcpp = if output.contains("stdlib=libc++") {
        "c++"
    } else {
        "stdc++"
    };
    println!("cargo:rustc-link-lib={}", libcpp);
}

fn get_llvm_config() -> (Cow<'static, str>, Version) {
    match Command::new("llvm-config").arg("--version").output() {
        Ok(x) => {
            // llvm-config was on our PATH. Easy.
            let ver = match Version::parse(&String::from_utf8_lossy(&*x.stdout)) {
                // Ignore partial error; particularly constructs like '3.8.0svn'
                // should be accepted, despite being invalid semver.
                Err(IncorrectParse(v, _)) | Ok(v) => v,
                _ => panic!("Could not determine LLVM version")
            };
            (Cow::Borrowed("llvm-config"), ver)
        }
        Err(_) => {
            panic!("llvm-config not found. Install LLVM before attempting to build.");
        }
    }
}
<commit_msg>Remove some uses of unwrap() to make failures easier to debug.<commit_after>extern crate semver;

use semver::{Version, VersionReq};
use semver::ParseError::IncorrectParse;
use std::borrow::Cow;
use std::path::PathBuf;
use std::str::from_utf8_unchecked;
use std::process::Command;

fn is_whitespace(x: &u8) -> bool {
    ['\n', '\t', ' '].contains(&(*x as char))
}

fn build_wrappers() -> (String, &'static str) {
    // Get Cargo directories
    let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
    let mut src_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
    src_dir.push("wrappers");

    // Run cmake generator
    match Command::new("cmake").arg(&src_dir).current_dir(&out_dir).status() {
        Err(e) => panic!("Failed to invoke CMake: {}", e),
        Ok(s) => {
            if !s.success() {
                panic!("CMake configuration of wrappers failed with status {}", s)
            }
        }
    }

    // Do the actual build
    match Command::new("cmake").arg("--build").arg(&out_dir).status() {
        Err(e) => panic!("Failed to invoke CMake: {}", e),
        Ok(s) => {
            if !s.success() {
                panic!("CMake build of wrappers failed with status {}", s)
            }
        }
    }

    (out_dir.into_os_string().into_string().unwrap(),
     "targetwrappers")
}

fn main() {
    let (wrappers_out_dir, wrappers_lib_name) = build_wrappers();
    println!("cargo:rustc-link-search=native={}", wrappers_out_dir);
    println!("cargo:rustc-link-lib=static={}", wrappers_lib_name);

    let minimum_llvm_version = VersionReq::parse(">=3.6").unwrap();
    let (llvm_config, version) = get_llvm_config();
    if minimum_llvm_version.matches(&version) {
        println!("Found LLVM version {}", version);
    } else {
        panic!("LLVM version 3.6 or higher is required. (Found {})", version);
    };

    // Are we using LLVM as a shared object or static library?
    let llvm_libtype = match std::env::var("CARGO_FEATURE_LLVM_DYLIB") {
        Ok(_) => "dylib",
        Err(_) => "static"
    };

    // llvm-config --ldflags: extract -L<dir> options
    let output = Command::new(&*llvm_config).arg("--ldflags")
        .output().ok().expect("Failed to get LDFLAGS from llvm-config").stdout;
    for arg in output.split(is_whitespace) {
        if arg.starts_with(b"-L") {
            println!("cargo:rustc-link-search=native={}", unsafe {
                from_utf8_unchecked(&arg[2..])
            });
        }
    }

    // llvm-config --libs --system-libs: extract -l<lib> options
    let output = Command::new(&*llvm_config).args(&["--libs", "--system-libs"])
        .output().ok().expect("Failed to get libs from llvm-config").stdout;
    for arg in output.split(is_whitespace) {
        if arg.starts_with(b"-l") {
            let arg = &arg[2..];
            let libtype = if arg.starts_with(b"LLVM") {
                llvm_libtype
            } else {
                "dylib"
            };
            println!("cargo:rustc-link-lib={}={}", libtype, unsafe {
                from_utf8_unchecked(arg)
            });
        }
    }

    // llvm-config --cxxflags: determine which libc++ to use: LLVM's or GCC's
    let output = String::from_utf8(
        Command::new(&*llvm_config).arg("--cxxflags")
            .output().ok().expect("Failed to get CXXFLAGS from llvm-config").stdout
    ).ok().expect("llvm-config --cxxflags gave invalid UTF-8");
    let libcpp = if output.contains("stdlib=libc++") {
        "c++"
    } else {
        "stdc++"
    };
    println!("cargo:rustc-link-lib={}", libcpp);
}

fn get_llvm_config() -> (Cow<'static, str>, Version) {
    match Command::new("llvm-config").arg("--version").output() {
        Ok(x) => {
            // llvm-config was on our PATH. Easy.
            let ver = match Version::parse(&String::from_utf8_lossy(&*x.stdout)) {
                // Ignore partial error; particularly constructs like '3.8.0svn'
                // should be accepted, despite being invalid semver.
                Err(IncorrectParse(v, _)) | Ok(v) => v,
                _ => panic!("Could not determine LLVM version")
            };
            (Cow::Borrowed("llvm-config"), ver)
        }
        Err(_) => {
            panic!("llvm-config not found. Install LLVM before attempting to build.");
        }
    }
}
<|endoftext|>


<commit_before>extern crate num_cpus;

use std::process::Command;
use std::env;

fn main() {
    Command::new("git").args(&["submodule", "update", "--init"]).status().unwrap();
    env::set_current_dir("bitcoin-core").unwrap();

    Command::new("sh").arg("./autogen.sh").status().unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();
    Command::new("sh").args(&["./configure",
                              &format!("--prefix={}", out_dir),
                              "--with-gui=no",
                              "--with-daemon=no",
                              "--with-utils=no",
                              "--disable-wallet",
                              "--disable-zmq",
                              "--disable-bench",
                              "--disable-tests",
                              "--with-miniupnpc=no"]).status().unwrap();
    Command::new("make").arg(format!("-j{}", num_cpus::get())).status().unwrap();
    Command::new("make").args(&[&format!("-j{}", num_cpus::get()), "install"]).status().unwrap();
    env::set_current_dir("src/secp256k1").unwrap();
    Command::new("make").args(&[&format!("-j{}", num_cpus::get()), "install"]).status().unwrap();

    let target = env::var("TARGET").unwrap();
    if !target.contains("msvc") {
        let cpplib = if target.contains("darwin") || target.contains("freebsd") { "c++" } else { "stdc++" };
        println!("cargo:rustc-link-lib=dylib={}", cpplib);
    }

    println!("cargo:rustc-link-search=native={}/lib", out_dir);
    println!("cargo:rustc-link-lib=static=bitcoinconsensus");
    println!("cargo:rustc-link-lib=static=secp256k1");
}
<commit_msg>cpus in a variable<commit_after>extern crate num_cpus;

use std::process::Command;
use std::env;

fn main() {
    Command::new("git").args(&["submodule", "update", "--init"]).status().unwrap();
    env::set_current_dir("bitcoin-core").unwrap();

    Command::new("sh").arg("./autogen.sh").status().unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();
    Command::new("sh").args(&["./configure",
                              &format!("--prefix={}", out_dir),
                              "--with-gui=no",
                              "--with-daemon=no",
                              "--with-utils=no",
                              "--disable-wallet",
                              "--disable-zmq",
                              "--disable-bench",
                              "--disable-tests",
                              "--with-miniupnpc=no"]).status().unwrap();
    let cpus = num_cpus::get();
    Command::new("make").arg(format!("-j{}", cpus)).status().unwrap();
    Command::new("make").args(&[&format!("-j{}", cpus), "install"]).status().unwrap();
    env::set_current_dir("src/secp256k1").unwrap();
    Command::new("make").args(&[&format!("-j{}", cpus), "install"]).status().unwrap();

    let target = env::var("TARGET").unwrap();
    if !target.contains("msvc") {
        let cpplib = if target.contains("darwin") || target.contains("freebsd") { "c++" } else { "stdc++" };
        println!("cargo:rustc-link-lib=dylib={}", cpplib);
    }

    println!("cargo:rustc-link-search=native={}/lib", out_dir);
    println!("cargo:rustc-link-lib=static=bitcoinconsensus");
    println!("cargo:rustc-link-lib=static=secp256k1");
}
<|endoftext|>