<commit_before>#![allow(unused_variables)]

extern crate orbclient;
extern crate sinulation;

#[cfg(target_os = "redox")]
use sinulation::Trig;

use super::vid;

// Had to store this somewhere.
/*cargo build --example tetrahedrane_example --target i386-unknown-redox.json -- -C no-prepopulate-passes -C no-stack-check -C opt-level=2 -Z no-landing-pads -A dead_code
*/

/// Stores data about rendering, window and camera.
pub struct Window {
    pub screen_x: u32,
    pub screen_y: u32,

    pub camera_x: f32,
    pub camera_y: f32,
    pub camera_z: f32,

    pub camera_x_y: f32,
    pub camera_x_z: f32,
    pub camera_y_z: f32,

    pub window: Box<orbclient::window::Window>,

    pub render_queue: Vec<vid::Triangle>,
}

impl Window {
    /// Create a new window.
    ///
    /// * `triangle_space` - how much space to preallocate for the triangles
    pub fn new(screen_x: u32, screen_y: u32, window_name: &str, triangle_space: usize) -> Window {
        let win = orbclient::window::Window::new_flags(10, 10, screen_x, screen_y, window_name, true).unwrap();
        Window {
            screen_x: screen_x,
            screen_y: screen_y,

            camera_x: 0.0,
            camera_y: 0.0,
            camera_z: 0.0,

            camera_x_y: 0.0,
            camera_x_z: 0.0,
            camera_y_z: 0.0,

            window: win,

            render_queue: Vec::with_capacity(triangle_space),
        }
    }

    /// Renders triangles onto the framebuffer.
    pub fn render(&mut self) {
        for mut triangle in &mut self.render_queue {
            let flat_1 = triangle.p1.flat_point(self.screen_x, self.screen_y,
                                                triangle.x + self.camera_x,
                                                triangle.y + self.camera_y,
                                                triangle.z + self.camera_z);
            let flat_2 = triangle.p2.flat_point(self.screen_x, self.screen_y,
                                                triangle.x + self.camera_x,
                                                triangle.y + self.camera_y,
                                                triangle.z + self.camera_z);
            let flat_3 = triangle.p3.flat_point(self.screen_x, self.screen_y,
                                                triangle.x + self.camera_x,
                                                triangle.y + self.camera_y,
                                                triangle.z + self.camera_z);

            self.window.line(flat_1.x, flat_1.y, flat_2.x, flat_2.y, triangle.color.orb_color());
            self.window.line(flat_3.x, flat_3.y, flat_2.x, flat_2.y, triangle.color.orb_color());
            self.window.line(flat_1.x, flat_1.y, flat_3.x, flat_3.y, triangle.color.orb_color());
        }

        let used_space = self.render_queue.len();

        self.render_queue = Vec::with_capacity(used_space);
    }

    /// Push a triangle onto the render queue.
    pub fn push(&mut self, triangle: vid::Triangle) {
        self.render_queue.push(triangle);
    }

    /// Push a group of triangles onto the render queue.
    pub fn push_group(&mut self, group: &vid::TriangleGroup) {
        for triangle in &group.triangles {
            self.push(triangle.clone());
        }
    }

    pub fn normalize_camera(&mut self) {
        #[cfg(not(target_os = "redox"))]
        use std::f32::consts::PI;

        #[cfg(target_os = "redox")]
        const PI: f32 = 3.141592653589793;

        if self.camera_x_z > (PI * 2.0) {
            self.camera_x_z -= (PI * 2.0);
        }

        if self.camera_x_y > (PI * 2.0) {
            self.camera_x_y -= (PI * 2.0);
        }

        if self.camera_y_z > (PI * 2.0) {
            self.camera_y_z -= (PI * 2.0);
        }
    }
}
<commit_msg>Added some docstrings<commit_after>#![allow(unused_variables)]

extern crate orbclient;
extern crate sinulation;

#[cfg(target_os = "redox")]
use sinulation::Trig;

use super::vid;

// Had to store this somewhere.
/*cargo build --example tetrahedrane_example --target i386-unknown-redox.json -- -C no-prepopulate-passes -C no-stack-check -C opt-level=2 -Z no-landing-pads -A dead_code
*/

/// Stores data about rendering, window and camera.
pub struct Window {
    pub screen_x: u32,
    pub screen_y: u32,

    pub camera_x: f32,
    pub camera_y: f32,
    pub camera_z: f32,

    pub camera_x_y: f32,
    pub camera_x_z: f32,
    pub camera_y_z: f32,

    pub window: Box<orbclient::window::Window>,

    pub render_queue: Vec<vid::Triangle>,
}

impl Window {
    /// Create a new window.
    ///
    /// * `triangle_space` - how much space to preallocate for the triangles
    pub fn new(screen_x: u32, screen_y: u32, window_name: &str, triangle_space: usize) -> Window {
        let win = orbclient::window::Window::new_flags(10, 10, screen_x, screen_y, window_name, true).unwrap();
        Window {
            screen_x: screen_x,
            screen_y: screen_y,

            camera_x: 0.0,
            camera_y: 0.0,
            camera_z: 0.0,

            camera_x_y: 0.0,
            camera_x_z: 0.0,
            camera_y_z: 0.0,

            window: win,

            render_queue: Vec::with_capacity(triangle_space),
        }
    }

    /// Renders triangles onto the framebuffer.
    pub fn render(&mut self) {
        for mut triangle in &mut self.render_queue {
            let flat_1 = triangle.p1.flat_point(self.screen_x, self.screen_y,
                                                triangle.x + self.camera_x,
                                                triangle.y + self.camera_y,
                                                triangle.z + self.camera_z);
            let flat_2 = triangle.p2.flat_point(self.screen_x, self.screen_y,
                                                triangle.x + self.camera_x,
                                                triangle.y + self.camera_y,
                                                triangle.z + self.camera_z);
            let flat_3 = triangle.p3.flat_point(self.screen_x, self.screen_y,
                                                triangle.x + self.camera_x,
                                                triangle.y + self.camera_y,
                                                triangle.z + self.camera_z);

            self.window.line(flat_1.x, flat_1.y, flat_2.x, flat_2.y, triangle.color.orb_color());
            self.window.line(flat_3.x, flat_3.y, flat_2.x, flat_2.y, triangle.color.orb_color());
            self.window.line(flat_1.x, flat_1.y, flat_3.x, flat_3.y, triangle.color.orb_color());
        }

        let used_space = self.render_queue.len();

        self.render_queue = Vec::with_capacity(used_space);
    }

    /// Push a triangle onto the render queue.
    pub fn push(&mut self, triangle: vid::Triangle) {
        self.render_queue.push(triangle);
    }

    /// Push a group of triangles onto the render queue.
    pub fn push_group(&mut self, group: &vid::TriangleGroup) {
        for triangle in &group.triangles {
            self.push(triangle.clone());
        }
    }

    /// Normalize the camera rotations.
    pub fn normalize_camera(&mut self) {
        #[cfg(not(target_os = "redox"))]
        use std::f32::consts::PI;

        #[cfg(target_os = "redox")]
        const PI: f32 = 3.141592653589793;

        if self.camera_x_z > (PI * 2.0) {
            self.camera_x_z -= (PI * 2.0);
        }

        if self.camera_x_y > (PI * 2.0) {
            self.camera_x_y -= (PI * 2.0);
        }

        if self.camera_y_z > (PI * 2.0) {
            self.camera_y_z -= (PI * 2.0);
        }
    }
}
<|endoftext|>


<commit_before>//! Utility functions for use within wlroots-rs

use std::ffi::CString;
use std::process::exit;

/// Converts a Rust string into C string without error handling.
/// If any error occurs, it is logged and then the program is immediantly
/// aborted.
pub fn safe_as_cstring<S>(string: S) -> CString
    where S: Into<Vec<u8>>
{
    match CString::new(string) {
        Ok(string) => string,
        Err(err) => {
            wlr_log!(L_ERROR,
                     "Error occured while trying to convert a Rust string to a C string {:?}",
                     err);
            exit(1)
        }
    }
}
<commit_msg>Added C string to Rust string util function<commit_after>//! Utility functions for use within wlroots-rs

use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::process::exit;

/// Converts a Rust string into C string without error handling.
/// If any error occurs, it is logged and then the program is immediantly
/// aborted.
pub fn safe_as_cstring<S>(string: S) -> CString
    where S: Into<Vec<u8>>
{
    match CString::new(string) {
        Ok(string) => string,
        Err(err) => {
            wlr_log!(L_ERROR,
                     "Error occured while trying to convert a Rust string to a C string {:?}",
                     err);
            exit(1)
        }
    }
}

/// Converts a C string into a Rust string without error handling.
/// The pointer passed to this function _must_ be valid.
pub unsafe fn c_to_rust_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        Some(CStr::from_ptr(c_str).to_string_lossy().into_owned())
    }
}
<|endoftext|>


<commit_before>use gemlab::util::PI;
use gemlab::StrError;
use russell_lab::Vector;

struct Processor<'a, T> {
    callback: Option<Box<dyn Fn(&mut Vector, &[f64], &T) -> Result<(), StrError> + 'a>>,
}

impl<'a, T> Processor<'a, T> {
    fn set_callback(&mut self, c: impl Fn(&mut Vector, &[f64], &T) -> Result<(), StrError> + 'a) {
        self.callback = Some(Box::new(c));
    }

    fn process_events(&self, args: &T) -> Result<(), StrError> {
        let ksi = vec![0.0; 2];
        let mut x = Vector::new(2);
        match &self.callback {
            Some(cb) => cb(&mut x, &ksi, args)?,
            None => (),
        }
        println!("got x =\n{}", x);
        Ok(())
    }
}

fn simple_callback<T>(_: &mut Vector, _: &[f64], _: &T) -> Result<(), StrError> {
    Err("nothing to see here")
}

pub struct NaturalToReal {
    pub rmin: f64,
    pub rmax: f64,
    pub amin: f64,
    pub amax: f64,
    pub zmin: f64,
    pub zmax: f64,
}

impl NaturalToReal {
    pub fn new() -> Self {
        NaturalToReal {
            rmin: 5.0,
            rmax: 10.0,
            amin: 30.0 * PI / 180.0,
            amax: 60.0 * PI / 180.0,
            zmin: 0.0,
            zmax: 1.0,
        }
    }
}

pub fn natural_to_real_cylindrical(x: &mut Vector, ksi: &[f64], args: &NaturalToReal) -> Result<(), StrError> {
    if x.dim() != ksi.len() {
        return Err("x.dim() must be equal to ksi.len()");
    }
    const KSI_MIN: f64 = -1.0;
    const KSI_DEL: f64 = 2.0;
    let r = args.rmin + (ksi[0] - KSI_MIN) * (args.rmax - args.rmin) / KSI_DEL;
    let a = args.amin + (ksi[1] - KSI_MIN) * (args.amax - args.amin) / KSI_DEL;
    x[0] = r * f64::cos(a);
    x[1] = r * f64::sin(a);
    if x.dim() == 3 {
        x[2] = args.zmin + (ksi[2] - KSI_MIN) * (args.zmax - args.zmin) / KSI_DEL;
    }
    Ok(())
}

fn main() -> Result<(), StrError> {
    let mut p = Processor {
        callback: Some(Box::new(simple_callback)),
        // callback: None,
    };
    let args = NaturalToReal::new();
    println!("{:?}", p.process_events(&args).err());
    let v = Vector::from(&[1.0, 2.0]);
    let my_fun = move |x: &mut Vector, ksi: &[f64], _: &NaturalToReal| {
        println!("using ksi = {:?}", ksi);
        x[0] = v[0];
        x[1] = v[1];
        Ok(())
    };
    // println!("v = {:?}", v);
    p.set_callback(my_fun);
    p.process_events(&args)?;
    p.set_callback(natural_to_real_cylindrical);
    p.process_events(&args)?;
    Ok(())
}
<commit_msg>Remove temp file: study on callbacks<commit_after><|endoftext|>


<commit_before>extern crate libc;
extern crate "termbox-sys" as termbox;

pub use self::running::running;
pub use self::style::{Style, RB_BOLD, RB_UNDERLINE, RB_REVERSE, RB_NORMAL};

use std::error::Error;
use std::fmt;
use std::kinds::marker;
use std::time::duration::Duration;

use termbox::RawEvent;
use libc::{c_int, c_uint};

#[deriving(Copy)]
pub enum Event {
    KeyEvent(u8, u16, u32),
    ResizeEvent(i32, i32),
    NoEvent
}

#[deriving(Copy)]
#[repr(C,u16)]
pub enum Color {
    Default =  0x00,
    Black =    0x01,
    Red =      0x02,
    Green =    0x03,
    Yellow =   0x04,
    Blue =     0x05,
    Magenta =  0x06,
    Cyan =     0x07,
    White =    0x08,
}

mod style {
    bitflags! {
        #[repr(C)]
        flags Style: u16 {
            const TB_NORMAL_COLOR = 0x000F,
            const RB_BOLD = 0x0100,
            const RB_UNDERLINE = 0x0200,
            const RB_REVERSE = 0x0400,
            const RB_NORMAL = 0x0000,
            const TB_ATTRIB = RB_BOLD.bits | RB_UNDERLINE.bits | RB_REVERSE.bits,
        }
    }

    impl Style {
        pub fn from_color(color: super::Color) -> Style {
            Style { bits: color as u16 & TB_NORMAL_COLOR.bits }
        }
    }
}

const NIL_RAW_EVENT: RawEvent = RawEvent { etype: 0, emod: 0, key: 0, ch: 0, w: 0, h: 0 };

// FIXME: Rust doesn't support this enum representation.
// #[deriving(Copy,FromPrimitive,Show)]
// #[repr(C,int)]
// pub enum EventErrorKind {
//     Error = -1,
// }
// pub type EventError = Option<EventErrorKind>;
#[allow(non_snake_case)]
pub mod EventErrorKind {
    #[deriving(Copy,Show)]
    pub struct Error;
}

pub type EventError = Option<EventErrorKind::Error>;

pub type EventResult<T> = Result<T, EventError>;

impl Error for EventError {
    fn description(&self) -> &str {
        match *self {
            // TODO: Check errno here
            Some(EventErrorKind::Error) => "Unknown error.",
            None => "Unexpected return code."
        }
    }
}

fn unpack_event(ev_type: c_int, ev: &RawEvent) -> EventResult<Event> {
    match ev_type {
        0 => Ok(Event::NoEvent),
        1 => Ok(Event::KeyEvent(ev.emod, ev.key, ev.ch)),
        2 => Ok(Event::ResizeEvent(ev.w, ev.h)),
        // FIXME: Rust doesn't support this error representation
        // res => FromPrimitive::from_int(res as int),
        -1 => Err(Some(EventErrorKind::Error)),
        _ => Err(None)
    }
}

#[deriving(Copy,FromPrimitive,Show)]
#[repr(C,int)]
pub enum InitErrorKind {
    UnsupportedTerminal = -1,
    FailedToOpenTty = -2,
    PipeTrapError = -3,
}

pub enum InitError {
    Opt(InitOption, Option<Box<Error>>),
    AlreadyOpen,
    TermBox(Option<InitErrorKind>),
}

impl fmt::Show for InitError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}", self.description())
    }
}

impl Error for InitError {
    fn description(&self) -> &str {
        match *self {
            InitError::Opt(InitOption::BufferStderr, _) => "Could not redirect stderr.",
            InitError::AlreadyOpen => "RustBox is already open.",
            InitError::TermBox(e) => e.map_or("Unexpected TermBox return code.", |e| match e {
                InitErrorKind::UnsupportedTerminal => "Unsupported terminal.",
                InitErrorKind::FailedToOpenTty => "Failed to open TTY.",
                InitErrorKind::PipeTrapError => "Pipe trap error.",
            }),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            InitError::Opt(_, Some(ref e)) => Some(&**e),
            _ => None
        }
    }
}

mod running {
    use std::sync::atomic::{mod, AtomicBool};

    // The state of the RustBox is protected by the lock.  Yay, global state!
    static RUSTBOX_RUNNING: AtomicBool = atomic::INIT_ATOMIC_BOOL;

    /// true iff RustBox is currently running.  Beware of races here--don't rely on this for anything
    /// critical unless you happen to know that RustBox cannot change state when it is called (a good
    /// usecase would be checking to see if it's worth risking double printing backtraces to avoid
    /// having them swallowed up by RustBox).
    pub fn running() -> bool {
        RUSTBOX_RUNNING.load(atomic::SeqCst)
    }

    // Internal RAII guard used to ensure we release the running lock whenever we acquire it.
    #[allow(missing_copy_implementations)]
    pub struct RunningGuard(());

    pub fn run() -> Option<RunningGuard> {
        // Ensure that we are not already running and simultaneously set RUSTBOX_RUNNING using an
        // atomic swap.  This ensures that contending threads don't trample each other.
        if RUSTBOX_RUNNING.swap(true, atomic::SeqCst) {
            // The Rustbox was already running.
            None
        } else {
            // The RustBox was not already running, and now we have the lock.
            Some(RunningGuard(()))
        }
    }

    impl Drop for RunningGuard {
        fn drop(&mut self) {
            // Indicate that we're free now.  We could probably get away with lower atomicity here,
            // but there's no reason to take that chance.
            RUSTBOX_RUNNING.store(false, atomic::SeqCst);
        }
    }
}

// RAII guard for input redirection
#[cfg(unix)]
mod redirect {
    use std::error::Error;

    use libc;
    use std::io::{util, IoError, PipeStream};
    use std::io::pipe::PipePair;
    use std::os::unix::AsRawFd;
    use super::{InitError, InitOption, RustBox};

    pub struct Redirect {
        pair: PipePair,
        fd: PipeStream,
    }

    impl Drop for Redirect {
        fn drop(&mut self) {
            // We make sure that we never actually create the Redirect without also putting it in a
            // RustBox.  This means that we know that this will always be dropped immediately after
            // the RustBox is destroyed.  We rely on destructor order here: destructors are always
            // executed top-down, so as long as this is included above the RunningGuard in the
            // RustBox struct, we can be confident that it is destroyed while we're still holding
            // onto the lock.

            unsafe {
                let old_fd = self.pair.writer.as_raw_fd();
                let new_fd = self.fd.as_raw_fd();
                // Reopen new_fd as writer.
                // (Note that if we fail here, we can't really do anything about it, so just ignore any
                // errors).
                if libc::dup2(old_fd, new_fd) != new_fd { return }
            }
            // Copy from reader to writer.
            drop(util::copy(&mut self.pair.reader, &mut self.pair.writer));
        }
    }

    fn redirect(new: PipeStream) -> Result<Redirect, Option<Box<Error>>> {
        // Create a pipe pair.
        let mut pair = try!(PipeStream::pair().map_err( |e| Some(box e as Box<Error>)));
        unsafe {
            let new_fd = new.as_raw_fd();
            // Copy new_fd to dup_fd.
            let dup_fd = match libc::dup(new_fd) {
                -1 => return Err(Some(box IoError::last_error() as Box<Error>)),
                fd => try!(PipeStream::open(fd).map_err( |e| Some(box e as Box<Error>))),
            };
            // Reopen new_fd as writer.
            let old_fd = pair.writer.as_raw_fd();
            let fd = libc::dup2(old_fd, new_fd);
            if fd == new_fd {
                // On success, the new file descriptor should be returned.  Replace the old one
                // with dup_fd, since we no longer need an explicit reference to the writer.
                pair.writer = dup_fd;
                Ok(Redirect {
                    pair: pair,
                    fd: new,
                })
            } else {
                Err(if fd == -1 { Some(box IoError::last_error() as Box<Error>) } else { None })
            }
        }
    }

    // The reason we take the rb reference is mostly to make sure we don't try to redirect before
    // the TermBox is set up.  Otherwise it is too easy to leave the file handles in a bad state.
    pub fn redirect_stderr(rb: &mut RustBox) -> Result<(), InitError> {
        match rb.stderr {
            Some(_) => {
                // Can only redirect once.
                Err(InitError::Opt(InitOption::BufferStderr, None))
            },
            None => {
                rb.stderr = Some(try!(redirect(
                            try!(PipeStream::open(libc::STDERR_FILENO)
                                 .map_err( |e| InitError::Opt(InitOption::BufferStderr,
                                                              Some(box e as Box<Error>)))))
                        .map_err( |e| InitError::Opt(InitOption::BufferStderr, e))));
                Ok(())
            }
        }
    }
}

#[cfg(not(unix))]
// Not sure how we'll do this on Windows, unimplemented for now.
mod redirect {
    pub enum Redirect { }

    pub fn redirect_stderr(_: &mut super::RustBox) -> Result<(), super::InitError> {
        Err(super::InitError::Opt(super::InitOption::BufferStderr, None))
    }
}

#[allow(missing_copy_implementations)]
pub struct RustBox {
    // Termbox is not thread safe
    no_sync: marker::NoSync,

    // We only bother to redirect stderr for the moment, since it's used for panic!
    stderr: Option<redirect::Redirect>,

    // RAII lock.
    //
    // Note that running *MUST* be the last field in the destructor, since destructors run in
    // top-down order.  Otherwise it will not properly protect the above fields.
    _running: running::RunningGuard,
}

#[deriving(Copy,Show)]
pub enum InitOption {
    /// Use this option to automatically buffer stderr while RustBox is running.  It will be
    /// written when RustBox exits.
    BufferStderr,
}

impl RustBox {
    pub fn init(opts: &[Option<InitOption>]) -> Result<RustBox, InitError> {
        // Acquire RAII lock.  This might seem like overkill, but it is easy to forget to release
        // it in the maze of error conditions below.
        let running = match running::run() {
            Some(r) => r,
            None => return Err(InitError::AlreadyOpen)
        };
        // Create the RustBox.
        let mut rb = unsafe {
            match termbox::tb_init() {
                0 => RustBox {
                    no_sync: marker::NoSync,
                    stderr: None,
                    _running: running,
                },
                res => {
                    return Err(InitError::TermBox(FromPrimitive::from_int(res as int)))
                }
            }
        };
        // Time to check our options.
        for opt in opts.iter().filter_map(|&opt| opt) {
            match opt {
                InitOption::BufferStderr => try!(redirect::redirect_stderr(&mut rb)),
            }
        }
        Ok(rb)
    }

    pub fn width(&self) -> uint {
        unsafe { termbox::tb_width() as uint }
    }

    pub fn height(&self) -> uint {
        unsafe { termbox::tb_height() as uint }
    }

    pub fn clear(&self) {
        unsafe { termbox::tb_clear() }
    }

    pub fn present(&self) {
        unsafe { termbox::tb_present() }
    }

    pub fn set_cursor(&self, x: int, y: int) {
        unsafe { termbox::tb_set_cursor(x as c_int, y as c_int) }
    }

    // Unsafe because u8 is not guaranteed to be a UTF-8 character
    pub unsafe fn change_cell(&self, x: uint, y: uint, ch: u32, fg: u16, bg: u16) {
        termbox::tb_change_cell(x as c_uint, y as c_uint, ch, fg, bg)
    }

    pub fn print(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, s: &str) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        for (i, ch) in s.chars().enumerate() {
            unsafe {
                self.change_cell(x+i, y, ch as u32, fg.bits(), bg.bits());
            }
        }
    }

    pub fn print_char(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, ch: char) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        unsafe {
            self.change_cell(x, y, ch as u32, fg.bits(), bg.bits());
        }
    }

    pub fn poll_event(&self) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_poll_event(&ev as *const RawEvent)
        };
        unpack_event(rc, &ev)
    }

    pub fn peek_event(&self, timeout: Duration) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_peek_event(&ev as *const RawEvent, timeout.num_milliseconds() as c_uint)
        };
        unpack_event(rc, &ev)
    }
}

impl Drop for RustBox {
    fn drop(&mut self) {
        // Since only one instance of the RustBox is ever accessible, we should not
        // need to do this atomically.
        // Note: we should definitely have RUSTBOX_RUNNING = true here.
        unsafe {
            termbox::tb_shutdown();
        }
    }
}
<commit_msg>Derive PartialEq for Color<commit_after>extern crate libc;
extern crate "termbox-sys" as termbox;

pub use self::running::running;
pub use self::style::{Style, RB_BOLD, RB_UNDERLINE, RB_REVERSE, RB_NORMAL};

use std::error::Error;
use std::fmt;
use std::kinds::marker;
use std::time::duration::Duration;

use termbox::RawEvent;
use libc::{c_int, c_uint};

#[deriving(Copy)]
pub enum Event {
    KeyEvent(u8, u16, u32),
    ResizeEvent(i32, i32),
    NoEvent
}

#[deriving(Copy, PartialEq)]
#[repr(C,u16)]
pub enum Color {
    Default =  0x00,
    Black =    0x01,
    Red =      0x02,
    Green =    0x03,
    Yellow =   0x04,
    Blue =     0x05,
    Magenta =  0x06,
    Cyan =     0x07,
    White =    0x08,
}

mod style {
    bitflags! {
        #[repr(C)]
        flags Style: u16 {
            const TB_NORMAL_COLOR = 0x000F,
            const RB_BOLD = 0x0100,
            const RB_UNDERLINE = 0x0200,
            const RB_REVERSE = 0x0400,
            const RB_NORMAL = 0x0000,
            const TB_ATTRIB = RB_BOLD.bits | RB_UNDERLINE.bits | RB_REVERSE.bits,
        }
    }

    impl Style {
        pub fn from_color(color: super::Color) -> Style {
            Style { bits: color as u16 & TB_NORMAL_COLOR.bits }
        }
    }
}

const NIL_RAW_EVENT: RawEvent = RawEvent { etype: 0, emod: 0, key: 0, ch: 0, w: 0, h: 0 };

// FIXME: Rust doesn't support this enum representation.
// #[deriving(Copy,FromPrimitive,Show)]
// #[repr(C,int)]
// pub enum EventErrorKind {
//     Error = -1,
// }
// pub type EventError = Option<EventErrorKind>;
#[allow(non_snake_case)]
pub mod EventErrorKind {
    #[deriving(Copy,Show)]
    pub struct Error;
}

pub type EventError = Option<EventErrorKind::Error>;

pub type EventResult<T> = Result<T, EventError>;

impl Error for EventError {
    fn description(&self) -> &str {
        match *self {
            // TODO: Check errno here
            Some(EventErrorKind::Error) => "Unknown error.",
            None => "Unexpected return code."
        }
    }
}

fn unpack_event(ev_type: c_int, ev: &RawEvent) -> EventResult<Event> {
    match ev_type {
        0 => Ok(Event::NoEvent),
        1 => Ok(Event::KeyEvent(ev.emod, ev.key, ev.ch)),
        2 => Ok(Event::ResizeEvent(ev.w, ev.h)),
        // FIXME: Rust doesn't support this error representation
        // res => FromPrimitive::from_int(res as int),
        -1 => Err(Some(EventErrorKind::Error)),
        _ => Err(None)
    }
}

#[deriving(Copy,FromPrimitive,Show)]
#[repr(C,int)]
pub enum InitErrorKind {
    UnsupportedTerminal = -1,
    FailedToOpenTty = -2,
    PipeTrapError = -3,
}

pub enum InitError {
    Opt(InitOption, Option<Box<Error>>),
    AlreadyOpen,
    TermBox(Option<InitErrorKind>),
}

impl fmt::Show for InitError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}", self.description())
    }
}

impl Error for InitError {
    fn description(&self) -> &str {
        match *self {
            InitError::Opt(InitOption::BufferStderr, _) => "Could not redirect stderr.",
            InitError::AlreadyOpen => "RustBox is already open.",
            InitError::TermBox(e) => e.map_or("Unexpected TermBox return code.", |e| match e {
                InitErrorKind::UnsupportedTerminal => "Unsupported terminal.",
                InitErrorKind::FailedToOpenTty => "Failed to open TTY.",
                InitErrorKind::PipeTrapError => "Pipe trap error.",
            }),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            InitError::Opt(_, Some(ref e)) => Some(&**e),
            _ => None
        }
    }
}

mod running {
    use std::sync::atomic::{mod, AtomicBool};

    // The state of the RustBox is protected by the lock.  Yay, global state!
    static RUSTBOX_RUNNING: AtomicBool = atomic::INIT_ATOMIC_BOOL;

    /// true iff RustBox is currently running.  Beware of races here--don't rely on this for anything
    /// critical unless you happen to know that RustBox cannot change state when it is called (a good
    /// usecase would be checking to see if it's worth risking double printing backtraces to avoid
    /// having them swallowed up by RustBox).
    pub fn running() -> bool {
        RUSTBOX_RUNNING.load(atomic::SeqCst)
    }

    // Internal RAII guard used to ensure we release the running lock whenever we acquire it.
    #[allow(missing_copy_implementations)]
    pub struct RunningGuard(());

    pub fn run() -> Option<RunningGuard> {
        // Ensure that we are not already running and simultaneously set RUSTBOX_RUNNING using an
        // atomic swap.  This ensures that contending threads don't trample each other.
        if RUSTBOX_RUNNING.swap(true, atomic::SeqCst) {
            // The Rustbox was already running.
            None
        } else {
            // The RustBox was not already running, and now we have the lock.
            Some(RunningGuard(()))
        }
    }

    impl Drop for RunningGuard {
        fn drop(&mut self) {
            // Indicate that we're free now.  We could probably get away with lower atomicity here,
            // but there's no reason to take that chance.
            RUSTBOX_RUNNING.store(false, atomic::SeqCst);
        }
    }
}

// RAII guard for input redirection
#[cfg(unix)]
mod redirect {
    use std::error::Error;

    use libc;
    use std::io::{util, IoError, PipeStream};
    use std::io::pipe::PipePair;
    use std::os::unix::AsRawFd;
    use super::{InitError, InitOption, RustBox};

    pub struct Redirect {
        pair: PipePair,
        fd: PipeStream,
    }

    impl Drop for Redirect {
        fn drop(&mut self) {
            // We make sure that we never actually create the Redirect without also putting it in a
            // RustBox.  This means that we know that this will always be dropped immediately after
            // the RustBox is destroyed.  We rely on destructor order here: destructors are always
            // executed top-down, so as long as this is included above the RunningGuard in the
            // RustBox struct, we can be confident that it is destroyed while we're still holding
            // onto the lock.

            unsafe {
                let old_fd = self.pair.writer.as_raw_fd();
                let new_fd = self.fd.as_raw_fd();
                // Reopen new_fd as writer.
                // (Note that if we fail here, we can't really do anything about it, so just ignore any
                // errors).
                if libc::dup2(old_fd, new_fd) != new_fd { return }
            }
            // Copy from reader to writer.
            drop(util::copy(&mut self.pair.reader, &mut self.pair.writer));
        }
    }

    fn redirect(new: PipeStream) -> Result<Redirect, Option<Box<Error>>> {
        // Create a pipe pair.
        let mut pair = try!(PipeStream::pair().map_err( |e| Some(box e as Box<Error>)));
        unsafe {
            let new_fd = new.as_raw_fd();
            // Copy new_fd to dup_fd.
            let dup_fd = match libc::dup(new_fd) {
                -1 => return Err(Some(box IoError::last_error() as Box<Error>)),
                fd => try!(PipeStream::open(fd).map_err( |e| Some(box e as Box<Error>))),
            };
            // Reopen new_fd as writer.
            let old_fd = pair.writer.as_raw_fd();
            let fd = libc::dup2(old_fd, new_fd);
            if fd == new_fd {
                // On success, the new file descriptor should be returned.  Replace the old one
                // with dup_fd, since we no longer need an explicit reference to the writer.
                pair.writer = dup_fd;
                Ok(Redirect {
                    pair: pair,
                    fd: new,
                })
            } else {
                Err(if fd == -1 { Some(box IoError::last_error() as Box<Error>) } else { None })
            }
        }
    }

    // The reason we take the rb reference is mostly to make sure we don't try to redirect before
    // the TermBox is set up.  Otherwise it is too easy to leave the file handles in a bad state.
    pub fn redirect_stderr(rb: &mut RustBox) -> Result<(), InitError> {
        match rb.stderr {
            Some(_) => {
                // Can only redirect once.
                Err(InitError::Opt(InitOption::BufferStderr, None))
            },
            None => {
                rb.stderr = Some(try!(redirect(
                            try!(PipeStream::open(libc::STDERR_FILENO)
                                 .map_err( |e| InitError::Opt(InitOption::BufferStderr,
                                                              Some(box e as Box<Error>)))))
                        .map_err( |e| InitError::Opt(InitOption::BufferStderr, e))));
                Ok(())
            }
        }
    }
}

#[cfg(not(unix))]
// Not sure how we'll do this on Windows, unimplemented for now.
mod redirect {
    pub enum Redirect { }

    pub fn redirect_stderr(_: &mut super::RustBox) -> Result<(), super::InitError> {
        Err(super::InitError::Opt(super::InitOption::BufferStderr, None))
    }
}

#[allow(missing_copy_implementations)]
pub struct RustBox {
    // Termbox is not thread safe
    no_sync: marker::NoSync,

    // We only bother to redirect stderr for the moment, since it's used for panic!
    stderr: Option<redirect::Redirect>,

    // RAII lock.
    //
    // Note that running *MUST* be the last field in the destructor, since destructors run in
    // top-down order.  Otherwise it will not properly protect the above fields.
    _running: running::RunningGuard,
}

#[deriving(Copy,Show)]
pub enum InitOption {
    /// Use this option to automatically buffer stderr while RustBox is running.  It will be
    /// written when RustBox exits.
    BufferStderr,
}

impl RustBox {
    pub fn init(opts: &[Option<InitOption>]) -> Result<RustBox, InitError> {
        // Acquire RAII lock.  This might seem like overkill, but it is easy to forget to release
        // it in the maze of error conditions below.
        let running = match running::run() {
            Some(r) => r,
            None => return Err(InitError::AlreadyOpen)
        };
        // Create the RustBox.
        let mut rb = unsafe {
            match termbox::tb_init() {
                0 => RustBox {
                    no_sync: marker::NoSync,
                    stderr: None,
                    _running: running,
                },
                res => {
                    return Err(InitError::TermBox(FromPrimitive::from_int(res as int)))
                }
            }
        };
        // Time to check our options.
        for opt in opts.iter().filter_map(|&opt| opt) {
            match opt {
                InitOption::BufferStderr => try!(redirect::redirect_stderr(&mut rb)),
            }
        }
        Ok(rb)
    }

    pub fn width(&self) -> uint {
        unsafe { termbox::tb_width() as uint }
    }

    pub fn height(&self) -> uint {
        unsafe { termbox::tb_height() as uint }
    }

    pub fn clear(&self) {
        unsafe { termbox::tb_clear() }
    }

    pub fn present(&self) {
        unsafe { termbox::tb_present() }
    }

    pub fn set_cursor(&self, x: int, y: int) {
        unsafe { termbox::tb_set_cursor(x as c_int, y as c_int) }
    }

    // Unsafe because u8 is not guaranteed to be a UTF-8 character
    pub unsafe fn change_cell(&self, x: uint, y: uint, ch: u32, fg: u16, bg: u16) {
        termbox::tb_change_cell(x as c_uint, y as c_uint, ch, fg, bg)
    }

    pub fn print(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, s: &str) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        for (i, ch) in s.chars().enumerate() {
            unsafe {
                self.change_cell(x+i, y, ch as u32, fg.bits(), bg.bits());
            }
        }
    }

    pub fn print_char(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, ch: char) {
        let fg = Style::from_color(fg) | (sty & style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        unsafe {
            self.change_cell(x, y, ch as u32, fg.bits(), bg.bits());
        }
    }

    pub fn poll_event(&self) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_poll_event(&ev as *const RawEvent)
        };
        unpack_event(rc, &ev)
    }

    pub fn peek_event(&self, timeout: Duration) -> EventResult<Event> {
        let ev = NIL_RAW_EVENT;
        let rc = unsafe {
            termbox::tb_peek_event(&ev as *const RawEvent, timeout.num_milliseconds() as c_uint)
        };
        unpack_event(rc, &ev)
    }
}

impl Drop for RustBox {
    fn drop(&mut self) {
        // Since only one instance of the RustBox is ever accessible, we should not
        // need to do this atomically.
        // Note: we should definitely have RUSTBOX_RUNNING = true here.
        unsafe {
            termbox::tb_shutdown();
        }
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    /*
     * The book doesn't have tokens for comments and
     * whitespaces. Introducing them the scanner can
     * deal with them uniformly and in a more
     * functional way.
     */
    Comment,
    Whitespace,
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn substring(&self, start: usize, end: usize) -> String {
        self.source.chars().skip(start).take(end - start).collect()
    }

    fn peek(&self) -> char() {
        if self.is_at_end() {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn peek_next(&self) -> char() {
        if self.current + 1 >= self.source.len() {
            '\0'
        } else {
            self.char_at(self.current + 1)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        if self.char_at(self.current) != expected {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.substring(self.start, self.current),
            line: self.line + 1, // Converts from 0-indexed to 1-indexed
        }
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.line;
        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' {
                self.line += 1
            }
            self.advance();
        }
        if self.is_at_end() {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        self.advance();
        Ok(Token::StringLiteral(self.substring(self.start + 1, self.current - 1)))
    }

    fn number(&mut self) -> Token {
        while is_digit(self.peek()) {
            self.advance();
        }
        if self.peek() == '.' && is_digit(self.peek_next()) {
            self.advance(); // Consume the .
            while is_digit(self.peek()) {
                self.advance();
            }
        }
        let literal = self.substring(self.start, self.current);
        let value = literal.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while is_alphanumeric(self.peek()) {
            self.advance();
        }
        // TODO: take a ref in the first place
        match self.substring(self.start, self.current).as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            ' ' => Token::Whitespace,
            '\r' => Token::Whitespace,
            '\t' => Token::Whitespace,
            '\n' => {
                self.line += 1;
                Token::Whitespace
            }
            '"' => try!(self.string()),
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, pos {}",
                                   c,
                                   self.line,
                                   self.current - 1));
            }
        };
        Ok(self.add_context(token))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            _ => tokens.push(token_with_context),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

#[cfg(test)]
mod tests {
    use scanner::*;

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }

    #[test]
    fn multiline_statements(){
        let tokens = scan(&r#"var a = 1.0;
                              var b = "Hello";"#.into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[10].token, Token::Eof);
        assert_eq!(tokens[1].line, 1);
        assert_eq!(tokens[9].line, 2);
    }
}<commit_msg>Added is_whitespace and moved line count to advance<commit_after>#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    /*
     * The book doesn't have tokens for comments and
     * whitespaces. Introducing them the scanner can
     * deal with them uniformly and in a more
     * functional way.
     */
    Comment,
    Whitespace,
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool{
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false
    }
}

impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 1, // 1-indexed,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn substring(&self, start: usize, end: usize) -> String {
        self.source.chars().skip(start).take(end - start).collect()
    }

    fn peek(&self) -> char() {
        if self.is_at_end() {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn peek_next(&self) -> char() {
        if self.current + 1 >= self.source.len() {
            '\0'
        } else {
            self.char_at(self.current + 1)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        let c = self.char_at(self.current - 1);
        if c == '\n' {
            self.line += 1;
        }
        c
    }

    fn is_match(&mut self, expected: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        if self.char_at(self.current) != expected {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.substring(self.start, self.current),
            line: self.line,
        }
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.line;
        while self.peek() != '"' && !self.is_at_end() {
            self.advance();
        }
        if self.is_at_end() {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        self.advance();
        Ok(Token::StringLiteral(self.substring(self.start + 1, self.current - 1)))
    }

    fn number(&mut self) -> Token {
        while is_digit(self.peek()) {
            self.advance();
        }
        if self.peek() == '.' && is_digit(self.peek_next()) {
            self.advance(); // Consume the .
            while is_digit(self.peek()) {
                self.advance();
            }
        }
        let literal = self.substring(self.start, self.current);
        let value = literal.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while is_alphanumeric(self.peek()) {
            self.advance();
        }
        // TODO: take a ref in the first place
        match self.substring(self.start, self.current).as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            },
            '"' => try!(self.string()),
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, pos {}",
                                   c,
                                   self.line,
                                   self.current - 1));
            }
        };
        Ok(self.add_context(token))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            _ => tokens.push(token_with_context),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

#[cfg(test)]
mod tests {
    use scanner::*;

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }

    #[test]
    fn multiline_statements(){
        let tokens = scan(&r#"var a = 1.0;
                              var b = "Hello";"#.into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[10].token, Token::Eof);
        assert_eq!(tokens[1].line, 1);
        assert_eq!(tokens[9].line, 2);
    }
}<|endoftext|>


<commit_before>use error::Error;
use header::Header;
use relocation::Relocation;
use dynamic::Dynamic;
use symbol::Symbol;
use strtab::Strtab;
use types;

use std::io::{Read, Seek, SeekFrom, Write};
use std::io::BufWriter;

#[derive(Default, Debug, Clone)]
pub struct SectionHeader {
    pub name:       u32,
    pub shtype:     types::SectionType,
    pub flags:      types::SectionFlags,
    pub addr:       u64,
    pub offset:     u64,
    pub size:       u64,
    pub link:       u32,
    pub info:       u32,
    pub addralign:  u64,
    pub entsize:    u64,
}

impl SectionHeader {
    pub fn entsize(eh: &Header) -> usize {
        4 + 4 + match eh.ident_class {
            types::Class::Class64 => 6 * 8,
            types::Class::Class32 => 6 * 4,
        } + 4 + 4
    }

    pub fn from_reader<R>(io: &mut R, eh: &Header) -> Result<SectionHeader, Error>
    where
        R: Read,
    {
        elf_dispatch_endianness!(eh => {
            let mut r = SectionHeader::default();
            r.name   = read_u32(io)?;
            let reb  = read_u32(io)?;
            r.shtype = types::SectionType(reb);

            elf_dispatch_uclass!(eh => {
                let reb = read_uclass(io)?;
                r.flags = match types::SectionFlags::from_bits(reb) {
                    Some(v) => v,
                    None => return Err(Error::InvalidSectionFlags(reb)),
                };
                r.addr   = read_uclass(io)?;
                r.offset = read_uclass(io)?;
                r.size   = read_uclass(io)?;
                r.link   = read_u32(io)?;
                r.info   = read_u32(io)?;
                r.addralign = read_uclass(io)?;
                r.entsize = read_uclass(io)?;
                Ok(r)
            })
        })
    }

    pub fn to_writer<R>(&self, eh: &Header, io: &mut R) -> Result<(), Error>
    where
        R: Write,
    {
        let mut w = BufWriter::new(io);
        elf_write_u32!(eh, w, self.name)?;
        elf_write_u32!(eh, w, self.shtype.to_u32())?;
        elf_write_uclass!(eh, w, self.flags.bits())?;

        elf_write_uclass!(eh, w, self.addr)?;
        elf_write_uclass!(eh, w, self.offset)?;
        elf_write_uclass!(eh, w, self.size)?;
        elf_write_u32!(eh, w, self.link)?;
        elf_write_u32!(eh, w, self.info)?;
        elf_write_uclass!(eh, w, self.addralign)?;
        elf_write_uclass!(eh, w, self.entsize)?;
        Ok(())
    }
}

#[derive(Debug, Clone)]
pub enum SectionContent {
    None,
    Unloaded,
    Raw(Vec<u8>),
    Relocations(Vec<Relocation>),
    Symbols(Vec<Symbol>),
    Dynamic(Vec<Dynamic>),
    Strtab(Strtab),
}

impl Default for SectionContent {
    fn default() -> Self {
        SectionContent::None
    }
}
impl SectionContent {
    pub fn as_dynamic_mut(&mut self) -> Option<&mut Vec<Dynamic>> {
        match self {
            &mut SectionContent::Dynamic(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn as_strtab_mut(&mut self) -> Option<&mut Strtab> {
        match self {
            &mut SectionContent::Strtab(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn as_symbols(&self) -> Option<&Vec<Symbol>> {
        match self {
            &SectionContent::Symbols(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_symbols_mut(&mut self) -> Option<&mut Vec<Symbol>> {
        match self {
            &mut SectionContent::Symbols(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_symbols(self) -> Option<Vec<Symbol>> {
        match self {
            SectionContent::Symbols(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_relocations(&self) -> Option<&Vec<Relocation>> {
        match self {
            &SectionContent::Relocations(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_relocations_mut(&mut self) -> Option<&mut Vec<Relocation>> {
        match self {
            &mut SectionContent::Relocations(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_relocations(self) -> Option<Vec<Relocation>> {
        match self {
            SectionContent::Relocations(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_raw(&self) -> Option<&Vec<u8>> {
        match self {
            &SectionContent::Raw(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_raw_mut(&mut self) -> Option<&mut Vec<u8>> {
        match self {
            &mut SectionContent::Raw(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_raw(self) -> Option<Vec<u8>> {
        match self {
            SectionContent::Raw(v) => Some(v),
            _ => None,
        }
    }
    pub fn size(&self, eh: &Header) -> usize {
        match self {
            &SectionContent::Unloaded => panic!("cannot size unloaded section"),
            &SectionContent::None => 0,
            &SectionContent::Raw(ref v) => v.len(),
            &SectionContent::Dynamic(ref v) => v.len() * Dynamic::entsize(eh),
            &SectionContent::Strtab(ref v) => v.len(eh),
            &SectionContent::Symbols(ref v) => v.len() * Symbol::entsize(eh),
            &SectionContent::Relocations(ref v) => v.len() * Relocation::entsize(eh),
        }
    }
}

#[derive(Debug, Default, Clone)]
pub struct Section {
    pub header:     SectionHeader,
    pub name:       Vec<u8>,
    pub content:    SectionContent,
    pub addrlock:   bool,
}

impl Section {
    pub fn size(&self, eh: &Header) -> usize {
        self.content.size(eh)
    }
    pub fn new(
        name:       Vec<u8>,
        shtype:     types::SectionType,
        flags:      types::SectionFlags,
        content:    SectionContent,
        link:       u32,
        info:       u32,
    ) -> Section {
        Section {
            name: name,
            header: SectionHeader {
                name: 0,
                shtype: shtype,
                flags: flags,
                addr: 0,
                offset: 0,
                size: 0,
                link: link,
                info: info,
                addralign: 0,
                entsize: 0,
            },
            content: content,
            addrlock: false,
        }
    }

    pub fn sync(
        &mut self,
        eh: &Header,
        mut linked: Option<&mut SectionContent>,
    ) -> Result<(), Error> {
        match self.content {
            SectionContent::Unloaded => {
                return Err(Error::SyncingUnloadedSection);
            },
            SectionContent::Relocations(_) => {
                self.header.entsize = Relocation::entsize(eh) as u64;
            }
            SectionContent::Symbols(ref mut vv) => {
                for (i, sym) in vv.iter().enumerate() {
                    if sym.bind == types::SymbolBind::GLOBAL {
                        self.header.info = i as u32;
                        break;
                    }
                }
                for v in vv {
                    v.sync(linked.as_mut().map(|r| &mut **r), eh)?;
                }
                self.header.entsize = Symbol::entsize(eh) as u64;
            }
            SectionContent::Dynamic(ref mut vv) => {
                for v in vv {
                    v.sync(linked.as_mut().map(|r| &mut **r), eh)?;
                }
                self.header.entsize = Dynamic::entsize(eh) as u64;
            }
            SectionContent::Strtab(_) => {
                self.header.entsize = Strtab::entsize(eh) as u64;
            }
            SectionContent::None | SectionContent::Raw(_) => {}
        }
        if self.header.shtype != types::SectionType::NOBITS {
            self.header.size = self.size(eh) as u64;
        }
        Ok(())
    }

    pub fn from_reader<T>(
        &mut self,
        mut io: T,
        linked: Option<&Section>,
        eh: &Header,
    ) -> Result<(), Error> where T: Read + Seek {
        match self.content {
            SectionContent::Unloaded => {},
            _ => return Ok(()),
        };
        if self.header.shtype == types::SectionType::NOBITS {
            self.content = SectionContent::None;
            return Ok(());
        };
        io.seek(SeekFrom::Start(self.header.offset))?;
        let mut bb = vec![0; self.header.size as usize];
        io.read_exact(&mut bb)?;
        let linked = linked.map(|s|&s.content);
        self.content = match self.header.shtype {
            types::SectionType::NOBITS => {
                unreachable!();
            },
            types::SectionType::STRTAB => {
                let io = bb.as_slice();
                Strtab::from_reader(io, linked, eh)?
            }
            types::SectionType::RELA => {
                let io = bb.as_slice();
                Relocation::from_reader(io, linked, eh)?
            }
            types::SectionType::SYMTAB | types::SectionType::DYNSYM => {
                let io = bb.as_slice();
                Symbol::from_reader(io, linked, eh)?
            }
            types::SectionType::DYNAMIC => {
                let io = bb.as_slice();
                Dynamic::from_reader(io, linked, eh)?
            }
            _ => {
                SectionContent::Raw(bb)
            }
        };
        Ok(())
    }

    pub fn to_writer<R>(
        &self,
        mut io: R,
        eh: &Header,
    ) -> Result<(), Error> where R: Write + Seek {
        match self.content {
            SectionContent::Unloaded => return Ok(()),
            _ => {},
        };
        io.seek(SeekFrom::Start(self.header.offset))?;

        let rs = match &self.content {
            &SectionContent::Unloaded => {
                return Err(Error::WritingUnloadedSection);
            },
            &SectionContent::Relocations(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Symbols(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Dynamic(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Strtab(ref v) => {
                v.to_writer(&mut io, eh)?
            }
            &SectionContent::None => {
                0
            },
            &SectionContent::Raw(ref raw) => {
                io.write(&raw)?
            }
        };

        assert_eq!(
            io.seek(SeekFrom::Current(0))?,
            self.header.offset + self.content.size(eh) as u64,
            "writing {} with header.size {} and content.size {} returned a written size {}",
            String::from_utf8_lossy(&self.name),
            self.content.size(eh),
            self.header.size,
            rs
            );

        Ok(())
    }

}
<commit_msg>add more section conversion<commit_after>use error::Error;
use header::Header;
use relocation::Relocation;
use dynamic::Dynamic;
use symbol::Symbol;
use strtab::Strtab;
use types;

use std::io::{Read, Seek, SeekFrom, Write};
use std::io::BufWriter;

#[derive(Default, Debug, Clone)]
pub struct SectionHeader {
    pub name:       u32,
    pub shtype:     types::SectionType,
    pub flags:      types::SectionFlags,
    pub addr:       u64,
    pub offset:     u64,
    pub size:       u64,
    pub link:       u32,
    pub info:       u32,
    pub addralign:  u64,
    pub entsize:    u64,
}

impl SectionHeader {
    pub fn entsize(eh: &Header) -> usize {
        4 + 4 + match eh.ident_class {
            types::Class::Class64 => 6 * 8,
            types::Class::Class32 => 6 * 4,
        } + 4 + 4
    }

    pub fn from_reader<R>(io: &mut R, eh: &Header) -> Result<SectionHeader, Error>
    where
        R: Read,
    {
        elf_dispatch_endianness!(eh => {
            let mut r = SectionHeader::default();
            r.name   = read_u32(io)?;
            let reb  = read_u32(io)?;
            r.shtype = types::SectionType(reb);

            elf_dispatch_uclass!(eh => {
                let reb = read_uclass(io)?;
                r.flags = match types::SectionFlags::from_bits(reb) {
                    Some(v) => v,
                    None => return Err(Error::InvalidSectionFlags(reb)),
                };
                r.addr   = read_uclass(io)?;
                r.offset = read_uclass(io)?;
                r.size   = read_uclass(io)?;
                r.link   = read_u32(io)?;
                r.info   = read_u32(io)?;
                r.addralign = read_uclass(io)?;
                r.entsize = read_uclass(io)?;
                Ok(r)
            })
        })
    }

    pub fn to_writer<R>(&self, eh: &Header, io: &mut R) -> Result<(), Error>
    where
        R: Write,
    {
        let mut w = BufWriter::new(io);
        elf_write_u32!(eh, w, self.name)?;
        elf_write_u32!(eh, w, self.shtype.to_u32())?;
        elf_write_uclass!(eh, w, self.flags.bits())?;

        elf_write_uclass!(eh, w, self.addr)?;
        elf_write_uclass!(eh, w, self.offset)?;
        elf_write_uclass!(eh, w, self.size)?;
        elf_write_u32!(eh, w, self.link)?;
        elf_write_u32!(eh, w, self.info)?;
        elf_write_uclass!(eh, w, self.addralign)?;
        elf_write_uclass!(eh, w, self.entsize)?;
        Ok(())
    }
}

#[derive(Debug, Clone)]
pub enum SectionContent {
    None,
    Unloaded,
    Raw(Vec<u8>),
    Relocations(Vec<Relocation>),
    Symbols(Vec<Symbol>),
    Dynamic(Vec<Dynamic>),
    Strtab(Strtab),
}

impl Default for SectionContent {
    fn default() -> Self {
        SectionContent::None
    }
}
impl SectionContent {
    pub fn as_dynamic_mut(&mut self) -> Option<&mut Vec<Dynamic>> {
        match self {
            &mut SectionContent::Dynamic(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn as_dynamic(&self) -> Option<&Vec<Dynamic>> {
        match self {
            &SectionContent::Dynamic(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn into_dynamic(self) -> Option<Vec<Dynamic>> {
        match self {
            SectionContent::Dynamic(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_strtab_mut(&mut self) -> Option<&mut Strtab> {
        match self {
            &mut SectionContent::Strtab(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn as_symbols(&self) -> Option<&Vec<Symbol>> {
        match self {
            &SectionContent::Symbols(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_symbols_mut(&mut self) -> Option<&mut Vec<Symbol>> {
        match self {
            &mut SectionContent::Symbols(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_symbols(self) -> Option<Vec<Symbol>> {
        match self {
            SectionContent::Symbols(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_relocations(&self) -> Option<&Vec<Relocation>> {
        match self {
            &SectionContent::Relocations(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_relocations_mut(&mut self) -> Option<&mut Vec<Relocation>> {
        match self {
            &mut SectionContent::Relocations(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_relocations(self) -> Option<Vec<Relocation>> {
        match self {
            SectionContent::Relocations(v) => Some(v),
            _ => None,
        }
    }
    pub fn as_raw(&self) -> Option<&Vec<u8>> {
        match self {
            &SectionContent::Raw(ref v) => Some(v),
            _ => None,
        }
    }
    pub fn as_raw_mut(&mut self) -> Option<&mut Vec<u8>> {
        match self {
            &mut SectionContent::Raw(ref mut v) => Some(v),
            _ => None,
        }
    }
    pub fn into_raw(self) -> Option<Vec<u8>> {
        match self {
            SectionContent::Raw(v) => Some(v),
            _ => None,
        }
    }
    pub fn size(&self, eh: &Header) -> usize {
        match self {
            &SectionContent::Unloaded => panic!("cannot size unloaded section"),
            &SectionContent::None => 0,
            &SectionContent::Raw(ref v) => v.len(),
            &SectionContent::Dynamic(ref v) => v.len() * Dynamic::entsize(eh),
            &SectionContent::Strtab(ref v) => v.len(eh),
            &SectionContent::Symbols(ref v) => v.len() * Symbol::entsize(eh),
            &SectionContent::Relocations(ref v) => v.len() * Relocation::entsize(eh),
        }
    }
}

#[derive(Debug, Default, Clone)]
pub struct Section {
    pub header:     SectionHeader,
    pub name:       Vec<u8>,
    pub content:    SectionContent,
    pub addrlock:   bool,
}

impl Section {
    pub fn size(&self, eh: &Header) -> usize {
        self.content.size(eh)
    }
    pub fn new(
        name:       Vec<u8>,
        shtype:     types::SectionType,
        flags:      types::SectionFlags,
        content:    SectionContent,
        link:       u32,
        info:       u32,
    ) -> Section {
        Section {
            name: name,
            header: SectionHeader {
                name: 0,
                shtype: shtype,
                flags: flags,
                addr: 0,
                offset: 0,
                size: 0,
                link: link,
                info: info,
                addralign: 0,
                entsize: 0,
            },
            content: content,
            addrlock: false,
        }
    }

    pub fn sync(
        &mut self,
        eh: &Header,
        mut linked: Option<&mut SectionContent>,
    ) -> Result<(), Error> {
        match self.content {
            SectionContent::Unloaded => {
                return Err(Error::SyncingUnloadedSection);
            },
            SectionContent::Relocations(_) => {
                self.header.entsize = Relocation::entsize(eh) as u64;
            }
            SectionContent::Symbols(ref mut vv) => {
                for (i, sym) in vv.iter().enumerate() {
                    if sym.bind == types::SymbolBind::GLOBAL {
                        self.header.info = i as u32;
                        break;
                    }
                }
                for v in vv {
                    v.sync(linked.as_mut().map(|r| &mut **r), eh)?;
                }
                self.header.entsize = Symbol::entsize(eh) as u64;
            }
            SectionContent::Dynamic(ref mut vv) => {
                for v in vv {
                    v.sync(linked.as_mut().map(|r| &mut **r), eh)?;
                }
                self.header.entsize = Dynamic::entsize(eh) as u64;
            }
            SectionContent::Strtab(_) => {
                self.header.entsize = Strtab::entsize(eh) as u64;
            }
            SectionContent::None | SectionContent::Raw(_) => {}
        }
        if self.header.shtype != types::SectionType::NOBITS {
            self.header.size = self.size(eh) as u64;
        }
        Ok(())
    }

    pub fn from_reader<T>(
        &mut self,
        mut io: T,
        linked: Option<&Section>,
        eh: &Header,
    ) -> Result<(), Error> where T: Read + Seek {
        match self.content {
            SectionContent::Unloaded => {},
            _ => return Ok(()),
        };
        if self.header.shtype == types::SectionType::NOBITS {
            self.content = SectionContent::None;
            return Ok(());
        };
        io.seek(SeekFrom::Start(self.header.offset))?;
        let mut bb = vec![0; self.header.size as usize];
        io.read_exact(&mut bb)?;
        let linked = linked.map(|s|&s.content);
        self.content = match self.header.shtype {
            types::SectionType::NOBITS => {
                unreachable!();
            },
            types::SectionType::STRTAB => {
                let io = bb.as_slice();
                Strtab::from_reader(io, linked, eh)?
            }
            types::SectionType::RELA => {
                let io = bb.as_slice();
                Relocation::from_reader(io, linked, eh)?
            }
            types::SectionType::SYMTAB | types::SectionType::DYNSYM => {
                let io = bb.as_slice();
                Symbol::from_reader(io, linked, eh)?
            }
            types::SectionType::DYNAMIC => {
                let io = bb.as_slice();
                Dynamic::from_reader(io, linked, eh)?
            }
            _ => {
                SectionContent::Raw(bb)
            }
        };
        Ok(())
    }

    pub fn to_writer<R>(
        &self,
        mut io: R,
        eh: &Header,
    ) -> Result<(), Error> where R: Write + Seek {
        match self.content {
            SectionContent::Unloaded => return Ok(()),
            _ => {},
        };
        io.seek(SeekFrom::Start(self.header.offset))?;

        let rs = match &self.content {
            &SectionContent::Unloaded => {
                return Err(Error::WritingUnloadedSection);
            },
            &SectionContent::Relocations(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Symbols(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Dynamic(ref vv) => {
                let mut rs = 0;
                for v in vv {
                    rs += v.to_writer(&mut io, eh)?;
                }
                rs
            }
            &SectionContent::Strtab(ref v) => {
                v.to_writer(&mut io, eh)?
            }
            &SectionContent::None => {
                0
            },
            &SectionContent::Raw(ref raw) => {
                io.write(&raw)?
            }
        };

        assert_eq!(
            io.seek(SeekFrom::Current(0))?,
            self.header.offset + self.content.size(eh) as u64,
            "writing {} with header.size {} and content.size {} returned a written size {}",
            String::from_utf8_lossy(&self.name),
            self.content.size(eh),
            self.header.size,
            rs
            );

        Ok(())
    }

}
<|endoftext|>


<commit_before>extern crate podio;

use std::io;
use podio::{LittleEndian, BigEndian};
use podio::{ReadPodExt, WritePodExt};

#[test]
fn write_be() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_u64::<BigEndian>(0x01_23_45_67_89_ab_cd_ef).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);

    writer.set_position(0);
    writer.write_u32::<BigEndian>(0x01_23_45_67).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x01, 0x23, 0x45, 0x67]);

    writer.set_position(0);
    writer.write_u16::<BigEndian>(0x01_23).unwrap();
    assert_eq!(&writer.get_ref()[0..2], &[0x01, 0x23]);
}

#[test]
fn write_le() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_u64::<LittleEndian>(0x01_23_45_67_89_ab_cd_ef).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);

    writer.set_position(0);
    writer.write_u32::<LittleEndian>(0x01_23_45_67).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x67, 0x45, 0x23, 0x01]);

    writer.set_position(0);
    writer.write_u16::<LittleEndian>(0x01_23).unwrap();
    assert_eq!(&writer.get_ref()[0..2], &[0x23, 0x01]);
}

#[test]
fn write_octet() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_u8(0x01).unwrap();
    assert_eq!(&writer.get_ref()[0..1], &[0x01]);
}

#[test]
fn write_float() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_f32::<LittleEndian>(10.12f32).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x85, 0xEB, 0x21, 0x41]);

    writer.set_position(0);
    writer.write_f32::<BigEndian>(10.12f32).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x41, 0x21, 0xEB, 0x85]);

    writer.set_position(0);
    writer.write_f64::<LittleEndian>(10.12f64).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0x3D, 0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0x24, 0x40]);

    writer.set_position(0);
    writer.write_f64::<BigEndian>(10.12f64).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0x40, 0x24, 0x3D, 0x70, 0xA3, 0xD7, 0x0A, 0x3D]);
}

#[test]
fn read_be() {
    let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
    let mut reader = io::Cursor::new(buf);

    reader.set_position(0);
    assert_eq!(reader.read_u64::<BigEndian>().unwrap(), 0x0123456789abcdef);

    reader.set_position(0);
    assert_eq!(reader.read_u32::<BigEndian>().unwrap(), 0x01234567);

    reader.set_position(0);
    assert_eq!(reader.read_u16::<BigEndian>().unwrap(), 0x0123);
}

#[test]
fn read_le() {
    let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
    let mut reader = io::Cursor::new(buf);

    reader.set_position(0);
    assert_eq!(reader.read_u64::<LittleEndian>().unwrap(), 0xefcdab8967452301);

    reader.set_position(0);
    assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), 0x67452301);

    reader.set_position(0);
    assert_eq!(reader.read_u16::<LittleEndian>().unwrap(), 0x2301);
}

#[test]
fn read_octet() {
    let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
    let mut reader = io::Cursor::new(buf);

    reader.set_position(0);
    assert_eq!(reader.read_u8().unwrap(), 0x01);
}

#[test]
fn read_float() {
    let mut buf: &[u8] = &[0x41, 0x21, 0xEB, 0x85];
    assert_eq!(buf.read_f32::<BigEndian>().unwrap(), 10.12f32);

    let mut buf: &[u8] = &[0x85, 0xEB, 0x21, 0x41];
    assert_eq!(buf.read_f32::<LittleEndian>().unwrap(), 10.12f32);

    let mut buf: &[u8] = &[0x40, 0x24, 0x3D, 0x70, 0xA3, 0xD7, 0x0A, 0x3D];
    assert_eq!(buf.read_f64::<BigEndian>().unwrap(), 10.12f64);

    let mut buf: &[u8] = &[0x3D, 0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0x24, 0x40];
    assert_eq!(buf.read_f64::<LittleEndian>().unwrap(), 10.12f64);
}

#[test]
fn read_exact() {
    let mut buf: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8];
    assert_eq!(<&[u8] as ReadPodExt>::read_exact(&mut buf, 2).unwrap(), [1,2]);
    assert_eq!(<&[u8] as ReadPodExt>::read_exact(&mut buf, 1).unwrap(), [3]);
    assert_eq!(<&[u8] as ReadPodExt>::read_exact(&mut buf, 0).unwrap(), []);
    assert_eq!(<&[u8] as ReadPodExt>::read_exact(&mut buf, 5).unwrap(), [4,5,6,7,8]);
}
<commit_msg>Update read_exact test<commit_after>extern crate podio;

use std::io;
use podio::{LittleEndian, BigEndian};
use podio::{ReadPodExt, WritePodExt};

#[test]
fn write_be() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_u64::<BigEndian>(0x01_23_45_67_89_ab_cd_ef).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);

    writer.set_position(0);
    writer.write_u32::<BigEndian>(0x01_23_45_67).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x01, 0x23, 0x45, 0x67]);

    writer.set_position(0);
    writer.write_u16::<BigEndian>(0x01_23).unwrap();
    assert_eq!(&writer.get_ref()[0..2], &[0x01, 0x23]);
}

#[test]
fn write_le() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_u64::<LittleEndian>(0x01_23_45_67_89_ab_cd_ef).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);

    writer.set_position(0);
    writer.write_u32::<LittleEndian>(0x01_23_45_67).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x67, 0x45, 0x23, 0x01]);

    writer.set_position(0);
    writer.write_u16::<LittleEndian>(0x01_23).unwrap();
    assert_eq!(&writer.get_ref()[0..2], &[0x23, 0x01]);
}

#[test]
fn write_octet() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_u8(0x01).unwrap();
    assert_eq!(&writer.get_ref()[0..1], &[0x01]);
}

#[test]
fn write_float() {
    let buf: &mut [u8] = &mut [0u8; 8];
    let mut writer = io::Cursor::new(buf);

    writer.set_position(0);
    writer.write_f32::<LittleEndian>(10.12f32).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x85, 0xEB, 0x21, 0x41]);

    writer.set_position(0);
    writer.write_f32::<BigEndian>(10.12f32).unwrap();
    assert_eq!(&writer.get_ref()[0..4], &[0x41, 0x21, 0xEB, 0x85]);

    writer.set_position(0);
    writer.write_f64::<LittleEndian>(10.12f64).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0x3D, 0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0x24, 0x40]);

    writer.set_position(0);
    writer.write_f64::<BigEndian>(10.12f64).unwrap();
    assert_eq!(&writer.get_ref()[0..8], &[0x40, 0x24, 0x3D, 0x70, 0xA3, 0xD7, 0x0A, 0x3D]);
}

#[test]
fn read_be() {
    let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
    let mut reader = io::Cursor::new(buf);

    reader.set_position(0);
    assert_eq!(reader.read_u64::<BigEndian>().unwrap(), 0x0123456789abcdef);

    reader.set_position(0);
    assert_eq!(reader.read_u32::<BigEndian>().unwrap(), 0x01234567);

    reader.set_position(0);
    assert_eq!(reader.read_u16::<BigEndian>().unwrap(), 0x0123);
}

#[test]
fn read_le() {
    let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
    let mut reader = io::Cursor::new(buf);

    reader.set_position(0);
    assert_eq!(reader.read_u64::<LittleEndian>().unwrap(), 0xefcdab8967452301);

    reader.set_position(0);
    assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), 0x67452301);

    reader.set_position(0);
    assert_eq!(reader.read_u16::<LittleEndian>().unwrap(), 0x2301);
}

#[test]
fn read_octet() {
    let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
    let mut reader = io::Cursor::new(buf);

    reader.set_position(0);
    assert_eq!(reader.read_u8().unwrap(), 0x01);
}

#[test]
fn read_float() {
    let mut buf: &[u8] = &[0x41, 0x21, 0xEB, 0x85];
    assert_eq!(buf.read_f32::<BigEndian>().unwrap(), 10.12f32);

    let mut buf: &[u8] = &[0x85, 0xEB, 0x21, 0x41];
    assert_eq!(buf.read_f32::<LittleEndian>().unwrap(), 10.12f32);

    let mut buf: &[u8] = &[0x40, 0x24, 0x3D, 0x70, 0xA3, 0xD7, 0x0A, 0x3D];
    assert_eq!(buf.read_f64::<BigEndian>().unwrap(), 10.12f64);

    let mut buf: &[u8] = &[0x3D, 0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0x24, 0x40];
    assert_eq!(buf.read_f64::<LittleEndian>().unwrap(), 10.12f64);
}

#[test]
fn read_exact() {
    let mut buf: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8];
    assert_eq!(buf.read_exact(2).unwrap(), [1,2]);
    assert_eq!(buf.read_exact(1).unwrap(), [3]);
    assert_eq!(buf.read_exact(0).unwrap(), []);
    assert_eq!(buf.read_exact(5).unwrap(), [4,5,6,7,8]);
    assert_eq!(buf.read_exact(0).unwrap(), []);
    assert!(buf.read_exact(1).is_err());
    assert_eq!(buf.read_exact(0).unwrap(), []);
}
<|endoftext|>


<commit_before><commit_msg>Make xelatex default latex command<commit_after><|endoftext|>


<commit_before>use support::project;

#[test]
fn test_git_clean_removes_local_branches() {
    let project = project("git-clean_removes_local").build();

    project.setup_command("git branch test1");
    project.setup_command("git branch test2");

    let verify = project.setup_command("git branch");

    assert!(verify.stdout().contains("test1"), verify.failure_message("test1"));
    assert!(verify.stdout().contains("test2"), verify.failure_message("test2"));

    let result = project.git_clean_command("-y").run();

    assert!(result.is_success(), result.failure_message("command to succeed"));
    assert!(result.stdout().contains("Deleted branch test1"), result.failure_message("command to delete test1"));
    assert!(result.stdout().contains("Deleted branch test2"), result.failure_message("command to delete test2"));
}

#[test]
fn test_git_clean_does_not_remove_ignored_local_branches() {
    let project = project("git-clean_removes_local").build();

    project.setup_command("git branch test1");
    project.setup_command("git branch test2");

    let verify = project.setup_command("git branch");

    assert!(verify.stdout().contains("test1"), verify.failure_message("test1"));
    assert!(verify.stdout().contains("test2"), verify.failure_message("test2"));

    let result = project.git_clean_command("-y -i test2").run();

    assert!(result.is_success(), result.failure_message("command to succeed"));
    assert!(result.stdout().contains("Deleted branch test1"), result.failure_message("command to delete test1"));
    assert!(!result.stdout().contains("Deleted branch test2"), result.failure_message("command to delete test2"));
}
<commit_msg>add test for list of branches<commit_after>use support::project;

#[test]
fn test_git_clean_removes_local_branches() {
    let project = project("git-clean_removes_local").build();

    project.setup_command("git branch test1");
    project.setup_command("git branch test2");

    let verify = project.setup_command("git branch");

    assert!(verify.stdout().contains("test1"), verify.failure_message("test1"));
    assert!(verify.stdout().contains("test2"), verify.failure_message("test2"));

    let result = project.git_clean_command("-y").run();

    assert!(result.is_success(), result.failure_message("command to succeed"));
    assert!(result.stdout().contains("Deleted branch test1"), result.failure_message("command to delete test1"));
    assert!(result.stdout().contains("Deleted branch test2"), result.failure_message("command to delete test2"));
}

#[test]
fn test_git_clean_does_not_remove_ignored_local_branches() {
    let project = project("git-clean_removes_local").build();

    project.setup_command("git branch test1");
    project.setup_command("git branch test2");

    let verify = project.setup_command("git branch");

    assert!(verify.stdout().contains("test1"), verify.failure_message("test1"));
    assert!(verify.stdout().contains("test2"), verify.failure_message("test2"));

    let result = project.git_clean_command("-y -i test2").run();

    assert!(result.is_success(), result.failure_message("command to succeed"));
    assert!(result.stdout().contains("Deleted branch test1"), result.failure_message("command to delete test1"));
    assert!(!result.stdout().contains("Deleted branch test2"), result.failure_message("command to delete test2"));
}

#[test]
fn test_git_clean_does_not_remove_list_of_ignored_local_branches() {
    let project = project("git-clean_removes_local").build();

    project.setup_command("git branch test1");
    project.setup_command("git branch test2");
    project.setup_command("git branch test3");

    let verify = project.setup_command("git branch");

    assert!(verify.stdout().contains("test1"), verify.failure_message("test1"));
    assert!(verify.stdout().contains("test2"), verify.failure_message("test2"));
    assert!(verify.stdout().contains("test3"), verify.failure_message("test3"));

    let result = project.git_clean_command("-y -i test1 -i test3").run();

    assert!(result.is_success(), result.failure_message("command to succeed"));
    assert!(!result.stdout().contains("Deleted branch test1"), result.failure_message("command to delete test1"));
    assert!(result.stdout().contains("Deleted branch test2"), result.failure_message("command to delete test2"));
    assert!(!result.stdout().contains("Deleted branch test3"), result.failure_message("command to delete test3"));
}
<|endoftext|>