<commit_before>use super::One;
use point::{Point, Position2D};
use std::ops::{Add, Sub, Mul};
use std::cmp::{Ord, min, max};
use std::iter::Iterator;

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct Rect<T> {
    top_left: Point<T>,
    bottom_right: Point<T>
}

impl<T> Rect<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{
    pub fn new(left: T, top: T, width: T, height: T) -> Rect<T> {
        Rect::<T> {
            bottom_right: Point::<T>::new(left + width - T::one(), top + height - T::one()),
            top_left: Point::<T>::new(left, top)
        }
    }

    pub fn from_points(top_left: Point<T>, bottom_right: Point<T>) -> Rect<T> {
        Rect { top_left: top_left, bottom_right: bottom_right }
    }

    pub fn width(&self) -> T {
        self.right() - self.left() + T::one()
    }

    pub fn height(&self) -> T {
        self.bottom() - self.top() + T::one()
    }

    pub fn top_left(&self) -> Point<T> {
        self.top_left
    }

    pub fn top(&self) -> T {
        self.top_left.y()
    }

    pub fn left(&self) -> T {
        self.top_left.x()
    }

    pub fn bottom_right(&self) -> Point<T> {
        self.bottom_right
    }

    pub fn bottom(&self) -> T {
        self.bottom_right.y()
    }

    pub fn right(&self) -> T {
        self.bottom_right.x()
    }

    pub fn area(&self) -> T {
        self.width() * self.height()
    }

    pub fn contains(&self, point: Point<T>) -> bool {
        self.left() <= point.x() && point.x() <= self.right() &&
        self.top() <= point.y() && point.y() <= self.bottom()
    }

    pub fn intersect(&self, other: &Rect<T>) -> Option<Rect<T>> {
        let left = max(self.left(), other.left());
        let right = min(self.right(), other.right());
        let top = max(self.top(), other.top());
        let bottom = min(self.bottom(), other.bottom());

        if left <= right && top <= bottom {
            Some(Point::new(left, top).rect(Point::new(right, bottom)))
        } else {
            None
        }
    }

    pub fn split_column_mut(&mut self, col: T) -> Rect<T> {
        let new_rect = Rect::from_points(Point::new(col, self.top()), self.bottom_right());
        *self.bottom_right.x_mut() = col - T::one();

        new_rect
    }

    pub fn split_row_mut(&mut self, row: T) -> Rect<T> {
        let new_rect = Rect::from_points(Point::new(self.left(), row), self.bottom_right());
        *self.bottom_right.y_mut() = row - T::one();

        new_rect
    }

    pub fn columns(&self) -> Vec<Rect<T>> {
        let mut columns: Vec<Rect<T>> = Vec::new();

        let mut x = self.left();
        while x <= self.right() {
            columns.push(Rect::new(x, self.top(), T::one(), self.height()));
            x = x + T::one();
        }

        columns
    }

    pub fn rows(&self) -> Vec<Rect<T>> {
        let mut rows: Vec<Rect<T>> = Vec::new();

        let mut y = self.top();
        while y <= self.bottom() {
            rows.push(Rect::new(self.left(), y, self.width(), T::one()));
            y = y + T::one();
        }

        rows
    }
}

impl<T> Rect<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{

    pub fn iter(&self) -> Iter<T> {
        self.into_iter()
    }
}

impl<T> IntoIterator for Rect<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{
    type Item = Point<T>;
    type IntoIter = Iter<T>;
    fn into_iter(self) -> Iter<T> {
        Iter::<T> { cur: self.top_left(), rect: self }
    }
}

pub struct Iter<T> {
    rect: Rect<T>,
    cur: Point<T>
}

impl<T> Iterator for Iter<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{
    type Item = Point<T>;
    fn next(&mut self) -> Option<Self::Item> {
        let point = self.cur;

        if self.cur.x() < self.rect.right() {
            *self.cur.x_mut() = self.cur.x() + T::one();
        } else if self.cur.y() <= self.rect.bottom() {
            *self.cur.x_mut() = self.rect.left();
            *self.cur.y_mut() = self.cur.y() + T::one();
        }

        if point.x() <= self.rect.right() && point.y() <= self.rect.bottom() {
            Some(point)
        } else {
            None
        }
    }
}
<commit_msg>Update rect module documentation<commit_after>use super::One;
use point::{Point, Position2D};
use std::ops::{Add, Sub, Mul};
use std::cmp::{Ord, min, max};
use std::iter::Iterator;

/// A generic rectangle structure.
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct Rect<T> {
    top_left: Point<T>,
    bottom_right: Point<T>
}

impl<T> Rect<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{
    /// Returns a new rectangle with the supplied position and dimensions.
    pub fn new(left: T, top: T, width: T, height: T) -> Rect<T> {
        Rect::<T> {
            bottom_right: Point::<T>::new(left + width - T::one(), top + height - T::one()),
            top_left: Point::<T>::new(left, top)
        }
    }

    /// Returns a new rectangle with the given top-left and bottom-right points.
    pub fn from_points(top_left: Point<T>, bottom_right: Point<T>) -> Rect<T> {
        Rect { top_left: top_left, bottom_right: bottom_right }
    }

    /// Returns the width of the rectangle.
    pub fn width(&self) -> T {
        self.right() - self.left() + T::one()
    }

    /// Returns the height of the rectangle.
    pub fn height(&self) -> T {
        self.bottom() - self.top() + T::one()
    }

    /// Returns a copy of the top-left point of the rectangle.
    pub fn top_left(&self) -> Point<T> {
        self.top_left
    }

    /// Returns the `y` coordinate of the top side of the rectangle.
    pub fn top(&self) -> T {
        self.top_left.y()
    }

    /// Returns the `x` coordinate of the left side of the rectangle.
    pub fn left(&self) -> T {
        self.top_left.x()
    }

    /// Returns a copy of the bottom-right point of the rectangle.
    pub fn bottom_right(&self) -> Point<T> {
        self.bottom_right
    }

    /// Returns the `y` coordinate of the bottom side of the rectangle.
    pub fn bottom(&self) -> T {
        self.bottom_right.y()
    }

    /// Returns the `x` coordinate of the right side of the rectangle.
    pub fn right(&self) -> T {
        self.bottom_right.x()
    }

    /// Returns the area of the rectangle.
    pub fn area(&self) -> T {
        self.width() * self.height()
    }

    /// Returns `true` if the given point lies within the bounds of the rectangle,
    /// and `false` otherwise.
    pub fn contains(&self, point: Point<T>) -> bool {
        self.left() <= point.x() && point.x() <= self.right() &&
        self.top() <= point.y() && point.y() <= self.bottom()
    }

    /// If `self` and `other` intersect, then the intersection of the two rectangles
    /// is returned as a new rectangle, otherwise `None` is returned.
    pub fn intersect(&self, other: &Rect<T>) -> Option<Rect<T>> {
        let left = max(self.left(), other.left());
        let right = min(self.right(), other.right());
        let top = max(self.top(), other.top());
        let bottom = min(self.bottom(), other.bottom());

        if left <= right && top <= bottom {
            Some(Point::new(left, top).rect(Point::new(right, bottom)))
        } else {
            None
        }
    }

    /// Splits the rectangle at the given column `col`. The right side of the left part
    /// of the resulting split will be at `col - 1`, and the left side of the right part
    /// will be at `col`. The current rectangle will be modified in-place to be the left
    /// part, and the right part will be returned as a new rectangle.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut r = Rect::new(0, 0, 10, 10);
    /// let s = r.split_column_mut(5);
    ///
    /// assert_eq!(r.top(), 0);
    /// assert_eq!(r.left(), 0);
    /// assert_eq!(r.bottom(), 9);
    /// assert_eq!(r.right(), 4);
    ///
    /// assert_eq!(s.top(), 0);
    /// assert_eq!(s.left(), 5);
    /// assert_eq!(s.bottom(), 9);
    /// assert_eq!(s.right(), 9);
    /// ```
    pub fn split_column_mut(&mut self, col: T) -> Rect<T> {
        let new_rect = Rect::from_points(Point::new(col, self.top()), self.bottom_right());
        *self.bottom_right.x_mut() = col - T::one();

        new_rect
    }

    /// Splits the rectangle at the given row `row`. The bottom side of the top part
    /// of the resulting split will be at `row - 1`, and the top side of the bottom part
    /// will be at `row`. The current rectangle will be modified in-place to be the top
    /// part, and the bottom part will be returned as a new rectangle.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut r = Rect::new(0, 0, 10, 10);
    /// let s = r.split_row_mut(5);
    ///
    /// assert_eq!(r.top(), 0);
    /// assert_eq!(r.left(), 0);
    /// assert_eq!(r.bottom(), 4);
    /// assert_eq!(r.right(), 9);
    ///
    /// assert_eq!(s.top(), 5);
    /// assert_eq!(s.left(), 0);
    /// assert_eq!(s.bottom(), 9);
    /// assert_eq!(s.right(), 9);
    /// ```
    pub fn split_row_mut(&mut self, row: T) -> Rect<T> {
        let new_rect = Rect::from_points(Point::new(self.left(), row), self.bottom_right());
        *self.bottom_right.y_mut() = row - T::one();

        new_rect
    }

    /// Returns a `Vec` containing a one-width rectangle for each column of the rectangle.
    pub fn columns(&self) -> Vec<Rect<T>> {
        let mut columns: Vec<Rect<T>> = Vec::new();

        let mut x = self.left();
        while x <= self.right() {
            columns.push(Rect::new(x, self.top(), T::one(), self.height()));
            x = x + T::one();
        }

        columns
    }

    /// Returns a `Vec` containing a one-height rectangle for each row of the rectangle.
    pub fn rows(&self) -> Vec<Rect<T>> {
        let mut rows: Vec<Rect<T>> = Vec::new();

        let mut y = self.top();
        while y <= self.bottom() {
            rows.push(Rect::new(self.left(), y, self.width(), T::one()));
            y = y + T::one();
        }

        rows
    }
}

impl<T> Rect<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{
    /// Returns an iterator over each point in the rectangle, going from left-to-right and
    /// top-to-bottom.
    pub fn iter(&self) -> Iter<T> {
        self.into_iter()
    }
}

impl<T> IntoIterator for Rect<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{
    type Item = Point<T>;
    type IntoIter = Iter<T>;
    fn into_iter(self) -> Iter<T> {
        Iter::<T> { cur: self.top_left(), rect: self }
    }
}

/// An iterator over a rectangle, returning each point within the rectangle going from
/// left-to-right and top-to-bottom.
pub struct Iter<T> {
    rect: Rect<T>,
    cur: Point<T>
}

impl<T> Iterator for Iter<T>
    where T: Add<i32, Output=T> +
             Add<T, Output=T> +
             Sub<T, Output=T> +
             Mul<T, Output=T> +
             Ord + One<T> + Default + Copy + Clone
{
    type Item = Point<T>;
    fn next(&mut self) -> Option<Self::Item> {
        let point = self.cur;

        if self.cur.x() < self.rect.right() {
            *self.cur.x_mut() = self.cur.x() + T::one();
        } else if self.cur.y() <= self.rect.bottom() {
            *self.cur.x_mut() = self.rect.left();
            *self.cur.y_mut() = self.cur.y() + T::one();
        }

        if point.x() <= self.rect.right() && point.y() <= self.rect.bottom() {
            Some(point)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>/*
    TODO(ceg): provide the goto-line,goto-offset functions

    We want:

    An Updated collection of metadata that tracks number of newlines in the document sub-blocks

    The root contains the total count of newlines

    NB: This is tied to the document implementation

                 (root)
                   |
               [ 3 + 6 ]
             /          \
        [ 1 + 2 ]     [ 2 + 4 ]
       /        \    /        \
    [1]        [2]  [2]       [4]

    each time a node is indexed
    the document impl must call update hierarchy with the build metadata diff

    the mode subscribe to the document events

    When a node is indexed/added/removed, the document notify us
    Then we build the node metadata
    and ask to update the hierarchy.

    To be fully async we must: have a shadowed tree that matched the document internal representation ?
    and keep a per node doc_revision

    Must we re-index before remove ?
*/

use std::any::Any;

use parking_lot::RwLock;

use std::rc::Rc;
use std::sync::Arc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;
use crate::core::editor::InputStageActionMap;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::document;
use crate::core::document::Document;

use crate::core::view::layout::LayoutEnv;
use crate::core::view::layout::ScreenOverlayFilter;

use crate::core::view;
use crate::core::view::View;
use crate::core::view::ViewEvent;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

use lazy_static::lazy_static;
use std::collections::HashMap;
// document meta data map
lazy_static! {
    static ref DOC_METADATA_MAP: Arc<RwLock<HashMap<document::Id, Arc<RwLock<Document<'static>>>>>> =
        Arc::new(RwLock::new(HashMap::new()));
}

struct LineNumberDocumentNodeMetaData {
    nl_count: u64,
    cr_count: u64,
    lf_count: u64,
}

impl LineNumberDocumentNodeMetaData {
    pub fn new() -> Self {
        dbg_println!("LineNumberMode");
        LineNumberDocumentNodeMetaData {
            nl_count: 0,
            cr_count: 0,
            lf_count: 0,
        }
    }
}

pub struct LineNumberMode {
    // add common fields
}

impl LineNumberMode {
    pub fn new() -> Self {
        dbg_println!("LineNumberMode");
        LineNumberMode {}
    }
}

pub struct LineNumberModeContext {
    // add per view fields
    target_vid: view::Id,
}

impl<'a> Mode for LineNumberMode {
    fn name(&self) -> &'static str {
        &"line-number-mode"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc line-number-mode ctx");
        let ctx = LineNumberModeContext { target_vid: 0 };
        Box::new(ctx)
    }

    fn configure_view(
        &self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        view: &mut View<'static>,
    ) {
        view.compose_screen_overlay_filters
            .borrow_mut()
            .push(Box::new(LineNumberOverlayFilter::new()));
    }

    fn on_view_event(
        &self,
        editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        src: ViewEventSource,
        dst: ViewEventDestination,
        _event: &ViewEvent,
    ) {
        dbg_println!("LINENUM on_view_event src: {:?} dst: {:?}", src, dst);

        let src = editor.view_map.get(&src.id).unwrap().write();
        let mut dst = editor.view_map.get(&dst.id).unwrap().write();
        let mut mode_ctx = dst.mode_ctx_mut::<LineNumberModeContext>("line-number-mode");
        mode_ctx.target_vid = src.id;

        let doc_id = src.document.as_ref().unwrap().read().id;
        let d = DOC_METADATA_MAP.as_ref().write().get_mut(&doc_id); //.unwrap();
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

pub struct LineNumberOverlayFilter {
    line_offsets: Vec<(u64, u64)>,
}

impl LineNumberOverlayFilter {
    pub fn new() -> Self {
        LineNumberOverlayFilter {
            line_offsets: vec![],
        }
    }
}

impl ScreenOverlayFilter<'_> for LineNumberOverlayFilter {
    fn name(&self) -> &'static str {
        &"LineNumberOverlay"
    }

    fn setup(&mut self, editor: &Editor, _env: &mut LayoutEnv, view: &Rc<RwLock<View>>) {
        let view = view.read();
        let mode_ctx = view.mode_ctx::<LineNumberModeContext>("line-number-mode");
        let target_vid = mode_ctx.target_vid;
        let src = editor.view_map.get(&target_vid).unwrap().read();
        self.line_offsets = src.screen.read().line_offset.clone();
    }

    fn run(&mut self, _view: &View, env: &mut LayoutEnv) -> () {
        dbg_println!("LINENUM RUN");
        env.screen.clear();
        for e in self.line_offsets.iter() {
            let s = format!("@{:>12}", e.0);
            for c in s.chars() {
                let mut cpi = CodepointInfo::new();
                cpi.displayed_cp = c;
                let ret = env.screen.push(cpi);
                dbg_println!("ret = {:?}", ret);
            }
            env.screen.select_next_line_index();
        }
    }

    fn finish(&mut self, view: &View, env: &mut LayoutEnv) -> () {
        dbg_println!("LINENUM FINISH");
    }
}
<commit_msg>remove debug<commit_after>/*
    TODO(ceg): provide the goto-line,goto-offset functions

    We want:

    An Updated collection of metadata that tracks number of newlines in the document sub-blocks

    The root contains the total count of newlines

    NB: This is tied to the document implementation

                 (root)
                   |
               [ 3 + 6 ]
             /          \
        [ 1 + 2 ]     [ 2 + 4 ]
       /        \    /        \
    [1]        [2]  [2]       [4]

    each time a node is indexed
    the document impl must call update hierarchy with the build metadata diff

    the mode subscribe to the document events

    When a node is indexed/added/removed, the document notify us
    Then we build the node metadata
    and ask to update the hierarchy.

    To be fully async we must: have a shadowed tree that matched the document internal representation ?
    and keep a per node doc_revision

    Must we re-index before remove ?
*/

use std::any::Any;

use parking_lot::RwLock;

use std::rc::Rc;
use std::sync::Arc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;
use crate::core::editor::InputStageActionMap;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::document;
use crate::core::document::Document;

use crate::core::view::layout::LayoutEnv;
use crate::core::view::layout::ScreenOverlayFilter;

use crate::core::view;
use crate::core::view::View;
use crate::core::view::ViewEvent;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

use lazy_static::lazy_static;
use std::collections::HashMap;
// document meta data map
lazy_static! {
    static ref DOC_METADATA_MAP: Arc<RwLock<HashMap<document::Id, Arc<RwLock<Document<'static>>>>>> =
        Arc::new(RwLock::new(HashMap::new()));
}

struct LineNumberDocumentNodeMetaData {
    nl_count: u64,
    cr_count: u64,
    lf_count: u64,
}

impl LineNumberDocumentNodeMetaData {
    pub fn new() -> Self {
        dbg_println!("LineNumberMode");
        LineNumberDocumentNodeMetaData {
            nl_count: 0,
            cr_count: 0,
            lf_count: 0,
        }
    }
}

pub struct LineNumberMode {
    // add common fields
}

impl LineNumberMode {
    pub fn new() -> Self {
        dbg_println!("LineNumberMode");
        LineNumberMode {}
    }
}

pub struct LineNumberModeContext {
    // add per view fields
    target_vid: view::Id,
}

impl<'a> Mode for LineNumberMode {
    fn name(&self) -> &'static str {
        &"line-number-mode"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc line-number-mode ctx");
        let ctx = LineNumberModeContext { target_vid: 0 };
        Box::new(ctx)
    }

    fn configure_view(
        &self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        view: &mut View<'static>,
    ) {
        view.compose_screen_overlay_filters
            .borrow_mut()
            .push(Box::new(LineNumberOverlayFilter::new()));
    }

    fn on_view_event(
        &self,
        editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        src: ViewEventSource,
        dst: ViewEventDestination,
        _event: &ViewEvent,
    ) {
        dbg_println!("LINENUM on_view_event src: {:?} dst: {:?}", src, dst);

        let src = editor.view_map.get(&src.id).unwrap().write();
        let mut dst = editor.view_map.get(&dst.id).unwrap().write();
        let mut mode_ctx = dst.mode_ctx_mut::<LineNumberModeContext>("line-number-mode");
        mode_ctx.target_vid = src.id;

        let doc_id = src.document.as_ref().unwrap().read().id;
        let d = DOC_METADATA_MAP.as_ref().write().get_mut(&doc_id); //.unwrap();
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

pub struct LineNumberOverlayFilter {
    line_offsets: Vec<(u64, u64)>,
}

impl LineNumberOverlayFilter {
    pub fn new() -> Self {
        LineNumberOverlayFilter {
            line_offsets: vec![],
        }
    }
}

impl ScreenOverlayFilter<'_> for LineNumberOverlayFilter {
    fn name(&self) -> &'static str {
        &"LineNumberOverlay"
    }

    fn setup(&mut self, editor: &Editor, _env: &mut LayoutEnv, view: &Rc<RwLock<View>>) {
        let view = view.read();
        let mode_ctx = view.mode_ctx::<LineNumberModeContext>("line-number-mode");
        let target_vid = mode_ctx.target_vid;
        let src = editor.view_map.get(&target_vid).unwrap().read();
        self.line_offsets = src.screen.read().line_offset.clone();
    }

    fn run(&mut self, _view: &View, env: &mut LayoutEnv) -> () {
        dbg_println!("LINENUM RUN");
        env.screen.clear();
        for e in self.line_offsets.iter() {
            let s = format!("@{:>12}", e.0);
            for c in s.chars() {
                let mut cpi = CodepointInfo::new();
                cpi.displayed_cp = c;
                env.screen.push(cpi);
            }
            env.screen.select_next_line_index();
        }
    }

    fn finish(&mut self, view: &View, env: &mut LayoutEnv) -> () {
        dbg_println!("LINENUM FINISH");
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Rust wrappers around the raw JS apis

use libc::types::os::arch::c95::{size_t, c_uint};
use libc::c_char;
use std::ffi;
use std::rc;
use std::str;
use std::u32;
use jsapi::*;
use jsapi::JSVersion::JSVERSION_LATEST;
use jsval::{JSVal, NullValue};
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSOPTION_DONT_REPORT_UNCAUGHT;
use JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING;
use ERR;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

/// A wrapper for the `JSRuntime` and `JSContext` structures in SpiderMonkey.
pub struct Runtime {
    pub rt: rt,
    pub cx: rc::Rc<Cx>,
}

impl Runtime {
    /// Creates a new `JSRuntime` and `JSContext`.
    pub fn new() -> Runtime {
        let js_runtime = rt();
        assert!({
            let ptr: *mut JSRuntime = (*js_runtime).ptr;
            !ptr.is_null()
        });

        // Unconstrain the runtime's threshold on nominal heap size, to avoid
        // triggering GC too often if operating continuously near an arbitrary
        // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
        // still in effect to cause periodical, and we hope hygienic,
        // last-ditch GCs from within the GC's allocator.
        unsafe {
            JS_SetGCParameter(js_runtime.ptr, JSGC_MAX_BYTES, u32::MAX);
        }

        let js_context = unsafe {
            JS_NewContext(js_runtime.ptr, default_stacksize as size_t)
        };
        assert!(!js_context.is_null());

        let js_context = rc::Rc::new(Cx {
            ptr: js_context,
            rt: js_runtime.clone(),
        });
        js_context.set_default_options_and_version();
        js_context.set_logging_error_reporter();
        unsafe {
            JS_SetGCZeal((*js_context).ptr, 0, JS_DEFAULT_ZEAL_FREQ);
        }

        Runtime {
            rt: js_runtime,
            cx: js_context,
        }
    }

    /// Returns the `JSRuntime` object.
    pub fn rt(&self) -> *mut JSRuntime {
        self.rt.ptr
    }

    /// Returns the `JSContext` object.
    pub fn cx(&self) -> *mut JSContext {
        self.cx.ptr
    }
}

pub type rt = rc::Rc<rt_rsrc>;

pub struct rt_rsrc {
    pub ptr : *mut JSRuntime,
}

impl Drop for rt_rsrc {
    fn drop(&mut self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

pub fn new_runtime(p: *mut JSRuntime) -> rt {
    return rc::Rc::new(rt_rsrc {
        ptr: p
    })
}

pub fn rt() -> rt {
    unsafe {
        let runtime = JS_Init(default_heapsize);
        return new_runtime(runtime);
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    pub ptr: *mut JSContext,
    pub rt: rt,
}

impl Drop for Cx {
    fn drop(&mut self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

impl Cx {
    pub fn set_default_options_and_version(&self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE |
                         JSOPTION_DONT_REPORT_UNCAUGHT |
                         JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING);
        self.set_version(JSVERSION_LATEST);
    }

    pub fn set_options(&self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    pub fn set_version(&self, v: JSVersion) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    pub fn set_logging_error_reporter(&self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, Some(reportError as unsafe extern "C" fn(*mut JSContext, *const c_char, *mut JSErrorReport)));
        }
    }

    pub fn set_error_reporter(&self, reportfn: unsafe extern "C" fn(*mut JSContext, *const c_char, *mut JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, Some(reportfn));
        }
    }

    pub fn evaluate_script(&self, glob: *mut JSObject, script: String, filename: String, line_num: usize)
                    -> Result<(),()> {
        let script_utf16: Vec<u16> = script.utf16_units().collect();
        let filename_cstr = ffi::CString::new(filename.as_bytes()).unwrap();
        let mut rval: JSVal = NullValue();
        debug!("Evaluating script from {} with content {}", filename, script);
        // SpiderMonkey does not approve of null pointers.
        let (ptr, len) = if script_utf16.len() == 0 {
            static empty: &'static [u16] = &[];
            (empty.as_ptr(), 0)
        } else {
            (script_utf16.as_ptr(), script_utf16.len() as c_uint)
        };
        assert!(!ptr.is_null());
        unsafe {
            if ERR == JS_EvaluateUCScript(self.ptr, glob, ptr, len,
                                          filename_cstr.as_ptr(), line_num as c_uint,
                                          &mut rval) {
                debug!("...err!");
                Err(())
            } else {
                // we could return the script result but then we'd have
                // to root it and so forth and, really, who cares?
                debug!("...ok!");
                Ok(())
            }
        }
    }
}

pub unsafe extern fn reportError(_cx: *mut JSContext, msg: *const c_char, report: *mut JSErrorReport) {
    let fnptr = (*report).filename;
    let fname = if !fnptr.is_null() {
        let c_str = ffi::CStr::from_ptr(fnptr);
        str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string()
    } else {
        "none".to_string()
    };
    let lineno = (*report).lineno;
    let c_str = ffi::CStr::from_ptr(msg);
    let msg = str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string();
    error!("Error at {}:{}: {}\n", fname, lineno, msg);
}

pub fn with_compartment<R, F: FnMut() -> R>(cx: *mut JSContext, object: *mut JSObject, mut cb: F) -> R {
    unsafe {
        let call = JS_EnterCrossCompartmentCall(cx, object);
        let result = cb();
        JS_LeaveCrossCompartmentCall(call);
        result
    }
}

#[cfg(test)]
pub mod test {
    use super::Runtime;

    #[test]
    pub fn dummy() {
        let rt = Runtime::new();
        rt.cx.set_default_options_and_version();
        rt.cx.set_logging_error_reporter();
    }

}
<commit_msg>Inline JSRuntime creation into Runtime::new().<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Rust wrappers around the raw JS apis

use libc::types::os::arch::c95::{size_t, c_uint};
use libc::c_char;
use std::ffi;
use std::rc;
use std::str;
use std::u32;
use jsapi::*;
use jsapi::JSVersion::JSVERSION_LATEST;
use jsval::{JSVal, NullValue};
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSOPTION_DONT_REPORT_UNCAUGHT;
use JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING;
use ERR;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

/// A wrapper for the `JSRuntime` and `JSContext` structures in SpiderMonkey.
pub struct Runtime {
    pub rt: rt,
    pub cx: rc::Rc<Cx>,
}

impl Runtime {
    /// Creates a new `JSRuntime` and `JSContext`.
    pub fn new() -> Runtime {
        let js_runtime = unsafe { JS_Init(default_heapsize) };
        assert!(!js_runtime.is_null());

        let js_runtime = rc::Rc::new(rt_rsrc {
            ptr: js_runtime
        });

        // Unconstrain the runtime's threshold on nominal heap size, to avoid
        // triggering GC too often if operating continuously near an arbitrary
        // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
        // still in effect to cause periodical, and we hope hygienic,
        // last-ditch GCs from within the GC's allocator.
        unsafe {
            JS_SetGCParameter(js_runtime.ptr, JSGC_MAX_BYTES, u32::MAX);
        }

        let js_context = unsafe {
            JS_NewContext(js_runtime.ptr, default_stacksize as size_t)
        };
        assert!(!js_context.is_null());

        let js_context = rc::Rc::new(Cx {
            ptr: js_context,
            rt: js_runtime.clone(),
        });
        js_context.set_default_options_and_version();
        js_context.set_logging_error_reporter();
        unsafe {
            JS_SetGCZeal((*js_context).ptr, 0, JS_DEFAULT_ZEAL_FREQ);
        }

        Runtime {
            rt: js_runtime,
            cx: js_context,
        }
    }

    /// Returns the `JSRuntime` object.
    pub fn rt(&self) -> *mut JSRuntime {
        self.rt.ptr
    }

    /// Returns the `JSContext` object.
    pub fn cx(&self) -> *mut JSContext {
        self.cx.ptr
    }
}

pub type rt = rc::Rc<rt_rsrc>;

pub struct rt_rsrc {
    pub ptr : *mut JSRuntime,
}

impl Drop for rt_rsrc {
    fn drop(&mut self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    pub ptr: *mut JSContext,
    pub rt: rt,
}

impl Drop for Cx {
    fn drop(&mut self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

impl Cx {
    pub fn set_default_options_and_version(&self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE |
                         JSOPTION_DONT_REPORT_UNCAUGHT |
                         JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING);
        self.set_version(JSVERSION_LATEST);
    }

    pub fn set_options(&self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    pub fn set_version(&self, v: JSVersion) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    pub fn set_logging_error_reporter(&self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, Some(reportError as unsafe extern "C" fn(*mut JSContext, *const c_char, *mut JSErrorReport)));
        }
    }

    pub fn set_error_reporter(&self, reportfn: unsafe extern "C" fn(*mut JSContext, *const c_char, *mut JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, Some(reportfn));
        }
    }

    pub fn evaluate_script(&self, glob: *mut JSObject, script: String, filename: String, line_num: usize)
                    -> Result<(),()> {
        let script_utf16: Vec<u16> = script.utf16_units().collect();
        let filename_cstr = ffi::CString::new(filename.as_bytes()).unwrap();
        let mut rval: JSVal = NullValue();
        debug!("Evaluating script from {} with content {}", filename, script);
        // SpiderMonkey does not approve of null pointers.
        let (ptr, len) = if script_utf16.len() == 0 {
            static empty: &'static [u16] = &[];
            (empty.as_ptr(), 0)
        } else {
            (script_utf16.as_ptr(), script_utf16.len() as c_uint)
        };
        assert!(!ptr.is_null());
        unsafe {
            if ERR == JS_EvaluateUCScript(self.ptr, glob, ptr, len,
                                          filename_cstr.as_ptr(), line_num as c_uint,
                                          &mut rval) {
                debug!("...err!");
                Err(())
            } else {
                // we could return the script result but then we'd have
                // to root it and so forth and, really, who cares?
                debug!("...ok!");
                Ok(())
            }
        }
    }
}

pub unsafe extern fn reportError(_cx: *mut JSContext, msg: *const c_char, report: *mut JSErrorReport) {
    let fnptr = (*report).filename;
    let fname = if !fnptr.is_null() {
        let c_str = ffi::CStr::from_ptr(fnptr);
        str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string()
    } else {
        "none".to_string()
    };
    let lineno = (*report).lineno;
    let c_str = ffi::CStr::from_ptr(msg);
    let msg = str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string();
    error!("Error at {}:{}: {}\n", fname, lineno, msg);
}

pub fn with_compartment<R, F: FnMut() -> R>(cx: *mut JSContext, object: *mut JSObject, mut cb: F) -> R {
    unsafe {
        let call = JS_EnterCrossCompartmentCall(cx, object);
        let result = cb();
        JS_LeaveCrossCompartmentCall(call);
        result
    }
}

#[cfg(test)]
pub mod test {
    use super::Runtime;

    #[test]
    pub fn dummy() {
        let rt = Runtime::new();
        rt.cx.set_default_options_and_version();
        rt.cx.set_logging_error_reporter();
    }

}
<|endoftext|>


<commit_before>// hex encoder and decoder used by rust-protobuf unittests

#![allow(dead_code)]

use std::char;

fn decode_hex_digit(digit: char) -> u8 {
    match digit {
        '0' ... '9' => digit as u8 - '0' as u8,
        'a' ... 'f' => digit as u8 - 'a' as u8 + 10,
        'A' ... 'F' => digit as u8 - 'A' as u8 + 10,
        _ => panic!(),
    }
}

pub fn decode_hex(hex: &str) -> Vec<u8> {
    let mut r: Vec<u8> = Vec::new();
    let mut pos = 0;
    loop {
        while pos < hex.chars().count() && hex.to_ascii().get(pos).unwrap().as_byte() == b' ' {
            pos += 1;
        }
        if hex.chars().count() - pos >= 2 {
            r.push((decode_hex_digit(hex.char_at(pos)) << 4) | decode_hex_digit(hex.char_at(pos + 1)));
            pos += 2;
            continue;
        }
        if pos == hex.chars().count() {
            break;
        }
        panic!("pos = {}d", pos);
    }
    r
}

fn encode_hex_digit(digit: u8) -> char {
    match char::from_digit(digit as uint, 16) {
        Some(c) => c,
        _ => panic!()
    }
}

fn encode_hex_byte(byte: u8) -> [char, ..2] {
    [encode_hex_digit(byte >> 4), encode_hex_digit(byte & 0x0Fu8)]
}

pub fn encode_hex(bytes: &[u8]) -> String {
    let strs: Vec<String> = bytes.iter().map(|byte| {
        String::from_chars(encode_hex_byte(*byte).as_slice())
    }).collect();
    strs.connect(" ")
}

#[cfg(test)]
mod test {

    use super::decode_hex;
    use super::encode_hex;

    #[test]
    fn test_decode_hex() {
        assert_eq!([].to_vec(), decode_hex(""));
        assert_eq!([0x00u8].to_vec(), decode_hex("00"));
        assert_eq!([0xffu8].to_vec(), decode_hex("ff"));
        assert_eq!([0xabu8].to_vec(), decode_hex("AB"));
        assert_eq!([0xfau8, 0x19].to_vec(), decode_hex("fa 19"));
    }

    #[test]
    fn test_encode_hex() {
        assert_eq!("".to_string(), encode_hex(&[]));
        assert_eq!("00".to_string(), encode_hex(&[0x00]));
        assert_eq!("ab".to_string(), encode_hex(&[0xab]));
        assert_eq!("01 a2 1a fe".to_string(), encode_hex(&[0x01, 0xa2, 0x1a, 0xfe]));
    }
}
<commit_msg>Fixed compile error<commit_after>// hex encoder and decoder used by rust-protobuf unittests

#![allow(dead_code)]

use std::char;

fn decode_hex_digit(digit: char) -> u8 {
    match digit {
        '0' ... '9' => digit as u8 - '0' as u8,
        'a' ... 'f' => digit as u8 - 'a' as u8 + 10,
        'A' ... 'F' => digit as u8 - 'A' as u8 + 10,
        _ => panic!(),
    }
}

pub fn decode_hex(hex: &str) -> Vec<u8> {
    let mut r: Vec<u8> = Vec::new();
    let mut pos = 0;
    loop {
        while pos < hex.chars().count() && hex.char_at(pos) == ' ' {
            pos += 1;
        }

        if hex.chars().count() - pos >= 2 {
            r.push((decode_hex_digit(hex.char_at(pos)) << 4) | decode_hex_digit(hex.char_at(pos + 1)));
            pos += 2;
            continue;
        }
        if pos == hex.chars().count() {
            break;
        }
        panic!("pos = {}d", pos);
    }
    r
}

fn encode_hex_digit(digit: u8) -> char {
    match char::from_digit(digit as uint, 16) {
        Some(c) => c,
        _ => panic!()
    }
}

fn encode_hex_byte(byte: u8) -> [char, ..2] {
    [encode_hex_digit(byte >> 4), encode_hex_digit(byte & 0x0Fu8)]
}

pub fn encode_hex(bytes: &[u8]) -> String {
    let strs: Vec<String> = bytes.iter().map(|byte| {
        String::from_chars(encode_hex_byte(*byte).as_slice())
    }).collect();
    strs.connect(" ")
}

#[cfg(test)]
mod test {

    use super::decode_hex;
    use super::encode_hex;

    #[test]
    fn test_decode_hex() {
        assert_eq!([].to_vec(), decode_hex(""));
        assert_eq!([0x00u8].to_vec(), decode_hex("00"));
        assert_eq!([0xffu8].to_vec(), decode_hex("ff"));
        assert_eq!([0xabu8].to_vec(), decode_hex("AB"));
        assert_eq!([0xfau8, 0x19].to_vec(), decode_hex("fa 19"));
    }

    #[test]
    fn test_encode_hex() {
        assert_eq!("".to_string(), encode_hex(&[]));
        assert_eq!("00".to_string(), encode_hex(&[0x00]));
        assert_eq!("ab".to_string(), encode_hex(&[0xab]));
        assert_eq!("01 a2 1a fe".to_string(), encode_hex(&[0x01, 0xa2, 0x1a, 0xfe]));
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use serde_json::value::Value;

use priority::TaskPriority;

pub type DateTime= String; // FIXME
pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type UUID    = String; // FIXME
pub type Urgency = f64;

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: DateTime,
    modified: Option<DateTime>,
    priority: TaskPriority,
    project: Project,
    status: Status,
    tags: Vec<Tag>,
    uuid: UUID,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64,
                desc: String,
                entry: DateTime,
                modified: Option<DateTime>,
                priority: TaskPriority,
                project: Project,
                status: Status,
                tags: Vec<Tag>,
                uuid: UUID,
                urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Option<Task> {
        if !v.is_object() {
            return None;
        }

        let map = v.as_object().unwrap();

        let keys = [ "id", "description", "entry", "status", "uuid", "urgency"];

        if keys.iter().any(|x| map.contains_key(*x)) {
            return None
        }

        Some(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : get_entry(&map),
            modified : get_modified(&map),
            priority : TaskPriority::from(get_priority(&map)),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &DateTime {
        &self.entry
    }

    pub fn modified(&self) -> Option<&DateTime> {
        self.modified.as_ref()
    }

    pub fn priority(&self) -> TaskPriority {
        self.priority.clone()
    }

    pub fn project(&self) -> &Project {
        &self.project
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &UUID {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let entry    = Value::String(self.entry);
        let priority = Value::String(self.priority.into());
        let project  = Value::String(self.project);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(self.uuid);
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        map.insert(String::from("entry")    , entry);
        if self.modified.is_some() {
            map.insert(String::from("modified") , Value::String(self.modified.unwrap()));
        }
        map.insert(String::from("priority") , priority);
        map.insert(String::from("project")  , project);
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> String {
    map.get("entry").unwrap().as_string().map(String::from).unwrap()
}

fn get_modified(map: &BTreeMap<String, Value>) -> Option<String> {
    map.get("modified")
        .map(|m| m.as_string().map(String::from).unwrap())
}

fn get_priority(map: &BTreeMap<String, Value>) -> &str {
    map.get("priority").unwrap().as_string().unwrap()
}

fn get_project(map: &BTreeMap<String, Value>) -> String {
    map.get("project").unwrap().as_string().map(String::from).unwrap()
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> String {
    map.get("uuid").unwrap().as_string().map(String::from).unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

#[cfg(test)]
mod test {
    use core::reader::Reader;
    use core::reader::JsonObjectReader;
    use super::Task;
    use priority::TaskPriority;
    use std::borrow::Borrow;

    #[test]
    fn test_from_json() {
        let json = String::from("{\"id\":1,\"description\":\"desc\",\"entry\":\"20150612T164806Z\",\"modified\":\"20160315T215656Z\",\"priority\":\"L\",\"project\":\"someproj\",\"status\":\"pending\",\"tags\":[\"test\",\"task\"],\"uuid\":\"93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3\",\"urgency\":1.0}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        let t = Task::from_value(v);
        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(t.id(), 1);
        assert_eq!(t.desc().clone(), String::from("desc"));
        assert_eq!(t.entry().clone(), String::from("20150612T164806Z"));
        assert_eq!(t.modified().clone(), Some(String::from("20160315T215656Z")));
        assert_eq!(t.priority(), TaskPriority::Low);
        assert_eq!(t.project().clone(), String::from("someproj"));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["test", "task"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), String::from("93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3"));
        assert_eq!(t.urgency() as u64, 1.0 as u64);
    }

}
<commit_msg>Make task priority optional<commit_after>use std::collections::BTreeMap;

use serde_json::value::Value;

use priority::TaskPriority;

pub type DateTime= String; // FIXME
pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type UUID    = String; // FIXME
pub type Urgency = f64;

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: DateTime,
    modified: Option<DateTime>,
    priority: Option<TaskPriority>,
    project: Project,
    status: Status,
    tags: Vec<Tag>,
    uuid: UUID,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64,
                desc: String,
                entry: DateTime,
                modified: Option<DateTime>,
                priority: Option<TaskPriority>,
                project: Project,
                status: Status,
                tags: Vec<Tag>,
                uuid: UUID,
                urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Option<Task> {
        if !v.is_object() {
            return None;
        }

        let map = v.as_object().unwrap();

        let keys = [ "id", "description", "entry", "status", "uuid", "urgency"];

        if keys.iter().any(|x| map.contains_key(*x)) {
            return None
        }

        Some(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : get_entry(&map),
            modified : get_modified(&map),
            priority : get_priority(&map),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &DateTime {
        &self.entry
    }

    pub fn modified(&self) -> Option<&DateTime> {
        self.modified.as_ref()
    }

    pub fn priority(&self) -> Option<&TaskPriority> {
        self.priority.as_ref()
    }

    pub fn project(&self) -> &Project {
        &self.project
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &UUID {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let entry    = Value::String(self.entry);
        let project  = Value::String(self.project);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(self.uuid);
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        map.insert(String::from("entry")    , entry);
        if self.modified.is_some() {
            map.insert(String::from("modified") , Value::String(self.modified.unwrap()));
        }
        if self.priority.is_some() {
            map.insert(String::from("priority") , Value::String(self.priority.unwrap().into()));
        }
        map.insert(String::from("project")  , project);
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> String {
    map.get("entry").unwrap().as_string().map(String::from).unwrap()
}

fn get_modified(map: &BTreeMap<String, Value>) -> Option<String> {
    map.get("modified")
        .map(|m| m.as_string().map(String::from).unwrap())
}

fn get_priority(map: &BTreeMap<String, Value>) -> Option<TaskPriority> {
    map.get("priority")
        .map(|m| m.as_string().map(String::from).unwrap())
        .map(|s| TaskPriority::from(&s[..]))
}

fn get_project(map: &BTreeMap<String, Value>) -> String {
    map.get("project").unwrap().as_string().map(String::from).unwrap()
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> String {
    map.get("uuid").unwrap().as_string().map(String::from).unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

#[cfg(test)]
mod test {
    use core::reader::Reader;
    use core::reader::JsonObjectReader;
    use super::Task;
    use priority::TaskPriority;
    use std::borrow::Borrow;

    #[test]
    fn test_from_json() {
        let json = String::from("{\"id\":1,\"description\":\"desc\",\"entry\":\"20150612T164806Z\",\"modified\":\"20160315T215656Z\",\"priority\":\"L\",\"project\":\"someproj\",\"status\":\"pending\",\"tags\":[\"test\",\"task\"],\"uuid\":\"93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3\",\"urgency\":1.0}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        let t = Task::from_value(v);
        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(t.id(), 1);
        assert_eq!(t.desc().clone(), String::from("desc"));
        assert_eq!(t.entry().clone(), String::from("20150612T164806Z"));
        assert_eq!(t.modified().clone(), Some(String::from("20160315T215656Z")));
        assert_eq!(t.priority(), TaskPriority::Low);
        assert_eq!(t.project().clone(), String::from("someproj"));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["test", "task"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), String::from("93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3"));
        assert_eq!(t.urgency() as u64, 1.0 as u64);
    }

}
<|endoftext|>


<commit_before>//!
//! # Tools
//!

use std::result::Result;
use std::error::Error;
use serde::*;
use serde_json::*;

///
/// Trait implemented by things that represent a tool
///
/// A tool is simply a routine that takes some input, does some processing
/// and returns a value or an error. The main difference between a tool and
/// a simple function is that a tool's input and output must be simple data,
/// which for Rust we define as 'can be serialized to JSON'.
///
/// Tools also have the requirement that they are encapsulated and can instantiate
/// themselves with no dependencies other than those they can discover from
/// their environment.
///
/// These two requirements mean that tools can be invoked simply by specifying
/// the input data (without necessarily having to know the exact Rust type involved!).
/// Test cases for tools can be specified as simple JSON data with no need for any
/// actual code. Tools can be turned into stand-alone command line programs or
/// web endpoints with no modification.
///
pub trait Tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    ///
    /// Invokes this tool with an input data structure and returns its result
    ///
    fn invoke(&self, input: TIn) -> Result<TOut, TErr>;
}

///
/// Allows a tool to be invoked using JSON input and output
///
/// Tools in Rust are normally implemented using strong types for convenience's
/// sake, but an important capability is that their input and output can be
/// easily serialized, in particular to JSON which is currently a particularly
/// convenient format for interopability.
///
pub trait ToolJson {
    ///
    /// Invokes this tool with its input and output specified using JSON
    ///
    fn invoke_json(&self, input: Value) -> Result<Value, Value>;
}

impl<TIn, TOut, TErr> ToolJson for Tool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = self.invoke(input_decoded);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}<commit_msg>Fix text<commit_after>//!
//! # Tools
//!

use std::result::Result;
use std::error::Error;
use serde::*;
use serde_json::*;

///
/// Trait implemented by things that represent a tool
///
/// A tool is simply a routine that takes some input, does some processing
/// and returns a value or an error. The main difference between a tool and
/// a simple function is that a tool's input and output must be simple data,
/// which for Rust we define as 'can be serialized to JSON'.
///
/// Tools also have the requirement that they are encapsulated and can instantiate
/// themselves with no dependencies other than those they can discover from
/// their environment.
///
/// These two requirements mean that tools can be invoked simply by specifying
/// the input data (without necessarily having to know the exact Rust type involved!).
/// Test cases for tools can be specified as simple JSON data with no need for any
/// actual code. Tools can be turned into stand-alone command line programs or
/// web endpoints with no modification.
///
pub trait Tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    ///
    /// Invokes this tool with an input data structure and returns its result
    ///
    fn invoke(&self, input: TIn) -> Result<TOut, TErr>;
}

///
/// Allows a tool to be invoked using JSON input and output
///
/// Tools in Rust are normally implemented using strong types for convenience's
/// sake, but an important capability is that their input and output can be
/// easily serialized, in particular to JSON which is currently a particularly
/// convenient format for interopability.
///
pub trait ToolJson {
    ///
    /// Invokes this tool with its input and output specified using JSON
    ///
    fn invoke_json(&self, input: Value) -> Result<Value, Value>;
}

impl<TIn, TOut, TErr> ToolJson for Tool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = self.invoke(input_decoded);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}
<|endoftext|>


<commit_before>//! A module process address spaces

use super::super::super::interrupts::{on, off};
use super::super::super::machine::{invlpg, vmm_on};
use super::super::super::process::{CURRENT_PROCESS, State};
use super::super::super::process::proc_table::PROCESS_TABLE;
use super::super::super::sync::{Barrier, Event, StaticSemaphore};
use super::super::physmem::Frame;
use super::{SHARED_PDES, NUM_SHARED, PD_ADDRESS, KMAP_ADDRESS, USER_ADDRESS, VMM_ON};
use super::structs::{VMTable, PagingEntry};

/// The address space of a single process
pub struct AddressSpace {
    /// paddr of this addr space's PD
    page_dir: usize,

    /// the index of the first un-kmapped page
    kmap_index: u8,

    /// a lock for the address space
    ///
    /// NOTE: to prevent deadlocks, *ALWAYS* acquire
    /// this lock before locks on frames.
    lock: StaticSemaphore,

    /// The PID of the process the current share request is going to
    req_pid: usize,

    /// The frame the current share request is for
    req_paddr: usize,

    /// A barrier for page-sharing
    req_bar: Barrier,

    /// A waiting list for other processes who are expecting this
    /// process to share with them
    req_wait: Event,
}

impl AddressSpace {
    /// Create a new address space and set up the PD.
    pub fn new() -> AddressSpace {
        // allocate a frame
        let (pd, pd_paddr) = VMTable::new();

        // copy the first entries of the pd to direct map
        unsafe {
            let mut i = 0;
            for pde in &SHARED_PDES {
                pd[i] = pde.clone();
                i += 1;
            }
        }

        // point the third PDE at the PD to map all page tables
        // so PD is at vaddr PD_ADDRESS
        unsafe {
            pd[NUM_SHARED].set_present(true); // present
            pd[NUM_SHARED].set_read_write(true); // read/write
            pd[NUM_SHARED].set_privelege_level(false); // kernel only
            pd[NUM_SHARED].set_caching(true); // write-through
            pd[NUM_SHARED].set_address(pd_paddr); // PD paddr
        }

        let a = AddressSpace {
            page_dir: pd_paddr,
            kmap_index: 0,
            lock: StaticSemaphore::new(1),
            req_pid: 0,
            req_paddr: 0,
            req_bar: Barrier::new(2),
            req_wait: Event::new(),
        };

        a
    }

    /// Map `virt` to `phys` in the address space. The method first
    /// tries to acquire the address space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space
    /// because it assumes that the PD is at PD_ADDRESS
    pub fn map(&mut self, phys: usize, virt: usize, lock: bool) {
        //unsafe {
        //    printf!("{:?} [map {:x} -> {:x}]\n", *CURRENT_PROCESS, virt, phys);
        //}

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        if lock {
            self.lock.down();
        }

        let mut pd = unsafe {&mut *PD_ADDRESS};
        let pde = &mut pd[pde_index];

        // invalidate TLB entry
        unsafe { invlpg(virt) };

        // if pd entry is not already there
        if !pde.is_flag(0) { // present bit
            // no pd entry yet => create one

            off();

            // set pde
            pde.set_read_write(true); // read/write
            pde.set_privelege_level(false); // kernel only
            pde.set_caching(false); // write-back
            pde.set_address(Frame::alloc()); // alloc a new frame
            pde.set_present(true); // present

            // clear the pt
            let mut pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};
            for p in 0..1024 {
                pt[p] = PagingEntry::new();
                unsafe { invlpg((pde_index << 22) | (p << 12)) };
            }

            on();
        }

        // follow pde to get pt
        let mut pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};
        let pte = &mut pt[pte_index];

        // if pt entry is not already there
        if !pte.is_flag(0) { // present bit
            // no pt entry yet -> create one

            off();

            // set pte
            pte.set_read_write(true); // read/write
            pte.set_privelege_level(false); // kernel only
            pte.set_caching(false); // write-back
            pte.set_address(phys); // point to frame
            pte.set_present(true); // present

            on();
        }

        if lock {
            self.lock.up();
        }
    }

    /// Map the given `paddr` for temporary use by the kernel and return a mut reference to the
    /// frame.
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn kmap(&mut self, paddr: usize) -> &mut Frame {
        // get next unmapped address
        let next = self.kmap_index;

        // increment
        if next == 255 {
            // unmap all if we have run out
            for i in 0..256 {
                self.unmap(unsafe { KMAP_ADDRESS } + i * 0x1000, true);
            }
            self.kmap_index = 0;
        } else {
            self.kmap_index += 1;
        }

        // get associated address
        let vaddr = unsafe { KMAP_ADDRESS } + (next as usize) * 0x1000;

        // map the frame
        self.map(paddr, vaddr, true);

        unsafe { &mut *(vaddr as *mut Frame) }
    }

    /// Remove any mapping for this virtual address. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn unmap(&mut self, virt: usize, lock: bool) {
        //unsafe {
        //    printf!("{:?} [unmap {:x}]\n", *CURRENT_PROCESS, virt);
        //}

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe {&mut *PD_ADDRESS};

        if lock {
            self.lock.down();
        }

        if pd[pde_index].is_flag(0) { // present bit
            off();

            let mut pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};

            // unmap and deallocate frame
            pt[pte_index].free(virt >= unsafe { USER_ADDRESS });

            // invalidate TLB entry
            unsafe { invlpg(virt) };

            on();

            // if page table is now empty,
            // unmap and deallocate it
            if (0..1024).all(|i| !pt[i].is_flag(0)) {
                pd[pde_index].free(virt >= unsafe { KMAP_ADDRESS });
            }
        }

        if lock {
            self.lock.up();
        }

        //unsafe {
        //    printf!("{:?} [unmaped {:x}]\n", *CURRENT_PROCESS, virt);
        //}
    }

    /// Returns the current physical address mapped to the given virtual address
    /// or None if it is not mapped. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn v_to_p(&mut self, virt: usize, lock: bool) -> Option<usize> {
        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe {&mut *PD_ADDRESS};

        if lock {
            self.lock.down();
        }

        let ret = if pd[pde_index].is_flag(0) { // present bit
            let pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};
            let pte = &pt[pte_index];

            if pte.is_flag(0) {
                Some(pt[pte_index].get_address())
            } else {
                None
            }
        } else {
            None
        };

        if lock {
            self.lock.up();
        }

        ret
    }

    /// Activate the current address space and turn on VM if needed
    pub fn activate(&mut self) {
        off();
        unsafe {
            vmm_on(self.page_dir);
            VMM_ON = true;
        }
        on();
    }

    /// Send a page-share request to the process with PID `pid` for
    /// the frame mapped to `vaddr` in this address space.
    ///
    /// Returns true if the request succeeded, and false if there was
    /// an error (e.g. the process has already died).
    ///
    /// Blocks until the request is accepted.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn request_share(&mut self, pid: usize, vaddr: usize) -> bool {

        // mark the frame and create the request
        self.lock.down();

        unsafe {
            self.req_pid = if let Some(p) = PROCESS_TABLE.get(pid) {
                if (*p).get_state() == State::TERMINATED {
                    return false;
                } else {
                    pid
                }
            } else {
                return false;
            };
        }
        self.req_paddr = if let Some(paddr) = self.v_to_p(vaddr, false) {
            paddr
        } else {
            return false;
        };

        Frame::share( unsafe {(*CURRENT_PROCESS).get_pid()}, vaddr, self.req_paddr);

        self.req_wait.notify();

        self.req_wait.reset();

        self.lock.up();

        self.req_bar.reach();

        true
    }

    /// Creates a mapping in this process's address space for the
    /// first page-share request from the process with the given PID
    /// to the given page.
    ///
    /// Returns true if success; false otherwise.
    ///
    /// Blocks until the request is completed.
    ///
    /// NOTE: This should run inside this processes address space
    pub fn accept_share(&mut self, pid: usize, vaddr: usize) -> bool {

        let other = unsafe {if let Some(p) = PROCESS_TABLE.get(pid) {
            p
        } else {
            return false;
        }};
        let addr_space = unsafe { &mut (*other).addr_space };
        let my_pid = unsafe { (*CURRENT_PROCESS).get_pid() };

        // wait until the other process creates a req for this pid
        loop {
            addr_space.lock.down();

            if addr_space.req_pid == my_pid {
                break;
            } else {
                off();
                addr_space.lock.up();
                addr_space.req_wait.wait();
                on();
            }
        }

        self.lock.down();

        // create a mapping
        Frame::share(my_pid, vaddr, addr_space.req_paddr);
        self.map(addr_space.req_paddr, vaddr, false);

        self.lock.up();

        addr_space.req_pid = 0;
        addr_space.req_paddr = 0;

        addr_space.lock.up();

        addr_space.req_bar.reach();

        true
    }

    /// Remove all non-kernel mappings in this address space.
    /// NOTE: must run while this address space is active
    pub fn clear(&mut self) {
        let pd = unsafe {&mut *PD_ADDRESS};

        self.lock.down();

        // for each present PDE, remove all
        // mappings associated with it
        for pde_index in (unsafe { NUM_SHARED } + 1)..1024 {
            if pd[pde_index].is_flag(0) { // present bit
                for pte_index in 0..1024 {
                    self.unmap((pde_index << 22) | (pte_index << 12), false);
                }
            }
        }

        self.lock.up();
    }
}

impl Drop for AddressSpace {
    /// Deallocate the page directory.
    /// NOTE: cannot run while the address space is active
    fn drop(&mut self) {
        // free the page directory
        Frame::free(self.page_dir >> 12);
    }
}

/// The Rust-side code of the page fault handler.
#[no_mangle]
pub unsafe extern "C" fn vmm_page_fault(/*context: *mut KContext,*/ fault_addr: usize) {
    // segfault! should be very rare with rust
    // first 13MiB are reserved by kernel
    if fault_addr < 0xD00000 {
        // TODO: only kill that process
        panic!("{:?} [segmentation violation @ 0x{:X}]",
               *CURRENT_PROCESS, fault_addr);
    }

    //printf!("page fault {:X}\n", fault_addr);

    if !CURRENT_PROCESS.is_null() {
        (*CURRENT_PROCESS).addr_space.map(Frame::alloc(), fault_addr, true);
    } else {
        panic!("Page fault @ 0x{:X} with no current process", fault_addr);
    }

    // memclr an alloced frame
    let page = &mut *(fault_addr as *mut Frame);
    for i in 0..1024 {
        page[i] = 0;
    }

    //printf!("page fault done {:X}\n", fault_addr);
}
<commit_msg>bug fix in VM<commit_after>//! A module process address spaces

use super::super::super::interrupts::{on, off};
use super::super::super::machine::{invlpg, vmm_on};
use super::super::super::process::{CURRENT_PROCESS, State};
use super::super::super::process::proc_table::PROCESS_TABLE;
use super::super::super::sync::{Barrier, Event, StaticSemaphore};
use super::super::physmem::Frame;
use super::{SHARED_PDES, NUM_SHARED, PD_ADDRESS, KMAP_ADDRESS, USER_ADDRESS, VMM_ON};
use super::structs::{VMTable, PagingEntry};

/// The address space of a single process
pub struct AddressSpace {
    /// paddr of this addr space's PD
    page_dir: usize,

    /// the index of the first un-kmapped page
    kmap_index: u8,

    /// a lock for the address space
    ///
    /// NOTE: to prevent deadlocks, *ALWAYS* acquire
    /// this lock before locks on frames.
    lock: StaticSemaphore,

    /// The PID of the process the current share request is going to
    req_pid: usize,

    /// The frame the current share request is for
    req_paddr: usize,

    /// A barrier for page-sharing
    req_bar: Barrier,

    /// A waiting list for other processes who are expecting this
    /// process to share with them
    req_wait: Event,
}

impl AddressSpace {
    /// Create a new address space and set up the PD.
    pub fn new() -> AddressSpace {
        // allocate a frame
        let (pd, pd_paddr) = VMTable::new();

        // copy the first entries of the pd to direct map
        unsafe {
            let mut i = 0;
            for pde in &SHARED_PDES {
                pd[i] = pde.clone();
                i += 1;
            }
        }

        // point the third PDE at the PD to map all page tables
        // so PD is at vaddr PD_ADDRESS
        unsafe {
            pd[NUM_SHARED].set_present(true); // present
            pd[NUM_SHARED].set_read_write(true); // read/write
            pd[NUM_SHARED].set_privelege_level(false); // kernel only
            pd[NUM_SHARED].set_caching(true); // write-through
            pd[NUM_SHARED].set_address(pd_paddr); // PD paddr
        }

        let a = AddressSpace {
            page_dir: pd_paddr,
            kmap_index: 0,
            lock: StaticSemaphore::new(1),
            req_pid: 0,
            req_paddr: 0,
            req_bar: Barrier::new(2),
            req_wait: Event::new(),
        };

        a
    }

    /// Map `virt` to `phys` in the address space. The method first
    /// tries to acquire the address space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space
    /// because it assumes that the PD is at PD_ADDRESS
    pub fn map(&mut self, phys: usize, virt: usize, lock: bool) {
        //unsafe {
        //    printf!("{:?} [map {:x} -> {:x}]\n", *CURRENT_PROCESS, virt, phys);
        //}

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        if lock {
            self.lock.down();
        }

        let mut pd = unsafe {&mut *PD_ADDRESS};
        let pde = &mut pd[pde_index];

        // invalidate TLB entry
        unsafe { invlpg(virt) };

        // if pd entry is not already there
        if !pde.is_flag(0) { // present bit
            // no pd entry yet => create one

            off();

            // set pde
            pde.set_read_write(true); // read/write
            pde.set_privelege_level(false); // kernel only
            pde.set_caching(false); // write-back
            pde.set_address(Frame::alloc()); // alloc a new frame
            pde.set_present(true); // present

            // clear the pt
            let mut pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};
            for p in 0..1024 {
                pt[p] = PagingEntry::new();
                unsafe { invlpg((pde_index << 22) | (p << 12)) };
            }

            on();
        }

        // follow pde to get pt
        let mut pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};
        let pte = &mut pt[pte_index];

        // if pt entry is not already there
        if !pte.is_flag(0) { // present bit
            // no pt entry yet -> create one

            off();

            // set pte
            pte.set_read_write(true); // read/write
            pte.set_privelege_level(false); // kernel only
            pte.set_caching(false); // write-back
            pte.set_address(phys); // point to frame
            pte.set_present(true); // present

            on();
        }

        if lock {
            self.lock.up();
        }
    }

    /// Map the given `paddr` for temporary use by the kernel and return a mut reference to the
    /// frame.
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn kmap(&mut self, paddr: usize) -> &mut Frame {
        // get next unmapped address
        let next = self.kmap_index;

        // increment
        if next == 255 {
            // unmap all if we have run out
            for i in 0..256 {
                self.unmap(unsafe { KMAP_ADDRESS } + i * 0x1000, true);
            }
            self.kmap_index = 0;
        } else {
            self.kmap_index += 1;
        }

        // get associated address
        let vaddr = unsafe { KMAP_ADDRESS } + (next as usize) * 0x1000;

        // map the frame
        self.map(paddr, vaddr, true);

        unsafe { &mut *(vaddr as *mut Frame) }
    }

    /// Remove any mapping for this virtual address. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: should only be called on the current address space because it assumes that the PD is
    /// at PD_ADDRESS
    pub fn unmap(&mut self, virt: usize, lock: bool) {
        //unsafe {
        //    printf!("{:?} [unmap {:x}]\n", *CURRENT_PROCESS, virt);
        //}

        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe {&mut *PD_ADDRESS};

        if lock {
            self.lock.down();
        }

        if pd[pde_index].is_flag(0) { // present bit
            off();

            let mut pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};

            // unmap and deallocate frame
            pt[pte_index].free(virt >= unsafe { USER_ADDRESS });

            // invalidate TLB entry
            unsafe { invlpg(virt) };

            on();

            // if page table is now empty,
            // unmap and deallocate it
            if (0..1024).all(|i| !pt[i].is_flag(0)) {
                pd[pde_index].free(virt >= unsafe { KMAP_ADDRESS });
            }
        }

        if lock {
            self.lock.up();
        }

        //unsafe {
        //    printf!("{:?} [unmaped {:x}]\n", *CURRENT_PROCESS, virt);
        //}
    }

    /// Returns the current physical address mapped to the given virtual address
    /// or None if it is not mapped. The method first tries to acquire the address
    /// space lock if `lock` is true.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn v_to_p(&mut self, virt: usize, lock: bool) -> Option<usize> {
        let pde_index = virt >> 22;
        let pte_index = (virt & 0x003F_F000) >> 12;

        let pd = unsafe {&mut *PD_ADDRESS};

        if lock {
            self.lock.down();
        }

        let ret = if pd[pde_index].is_flag(0) { // present bit
            let pt = unsafe {&mut *(((NUM_SHARED<<22) | (pde_index<<12)) as *mut VMTable)};
            let pte = &pt[pte_index];

            if pte.is_flag(0) {
                Some(pt[pte_index].get_address())
            } else {
                None
            }
        } else {
            None
        };

        if lock {
            self.lock.up();
        }

        ret
    }

    /// Activate the current address space and turn on VM if needed
    pub fn activate(&mut self) {
        off();
        unsafe {
            vmm_on(self.page_dir);
            VMM_ON = true;
        }
        on();
    }

    /// Send a page-share request to the process with PID `pid` for
    /// the frame mapped to `vaddr` in this address space.
    ///
    /// Returns true if the request succeeded, and false if there was
    /// an error (e.g. the process has already died).
    ///
    /// Blocks until the request is accepted.
    ///
    /// NOTE: this has to run while the addresss space is active.
    pub fn request_share(&mut self, pid: usize, vaddr: usize) -> bool {

        // mark the frame and create the request
        self.lock.down();

        unsafe {
            self.req_pid = if let Some(p) = PROCESS_TABLE.get(pid) {
                if (*p).get_state() == State::TERMINATED {
                    return false;
                } else {
                    pid
                }
            } else {
                return false;
            };
        }
        self.req_paddr = if let Some(paddr) = self.v_to_p(vaddr, false) {
            paddr
        } else {
            return false;
        };

        Frame::share( unsafe {(*CURRENT_PROCESS).get_pid()}, vaddr, self.req_paddr);

        self.req_wait.notify();

        self.req_wait.reset();

        self.lock.up();

        self.req_bar.reach();

        true
    }

    /// Creates a mapping in this process's address space for the
    /// first page-share request from the process with the given PID
    /// to the given page.
    ///
    /// Returns true if success; false otherwise.
    ///
    /// Blocks until the request is completed.
    ///
    /// NOTE: This should run inside this processes address space
    pub fn accept_share(&mut self, pid: usize, vaddr: usize) -> bool {

        let other = unsafe {if let Some(p) = PROCESS_TABLE.get(pid) {
            p
        } else {
            return false;
        }};
        let addr_space = unsafe { &mut (*other).addr_space };
        let my_pid = unsafe { (*CURRENT_PROCESS).get_pid() };

        // wait until the other process creates a req for this pid
        loop {
            addr_space.lock.down();

            if addr_space.req_pid == my_pid {
                break;
            } else {
                off();
                addr_space.lock.up();
                addr_space.req_wait.wait();
                on();
            }
        }

        self.lock.down();

        // create a mapping
        Frame::share(my_pid, vaddr, addr_space.req_paddr);
        self.map(addr_space.req_paddr, vaddr, false);

        self.lock.up();

        addr_space.req_pid = 0;
        addr_space.req_paddr = 0;

        addr_space.lock.up();

        addr_space.req_bar.reach();

        true
    }

    /// Remove all non-kernel mappings in this address space.
    /// NOTE: must run while this address space is active
    pub fn clear(&mut self) {
        let pd = unsafe {&mut *PD_ADDRESS};

        self.lock.down();

        // for each present PDE, remove all
        // mappings associated with it
        for pde_index in (unsafe { NUM_SHARED } + 1)..1024 {
            if pd[pde_index].is_flag(0) { // present bit
                for pte_index in 0..1024 {
                    self.unmap((pde_index << 22) | (pte_index << 12), false);
                }
            }
        }

        self.lock.up();
    }
}

impl Drop for AddressSpace {
    /// Deallocate the page directory.
    /// NOTE: cannot run while the address space is active
    fn drop(&mut self) {
        // free the page directory
        Frame::free(self.page_dir >> 12);
    }
}

/// The Rust-side code of the page fault handler.
#[no_mangle]
pub unsafe extern "C" fn vmm_page_fault(/*context: *mut KContext,*/ fault_addr: usize) {
    // segfault! should be very rare with rust
    // first 13MiB are reserved by kernel
    if fault_addr < 0xD00000 {
        // TODO: only kill that process
        panic!("{:?} [segmentation violation @ 0x{:X}]",
               *CURRENT_PROCESS, fault_addr);
    }

    //printf!("page fault {:X}\n", fault_addr);

    if !CURRENT_PROCESS.is_null() {
        (*CURRENT_PROCESS).addr_space.map(Frame::alloc(), fault_addr, true);
    } else {
        panic!("Page fault @ 0x{:X} with no current process", fault_addr);
    }

    // memclr an alloced frame
    let page = &mut *((fault_addr & 0xFFFF_F000) as *mut Frame);
    for i in 0..1024 {
        page[i] = 0;
    }

    //printf!("page fault done {:X}\n", fault_addr);
}
<|endoftext|>