<commit_before>//! As always, windows has something very different than unix, we mainly want
//! to avoid having to depend too much on libunwind for windows.
//!
//! If you google around, you'll find a fair bit of references to built-in
//! functions to get backtraces on windows. It turns out that most of these are
//! in an external library called dbghelp. I was unable to find this library
//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
//! of it.
//!
//! You'll also find that there's a function called CaptureStackBackTrace
//! mentioned frequently (which is also easy to use), but sadly I didn't have a
//! copy of that function in my mingw install (maybe it was broken?). Instead,
//! this takes the route of using StackWalk64 in order to walk the stack.

#![allow(deprecated)] // dynamic_lib

use io;
use libc::c_void;
use mem;
use ptr;
use sys::c;
use sys::dynamic_lib::DynamicLibrary;
use sys_common::backtrace::Frame;

macro_rules! sym {
    ($lib:expr, $e:expr, $t:ident) => (
        $lib.symbol($e).map(|f| unsafe {
            $crate::mem::transmute::<usize, $t>(f)
        })
    )
}

mod printing;

#[cfg(target_env = "gnu")]
#[path = "backtrace_gnu.rs"]
pub mod gnu;

pub use self::printing::{foreach_symbol_fileline, resolve_symname};
use self::printing::{load_printing_fns_64, load_printing_fns_ex};

pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
    let dbghelp = DynamicLibrary::open("dbghelp.dll")?;

    // Fetch the symbols necessary from dbghelp.dll
    let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
    let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;

    // StackWalkEx might not be present and we'll fall back to StackWalk64
    let sw_var = match sym!(dbghelp, "StackWalkEx", StackWalkExFn) {
        Ok(StackWalkEx) => {
            StackWalkVariant::StackWalkEx(StackWalkEx, load_printing_fns_ex(&dbghelp)?)
        }
        Err(e) => match sym!(dbghelp, "StackWalk64", StackWalk64Fn) {
            Ok(StackWalk64) => {
                StackWalkVariant::StackWalk64(StackWalk64, load_printing_fns_64(&dbghelp)?)
            }
            Err(..) => return Err(e),
        },
    };

    // Allocate necessary structures for doing the stack walk
    let process = unsafe { c::GetCurrentProcess() };

    let backtrace_context = BacktraceContext {
        handle: process,
        SymCleanup,
        StackWalkVariant: sw_var,
        dbghelp,
    };

    // Initialize this process's symbols
    let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
    if ret != c::TRUE {
        return Ok((0, backtrace_context));
    }

    // And now that we're done with all the setup, do the stack walking!
    match backtrace_context.StackWalkVariant {
        StackWalkVariant::StackWalkEx(StackWalkEx, _) => {
            set_frames(StackWalkEx, frames).map(|i| (i, backtrace_context))
        }

        StackWalkVariant::StackWalk64(StackWalk64, _) => {
            set_frames(StackWalk64, frames).map(|i| (i, backtrace_context))
        }
    }
}

fn set_frames<W: StackWalker>(StackWalk: W, frames: &mut [Frame]) -> io::Result<usize> {
    let process = unsafe { c::GetCurrentProcess() };
    let thread = unsafe { c::GetCurrentProcess() };
    let mut context: c::CONTEXT = unsafe { mem::zeroed() };
    unsafe { c::RtlCaptureContext(&mut context) };
    let mut frame = W::Item::new();
    let image = frame.init(&context);

    let mut i = 0;
    while i < frames.len()
        && StackWalk.walk(image, process, thread, &mut frame, &mut context) == c::TRUE
    {
        let addr = frame.get_addr();
        frames[i] = Frame {
            symbol_addr: addr,
            exact_position: addr,
            inline_context: 0,
        };

        i += 1
    }
    Ok(i)
}

type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL;
type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL;

type StackWalkExFn = unsafe extern "system" fn(
    c::DWORD,
    c::HANDLE,
    c::HANDLE,
    *mut c::STACKFRAME_EX,
    *mut c::CONTEXT,
    *mut c_void,
    *mut c_void,
    *mut c_void,
    *mut c_void,
    c::DWORD,
) -> c::BOOL;

type StackWalk64Fn = unsafe extern "system" fn(
    c::DWORD,
    c::HANDLE,
    c::HANDLE,
    *mut c::STACKFRAME64,
    *mut c::CONTEXT,
    *mut c_void,
    *mut c_void,
    *mut c_void,
    *mut c_void,
) -> c::BOOL;

trait StackWalker {
    type Item: StackFrame;

    fn walk(
        &self,
        _: c::DWORD,
        _: c::HANDLE,
        _: c::HANDLE,
        _: &mut Self::Item,
        _: &mut c::CONTEXT
    ) -> c::BOOL;
}

impl StackWalker for StackWalkExFn {
    type Item = c::STACKFRAME_EX;
    fn walk(
        &self,
        image: c::DWORD,
        process: c::HANDLE,
        thread: c::HANDLE,
        frame: &mut Self::Item,
        context: &mut c::CONTEXT,
    ) -> c::BOOL {
        unsafe {
            self(
                image,
                process,
                thread,
                frame,
                context,
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                0,
            )
        }
    }
}

impl StackWalker for StackWalk64Fn {
    type Item = c::STACKFRAME64;
    fn walk(
        &self,
        image: c::DWORD,
        process: c::HANDLE,
        thread: c::HANDLE,
        frame: &mut Self::Item,
        context: &mut c::CONTEXT,
    ) -> c::BOOL {
        unsafe {
            self(
                image,
                process,
                thread,
                frame,
                context,
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
            )
        }
    }
}

trait StackFrame {
    fn new() -> Self;
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD;
    fn get_addr(&self) -> *const u8;
}

impl StackFrame for c::STACKFRAME_EX {
    fn new() -> c::STACKFRAME_EX {
        unsafe { mem::zeroed() }
    }

    #[cfg(target_arch = "x86")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Eip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Esp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Ebp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_I386
    }

    #[cfg(target_arch = "x86_64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Rip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Rsp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Rbp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_AMD64
    }

    #[cfg(target_arch = "arm")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.R11 as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARMNT
    }

    #[cfg(target_arch = "aarch64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Fp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARM64
    }

    fn get_addr(&self) -> *const u8 {
        (self.AddrPC.Offset - 1) as *const u8
    }
}

impl StackFrame for c::STACKFRAME64 {
    fn new() -> c::STACKFRAME64 {
        unsafe { mem::zeroed() }
    }

    #[cfg(target_arch = "x86")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Eip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Esp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Ebp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_I386
    }

    #[cfg(target_arch = "x86_64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Rip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Rsp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Rbp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_AMD64
    }

    #[cfg(target_arch = "arm")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.R11 as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARMNT
    }

    #[cfg(target_arch = "aarch64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Fp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARM64
    }

    fn get_addr(&self) -> *const u8 {
        (self.AddrPC.Offset - 1) as *const u8
    }
}

enum StackWalkVariant {
    StackWalkEx(StackWalkExFn, printing::PrintingFnsEx),
    StackWalk64(StackWalk64Fn, printing::PrintingFns64),
}

pub struct BacktraceContext {
    handle: c::HANDLE,
    SymCleanup: SymCleanupFn,
    // Only used in printing for msvc and not gnu
    // The gnu version is effectively a ZST dummy.
    #[allow(dead_code)]
    StackWalkVariant: StackWalkVariant,
    // keeping DynamycLibrary loaded until its functions no longer needed
    #[allow(dead_code)]
    dbghelp: DynamicLibrary,
}

impl Drop for BacktraceContext {
    fn drop(&mut self) {
        unsafe {
            (self.SymCleanup)(self.handle);
        }
    }
}
<commit_msg>Rollup merge of #57238 - Zoxc:bt-fix, r=alexcrichton<commit_after>//! As always, windows has something very different than unix, we mainly want
//! to avoid having to depend too much on libunwind for windows.
//!
//! If you google around, you'll find a fair bit of references to built-in
//! functions to get backtraces on windows. It turns out that most of these are
//! in an external library called dbghelp. I was unable to find this library
//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
//! of it.
//!
//! You'll also find that there's a function called CaptureStackBackTrace
//! mentioned frequently (which is also easy to use), but sadly I didn't have a
//! copy of that function in my mingw install (maybe it was broken?). Instead,
//! this takes the route of using StackWalk64 in order to walk the stack.

#![allow(deprecated)] // dynamic_lib

use io;
use libc::c_void;
use mem;
use ptr;
use sys::c;
use sys::dynamic_lib::DynamicLibrary;
use sys_common::backtrace::Frame;

macro_rules! sym {
    ($lib:expr, $e:expr, $t:ident) => (
        $lib.symbol($e).map(|f| unsafe {
            $crate::mem::transmute::<usize, $t>(f)
        })
    )
}

mod printing;

#[cfg(target_env = "gnu")]
#[path = "backtrace_gnu.rs"]
pub mod gnu;

pub use self::printing::{foreach_symbol_fileline, resolve_symname};
use self::printing::{load_printing_fns_64, load_printing_fns_ex};

pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
    let dbghelp = DynamicLibrary::open("dbghelp.dll")?;

    // Fetch the symbols necessary from dbghelp.dll
    let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
    let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;

    // StackWalkEx might not be present and we'll fall back to StackWalk64
    let sw_var = match sym!(dbghelp, "StackWalkEx", StackWalkExFn) {
        Ok(StackWalkEx) => {
            StackWalkVariant::StackWalkEx(StackWalkEx, load_printing_fns_ex(&dbghelp)?)
        }
        Err(e) => match sym!(dbghelp, "StackWalk64", StackWalk64Fn) {
            Ok(StackWalk64) => {
                StackWalkVariant::StackWalk64(StackWalk64, load_printing_fns_64(&dbghelp)?)
            }
            Err(..) => return Err(e),
        },
    };

    // Allocate necessary structures for doing the stack walk
    let process = unsafe { c::GetCurrentProcess() };

    let backtrace_context = BacktraceContext {
        handle: process,
        SymCleanup,
        StackWalkVariant: sw_var,
        dbghelp,
    };

    // Initialize this process's symbols
    let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) };
    if ret != c::TRUE {
        return Ok((0, backtrace_context));
    }

    // And now that we're done with all the setup, do the stack walking!
    match backtrace_context.StackWalkVariant {
        StackWalkVariant::StackWalkEx(StackWalkEx, _) => {
            set_frames(StackWalkEx, frames).map(|i| (i, backtrace_context))
        }

        StackWalkVariant::StackWalk64(StackWalk64, _) => {
            set_frames(StackWalk64, frames).map(|i| (i, backtrace_context))
        }
    }
}

fn set_frames<W: StackWalker>(StackWalk: W, frames: &mut [Frame]) -> io::Result<usize> {
    let process = unsafe { c::GetCurrentProcess() };
    let thread = unsafe { c::GetCurrentProcess() };
    let mut context: c::CONTEXT = unsafe { mem::zeroed() };
    unsafe { c::RtlCaptureContext(&mut context) };
    let mut frame = W::Item::new();
    let image = frame.init(&context);

    let mut i = 0;
    while i < frames.len()
        && StackWalk.walk(image, process, thread, &mut frame, &mut context) == c::TRUE
    {
        let addr = frame.get_addr();
        frames[i] = Frame {
            symbol_addr: addr,
            exact_position: addr,
            inline_context: frame.get_inline_context(),
        };

        i += 1
    }
    Ok(i)
}

type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL;
type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL;

type StackWalkExFn = unsafe extern "system" fn(
    c::DWORD,
    c::HANDLE,
    c::HANDLE,
    *mut c::STACKFRAME_EX,
    *mut c::CONTEXT,
    *mut c_void,
    *mut c_void,
    *mut c_void,
    *mut c_void,
    c::DWORD,
) -> c::BOOL;

type StackWalk64Fn = unsafe extern "system" fn(
    c::DWORD,
    c::HANDLE,
    c::HANDLE,
    *mut c::STACKFRAME64,
    *mut c::CONTEXT,
    *mut c_void,
    *mut c_void,
    *mut c_void,
    *mut c_void,
) -> c::BOOL;

trait StackWalker {
    type Item: StackFrame;

    fn walk(
        &self,
        _: c::DWORD,
        _: c::HANDLE,
        _: c::HANDLE,
        _: &mut Self::Item,
        _: &mut c::CONTEXT
    ) -> c::BOOL;
}

impl StackWalker for StackWalkExFn {
    type Item = c::STACKFRAME_EX;
    fn walk(
        &self,
        image: c::DWORD,
        process: c::HANDLE,
        thread: c::HANDLE,
        frame: &mut Self::Item,
        context: &mut c::CONTEXT,
    ) -> c::BOOL {
        unsafe {
            self(
                image,
                process,
                thread,
                frame,
                context,
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                0,
            )
        }
    }
}

impl StackWalker for StackWalk64Fn {
    type Item = c::STACKFRAME64;
    fn walk(
        &self,
        image: c::DWORD,
        process: c::HANDLE,
        thread: c::HANDLE,
        frame: &mut Self::Item,
        context: &mut c::CONTEXT,
    ) -> c::BOOL {
        unsafe {
            self(
                image,
                process,
                thread,
                frame,
                context,
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
            )
        }
    }
}

trait StackFrame {
    fn new() -> Self;
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD;
    fn get_addr(&self) -> *const u8;
    fn get_inline_context(&self) -> u32;
}

impl StackFrame for c::STACKFRAME_EX {
    fn new() -> c::STACKFRAME_EX {
        unsafe { mem::zeroed() }
    }

    #[cfg(target_arch = "x86")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Eip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Esp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Ebp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_I386
    }

    #[cfg(target_arch = "x86_64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Rip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Rsp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Rbp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_AMD64
    }

    #[cfg(target_arch = "arm")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.R11 as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARMNT
    }

    #[cfg(target_arch = "aarch64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Fp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARM64
    }

    fn get_addr(&self) -> *const u8 {
        (self.AddrPC.Offset - 1) as *const u8
    }

    fn get_inline_context(&self) -> u32 {
        self.InlineFrameContext
    }
}

impl StackFrame for c::STACKFRAME64 {
    fn new() -> c::STACKFRAME64 {
        unsafe { mem::zeroed() }
    }

    #[cfg(target_arch = "x86")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Eip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Esp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Ebp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_I386
    }

    #[cfg(target_arch = "x86_64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Rip as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Rsp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Rbp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_AMD64
    }

    #[cfg(target_arch = "arm")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.R11 as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARMNT
    }

    #[cfg(target_arch = "aarch64")]
    fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD {
        self.AddrPC.Offset = ctx.Pc as u64;
        self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrStack.Offset = ctx.Sp as u64;
        self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat;
        self.AddrFrame.Offset = ctx.Fp as u64;
        self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat;
        c::IMAGE_FILE_MACHINE_ARM64
    }

    fn get_addr(&self) -> *const u8 {
        (self.AddrPC.Offset - 1) as *const u8
    }

    fn get_inline_context(&self) -> u32 {
        0
    }
}

enum StackWalkVariant {
    StackWalkEx(StackWalkExFn, printing::PrintingFnsEx),
    StackWalk64(StackWalk64Fn, printing::PrintingFns64),
}

pub struct BacktraceContext {
    handle: c::HANDLE,
    SymCleanup: SymCleanupFn,
    // Only used in printing for msvc and not gnu
    // The gnu version is effectively a ZST dummy.
    #[allow(dead_code)]
    StackWalkVariant: StackWalkVariant,
    // keeping DynamycLibrary loaded until its functions no longer needed
    #[allow(dead_code)]
    dbghelp: DynamicLibrary,
}

impl Drop for BacktraceContext {
    fn drop(&mut self) {
        unsafe {
            (self.SymCleanup)(self.handle);
        }
    }
}
<|endoftext|>


<commit_before>/**
Katy Williamson kcw4sn
Ben Terner bt3ze
@virginia.edu

Final Project for CS4414 - Operating Systems
A parallel MST implementation on images in Rust

*/

/*
some documentation:
Pixel object is composed of r,g,b values, a color, and an x,y index that takes the place of its adjacency list
Point is just an (x,y) value to denote a pixel's location
Edge contains two points: source and dest, along with the edge cost between them

*/

extern mod extra;
use std::{os, num, hashmap} ;
use extra::{arc,priority_queue};

struct Pixel{
    r: int,
    b: int,
    g: int,
    color: int,
    x: int,
    y: int
}

impl Pixel {
    fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
        Pixel{
            r: red,
            b: blue,
            g: green,
            color: -1,
            x: x,
            y: y
        }
    }
}

impl Clone for Pixel{
    fn clone(&self) -> Pixel {
        Pixel{
            r: self.r,
            b: self.b,
            g: self.g,
            color: self.color,
            x: self.x,
            y: self.y
        }
    }
}

struct Point {
    x: int,
    y: int
}

impl Point{
    fn new(x: int, y: int)-> Point {
        Point {
            x: x,
            y: y
        }
    }
}

struct Edge {
    source: Point,
    dest: Point,
    cost: float
}

impl Edge {
    fn new(s: Point, d: Point, cost: float) -> Edge {
        Edge {
            source: s,
            dest: d,
            cost: cost
        }
    }
}

impl Ord for Edge {
    fn lt(&self, other: &Edge) -> bool {
        self.cost < other.cost
    }
}

fn edgeCost(a: &Pixel, b: &Pixel) -> float {
    num::sqrt( (
            (a.r-b.r)*(a.r-b.r) + (a.g-b.g)*(a.g-b.g) + (a.b-b.b)*(a.b-b.b) ) as float)
}

fn readImage( filename: &~str) -> ~ [ ~[Pixel]] {
    let mut retval: ~[~[Pixel]] = ~[];

    let file: &str = filename.to_owned();
    let file2: ~str = file.to_owned();
    let split_filename: ~[&str] = file.split_iter('.').collect();
    let file_data: &str = split_filename[0] + ".txt";

    println(fmt!("The image name is [%?]", file));
    println(fmt!("The image data will be stored at [%?]", file_data));

    //read in the .txt associated with the file title
    let path = &PosixPath(file_data);

    match std::io::file_reader(path) {
	Ok(reader) => {
	    //run the java program to process the image
	    let program: &str = "java ImageReader";
	    let argv: ~[~str] = ~[file2];

	    let mut prog = std::run::Process::new(program, argv,
		std::run::ProcessOptions {
			env: None,
			dir: None,
			in_fd: None,
			out_fd: None,
			err_fd: None
		}
	    );
	    prog.finish();

	    //parse out the array dimensions
    	    let dimensions_str: &str = reader.read_line();
    	    println(fmt!("dimensons: %s",dimensions_str));
	    let mut h: int = 0;
	    let mut w: int = 0;
	    match dimensions_str[0] as char {
		'H' => {
		    let split_dim: ~[&str] = dimensions_str.split_iter('W').collect();
		    h = from_str(split_dim[0].slice(1,split_dim[0].len())).unwrap();
		    w = from_str(split_dim[1]).unwrap();
		    println(fmt!("Dimensions passed: H %? W %?",h,w));
		    //let retval: ~[ ~[Pixel, ..w], ..h];
		}
		_ => {println("Dimensions corrupted! Abort");}
	    }

	    //fill in the retVal array with pixel data
            //let mut rowfirst = false;
	    for row in range(0,h){
//                if(!rowfirst) {
                retval.push(~[]);
                //rowfirst = true; }
		for col in range(0,w){
	    	    if !reader.eof() {
			let line: &str = reader.read_line();
			//println(line);
			let rpos:uint = match line.find('R') { Some(n)=>n,None=>line.len() };
			let gpos:uint = match line.find('G') { Some(n)=>n,None=>line.len() };
			let bpos:uint = match line.find('B') { Some(n)=>n,None=>line.len() };

			if rpos == line.len() || gpos == line.len() || bpos == line.len() {
			    println("Pixel data corrupted! Abort");
			}

			let R:int = from_str(line.slice(rpos+1,gpos)).unwrap();
			let G:int = from_str(line.slice(gpos+1,bpos)).unwrap();
			let B:int = from_str(line.slice(bpos+1,line.len())).unwrap();

			//println(fmt!("R %? G %? B %?",R,G,B));

			//fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
			retval[row].push(Pixel::new(R,G,B,col,row));

		    }
		    else {
			println("Data file corrupted! Abort");
		    }
		}
	    }
	},
	Err(err)   => { fail!(err) }
    }

    retval
}

fn main(){
    let argv =  os::args();
    println(argv.to_str());

    let pixels: ~[ ~[Pixel] ] = readImage(&argv[1]);
    let height = pixels.len() as int;
    let width = pixels[0].len() as int;

    println(fmt!("Dimensions received: H %? W %?",height,width));

    let mut find_arcs: ~[ ~[arc::RWArc<Pixel>] ] =  ~[];
    for h in range(0,height){
	find_arcs.push(~[]);
        for w in range(0,width){
            println(fmt!("pix %?",pixels[h][w]));
	    find_arcs[h].push(arc::RWArc::new(pixels[h][w]));
	}
    }

  /*  let mut find_arcs: ~[ ~[arc::RWArc<Pixel>] ] =  ~[ ~[]];
    let mut heit = 0;
    let mut wid = 0;
    loop {
        loop {
            find_arcs[heit][wid] = arc::RWArc::new(pixels[heit][wid]);
            wid+=1;
            if(wid == width) {
                wid = 0;
                break;
            }
        }
        heit+=1;
        if(heit == height) {
            break;
        }
    }*/

    let arcs = find_arcs; // now we have an immutable array that can be shared

    let corners = [ (0,0),(width-1,0),(0,height-1),(width-1,height-1) ];

    for &corner in corners.iter() {
        let (a,b) = corner;
        let shared_arcs = arcs.clone();
        do spawn { // split into threads here
            let c = a;
            let d = b;
            let mut x=a;
            let mut y=b;
            /*
            weird compiler error:
            let corners = [ (0,0),(0,width-1),(height-1,0),(height-1,width-1) ];
            for &corner in corners.iter(){
            do spawn {
            let mut x = 0;
            let mut y = 0;
            (x,y) = corner
                 }
             }
             */

            let mut queue: priority_queue::PriorityQueue<Edge> =  priority_queue::PriorityQueue::new();
            let mut visited: hashmap::HashMap<(int,int),bool> = hashmap::HashMap::new();
            let mut newvisit: bool = true;

            println(fmt!("start at (%i,%i)",c,d));

            loop { // iterate through items in the queue, which contains all of the edges/vertices
                if(newvisit){
                   println(fmt!("new visit!: (%i,%i) : (%i,%i)",c,d,x,y));
                    let neighbors = [ (x,y-1),(x+1,y), (x,y+1), (x-1,y)];
                    for &coord in neighbors.iter() {
                        let (w,z) = coord;
                        if w >=0 && w < width && z >=0 && z < height { // bounds checking
                            // if neighbor at coord has not been or colored
                            let mut newvertex = false;
                            let mut uncolored = false;
                            do shared_arcs[z][w].read |dest| {
                                if dest.color < 0 {
                                    uncolored = true; }
                            }
                            if uncolored {
                                do shared_arcs[z][w].write |dest| {
                                    if dest.color < 0 {
                                        do shared_arcs[y][x].read |src| {
                                            dest.color = src.color;
                                        }
                                        newvertex = true;
                                    }
                                }
                            }
                            if newvertex { // then we have a new vertex
                                do shared_arcs[y][x].read |src| {
                                    do shared_arcs[z][w].read |dest| {
                                        queue.push( Edge::new(Point::new(x,y),Point::new(w,z),edgeCost(src,dest)));
                                    }
                                }
                            }
                        }
                    }
                }

                let edge = queue.maybe_pop();
                match edge {
                    Some(e) => {
                        // not in any tree
                        let coord = (e.dest.x,e.dest.y);
//                        println(fmt!("before runtime error? %?",coord));
                        if !visited.contains_key(&coord){
//if *visited.get(&coord) == false { // use visited hashmap to figure out if we're at a new vertex. returns false if it can't return a vertex?
                            visited.insert(coord,true);
                            x = e.dest.x;
                            y = e.dest.y;
                            newvisit = true;
//                            println(fmt!("%? %f",coord,e.cost));
                        } else {
                            newvisit = false;
                        }
                    },
                    None => { break; } // this should execute at the end when there are no more unvisited or uncolored nodes for a thread
                }
            }
        }
    }
}
<commit_msg>hard to tell if we're having problems.<commit_after>/**
Katy Williamson kcw4sn
Ben Terner bt3ze
@virginia.edu

Final Project for CS4414 - Operating Systems
A parallel MST implementation on images in Rust

*/

/*
some documentation:
Pixel object is composed of r,g,b values, a color, and an x,y index that takes the place of its adjacency list
Point is just an (x,y) value to denote a pixel's location
Edge contains two points: source and dest, along with the edge cost between them

*/

extern mod extra;
use std::{os, num, hashmap} ;
use extra::{arc,priority_queue};

struct Pixel{
    r: int,
    b: int,
    g: int,
    color: int,
    x: int,
    y: int
}

impl Pixel {
    fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
        Pixel{
            r: red,
            b: blue,
            g: green,
            color: -1,
            x: x,
            y: y
        }
    }
}

impl Clone for Pixel{
    fn clone(&self) -> Pixel {
        Pixel{
            r: self.r,
            b: self.b,
            g: self.g,
            color: self.color,
            x: self.x,
            y: self.y
        }
    }
}

struct Point {
    x: int,
    y: int
}

impl Point{
    fn new(x: int, y: int)-> Point {
        Point {
            x: x,
            y: y
        }
    }
}

struct Edge {
    source: Point,
    dest: Point,
    cost: float
}

impl Edge {
    fn new(s: Point, d: Point, cost: float) -> Edge {
        Edge {
            source: s,
            dest: d,
            cost: cost
        }
    }
}

impl Ord for Edge {
    fn lt(&self, other: &Edge) -> bool {
        self.cost < other.cost
    }
}

fn edgeCost(a: &Pixel, b: &Pixel) -> float {
    num::sqrt( (
            (a.r-b.r)*(a.r-b.r) + (a.g-b.g)*(a.g-b.g) + (a.b-b.b)*(a.b-b.b) ) as float)
}

fn readImage( filename: &~str) -> ~ [ ~[Pixel]] {
    let mut retval: ~[~[Pixel]] = ~[];

    let file: &str = filename.to_owned();
    let file2: ~str = file.to_owned();
    let split_filename: ~[&str] = file.split_iter('.').collect();
    let file_data: &str = split_filename[0] + ".txt";

    println(fmt!("The image name is [%?]", file));
    println(fmt!("The image data will be stored at [%?]", file_data));

    //read in the .txt associated with the file title
    let path = &PosixPath(file_data);

    match std::io::file_reader(path) {
	Ok(reader) => {
	    //run the java program to process the image
	    let program: &str = "java ImageReader";
	    let argv: ~[~str] = ~[file2];

	    let mut prog = std::run::Process::new(program, argv,
		std::run::ProcessOptions {
			env: None,
			dir: None,
			in_fd: None,
			out_fd: None,
			err_fd: None
		}
	    );
	    prog.finish();

	    //parse out the array dimensions
    	    let dimensions_str: &str = reader.read_line();
    	    println(fmt!("dimensons: %s",dimensions_str));
	    let mut h: int = 0;
	    let mut w: int = 0;
	    match dimensions_str[0] as char {
		'H' => {
		    let split_dim: ~[&str] = dimensions_str.split_iter('W').collect();
		    h = from_str(split_dim[0].slice(1,split_dim[0].len())).unwrap();
		    w = from_str(split_dim[1]).unwrap();
		    println(fmt!("Dimensions passed: H %? W %?",h,w));
		    //let retval: ~[ ~[Pixel, ..w], ..h];
		}
		_ => {println("Dimensions corrupted! Abort");}
	    }

	    //fill in the retVal array with pixel data
            //let mut rowfirst = false;
	    for row in range(0,h){
//                if(!rowfirst) {
                retval.push(~[]);
                //rowfirst = true; }
		for col in range(0,w){
	    	    if !reader.eof() {
			let line: &str = reader.read_line();
			//println(line);
			let rpos:uint = match line.find('R') { Some(n)=>n,None=>line.len() };
			let gpos:uint = match line.find('G') { Some(n)=>n,None=>line.len() };
			let bpos:uint = match line.find('B') { Some(n)=>n,None=>line.len() };

			if rpos == line.len() || gpos == line.len() || bpos == line.len() {
			    println("Pixel data corrupted! Abort");
			}

			let R:int = from_str(line.slice(rpos+1,gpos)).unwrap();
			let G:int = from_str(line.slice(gpos+1,bpos)).unwrap();
			let B:int = from_str(line.slice(bpos+1,line.len())).unwrap();

			//println(fmt!("R %? G %? B %?",R,G,B));

			//fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
			retval[row].push(Pixel::new(R,G,B,col,row));

		    }
		    else {
			println("Data file corrupted! Abort");
		    }
		}
	    }
	},
	Err(err)   => { fail!(err) }
    }

    retval
}

fn main(){
    let argv =  os::args();
    println(argv.to_str());

    let pixels: ~[ ~[Pixel] ] = readImage(&argv[1]);
    let height = pixels.len() as int;
    let width = pixels[0].len() as int;

    println(fmt!("Dimensions received: H %? W %?",height,width));

    let mut find_arcs: ~[ ~[arc::RWArc<Pixel>] ] =  ~[];
    for h in range(0,height){
	find_arcs.push(~[]);
        for w in range(0,width){
 //           println(fmt!("pix %?",pixels[h][w]));
	    find_arcs[h].push(arc::RWArc::new(pixels[h][w]));
	}
    }

    let arcs = find_arcs; // now we have an immutable array that can be shared

    let corners = [ (0,0),(width-1,0),(0,height-1),(width-1,height-1) ];

    for &corner in corners.iter() {
        let (a,b) = corner;
        let shared_arcs = arcs.clone();
        do spawn { // split into threads here
            let c = a;
            let d = b;
            let mut x=a;
            let mut y=b;

            let mut queue: priority_queue::PriorityQueue<Edge> =  priority_queue::PriorityQueue::new();
            let mut visited: hashmap::HashMap<(int,int),bool> = hashmap::HashMap::new();
            let mut newvisit: bool = true;

            println(fmt!("start at (%i,%i)",c,d));

            loop { // iterate through items in the queue, which contains all of the edges/vertices
                if(newvisit){
                   println(fmt!("(%i,%i) : (%i,%i)",c,d,x,y));
                    let neighbors = [ (x,y-1),(x+1,y), (x,y+1), (x-1,y)];
                    for &coord in neighbors.iter() {
                        let (w,z) = coord;
                        if w >=0 && w < width && z >=0 && z < height { // bounds checking
                            // if neighbor at coord has not been or colored
                            let mut newvertex = false;
                            let mut uncolored = false;
                            do shared_arcs[z][w].read |dest| {
                                if dest.color < 0 {
                                    uncolored = true; }
                            }
                            if uncolored {
                                do shared_arcs[z][w].write |dest| {
                                    if dest.color < 0 {
                                        do shared_arcs[y][x].read |src| {
                                            dest.color = src.color;
                                        }
                                        newvertex = true;
                                    }
                                }
                            }
                            if newvertex { // then we have a new vertex
                                do shared_arcs[y][x].read |src| {
                                    do shared_arcs[z][w].read |dest| {
                                        queue.push( Edge::new(Point::new(x,y),Point::new(w,z),edgeCost(src,dest)));
                                    }
                                }
                            }
                        }
                    }
                }

                let edge = queue.maybe_pop();
                match edge {
                    Some(e) => {
                        println(fmt!("(%i,%i) pop (%i,%i)-%f-(%i,%i)",c,d,e.source.x,e.source.y,e.cost,e.dest.x,e.dest.y));
                        // not in any tree
                        let coord = (e.dest.x,e.dest.y);
//                        println(fmt!("before runtime error? %?",coord));
                        if !visited.contains_key(&coord){
//if *visited.get(&coord) == false { // use visited hashmap to figure out if we're at a new vertex. returns false if it can't return a vertex?
                            visited.insert(coord,true);
                            x = e.dest.x;
                            y = e.dest.y;
                            newvisit = true;
//                            println(fmt!("%? %f",coord,e.cost));
                        } else {
                            newvisit = false;
                        }
                    },
                    None => { break; } // this should execute at the end when there are no more unvisited or uncolored nodes for a thread
                }
            }

        }
    }
}
<|endoftext|>


<commit_before>//! The parser works based on the following grammar
//!
//! Equation ==> Term { "+" Term }
//!           |  Term { "-" Term }
//!
//! Term     ==> Function Product
//!           |  Product
//!
//! Product  ==> Factor { "*" Factor }
//!           |  Factor { "/" Factor }
//!
//! Factor   ==> "-" Factor
//!           |  Exponent { "^" Factor }
//!
//! Exponent ==> Number { "!" }
//!
//! Number   ==> "(" Equation ")"
//!           |  "|" Equation "|"
//!           |  Constant
//!           |  { Digit }
//!
//! Function ==> "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | "sqrt" | "abs" | "exp"
//!           |  "ln" | "log"
//!
//! Constant ==> "pi" | "e" | "phi"
//!
//! Digit    ==> "0".."9"

use std::str::Chars;
use std::iter::Peekable;
use errors::{CalcrResult, CalcrError};
use ast::{Ast, AstFunc};
use ast::AstBranch::*;
use ast::AstVal::*;
use ast::AstFunc::*;
use ast::AstOp::*;
use ast::AstConst::*;

pub fn parse_equation(eq: String) -> CalcrResult<Ast> {
	let mut parser = Parser {
		pos: 0,
		iter: eq.chars().peekable(),
		paren_level: 0,
		abs_level: 0,
	};
	parser.parse_equation()
}

pub struct Parser<'a> {
    pos: usize,
    iter: Peekable<Chars<'a>>,
    paren_level: u32,
    abs_level: u32,
}

impl<'a> Parser<'a> {
    fn parse_equation(&mut self) -> CalcrResult<Ast> {
        let mut lhs = try!(self.parse_term());
        self.consume_whitespace();
        while self.peek_char() == Some('+') || self.peek_char() == Some('-') {
            let op_pos = self.pos;
            let op = if self.consume_char() == '+' {
                Plus
            } else {
                Minus
            };
            self.consume_whitespace();
            let rhs = try!(self.parse_term());
            lhs = Ast {
                val: Op(op),
                span: (op_pos, op_pos),
                branches: Binary(Box::new(lhs), Box::new(rhs)),
            };
            self.consume_whitespace();
        }
        if self.peek_char() == Some(')') && self.paren_level < 1 {
            Err(CalcrError {
                desc: format!("Missing opening parentheses"),
                span: (self.pos, self.pos),
            })
        } else if self.peek_char() == Some('|') && self.abs_level < 1 {
            Err(CalcrError {
                desc: format!("Missing opening abs delimiter"),
                span: (self.pos, self.pos),
            })
        } else {
            Ok(lhs)
        }
    }

    fn parse_term(&mut self) -> CalcrResult<Ast> {
        let begin_pos = self.pos;
        if let Some(func) = self.consume_function() {
            let end_pos = self.pos;
            self.consume_whitespace();
            let arg = try!(self.parse_product());
            Ok(Ast {
                val: Func(func),
                span: (begin_pos, end_pos),
                branches: Unary(Box::new(arg)),
            })
        } else {
            self.parse_product()
        }
    }

    fn parse_product(&mut self) -> CalcrResult<Ast> {
        let mut lhs = try!(self.parse_factor());
        self.consume_whitespace();
        while self.peek_char() == Some('*') || self.peek_char() == Some('/') {
            let op_pos = self.pos;
            let op = if self.consume_char() == '*' {
                Mult
            } else {
                Div
            };
            self.consume_whitespace();
            let rhs = try!(self.parse_factor());
            lhs = Ast {
                val: Op(op),
                span: (op_pos, op_pos),
                branches: Binary(Box::new(lhs), Box::new(rhs)),
            };
            self.consume_whitespace();
        }
        Ok(lhs)
    }

    fn parse_factor(&mut self) -> CalcrResult<Ast> {
        if self.peek_char() == Some('-') {
            let op_pos = self.pos;
            self.consume_char();
            let rhs = try!(self.parse_factor());
            Ok(Ast {
                val: Op(Neg),
                span: (op_pos, op_pos),
                branches: Unary(Box::new(rhs)),
            })
        } else {
            let lhs = try!(self.parse_exponent());
            if self.peek_char() == Some('^') {
                let op_pos = self.pos;
                self.consume_char();
                let rhs = try!(self.parse_factor());
                Ok(Ast {
                    val: Op(Pow),
                    span: (op_pos, op_pos),
                    branches: Binary(Box::new(lhs), Box::new(rhs)),
                })
            } else {
                Ok(lhs)
            }
        }
    }

    fn parse_exponent(&mut self) -> CalcrResult<Ast> {
        let mut out = try!(self.parse_number());
        self.consume_whitespace();

        // we don't parse the factorial signs (`!`) using recursion, since we need to put the
        // current `out` at the bottum of the tree at each step, so it is easier if we have access
        // to each node as we create them.
        while self.peek_char() == Some('!') {
            let op_pos = self.pos;
            self.consume_char();
            self.consume_whitespace();
            out = Ast {
                val: Op(Fact),
                span: (op_pos, op_pos),
                branches: Unary(Box::new(out)),
            };
        }
        Ok(out)
    }

    fn parse_number(&mut self) -> CalcrResult<Ast> {
        match self.peek_char() {
            Some('(') => {
                // store the current pos in case we need to report a paren error
                let pre_pos = self.pos;
                self.consume_char();
                self.consume_whitespace();
                self.paren_level += 1;
                let eq = try!(self.parse_equation());

                if self.eof() || self.consume_char() != ')' {
                    Err(CalcrError {
                        desc: "Missing closing parentheses".to_string(),
                        span: (pre_pos, pre_pos),
                    })
                } else {
                    self.paren_level -= 1;
                    Ok(eq)
                }
            },
            Some('|') => {
                // store the current pos in case we need to report an abs delim error
                let pre_pos = self.pos;
                self.consume_char();
                self.consume_whitespace();
                self.abs_level += 1;
                let eq = try!(self.parse_equation());

                if self.eof() || self.consume_char() != '|' {
                    Err(CalcrError {
                        desc: "Missing closing abs delimiter".to_string(),
                        span: (pre_pos, pre_pos),
                    })
                } else {
                    self.abs_level -= 1;
                    Ok(Ast {
                        val: Func(Abs),
                        span: (pre_pos, pre_pos),
                        branches: Unary(Box::new(eq)),
                    })
                }
            },
            Some(ch) if ch.is_alphabetic() => {
                let cnst_str = self.consume_while(|ch| ch.is_alphabetic());
                let cnst = match cnst_str.as_ref() {
                    "pi" => Pi,
                    "e" => E,
                    "phi" => Phi,
                    _ => return Err(CalcrError {
                        desc: format!("Invalid function or constant: {}", cnst_str),
                        span: (self.pos - cnst_str.len(), self.pos),
                    }),
                };
                Ok(Ast {
                    val: Const(cnst),
                    span: (self.pos - cnst_str.len(), self.pos),
                    branches: Leaf,
                })
            },
            Some(ch) if ch.is_numeric() => {
                let num_str = self.consume_while(|ch| ch.is_numeric() || ch == '.');
                if let Ok(num) = num_str.parse::<f64>() {
                    Ok(Ast {
                        val: Num(num),
                        span: (self.pos - num_str.len(), self.pos),
                        branches: Leaf,
                    })
                } else {
                    Err(CalcrError {
                        desc: format!("Invalid number: {}", num_str),
                        span: (self.pos - num_str.len(), self.pos),
                    })
                }
            },
            _ => Err(CalcrError {
                desc: format!("Expected number or constant"),
                span: (self.pos, self.pos),
            }),
        }
    }

    fn consume_function(&mut self) -> Option<AstFunc> {
        let pre_pos = self.pos;
        match self.consume_while(|ch| ch.is_alphabetic()).as_ref() {
            "sin" => Some(Sin),
            "cos" => Some(Cos),
            "tan" => Some(Tan),
            "asin" => Some(Asin),
            "acos" => Some(Acos),
            "atan" => Some(Atan),
            "sqrt" => Some(Sqrt),
            "abs" => Some(Abs),
            "exp" => Some(Exp),
            "ln" => Some(Ln),
            "log" => Some(Log),
            _ => {
                // no function found, so restore the previous position
                self.pos = pre_pos;
                None
            },
        }
    }

    /// Peeks at the next `char` and returns `Some` if one was found, or `None` if none are left
    fn peek_char(&mut self) -> Option<char> {
        self.iter.peek().map(|ch| *ch)
    }

    /// Consumes a `char` - thereby advanding `pos` - and returns it
    ///
    /// # Panics
    /// This function panics if there are no more chars to consume
    fn consume_char(&mut self) -> char {
        let ch = self.iter.next();
        self.pos += 1;
        ch.unwrap().to_lowercase().next().unwrap()
    }

    /// Consumes `char`s long as `pred` returns true and we are not eof
    ///
    /// The `char`s are returned as a `String`. Note that unlike `consume_char` this function will
    /// panic.
    fn consume_while<F>(&mut self, pred: F) -> String where F: Fn(char) -> bool {
        let mut out = String::new();
        loop {
            match self.peek_char() {
                Some(ch) if pred(ch) => out.push(self.consume_char()),
                _ => break,
            }
        }
        out
    }

    /// Consumes any current whitespace
    fn consume_whitespace(&mut self) {
        self.consume_while(|ch| ch.is_whitespace());
    }

    /// Returns true if we are the end of input
    fn eof(&mut self) -> bool {
        self.iter.peek().is_none()
    }
}<commit_msg>Fix error in doc comment<commit_after>//! The parser works based on the following grammar
//!
//! Equation ==> Term { "+" Term }
//!           |  Term { "-" Term }
//!
//! Term     ==> Function Product
//!           |  Product
//!
//! Product  ==> Factor { "*" Factor }
//!           |  Factor { "/" Factor }
//!
//! Factor   ==> "-" Factor
//!           |  Exponent { "^" Factor }
//!
//! Exponent ==> Number { "!" }
//!
//! Number   ==> "(" Equation ")"
//!           |  "|" Equation "|"
//!           |  Constant
//!           |  { Digit }
//!
//! Function ==> "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | "sqrt" | "abs" | "exp"
//!           |  "ln" | "log"
//!
//! Constant ==> "pi" | "e" | "phi"
//!
//! Digit    ==> "0".."9"

use std::str::Chars;
use std::iter::Peekable;
use errors::{CalcrResult, CalcrError};
use ast::{Ast, AstFunc};
use ast::AstBranch::*;
use ast::AstVal::*;
use ast::AstFunc::*;
use ast::AstOp::*;
use ast::AstConst::*;

pub fn parse_equation(eq: String) -> CalcrResult<Ast> {
	let mut parser = Parser {
		pos: 0,
		iter: eq.chars().peekable(),
		paren_level: 0,
		abs_level: 0,
	};
	parser.parse_equation()
}

pub struct Parser<'a> {
    pos: usize,
    iter: Peekable<Chars<'a>>,
    paren_level: u32,
    abs_level: u32,
}

impl<'a> Parser<'a> {
    fn parse_equation(&mut self) -> CalcrResult<Ast> {
        let mut lhs = try!(self.parse_term());
        self.consume_whitespace();
        while self.peek_char() == Some('+') || self.peek_char() == Some('-') {
            let op_pos = self.pos;
            let op = if self.consume_char() == '+' {
                Plus
            } else {
                Minus
            };
            self.consume_whitespace();
            let rhs = try!(self.parse_term());
            lhs = Ast {
                val: Op(op),
                span: (op_pos, op_pos),
                branches: Binary(Box::new(lhs), Box::new(rhs)),
            };
            self.consume_whitespace();
        }
        if self.peek_char() == Some(')') && self.paren_level < 1 {
            Err(CalcrError {
                desc: format!("Missing opening parentheses"),
                span: (self.pos, self.pos),
            })
        } else if self.peek_char() == Some('|') && self.abs_level < 1 {
            Err(CalcrError {
                desc: format!("Missing opening abs delimiter"),
                span: (self.pos, self.pos),
            })
        } else {
            Ok(lhs)
        }
    }

    fn parse_term(&mut self) -> CalcrResult<Ast> {
        let begin_pos = self.pos;
        if let Some(func) = self.consume_function() {
            let end_pos = self.pos;
            self.consume_whitespace();
            let arg = try!(self.parse_product());
            Ok(Ast {
                val: Func(func),
                span: (begin_pos, end_pos),
                branches: Unary(Box::new(arg)),
            })
        } else {
            self.parse_product()
        }
    }

    fn parse_product(&mut self) -> CalcrResult<Ast> {
        let mut lhs = try!(self.parse_factor());
        self.consume_whitespace();
        while self.peek_char() == Some('*') || self.peek_char() == Some('/') {
            let op_pos = self.pos;
            let op = if self.consume_char() == '*' {
                Mult
            } else {
                Div
            };
            self.consume_whitespace();
            let rhs = try!(self.parse_factor());
            lhs = Ast {
                val: Op(op),
                span: (op_pos, op_pos),
                branches: Binary(Box::new(lhs), Box::new(rhs)),
            };
            self.consume_whitespace();
        }
        Ok(lhs)
    }

    fn parse_factor(&mut self) -> CalcrResult<Ast> {
        if self.peek_char() == Some('-') {
            let op_pos = self.pos;
            self.consume_char();
            let rhs = try!(self.parse_factor());
            Ok(Ast {
                val: Op(Neg),
                span: (op_pos, op_pos),
                branches: Unary(Box::new(rhs)),
            })
        } else {
            let lhs = try!(self.parse_exponent());
            if self.peek_char() == Some('^') {
                let op_pos = self.pos;
                self.consume_char();
                let rhs = try!(self.parse_factor());
                Ok(Ast {
                    val: Op(Pow),
                    span: (op_pos, op_pos),
                    branches: Binary(Box::new(lhs), Box::new(rhs)),
                })
            } else {
                Ok(lhs)
            }
        }
    }

    fn parse_exponent(&mut self) -> CalcrResult<Ast> {
        let mut out = try!(self.parse_number());
        self.consume_whitespace();

        // we don't parse the factorial signs (`!`) using recursion, since we need to put the
        // current `out` at the bottum of the tree at each step, so it is easier if we have access
        // to each node as we create them.
        while self.peek_char() == Some('!') {
            let op_pos = self.pos;
            self.consume_char();
            self.consume_whitespace();
            out = Ast {
                val: Op(Fact),
                span: (op_pos, op_pos),
                branches: Unary(Box::new(out)),
            };
        }
        Ok(out)
    }

    fn parse_number(&mut self) -> CalcrResult<Ast> {
        match self.peek_char() {
            Some('(') => {
                // store the current pos in case we need to report a paren error
                let pre_pos = self.pos;
                self.consume_char();
                self.consume_whitespace();
                self.paren_level += 1;
                let eq = try!(self.parse_equation());

                if self.eof() || self.consume_char() != ')' {
                    Err(CalcrError {
                        desc: "Missing closing parentheses".to_string(),
                        span: (pre_pos, pre_pos),
                    })
                } else {
                    self.paren_level -= 1;
                    Ok(eq)
                }
            },
            Some('|') => {
                // store the current pos in case we need to report an abs delim error
                let pre_pos = self.pos;
                self.consume_char();
                self.consume_whitespace();
                self.abs_level += 1;
                let eq = try!(self.parse_equation());

                if self.eof() || self.consume_char() != '|' {
                    Err(CalcrError {
                        desc: "Missing closing abs delimiter".to_string(),
                        span: (pre_pos, pre_pos),
                    })
                } else {
                    self.abs_level -= 1;
                    Ok(Ast {
                        val: Func(Abs),
                        span: (pre_pos, pre_pos),
                        branches: Unary(Box::new(eq)),
                    })
                }
            },
            Some(ch) if ch.is_alphabetic() => {
                let cnst_str = self.consume_while(|ch| ch.is_alphabetic());
                let cnst = match cnst_str.as_ref() {
                    "pi" => Pi,
                    "e" => E,
                    "phi" => Phi,
                    _ => return Err(CalcrError {
                        desc: format!("Invalid function or constant: {}", cnst_str),
                        span: (self.pos - cnst_str.len(), self.pos),
                    }),
                };
                Ok(Ast {
                    val: Const(cnst),
                    span: (self.pos - cnst_str.len(), self.pos),
                    branches: Leaf,
                })
            },
            Some(ch) if ch.is_numeric() => {
                let num_str = self.consume_while(|ch| ch.is_numeric() || ch == '.');
                if let Ok(num) = num_str.parse::<f64>() {
                    Ok(Ast {
                        val: Num(num),
                        span: (self.pos - num_str.len(), self.pos),
                        branches: Leaf,
                    })
                } else {
                    Err(CalcrError {
                        desc: format!("Invalid number: {}", num_str),
                        span: (self.pos - num_str.len(), self.pos),
                    })
                }
            },
            _ => Err(CalcrError {
                desc: format!("Expected number or constant"),
                span: (self.pos, self.pos),
            }),
        }
    }

    fn consume_function(&mut self) -> Option<AstFunc> {
        let pre_pos = self.pos;
        match self.consume_while(|ch| ch.is_alphabetic()).as_ref() {
            "sin" => Some(Sin),
            "cos" => Some(Cos),
            "tan" => Some(Tan),
            "asin" => Some(Asin),
            "acos" => Some(Acos),
            "atan" => Some(Atan),
            "sqrt" => Some(Sqrt),
            "abs" => Some(Abs),
            "exp" => Some(Exp),
            "ln" => Some(Ln),
            "log" => Some(Log),
            _ => {
                // no function found, so restore the previous position
                self.pos = pre_pos;
                None
            },
        }
    }

    /// Peeks at the next `char` and returns `Some` if one was found, or `None` if none are left
    fn peek_char(&mut self) -> Option<char> {
        self.iter.peek().map(|ch| *ch)
    }

    /// Consumes a `char` - thereby advanding `pos` - and returns it
    ///
    /// # Panics
    /// This function panics if there are no more chars to consume
    fn consume_char(&mut self) -> char {
        let ch = self.iter.next();
        self.pos += 1;
        ch.unwrap().to_lowercase().next().unwrap()
    }

    /// Consumes `char`s long as `pred` returns true and we are not eof
    ///
    /// The `char`s are returned as a `String`. Note that unlike `consume_char` this function will
    /// not panic.
    fn consume_while<F>(&mut self, pred: F) -> String where F: Fn(char) -> bool {
        let mut out = String::new();
        loop {
            match self.peek_char() {
                Some(ch) if pred(ch) => out.push(self.consume_char()),
                _ => break,
            }
        }
        out
    }

    /// Consumes any current whitespace
    fn consume_whitespace(&mut self) {
        self.consume_while(|ch| ch.is_whitespace());
    }

    /// Returns true if we are the end of input
    fn eof(&mut self) -> bool {
        self.iter.peek().is_none()
    }
}<|endoftext|>


<commit_before>//! XML-RPC response parser.

use {Value, Response, Fault};
use error::ParseError;

use base64;
use xml::reader::{XmlEvent, EventReader};
use xml::name::OwnedName;
use xml::common::Position;
use xml::ParserConfig;
use iso8601::datetime;
use std::io::{self, ErrorKind, Read};
use std::collections::BTreeMap;

pub type ParseResult<T> = Result<T, ParseError>;

pub struct Parser<'a, R: Read + 'a> {
    reader: EventReader<&'a mut R>,
}

impl<'a, R: Read> Parser<'a, R> {
    pub fn new(reader: &'a mut R) -> Self {
        Parser {
            reader: EventReader::new_with_config(reader, ParserConfig {
                cdata_to_characters: true,
                ..Default::default()
            }),
        }
    }

    /// Reads an `XmlEvent` from a reader, disposing events we want to ignore.
    fn pull_event(&mut self) -> ParseResult<XmlEvent> {
        loop {
            let event = self.reader.next()?;
            match event {
                XmlEvent::StartDocument { .. }
                | XmlEvent::Comment(_)
                | XmlEvent::Whitespace(_)
                | XmlEvent::ProcessingInstruction { .. } => {},
                XmlEvent::StartElement { .. }
                | XmlEvent::EndElement { .. }
                | XmlEvent::EndDocument
                | XmlEvent::CData(_)
                | XmlEvent::Characters(_) => {
                    return Ok(event);
                }
            }
        }
    }

    /// Expects an opening tag like `<tag>` without attributes (and a local name without namespaces).
    fn expect_open(&mut self, tag: &str) -> ParseResult<()> {
        match self.pull_event()? {
            XmlEvent::StartElement { ref name, ref attributes, .. }
            if name == &OwnedName::local(tag) => {
                if !attributes.is_empty() {
                    return self.unexpected(format!("unexpected attributes in <{}>", tag));
                }

                Ok(())
            }
            _ => return self.unexpected(format!("expected <{}>", tag)),
        }
    }

    /// Expects a closing tag like `</tag>` with a local name without namespaces.
    fn expect_close(&mut self, tag: &str) -> ParseResult<()> {
        match self.pull_event()? {
            XmlEvent::EndElement { ref name } if name == &OwnedName::local(tag) => {
                Ok(())
            }
            _ => self.unexpected(format!("expected </{}>", tag)),
        }
    }

    /// Builds and returns an `Err(UnexpectedXml)`.
    fn unexpected<T, E: ToString>(&self, expected: E) -> ParseResult<T> {
        let expected = expected.to_string();
        let position = self.reader.position();

        Err(ParseError::UnexpectedXml {
            expected: expected,
            position: position,
        })
    }

    fn parse_response(&mut self) -> ParseResult<Response> {
        let response: Response;

        // <methodResponse>
        self.expect_open("methodResponse")?;

        // <fault> / <params>
        match self.pull_event()? {
            XmlEvent::StartElement { ref name, ref attributes, .. } => {
                if !attributes.is_empty() {
                    return self.unexpected("unexpected attributes");
                }

                if name == &OwnedName::local("fault") {
                    let value = self.parse_value()?;
                    let fault = Fault::from_value(&value).ok_or_else(|| {
                        io::Error::new(ErrorKind::Other, "malformed <fault>")
                    })?;
                    response = Err(fault);
                } else if name == &OwnedName::local("params") {
                    // <param>
                    self.expect_open("param")?;

                    let value = self.parse_value()?;
                    response = Ok(value);

                    // </param>
                    self.expect_close("param")?;
                } else {
                    return self.unexpected(format!("expected <fault> or <params>, got {}", name));
                }
            }
            _ => return self.unexpected("expected <fault> or <params>"),
        }

        Ok(response)
    }

    pub fn parse_value(&mut self) -> ParseResult<Value> {
        // <value>
        self.expect_open("value")?;

        let value = self.parse_value_inner()?;

        // </value>
        self.expect_close("value")?;

        Ok(value)
    }

    fn parse_value_inner(&mut self) -> ParseResult<Value> {
        let value: Value;

        // Raw string or specific type tag
        value = match self.pull_event()? {
            XmlEvent::StartElement { ref name, ref attributes, .. } => {
                if !attributes.is_empty() {
                    return self.unexpected(format!("unexpected attributes in <{}>", name));
                }

                if name == &OwnedName::local("struct") {
                    let mut members = BTreeMap::new();
                    loop {
                        match self.pull_event()? {
                            XmlEvent::EndElement { ref name } if name == &OwnedName::local("struct") => break,
                            XmlEvent::StartElement { ref name, ref attributes, .. } if name == &OwnedName::local("member") => {
                                // <member>
                                if !attributes.is_empty() {
                                    return self.unexpected(format!("unexpected attributes in <{}>", name));
                                }

                                // <name>NAME</name>
                                self.expect_open("name")?;
                                let name = match self.pull_event()? {
                                    XmlEvent::Characters(string) => string,
                                    _ => return self.unexpected("expected CDATA"),
                                };
                                self.expect_close("name")?;

                                // Value
                                let value = self.parse_value()?;

                                // </member>
                                self.expect_close("member")?;

                                members.insert(name, value);
                            }
                            _ => return self.unexpected("expected </struct> or <member>"),
                        }
                    }

                    Value::Struct(members)
                } else if name == &OwnedName::local("array") {
                    let mut elements: Vec<Value> = Vec::new();
                    self.expect_open("data")?;
                    loop {
                        match self.pull_event()? {
                            XmlEvent::EndElement { ref name } if name == &OwnedName::local("data") => break,
                            XmlEvent::StartElement { ref name, .. } if name == &OwnedName::local("value") => {
                                elements.push(self.parse_value_inner()?);
                                self.expect_close("value")?;
                            }
                            _event => return self.unexpected("expected </data> or <value>")
                        }
                    }
                    self.expect_close("array")?;
                    Value::Array(elements)
                } else if name == &OwnedName::local("nil") {
                    self.expect_close("nil")?;
                    Value::Nil
                } else {
                    // All other types expect raw characters...
                    let data = match self.pull_event()? {
                        XmlEvent::Characters(string) => string,
                        _ => return self.unexpected("expected characters"),
                    };

                    // ...and a corresponding close tag
                    self.expect_close(&name.local_name)?;

                    if name == &OwnedName::local("i4") || name == &OwnedName::local("int") {
                        Value::Int(data.parse::<i32>().map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for integer: {}", data))
                        })?)
                    } else if name == &OwnedName::local("i8") {
                        Value::Int64(data.parse::<i64>().map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for 64-bit integer: {}", data))
                        })?)
                    } else if name == &OwnedName::local("boolean") {
                        let val = match data.trim() {
                            "0" => false,
                            "1" => true,
                            _ => return Err(io::Error::new(ErrorKind::Other, format!("invalid value for <boolean>: {}", data)).into())
                        };

                        Value::Bool(val)
                    } else if name == &OwnedName::local("string") {
                        Value::String(data.clone())
                    } else if name == &OwnedName::local("double") {
                        Value::Double(data.parse::<f64>().map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for double: {}", data))
                        })?)
                    } else if name == &OwnedName::local("dateTime.iso8601") {
                        Value::DateTime(datetime(&data).map_err(|e| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for dateTime.iso8601: {} ({})", data, e))
                        })?)
                    } else if name == &OwnedName::local("base64") {
                        Value::Base64(base64::decode(&data).map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for base64: {}", data))
                        })?)
                    } else {
                        return self.unexpected("invalid <value> content");
                    }
                }
            }
            XmlEvent::Characters(string) => {
                Value::String(string)
            }
            _ => return self.unexpected("invalid <value> content"),
        };

        Ok(value)
    }
}

/// Parses a response from an XML reader.
pub fn parse_response<R: Read>(reader: &mut R) -> ParseResult<Response> {
    Parser::new(reader).parse_response()
}

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

    use {Response, Value};
    use error::Fault;
    use std::fmt::Debug;

    fn read_response(xml: &str) -> ParseResult<Response> {
        parse_response(&mut xml.as_bytes())
    }

    fn read_value(xml: &str) -> ParseResult<Value> {
        Parser::new(&mut xml.as_bytes()).parse_value()
    }

    fn assert_ok<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => println!("assert_ok successful on Ok: {:?}", t),
            Err(e) => panic!("assert_ok called on Err value: {:?}", e),
        }
    }

    fn assert_err<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => panic!("assert_err called on Ok value: {:?}", t),
            Err(e) => println!("assert_err successful on Err: {:?}", e),
        }
    }

    #[test]
    fn parses_fault() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn ignores_additional_fault_fields() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            <member>
               <name>unnecessaryParameter</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn rejects_invalid_faults() {
        // Make sure to reject type errors in <fault>s - They're specified to contain specifically
        // typed fields.
        assert!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><string>I'm not an int!</string></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##).is_err());
    }

    #[test]
    fn parses_string_value_with_whitespace() {
        assert_eq!(read_value("<value><string>  I'm a string!  </string></value>"),
            Ok(Value::String("  I'm a string!  ".into())));
    }

    #[test]
    fn parses_64bit_int() {
        assert_eq!(read_value("<value><i8>12345</i8></value>"),
            Ok(Value::Int64(12345)));
        assert_eq!(read_value("<value><i8>-100100100100</i8></value>"),
            Ok(Value::Int64(-100100100100)));
    }

    #[test]
    fn parses_date_values() {
        assert_ok(read_value("<value><dateTime.iso8601>2015-02-18T23:16:09Z</dateTime.iso8601></value>"));
        assert_ok(read_value("<value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>"));
    }

    #[test]
    fn parses_array_values() {
        assert_eq!(read_value(r#"
                <value><array><data>
                    <value><i4>5</i4></value>
                    <value><string>a</string></value>
                </data></array></value>"#),
            Ok(Value::Array(vec![Value::Int(5), Value::String("a".into())])));
    }

    #[test]
    fn parses_raw_value_as_string() {
        assert_eq!(read_value("<value>\t  I'm a string!  </value>"),
            Ok(Value::String("\t  I'm a string!  ".into())));
    }

    #[test]
    fn parses_nil_values() {
        assert_eq!(read_value("<value><nil/></value>"), Ok(Value::Nil));
        assert_eq!(read_value("<value><nil></nil></value>"), Ok(Value::Nil));
    }

    #[test]
    fn unescapes_values() {
        assert_eq!(read_value("<value><string>abc<abc&abc</string></value>"),
            Ok(Value::String("abc<abc&abc".into())));
    }

    #[test]
    fn rejects_value_with_attributes() {
        // XXX we *should* reject everything with attributes (right?)
        assert_err(read_value(r#"<value name="ble">\t  I'm a string!  </value>"#));
    }
}
<commit_msg>Handle empty string/base64 values<commit_after>//! XML-RPC response parser.

use {Value, Response, Fault};
use error::ParseError;

use base64;
use xml::reader::{XmlEvent, EventReader};
use xml::name::OwnedName;
use xml::common::Position;
use xml::ParserConfig;
use iso8601::datetime;
use std::io::{self, ErrorKind, Read};
use std::collections::BTreeMap;

pub type ParseResult<T> = Result<T, ParseError>;

pub struct Parser<'a, R: Read + 'a> {
    reader: EventReader<&'a mut R>,
}

impl<'a, R: Read> Parser<'a, R> {
    pub fn new(reader: &'a mut R) -> Self {
        Parser {
            reader: EventReader::new_with_config(reader, ParserConfig {
                cdata_to_characters: true,
                ..Default::default()
            }),
        }
    }

    /// Reads an `XmlEvent` from a reader, disposing events we want to ignore.
    fn pull_event(&mut self) -> ParseResult<XmlEvent> {
        loop {
            let event = self.reader.next()?;
            match event {
                XmlEvent::StartDocument { .. }
                | XmlEvent::Comment(_)
                | XmlEvent::Whitespace(_)
                | XmlEvent::ProcessingInstruction { .. } => {},
                XmlEvent::StartElement { .. }
                | XmlEvent::EndElement { .. }
                | XmlEvent::EndDocument
                | XmlEvent::CData(_)
                | XmlEvent::Characters(_) => {
                    return Ok(event);
                }
            }
        }
    }

    /// Expects an opening tag like `<tag>` without attributes (and a local name without namespaces).
    fn expect_open(&mut self, tag: &str) -> ParseResult<()> {
        match self.pull_event()? {
            XmlEvent::StartElement { ref name, ref attributes, .. }
            if name == &OwnedName::local(tag) => {
                if !attributes.is_empty() {
                    return self.unexpected(format!("unexpected attributes in <{}>", tag));
                }

                Ok(())
            }
            _ => return self.unexpected(format!("expected <{}>", tag)),
        }
    }

    /// Expects a closing tag like `</tag>` with a local name without namespaces.
    fn expect_close(&mut self, tag: &str) -> ParseResult<()> {
        match self.pull_event()? {
            XmlEvent::EndElement { ref name } if name == &OwnedName::local(tag) => {
                Ok(())
            }
            _ => self.unexpected(format!("expected </{}>", tag)),
        }
    }

    /// Builds and returns an `Err(UnexpectedXml)`.
    fn unexpected<T, E: ToString>(&self, expected: E) -> ParseResult<T> {
        let expected = expected.to_string();
        let position = self.reader.position();

        Err(ParseError::UnexpectedXml {
            expected: expected,
            position: position,
        })
    }

    fn parse_response(&mut self) -> ParseResult<Response> {
        let response: Response;

        // <methodResponse>
        self.expect_open("methodResponse")?;

        // <fault> / <params>
        match self.pull_event()? {
            XmlEvent::StartElement { ref name, ref attributes, .. } => {
                if !attributes.is_empty() {
                    return self.unexpected("unexpected attributes");
                }

                if name == &OwnedName::local("fault") {
                    let value = self.parse_value()?;
                    let fault = Fault::from_value(&value).ok_or_else(|| {
                        io::Error::new(ErrorKind::Other, "malformed <fault>")
                    })?;
                    response = Err(fault);
                } else if name == &OwnedName::local("params") {
                    // <param>
                    self.expect_open("param")?;

                    let value = self.parse_value()?;
                    response = Ok(value);

                    // </param>
                    self.expect_close("param")?;
                } else {
                    return self.unexpected(format!("expected <fault> or <params>, got {}", name));
                }
            }
            _ => return self.unexpected("expected <fault> or <params>"),
        }

        Ok(response)
    }

    pub fn parse_value(&mut self) -> ParseResult<Value> {
        // <value>
        self.expect_open("value")?;

        let value = self.parse_value_inner()?;

        // </value>
        self.expect_close("value")?;

        Ok(value)
    }

    fn parse_value_inner(&mut self) -> ParseResult<Value> {
        let value: Value;

        // Raw string or specific type tag
        value = match self.pull_event()? {
            XmlEvent::StartElement { ref name, ref attributes, .. } => {
                if !attributes.is_empty() {
                    return self.unexpected(format!("unexpected attributes in <{}>", name));
                }

                if name == &OwnedName::local("struct") {
                    let mut members = BTreeMap::new();
                    loop {
                        match self.pull_event()? {
                            XmlEvent::EndElement { ref name } if name == &OwnedName::local("struct") => break,
                            XmlEvent::StartElement { ref name, ref attributes, .. } if name == &OwnedName::local("member") => {
                                // <member>
                                if !attributes.is_empty() {
                                    return self.unexpected(format!("unexpected attributes in <{}>", name));
                                }

                                // <name>NAME</name>
                                self.expect_open("name")?;
                                let name = match self.pull_event()? {
                                    XmlEvent::Characters(string) => string,
                                    _ => return self.unexpected("expected CDATA"),
                                };
                                self.expect_close("name")?;

                                // Value
                                let value = self.parse_value()?;

                                // </member>
                                self.expect_close("member")?;

                                members.insert(name, value);
                            }
                            _ => return self.unexpected("expected </struct> or <member>"),
                        }
                    }

                    Value::Struct(members)
                } else if name == &OwnedName::local("array") {
                    let mut elements: Vec<Value> = Vec::new();
                    self.expect_open("data")?;
                    loop {
                        match self.pull_event()? {
                            XmlEvent::EndElement { ref name } if name == &OwnedName::local("data") => break,
                            XmlEvent::StartElement { ref name, .. } if name == &OwnedName::local("value") => {
                                elements.push(self.parse_value_inner()?);
                                self.expect_close("value")?;
                            }
                            _event => return self.unexpected("expected </data> or <value>")
                        }
                    }
                    self.expect_close("array")?;
                    Value::Array(elements)
                } else if name == &OwnedName::local("nil") {
                    self.expect_close("nil")?;
                    Value::Nil
                } else if name == &OwnedName::local("string") {
                    let string = match self.pull_event()? {
                        XmlEvent::Characters(string) => {
                            self.expect_close(&name.local_name)?;
                            string
                        },
                        XmlEvent::EndElement { name: ref end_name } if end_name == name => String::new(),
                        _ => return self.unexpected("expected characters or </string>"),
                    };
                    Value::String(string)
                } else if name == &OwnedName::local("base64") {
                    let data = match self.pull_event()? {
                        XmlEvent::Characters(ref string) => base64::decode(string).map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for base64: {}", string))
                        })?,
                        XmlEvent::EndElement { name: ref end_name } if end_name == name => Vec::new(),
                        _ => return self.unexpected("expected characters or </base64>"),
                    };
                    Value::Base64(data)
                } else {
                    // All other types expect raw characters...
                    let data = match self.pull_event()? {
                        XmlEvent::Characters(string) => string,
                        _ => return self.unexpected("expected characters"),
                    };

                    // ...and a corresponding close tag
                    self.expect_close(&name.local_name)?;

                    if name == &OwnedName::local("i4") || name == &OwnedName::local("int") {
                        Value::Int(data.parse::<i32>().map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for integer: {}", data))
                        })?)
                    } else if name == &OwnedName::local("i8") {
                        Value::Int64(data.parse::<i64>().map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for 64-bit integer: {}", data))
                        })?)
                    } else if name == &OwnedName::local("boolean") {
                        let val = match data.trim() {
                            "0" => false,
                            "1" => true,
                            _ => return Err(io::Error::new(ErrorKind::Other, format!("invalid value for <boolean>: {}", data)).into())
                        };

                        Value::Bool(val)
                    } else if name == &OwnedName::local("double") {
                        Value::Double(data.parse::<f64>().map_err(|_| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for double: {}", data))
                        })?)
                    } else if name == &OwnedName::local("dateTime.iso8601") {
                        Value::DateTime(datetime(&data).map_err(|e| {
                            io::Error::new(ErrorKind::Other, format!("invalid value for dateTime.iso8601: {} ({})", data, e))
                        })?)
                    } else {
                        return self.unexpected("invalid <value> content");
                    }
                }
            }
            XmlEvent::Characters(string) => {
                Value::String(string)
            }
            _ => return self.unexpected("invalid <value> content"),
        };

        Ok(value)
    }
}

/// Parses a response from an XML reader.
pub fn parse_response<R: Read>(reader: &mut R) -> ParseResult<Response> {
    Parser::new(reader).parse_response()
}

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

    use {Response, Value};
    use error::Fault;
    use std::fmt::Debug;

    fn read_response(xml: &str) -> ParseResult<Response> {
        parse_response(&mut xml.as_bytes())
    }

    fn read_value(xml: &str) -> ParseResult<Value> {
        Parser::new(&mut xml.as_bytes()).parse_value()
    }

    fn assert_ok<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => println!("assert_ok successful on Ok: {:?}", t),
            Err(e) => panic!("assert_ok called on Err value: {:?}", e),
        }
    }

    fn assert_err<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => panic!("assert_err called on Ok value: {:?}", t),
            Err(e) => println!("assert_err successful on Err: {:?}", e),
        }
    }

    #[test]
    fn parses_fault() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn ignores_additional_fault_fields() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            <member>
               <name>unnecessaryParameter</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn rejects_invalid_faults() {
        // Make sure to reject type errors in <fault>s - They're specified to contain specifically
        // typed fields.
        assert!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><string>I'm not an int!</string></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##).is_err());
    }

    #[test]
    fn parses_string_value_with_whitespace() {
        assert_eq!(read_value("<value><string>  I'm a string!  </string></value>"),
            Ok(Value::String("  I'm a string!  ".into())));
    }

    #[test]
    fn parses_64bit_int() {
        assert_eq!(read_value("<value><i8>12345</i8></value>"),
            Ok(Value::Int64(12345)));
        assert_eq!(read_value("<value><i8>-100100100100</i8></value>"),
            Ok(Value::Int64(-100100100100)));
    }

    #[test]
    fn parses_int_with_plus_sign() {
        // "You can include a plus or minus at the beginning of a string of numeric characters."
        assert_eq!(read_value("<value><int>+1234</int></value>"),
            Ok(Value::Int(1234)));
    }

    #[test]
    fn parses_date_values() {
        assert_ok(read_value("<value><dateTime.iso8601>2015-02-18T23:16:09Z</dateTime.iso8601></value>"));
        assert_ok(read_value("<value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>"));
        assert_err(read_value("<value><dateTime.iso8601></dateTime.iso8601></value>"));
        assert_err(read_value("<value><dateTime.iso8601>ILLEGAL VALUE :(</dateTime.iso8601></value>"));
    }

    #[test]
    fn parses_array_values() {
        assert_eq!(read_value(r#"
                <value><array><data>
                    <value><i4>5</i4></value>
                    <value><string>a</string></value>
                </data></array></value>"#),
            Ok(Value::Array(vec![Value::Int(5), Value::String("a".into())])));
    }

    #[test]
    fn parses_raw_value_as_string() {
        assert_eq!(read_value("<value>\t  I'm a string!  </value>"),
            Ok(Value::String("\t  I'm a string!  ".into())));
        // FIXME: Are empty <value>-tags also supposed to be parsed as empty strings?
    }

    #[test]
    fn parses_nil_values() {
        assert_eq!(read_value("<value><nil/></value>"), Ok(Value::Nil));
        assert_eq!(read_value("<value><nil></nil></value>"), Ok(Value::Nil));
        assert_err(read_value("<value><nil>ILLEGAL</nil></value>"));
    }

    #[test]
    fn unescapes_values() {
        assert_eq!(read_value("<value><string>abc<abc&abc</string></value>"),
            Ok(Value::String("abc<abc&abc".into())));
    }

    #[test]
    fn parses_empty_string() {
        assert_eq!(read_value("<value><string></string></value>"),
            Ok(Value::String(String::new())));
        assert_eq!(read_value("<value><string/></value>"),
            Ok(Value::String(String::new())));
    }

    #[test]
    fn parses_empty_base64() {
        assert_eq!(read_value("<value><base64></base64></value>"),
            Ok(Value::Base64(Vec::new())));
        assert_eq!(read_value("<value><base64/></value>"),
            Ok(Value::Base64(Vec::new())));
    }

    #[test]
    fn rejects_value_with_attributes() {
        // XXX we *should* reject everything with attributes (right?)
        assert_err(read_value(r#"<value name="ble">\t  I'm a string!  </value>"#));
    }
}
<|endoftext|>