<commit_before>use std::{cmp, fmt, mem, ptr};
use std::num::UnsignedInt;
use std::old_io::IoResult;
use std::raw::Slice as RawSlice;
use alloc::heap;
use super::{Buf, MutBuf};

/// Buf backed by a continous chunk of memory. Maintains a read cursor and a
/// write cursor. When reads and writes reach the end of the allocated buffer,
/// wraps around to the start.
pub struct RingBuf {
    ptr: *mut u8,  // Pointer to the memory
    cap: usize,     // Capacity of the buffer
    pos: usize,     // Offset of read cursor
    len: usize      // Number of bytes to read
}

// TODO: There are most likely many optimizations that can be made
impl RingBuf {
    pub fn new(mut capacity: usize) -> RingBuf {
        // Handle the 0 length buffer case
        if capacity == 0 {
            return RingBuf {
                ptr: ptr::null_mut(),
                cap: 0,
                pos: 0,
                len: 0
            }
        }

        // Round to the next power of 2 for better alignment
        capacity = UnsignedInt::next_power_of_two(capacity);

        // Allocate the memory
        let ptr = unsafe { heap::allocate(capacity, mem::min_align_of::<u8>()) };

        RingBuf {
            ptr: ptr as *mut u8,
            cap: capacity,
            pos: 0,
            len: 0
        }
    }

    pub fn is_full(&self) -> bool {
        self.cap == self.len
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    #[inline]
    pub fn capacity(&self) -> usize {
        self.cap
    }

    // Access readable bytes as a Buf
    #[inline]
    pub fn reader<'a>(&'a mut self) -> RingBufReader<'a> {
        RingBufReader { ring: self }
    }

    // Access writable bytes as a Buf
    #[inline]
    pub fn writer<'a>(&'a mut self) -> RingBufWriter<'a> {
        RingBufWriter { ring: self }
    }

    #[inline]
    fn read_remaining(&self) -> usize {
        self.len
    }

    #[inline]
    fn write_remaining(&self) -> usize {
        self.cap - self.len
    }

    fn advance_reader(&mut self, mut cnt: usize) {
        cnt = cmp::min(cnt, self.read_remaining());

        self.pos += cnt;
        self.pos %= self.cap;
        self.len -= cnt;
    }

    #[inline]
    fn advance_writer(&mut self, mut cnt: usize) {
        cnt = cmp::min(cnt, self.write_remaining());
        self.len += cnt;
    }

    fn as_ptr(&self) -> *const u8 {
        self.ptr as *const u8
    }

    fn as_slice<'a>(&'a self) -> &'a [u8] {
        unsafe {
            mem::transmute(RawSlice {
                data: self.as_ptr(), len: self.cap
            })
        }
    }

    fn as_mut_slice<'a>(&'a mut self) -> &'a mut [u8] {
        unsafe { mem::transmute(self.as_slice()) }
    }
}

impl Clone for RingBuf {
    fn clone(&self) -> RingBuf {
        use std::cmp;

        let mut ret = RingBuf::new(self.cap);

        ret.pos = self.pos;
        ret.len = self.len;

        unsafe {
            let to = self.pos + self.len;

            if to > self.cap {
                ptr::copy_memory(ret.ptr, self.ptr as *const u8, to % self.cap);
            }

            ptr::copy_memory(
                ret.ptr.offset(self.pos as isize),
                self.ptr.offset(self.pos as isize) as *const u8,
                cmp::min(self.len, self.cap - self.pos));
        }

        ret
    }

    // TODO: an improved version of clone_from is possible that potentially
    // re-uses the buffer
}

impl fmt::Debug for RingBuf {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "RingBuf[.. {}]", self.len)
    }
}

impl Drop for RingBuf {
    fn drop(&mut self) {
        if self.cap > 0 {
            unsafe {
                heap::deallocate(self.ptr, self.cap, mem::min_align_of::<u8>())
            }
        }
    }
}

pub struct RingBufReader<'a> {
    ring: &'a mut RingBuf
}

impl<'a> Buf for RingBufReader<'a> {
    #[inline]
    fn remaining(&self) -> usize {
        self.ring.read_remaining()
    }

    fn bytes<'b>(&'b self) -> &'b [u8] {
        let mut to = self.ring.pos + self.ring.len;

        if to > self.ring.cap {
            to = self.ring.cap
        }

        &self.ring.as_slice()[self.ring.pos..to]
    }

    fn advance(&mut self, cnt: usize) {
        self.ring.advance_reader(cnt)
    }
}

impl<'a> Reader for RingBufReader<'a> {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        super::read(self, buf)
    }
}

pub struct RingBufWriter<'a> {
    ring: &'a mut RingBuf
}

impl<'a> Buf for RingBufWriter<'a> {
    #[inline]
    fn remaining(&self) -> usize {
        self.ring.write_remaining()
    }

    fn bytes<'b>(&'b self) -> &'b [u8] {
        let mut from;
        let mut to;

        from = self.ring.pos + self.ring.len;
        from %= self.ring.cap;

        to = from + self.remaining();

        if to >= self.ring.cap {
            to = self.ring.cap;
        }

        &self.ring.as_slice()[from..to]
    }

    fn advance(&mut self, cnt: usize) {
        self.ring.advance_writer(cnt)
    }
}

impl<'a> MutBuf for RingBufWriter<'a> {
    fn mut_bytes<'b>(&'b mut self) -> &'b mut [u8] {
        unsafe { mem::transmute(self.bytes()) }
    }
}

impl<'a> Writer for RingBufWriter<'a> {
    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
        super::write(self, buf)
    }
}

unsafe impl Send for RingBuf { }

#[cfg(test)]
mod test {
    use std::old_io::EndOfFile;
    use buf::{Buf, RingBuf};

    #[test]
    pub fn test_initial_buf_empty() {
        let mut buf = RingBuf::new(100);

        assert!(buf.capacity() == 128);
        assert!(buf.reader().bytes().is_empty());
    }

    #[test]
    pub fn test_using_ring_buffer() {
        let mut buf = RingBuf::new(128);

        buf.writer().write_all(b"hello").unwrap();
        assert!(buf.writer().remaining() == 123);

        let read = buf.reader().read_exact(5).unwrap();
        assert!(read.as_slice() == b"hello");
    }

    #[test]
    pub fn test_restarting_ring_buffer() {
        let mut buf = RingBuf::new(8);

        buf.writer().write_all(b"hello").unwrap();
        assert!(buf.writer().remaining() == 3);

        let read = buf.reader().read_exact(5).unwrap();
        assert!(read.as_slice() == b"hello");
        assert!(buf.writer().remaining() == 8, "actual={}", buf.writer().remaining());
    }

    #[test]
    pub fn test_overflowing_ring_buffer() {
        let mut buf = RingBuf::new(8);

        buf.writer().write_all(b"hello").unwrap();
        assert!(buf.writer().write(b"world").unwrap_err().kind == EndOfFile);
    }
}
<commit_msg>Fix a warning in the ring buf test<commit_after>use std::{cmp, fmt, mem, ptr};
use std::num::UnsignedInt;
use std::old_io::IoResult;
use std::raw::Slice as RawSlice;
use alloc::heap;
use super::{Buf, MutBuf};

/// Buf backed by a continous chunk of memory. Maintains a read cursor and a
/// write cursor. When reads and writes reach the end of the allocated buffer,
/// wraps around to the start.
pub struct RingBuf {
    ptr: *mut u8,  // Pointer to the memory
    cap: usize,     // Capacity of the buffer
    pos: usize,     // Offset of read cursor
    len: usize      // Number of bytes to read
}

// TODO: There are most likely many optimizations that can be made
impl RingBuf {
    pub fn new(mut capacity: usize) -> RingBuf {
        // Handle the 0 length buffer case
        if capacity == 0 {
            return RingBuf {
                ptr: ptr::null_mut(),
                cap: 0,
                pos: 0,
                len: 0
            }
        }

        // Round to the next power of 2 for better alignment
        capacity = UnsignedInt::next_power_of_two(capacity);

        // Allocate the memory
        let ptr = unsafe { heap::allocate(capacity, mem::min_align_of::<u8>()) };

        RingBuf {
            ptr: ptr as *mut u8,
            cap: capacity,
            pos: 0,
            len: 0
        }
    }

    pub fn is_full(&self) -> bool {
        self.cap == self.len
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    #[inline]
    pub fn capacity(&self) -> usize {
        self.cap
    }

    // Access readable bytes as a Buf
    #[inline]
    pub fn reader<'a>(&'a mut self) -> RingBufReader<'a> {
        RingBufReader { ring: self }
    }

    // Access writable bytes as a Buf
    #[inline]
    pub fn writer<'a>(&'a mut self) -> RingBufWriter<'a> {
        RingBufWriter { ring: self }
    }

    #[inline]
    fn read_remaining(&self) -> usize {
        self.len
    }

    #[inline]
    fn write_remaining(&self) -> usize {
        self.cap - self.len
    }

    fn advance_reader(&mut self, mut cnt: usize) {
        cnt = cmp::min(cnt, self.read_remaining());

        self.pos += cnt;
        self.pos %= self.cap;
        self.len -= cnt;
    }

    #[inline]
    fn advance_writer(&mut self, mut cnt: usize) {
        cnt = cmp::min(cnt, self.write_remaining());
        self.len += cnt;
    }

    fn as_ptr(&self) -> *const u8 {
        self.ptr as *const u8
    }

    fn as_slice<'a>(&'a self) -> &'a [u8] {
        unsafe {
            mem::transmute(RawSlice {
                data: self.as_ptr(), len: self.cap
            })
        }
    }

    fn as_mut_slice<'a>(&'a mut self) -> &'a mut [u8] {
        unsafe { mem::transmute(self.as_slice()) }
    }
}

impl Clone for RingBuf {
    fn clone(&self) -> RingBuf {
        use std::cmp;

        let mut ret = RingBuf::new(self.cap);

        ret.pos = self.pos;
        ret.len = self.len;

        unsafe {
            let to = self.pos + self.len;

            if to > self.cap {
                ptr::copy_memory(ret.ptr, self.ptr as *const u8, to % self.cap);
            }

            ptr::copy_memory(
                ret.ptr.offset(self.pos as isize),
                self.ptr.offset(self.pos as isize) as *const u8,
                cmp::min(self.len, self.cap - self.pos));
        }

        ret
    }

    // TODO: an improved version of clone_from is possible that potentially
    // re-uses the buffer
}

impl fmt::Debug for RingBuf {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "RingBuf[.. {}]", self.len)
    }
}

impl Drop for RingBuf {
    fn drop(&mut self) {
        if self.cap > 0 {
            unsafe {
                heap::deallocate(self.ptr, self.cap, mem::min_align_of::<u8>())
            }
        }
    }
}

pub struct RingBufReader<'a> {
    ring: &'a mut RingBuf
}

impl<'a> Buf for RingBufReader<'a> {
    #[inline]
    fn remaining(&self) -> usize {
        self.ring.read_remaining()
    }

    fn bytes<'b>(&'b self) -> &'b [u8] {
        let mut to = self.ring.pos + self.ring.len;

        if to > self.ring.cap {
            to = self.ring.cap
        }

        &self.ring.as_slice()[self.ring.pos..to]
    }

    fn advance(&mut self, cnt: usize) {
        self.ring.advance_reader(cnt)
    }
}

impl<'a> Reader for RingBufReader<'a> {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        super::read(self, buf)
    }
}

pub struct RingBufWriter<'a> {
    ring: &'a mut RingBuf
}

impl<'a> Buf for RingBufWriter<'a> {
    #[inline]
    fn remaining(&self) -> usize {
        self.ring.write_remaining()
    }

    fn bytes<'b>(&'b self) -> &'b [u8] {
        let mut from;
        let mut to;

        from = self.ring.pos + self.ring.len;
        from %= self.ring.cap;

        to = from + self.remaining();

        if to >= self.ring.cap {
            to = self.ring.cap;
        }

        &self.ring.as_slice()[from..to]
    }

    fn advance(&mut self, cnt: usize) {
        self.ring.advance_writer(cnt)
    }
}

impl<'a> MutBuf for RingBufWriter<'a> {
    fn mut_bytes<'b>(&'b mut self) -> &'b mut [u8] {
        unsafe { mem::transmute(self.bytes()) }
    }
}

impl<'a> Writer for RingBufWriter<'a> {
    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
        super::write(self, buf)
    }
}

unsafe impl Send for RingBuf { }

#[cfg(test)]
mod test {
    use std::old_io::EndOfFile;
    use buf::{Buf, RingBuf};

    #[test]
    pub fn test_initial_buf_empty() {
        let mut buf = RingBuf::new(100);

        assert!(buf.capacity() == 128);
        assert!(buf.reader().bytes().is_empty());
    }

    #[test]
    pub fn test_using_ring_buffer() {
        let mut buf = RingBuf::new(128);

        buf.writer().write_all(b"hello").unwrap();
        assert!(buf.writer().remaining() == 123);

        let read = buf.reader().read_exact(5).unwrap();
        assert!(read.as_slice() == b"hello");
    }

    #[test]
    pub fn test_restarting_ring_buffer() {
        let mut buf = RingBuf::new(8);

        buf.writer().write_all(b"hello").unwrap();
        assert!(buf.writer().remaining() == 3);

        let read = buf.reader().read_exact(5).unwrap();
        assert!(read.as_slice() == b"hello");
        assert!(buf.writer().remaining() == 8, "actual={}", buf.writer().remaining());
    }

    #[test]
    pub fn test_overflowing_ring_buffer() {
        let mut buf = RingBuf::new(8);

        buf.writer().write_all(b"hello").unwrap();
        assert!(buf.writer().write_all(b"world").unwrap_err().kind == EndOfFile);
    }
}
<|endoftext|>


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

use condition::Condition;
use dispatcher::demux::Demultiplexer;
use dispatcher::handlers;
use dispatcher::request::{Request, RequestHandler};
use reactor::{Event, EventDemultiplexer, EventHandler, Reactor};

pub struct RequestReactor {
    handlers: BTreeMap<RequestHandler, Box<EventHandler<Request, Handler=RequestHandler>>>,
    demultiplexer: Demultiplexer<Request>,
    exit_condition: Condition
}

impl RequestReactor {
    pub fn new(demultiplexer: Demultiplexer<Request>) -> RequestReactor {
        let exit_condition = Condition::new(false);
        let exit_handler = Box::new(handlers::exit::ExitHandler::new(exit_condition.clone()));
        let event_handler = Box::new(handlers::event::EventHandler::new());

        let mut reactor = RequestReactor {
            demultiplexer: demultiplexer,
            exit_condition: exit_condition,
            handlers: BTreeMap::new()
        };

        reactor.register_handler(exit_handler);
        reactor.register_handler(event_handler);
        reactor
    }
}

impl Reactor for RequestReactor {
    type Event = Request;
    type Handler = RequestHandler;
    fn handle_events(&mut self) {
        while !self.exit_condition.is_active() {
            if let Some(request) = self.demultiplexer.select() {
                let mut handler = self.handlers.get_mut(&request.handler()).unwrap();
                handler.handle_event(request);
            } else {
                break;
            }
        }
    }
    fn register_handler(&mut self, handler: Box<EventHandler<Self::Event, Handler=RequestHandler>>) {
        self.handlers.insert(handler.handler(), handler);
    }
    fn remove_handler(&mut self, handler: &EventHandler<Self::Event, Handler=RequestHandler>){}
}
<commit_msg>RequestReactor: simplify new() method<commit_after>use std::collections::BTreeMap;

use condition::Condition;
use dispatcher::demux::Demultiplexer;
use dispatcher::handlers;
use dispatcher::request::{Request, RequestHandler};
use reactor::{Event, EventDemultiplexer, EventHandler, Reactor};

pub struct RequestReactor {
    handlers: BTreeMap<RequestHandler, Box<EventHandler<Request, Handler=RequestHandler>>>,
    demultiplexer: Demultiplexer<Request>,
    exit_condition: Condition
}

impl RequestReactor {
    pub fn new(demultiplexer: Demultiplexer<Request>, exit_condition: Condition) -> RequestReactor {
        RequestReactor {
            demultiplexer: demultiplexer,
            exit_condition: exit_condition,
            handlers: BTreeMap::new()
        }
    }
}

impl Reactor for RequestReactor {
    type Event = Request;
    type Handler = RequestHandler;
    fn handle_events(&mut self) {
        while !self.exit_condition.is_active() {
            if let Some(request) = self.demultiplexer.select() {
                let mut handler = self.handlers.get_mut(&request.handler()).unwrap();
                handler.handle_event(request);
            } else {
                break;
            }
        }
    }
    fn register_handler(&mut self, handler: Box<EventHandler<Self::Event, Handler=RequestHandler>>) {
        self.handlers.insert(handler.handler(), handler);
    }
    fn remove_handler(&mut self, handler: &EventHandler<Self::Event, Handler=RequestHandler>){}
}
<|endoftext|>


<commit_before>// TODO : remove the expect...
#[macro_use]
extern crate rustfbp;
extern crate capnp;

#[derive(Debug)]
struct Graph {
    nodes: Vec<(String, String)>,
    edges: Vec<(String, String, String, String, String, String)>,
    iips: Vec<(String, String, String, String)>,
    ext_in: Vec<(String, String, String, String)>,
    ext_out: Vec<(String, String, String, String)>,
}

#[derive(PartialEq, Debug)]
enum State {
    Break, Comp, Port, IIP,
    CompPort, CompPortBind, CompPortBindPort,
    CompPortExternal, CompPortExternalPort,
    PortExternal, PortExternalPort,
    IIPBind, IIPBindPort,
    ErrorS
}
use State::*;

enum Literal {
    Comp(String, String),
    Port(String, String),
    IIP(String),
    Bind, External,
}

component! {
    development_fbp_parser_semantic, contracts(fbp_graph, fbp_lexical, fbp_semantic_error)
    inputs(input: fbp_lexical),
    inputs_array(),
    outputs(output: fbp_graph, error: fbp_semantic_error),
    outputs_array(),
    option(),
    acc(),
    fn run(&mut self) -> Result<()> {
        let mut ip = try!(self.ports.recv("input"));
        let literal: fbp_lexical::Reader = try!(ip.get_root());
        let literal = try!(literal.which());

        match literal {
            fbp_lexical::Start(path) => {
                match handle_stream(&self) {
                    Ok(graph) => { try!(send_graph(&self, try!(path), &graph)) },
                    Err(errors) => {
                        let mut new_ip = IP::new();
                        {
                            let mut ip = new_ip.init_root::<fbp_semantic_error::Builder>();
                            ip.set_path(try!(path));
                            {
                                let mut nodes = ip.init_parsing(errors.len() as u32);
                                let mut i = 0;
                                for n in &errors {
                                    nodes.borrow().set(i, &n[..]);
                                    i += 1;
                                }
                            }
                        }
                        let _ = self.ports.send("error", new_ip);
                    },
                }
            }
            _ => { return Err(result::Error::Misc("bad stream".to_string())); },
        }
        Ok(())
    }
}

fn handle_stream(comp: &development_fbp_parser_semantic) -> std::result::Result<Graph, Vec<String>> {
    let mut state = Break;
    let mut stack: Vec<Literal> = vec![];
    let mut graph = Graph {
        nodes: vec![],
        edges: vec![],
        iips: vec![],
        ext_in: vec![],
        ext_out: vec![]
    };
    let mut errors: Vec<String> = vec![];
    let mut line: usize = 1;

    loop {

        let mut ip = comp.ports.recv("input").expect("development_fbp_parser_semantic : unable to receive");
        let literal: fbp_lexical::Reader = ip.get_root().expect("development_fbp_parser_semantic : not a literal");
        let literal = literal.which().expect("development_fbp_parser_semantic : cannot which");
        match literal {
            fbp_lexical::End(_) => {
                break;
            },
            fbp_lexical::Token(t) => {
                let token = t.which().expect("cannot which token");
                match token {
                    fbp_lexical::token::Bind(_) => {
                        state = match state {
                            CompPort => { CompPortBind },
                            ErrorS => { ErrorS },
                            IIP => { IIPBind },
                            _ => {
                                errors.push(format!("line {} : -> found, one of {} expected", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::External(_) => {
                        state = match state {
                            CompPort => { CompPortExternal },
                            Port => { PortExternal },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : => found, one of {} expected", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Port(port) => {
                        stack.push(Literal::Port(port.get_name().unwrap().to_string(), port.get_selection().unwrap().to_string()));
                        state = match state {
                            Comp => { CompPort },
                            CompPortBind => { CompPortBindPort },
                            CompPortExternal => {
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                let out_c = stack.pop().unwrap();
                                {
                                    let (in_p_n, _) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(ref n, ref s) = out_c { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_out.push((out_c_n.clone(), out_p_n, out_p_s, in_p_n));
                                }
                                CompPortExternalPort },
                            Break => { Port },
                            PortExternal => { PortExternalPort },
                            ErrorS => { ErrorS },
                            IIPBind => { IIPBindPort },
                            _ => {
                                errors.push(format!("line {} : Port {}[{}] found, one of {} expected", line, port.get_name().unwrap(), port.get_selection().unwrap(), get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Comp(comp) => {
                        if comp.get_sort().unwrap() != "" {
                            graph.nodes.push((comp.get_name().unwrap().to_string(), comp.get_sort().unwrap().to_string()));
                        }
                        stack.push(Literal::Comp(comp.get_name().unwrap().to_string(), comp.get_sort().unwrap().to_string()));
                        state = match state {
                            CompPortBindPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                let out_c = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(n, s) = out_c { (n, s) } else { unreachable!() };
                                    graph.edges.push((out_c_n, out_p_n, out_p_s, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            },
                            PortExternalPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_in.push((out_p_n, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            IIPBindPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let iip = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let iip = if let Literal::IIP(iip) = iip { iip } else { unreachable!() };
                                    graph.iips.push((iip, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            Break => { Comp },
                            ErrorS => { stack = vec![stack.pop().unwrap()]; Comp },
                            _ => {
                                errors.push(format!("line {} : Comp {}({}) found, one of {} expected", line, comp.get_name().unwrap(), comp.get_sort().unwrap(), get_expected(&state)));
                                ErrorS
                            },
                        }
                    },
                    fbp_lexical::token::Iip(iip) => {
                        let iip = iip.expect("no iip");
                        stack.push(Literal::IIP(iip.to_string()));
                        state = match state {
                            ErrorS => { IIP },
                            Break => { IIP },
                            _ => {
                                errors.push(format!("line {} : IIP '{}' found, one of {} expected", line, iip, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Break(_) => {
                        line += 1;
                        state = match state {
                            CompPortBind => { state },
                            IIPBind => { state },
                            Comp => { stack.clear(); Break },
                            CompPortExternalPort => { stack.clear(); Break },
                            Break => { Break },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : NewLine found, one of {} expected", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    }
                }
            },
            _ => { panic!("bad stream"); },
        }
    }
    if errors.len() > 0 {
        Err(errors)
    } else {
        Ok(graph)
    }
}

fn get_expected(state: &State) -> String {
    match *state {
        Break => { "[Component, Port, IIP, NewLine]".into() },
        Comp => { "[Port, NewLine]".into() },
        Port => { "[Component, ->, =>]".into() },
        IIP => { "[->]".into() },
        CompPort => { "[->, =>]".into() },
        CompPortBind => { "[Port]".into() },
        CompPortBindPort => { "[Component]".into() },
        CompPortExternal => { "[Port]".into() },
        CompPortExternalPort => { "[NewLine]".into() },
        PortExternal => { "[Port]".into() },
        PortExternalPort => { "[Component]".into() },
        IIPBind => { "[Port]".into() },
        IIPBindPort => { "[Component]".into() },
        ErrorS => { unreachable!() }
    }
}

fn send_graph(comp: &development_fbp_parser_semantic, path: &str, graph: &Graph) -> Result<()> {
    let mut new_ip = IP::new();
    {
        let mut ip = new_ip.init_root::<fbp_graph::Builder>();
        ip.set_path(path);
        {
            let mut nodes = ip.borrow().init_nodes(graph.nodes.len() as u32);
            let mut i = 0;
            for n in &graph.nodes {
                nodes.borrow().get(i).set_name(&n.0[..]);
                nodes.borrow().get(i).set_sort(&n.1[..]);
                i += 1;
            }
        }
        {
            let mut edges = ip.borrow().init_edges(graph.edges.len() as u32);
            let mut i = 0;
            for e in &graph.edges {
                edges.borrow().get(i).set_o_name(&e.0[..]);
                edges.borrow().get(i).set_o_port(&e.1[..]);
                edges.borrow().get(i).set_o_selection(&e.2[..]);
                edges.borrow().get(i).set_i_port(&e.3[..]);
                edges.borrow().get(i).set_i_selection(&e.4[..]);
                edges.borrow().get(i).set_i_name(&e.5[..]);
                i += 1;
            }
        }
        {
            let mut iips = ip.borrow().init_iips(graph.iips.len() as u32);
            let mut i = 0;
            for iip in &graph.iips {
                iips.borrow().get(i).set_iip(&iip.0[..]);
                iips.borrow().get(i).set_port(&iip.1[..]);
                iips.borrow().get(i).set_selection(&iip.2[..]);
                iips.borrow().get(i).set_comp(&iip.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_inputs(graph.ext_in.len() as u32);
            let mut i = 0;
            for e in &graph.ext_in {
                ext.borrow().get(i).set_name(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_comp(&e.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_outputs(graph.ext_out.len() as u32);
            let mut i = 0;
            for e in &graph.ext_out {
                ext.borrow().get(i).set_comp(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_name(&e.3[..]);
                i += 1;
            }
        }
    }
    let _ = comp.ports.send("output", new_ip);
    Ok(())
}
<commit_msg>problem: expect and unwrap<commit_after>#![feature(question_mark)]
#[macro_use]
extern crate rustfbp;
extern crate capnp;

#[derive(Debug)]
struct Graph {
    nodes: Vec<(String, String)>,
    edges: Vec<(String, String, String, String, String, String)>,
    iips: Vec<(String, String, String, String)>,
    ext_in: Vec<(String, String, String, String)>,
    ext_out: Vec<(String, String, String, String)>,
}

#[derive(PartialEq, Debug)]
enum State {
    Break, Comp, Port, IIP,
    CompPort, CompPortBind, CompPortBindPort,
    CompPortExternal, CompPortExternalPort,
    PortExternal, PortExternalPort,
    IIPBind, IIPBindPort,
    ErrorS
}
use State::*;

enum Literal {
    Comp(String, String),
    Port(String, String),
    IIP(String),
    Bind, External,
}

component! {
    development_fbp_parser_semantic, contracts(fbp_graph, fbp_lexical, fbp_semantic_error)
    inputs(input: fbp_lexical),
    inputs_array(),
    outputs(output: fbp_graph, error: fbp_semantic_error),
    outputs_array(),
    option(),
    acc(),
    fn run(&mut self) -> Result<()> {
        let mut ip = try!(self.ports.recv("input"));
        let literal: fbp_lexical::Reader = try!(ip.get_root());
        let literal = try!(literal.which());

        match literal {
            fbp_lexical::Start(path) => {
                match handle_stream(&self)? {
                    Ok(graph) => { send_graph(&self, try!(path), &graph)? },
                    Err(errors) => {
                        let mut new_ip = IP::new();
                        {
                            let mut ip = new_ip.init_root::<fbp_semantic_error::Builder>();
                            ip.set_path(path?);
                            {
                                let mut nodes = ip.init_parsing(errors.len() as u32);
                                let mut i = 0;
                                for n in &errors {
                                    nodes.borrow().set(i, &n[..]);
                                    i += 1;
                                }
                            }
                        }
                        let _ = self.ports.send("error", new_ip);
                    },
                }
            }
            _ => { return Err(result::Error::Misc("bad stream".to_string())); },
        }
        Ok(())
    }
}

fn handle_stream(comp: &development_fbp_parser_semantic) -> Result<std::result::Result<Graph, Vec<String>>> {
    let mut state = Break;
    let mut stack: Vec<Literal> = vec![];
    let mut graph = Graph {
        nodes: vec![],
        edges: vec![],
        iips: vec![],
        ext_in: vec![],
        ext_out: vec![]
    };
    let mut errors: Vec<String> = vec![];
    let mut line: usize = 1;

    loop {

        let mut ip = comp.ports.recv("input")?;
        let literal: fbp_lexical::Reader = ip.get_root()?;
        let literal = literal.which()?;
        match literal {
            fbp_lexical::End(_) => {
                break;
            },
            fbp_lexical::Token(t) => {
                let token = t.which()?;
                match token {
                    fbp_lexical::token::Bind(_) => {
                        state = match state {
                            CompPort => { CompPortBind },
                            ErrorS => { ErrorS },
                            IIP => { IIPBind },
                            _ => {
                                errors.push(format!("line {} : -> found, one of {} expected", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::External(_) => {
                        state = match state {
                            CompPort => { CompPortExternal },
                            Port => { PortExternal },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : => found, one of {} expected", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Port(port) => {
                        stack.push(Literal::Port(port.get_name()?.to_string(), port.get_selection()?.to_string()));
                        state = match state {
                            Comp => { CompPort },
                            CompPortBind => { CompPortBindPort },
                            CompPortExternal => {
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_p_n, _) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(ref n, ref s) = out_c { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_out.push((out_c_n.clone(), out_p_n, out_p_s, in_p_n));
                                }
                                CompPortExternalPort },
                            Break => { Port },
                            PortExternal => { PortExternalPort },
                            ErrorS => { ErrorS },
                            IIPBind => { IIPBindPort },
                            _ => {
                                errors.push(format!("line {} : Port {}[{}] found, one of {} expected", line, port.get_name()?, port.get_selection()?, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Comp(comp) => {
                        if comp.get_sort()? != "" {
                            graph.nodes.push((comp.get_name()?.to_string(), comp.get_sort()?.to_string()));
                        }
                        stack.push(Literal::Comp(comp.get_name()?.to_string(), comp.get_sort()?.to_string()));
                        state = match state {
                            CompPortBindPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(n, s) = out_c { (n, s) } else { unreachable!() };
                                    graph.edges.push((out_c_n, out_p_n, out_p_s, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            },
                            PortExternalPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_in.push((out_p_n, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            IIPBindPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let iip = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let iip = if let Literal::IIP(iip) = iip { iip } else { unreachable!() };
                                    graph.iips.push((iip, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            Break => { Comp },
                            ErrorS => { stack = vec![stack.pop().ok_or(result::Error::Misc("stack problem".into()))?]; Comp },
                            _ => {
                                errors.push(format!("line {} : Comp {}({}) found, one of {} expected", line, comp.get_name()?, comp.get_sort()?, get_expected(&state)));
                                ErrorS
                            },
                        }
                    },
                    fbp_lexical::token::Iip(iip) => {
                        let iip = iip?;
                        stack.push(Literal::IIP(iip.to_string()));
                        state = match state {
                            ErrorS => { IIP },
                            Break => { IIP },
                            _ => {
                                errors.push(format!("line {} : IIP '{}' found, one of {} expected", line, iip, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Break(_) => {
                        line += 1;
                        state = match state {
                            CompPortBind => { state },
                            IIPBind => { state },
                            Comp => { stack.clear(); Break },
                            CompPortExternalPort => { stack.clear(); Break },
                            Break => { Break },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : NewLine found, one of {} expected", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    }
                }
            },
            _ => { panic!("bad stream"); },
        }
    }
    if errors.len() > 0 {
        Ok(Err(errors))
    } else {
        Ok(Ok(graph))
    }
}

fn get_expected(state: &State) -> String {
    match *state {
        Break => { "[Component, Port, IIP, NewLine]".into() },
        Comp => { "[Port, NewLine]".into() },
        Port => { "[Component, ->, =>]".into() },
        IIP => { "[->]".into() },
        CompPort => { "[->, =>]".into() },
        CompPortBind => { "[Port]".into() },
        CompPortBindPort => { "[Component]".into() },
        CompPortExternal => { "[Port]".into() },
        CompPortExternalPort => { "[NewLine]".into() },
        PortExternal => { "[Port]".into() },
        PortExternalPort => { "[Component]".into() },
        IIPBind => { "[Port]".into() },
        IIPBindPort => { "[Component]".into() },
        ErrorS => { unreachable!() }
    }
}

fn send_graph(comp: &development_fbp_parser_semantic, path: &str, graph: &Graph) -> Result<()> {
    let mut new_ip = IP::new();
    {
        let mut ip = new_ip.init_root::<fbp_graph::Builder>();
        ip.set_path(path);
        {
            let mut nodes = ip.borrow().init_nodes(graph.nodes.len() as u32);
            let mut i = 0;
            for n in &graph.nodes {
                nodes.borrow().get(i).set_name(&n.0[..]);
                nodes.borrow().get(i).set_sort(&n.1[..]);
                i += 1;
            }
        }
        {
            let mut edges = ip.borrow().init_edges(graph.edges.len() as u32);
            let mut i = 0;
            for e in &graph.edges {
                edges.borrow().get(i).set_o_name(&e.0[..]);
                edges.borrow().get(i).set_o_port(&e.1[..]);
                edges.borrow().get(i).set_o_selection(&e.2[..]);
                edges.borrow().get(i).set_i_port(&e.3[..]);
                edges.borrow().get(i).set_i_selection(&e.4[..]);
                edges.borrow().get(i).set_i_name(&e.5[..]);
                i += 1;
            }
        }
        {
            let mut iips = ip.borrow().init_iips(graph.iips.len() as u32);
            let mut i = 0;
            for iip in &graph.iips {
                iips.borrow().get(i).set_iip(&iip.0[..]);
                iips.borrow().get(i).set_port(&iip.1[..]);
                iips.borrow().get(i).set_selection(&iip.2[..]);
                iips.borrow().get(i).set_comp(&iip.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_inputs(graph.ext_in.len() as u32);
            let mut i = 0;
            for e in &graph.ext_in {
                ext.borrow().get(i).set_name(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_comp(&e.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_outputs(graph.ext_out.len() as u32);
            let mut i = 0;
            for e in &graph.ext_out {
                ext.borrow().get(i).set_comp(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_name(&e.3[..]);
                i += 1;
            }
        }
    }
    let _ = comp.ports.send("output", new_ip);
    Ok(())
}
<|endoftext|>


<commit_before>//! Implements a command for uploading proguard mapping files.
use std::fs;
use std::io::Write;
use std::path::PathBuf;

use prelude::*;
use utils::{ArgExt, TempFile, copy_with_progress, make_byte_progress_bar,
            get_sha1_checksum, AndroidManifest, dump_proguard_uuids_as_properties};
use config::Config;
use api::{Api, AssociateDsyms};

use clap::{App, Arg, ArgMatches};
use uuid::Uuid;
use zip;
use proguard::MappingView;
use console::style;

#[derive(Debug)]
struct MappingRef {
    pub path: PathBuf,
    pub size: u64,
    pub uuid: Uuid,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads proguard mapping files to a project")
        .org_project_args()
        .arg(Arg::with_name("paths")
            .value_name("PATH")
            .help("The path to the mapping files")
            .multiple(true)
            .number_of_values(1)
            .index(1))
        .arg(Arg::with_name("version")
             .long("version")
             .value_name("VERSION")
             .requires("app_id")
             .help("Optionally associate the mapping files with a humand \
                    readable version.  This helps you understand which \
                    proguard files go with which version of your app."))
        .arg(Arg::with_name("version_code")
             .long("version-code")
             .value_name("VERSION_CODE")
             .requires("app_id")
             .requires("version")
             .help("Optionally associate the mapping files with a version \
                    code.  This helps you understand which proguard files \
                    go with which version of your app."))
        .arg(Arg::with_name("app_id")
             .long("app-id")
             .value_name("APP_ID")
             .requires("version")
             .help("Optionally associate the mapping files with an application \
                    ID.  If you have multiple apps in one sentry project you can \
                    then easlier tell them apart."))
        .arg(Arg::with_name("platform")
             .long("platform")
             .value_name("PLATFORM")
             .requires("app_id")
             .help("Optionally defines the platform for the app association. \
                    This defaults to 'android'."))
        .arg(Arg::with_name("no_reprocessing")
             .long("no-reprocessing")
             .help("Does not trigger reprocessing after upload"))
        .arg(Arg::with_name("no_upload")
             .long("no-upload")
             .help("Disables the actual upload.  This runs all steps for the \
                    processing but does not trigger the upload (this also \
                    automatically disables reprocessing.  This is useful if you \
                    just want to verify the mapping files and update manifests."))
        .arg(Arg::with_name("android_manifest")
             .long("android-manifest")
             .value_name("PATH")
             .conflicts_with("app_id")
             .help("If provided the path to a processed android manifest \
                    needs to be supplied.  In that case version and version \
                    code as well as the package are read from there. \
                    Additionally if --update-manifest is provided the \
                    manifest is updated with the proguard UUID."))
        .arg(Arg::with_name("write_properties")
             .long("write-properties")
             .value_name("PATH")
             .help("Write the UUIDs for the processed mapping files into \
                    the given properties file."))
        .arg(Arg::with_name("require_one")
             .long("require-one")
             .help("Requires at least one file to upload or the command will error."))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);

    let paths: Vec<_> = match matches.values_of("paths") {
        Some(paths) => paths.collect(),
        None => { return Ok(()); }
    };
    let mut mappings = vec![];
    let mut all_checksums = vec![];

    let android_manifest = if let Some(path) = matches.value_of("android_manifest") {
        Some(AndroidManifest::from_path(path)?)
    } else {
        None
    };

    // since the mappings are quite small we don't bother doing a second http
    // request to figure out if any of the checksums are missing.  We just ship
    // them all up.
    for path in &paths {
        let md = fs::metadata(path)?;
        let mapping = MappingView::from_path(path)?;
        if !mapping.has_line_info() {
            println_stderr!("warning: proguard mapping '{}' was ignored because it \
                             does not contain any line information.", path);
        } else {
            let mut f = fs::File::open(path)?;
            all_checksums.push(get_sha1_checksum(&mut f)?);
            mappings.push(MappingRef {
                path: PathBuf::from(path),
                size: md.len(),
                uuid: mapping.uuid(),
            });
        }
    }

    if mappings.is_empty() && matches.is_present("require_one") {
        fail!("found no mapping files to upload");
    }

    println!("{} compressing mappings", style("[1/2]").dim());
    let tf = TempFile::new()?;

    // add a scope here so we will flush before uploading
    {
        let mut zip = zip::ZipWriter::new(tf.open());
        for mapping in &mappings {
            let pb = make_byte_progress_bar(mapping.size);
            zip.start_file(format!("proguard/{}.txt", mapping.uuid),
                           zip::write::FileOptions::default())?;
            copy_with_progress(&pb, &mut fs::File::open(&mapping.path)?, &mut zip)?;
            pb.finish_and_clear();
        }
    }

    if !matches.is_present("no_upload") {
        println!("{} uploading mappings", style("[2/2]").dim());
        let rv = api.upload_dsyms(&org, &project, tf.path())?;
        println!("Uploaded a total of {} new mapping files",
                 style(rv.len()).yellow());
        if rv.len() > 0 {
            println!("Newly uploaded debug symbols:");
            for df in rv {
                println!("  {}", style(&df.uuid).dim());
            }
        }
    } else {
        println!("Skipping upload.");
    }

    // write UUIDs into the mapping file.
    if let Some(p) = matches.value_of("write_properties") {
        let uuids: Vec<_> = mappings.iter().map(|x| x.uuid).collect();
        dump_proguard_uuids_as_properties(p, &uuids)?;
    }

    // update the uuids
    if let Some(android_manifest) = android_manifest {
        api.associate_android_proguard_mappings(
            &org, &project, &android_manifest, all_checksums)?;

    // if values are given associate
    } else if let Some(app_id) = matches.value_of("app_id") {
        api.associate_dsyms(&org, &project, &AssociateDsyms {
            platform: matches.value_of("platform").unwrap_or("android").to_string(),
            checksums: all_checksums,
            name: app_id.to_string(),
            app_id: app_id.to_string(),
            version: matches.value_of("version").unwrap().to_string(),
            build: matches.value_of("version_code").map(|x| x.to_string()),
        })?;
    }

    // If wanted trigger reprocessing
    if !matches.is_present("no_reprocessing") &&
       !matches.is_present("no_upload") {
        if !api.trigger_reprocessing(&org, &project)? {
            println!("Server does not support reprocessing. Not triggering.");
        }
    } else {
        println!("Skipped reprocessing.");
    }

    Ok(())
}
<commit_msg>Fixed the docs for upload-proguard<commit_after>//! Implements a command for uploading proguard mapping files.
use std::fs;
use std::io::Write;
use std::path::PathBuf;

use prelude::*;
use utils::{ArgExt, TempFile, copy_with_progress, make_byte_progress_bar,
            get_sha1_checksum, AndroidManifest, dump_proguard_uuids_as_properties};
use config::Config;
use api::{Api, AssociateDsyms};

use clap::{App, Arg, ArgMatches};
use uuid::Uuid;
use zip;
use proguard::MappingView;
use console::style;

#[derive(Debug)]
struct MappingRef {
    pub path: PathBuf,
    pub size: u64,
    pub uuid: Uuid,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads proguard mapping files to a project")
        .org_project_args()
        .arg(Arg::with_name("paths")
            .value_name("PATH")
            .help("The path to the mapping files")
            .multiple(true)
            .number_of_values(1)
            .index(1))
        .arg(Arg::with_name("version")
             .long("version")
             .value_name("VERSION")
             .requires("app_id")
             .help("Optionally associate the mapping files with a humand \
                    readable version.  This helps you understand which \
                    proguard files go with which version of your app."))
        .arg(Arg::with_name("version_code")
             .long("version-code")
             .value_name("VERSION_CODE")
             .requires("app_id")
             .requires("version")
             .help("Optionally associate the mapping files with a version \
                    code.  This helps you understand which proguard files \
                    go with which version of your app."))
        .arg(Arg::with_name("app_id")
             .long("app-id")
             .value_name("APP_ID")
             .requires("version")
             .help("Optionally associate the mapping files with an application \
                    ID.  If you have multiple apps in one sentry project you can \
                    then easlier tell them apart."))
        .arg(Arg::with_name("platform")
             .long("platform")
             .value_name("PLATFORM")
             .requires("app_id")
             .help("Optionally defines the platform for the app association. \
                    This defaults to 'android'."))
        .arg(Arg::with_name("no_reprocessing")
             .long("no-reprocessing")
             .help("Does not trigger reprocessing after upload"))
        .arg(Arg::with_name("no_upload")
             .long("no-upload")
             .help("Disables the actual upload.  This runs all steps for the \
                    processing but does not trigger the upload (this also \
                    automatically disables reprocessing.  This is useful if you \
                    just want to verify the mapping files and write the \
                    proguard UUIDs into a proeprties file."))
        .arg(Arg::with_name("android_manifest")
             .long("android-manifest")
             .value_name("PATH")
             .conflicts_with("app_id")
             .help("If provided the path to a processed android manifest \
                    needs to be supplied.  In that case version and version \
                    code as well as the package are read from there."))
        .arg(Arg::with_name("write_properties")
             .long("write-properties")
             .value_name("PATH")
             .help("Write the UUIDs for the processed mapping files into \
                    the given properties file."))
        .arg(Arg::with_name("require_one")
             .long("require-one")
             .help("Requires at least one file to upload or the command will error."))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);

    let paths: Vec<_> = match matches.values_of("paths") {
        Some(paths) => paths.collect(),
        None => { return Ok(()); }
    };
    let mut mappings = vec![];
    let mut all_checksums = vec![];

    let android_manifest = if let Some(path) = matches.value_of("android_manifest") {
        Some(AndroidManifest::from_path(path)?)
    } else {
        None
    };

    // since the mappings are quite small we don't bother doing a second http
    // request to figure out if any of the checksums are missing.  We just ship
    // them all up.
    for path in &paths {
        let md = fs::metadata(path)?;
        let mapping = MappingView::from_path(path)?;
        if !mapping.has_line_info() {
            println_stderr!("warning: proguard mapping '{}' was ignored because it \
                             does not contain any line information.", path);
        } else {
            let mut f = fs::File::open(path)?;
            all_checksums.push(get_sha1_checksum(&mut f)?);
            mappings.push(MappingRef {
                path: PathBuf::from(path),
                size: md.len(),
                uuid: mapping.uuid(),
            });
        }
    }

    if mappings.is_empty() && matches.is_present("require_one") {
        fail!("found no mapping files to upload");
    }

    println!("{} compressing mappings", style("[1/2]").dim());
    let tf = TempFile::new()?;

    // add a scope here so we will flush before uploading
    {
        let mut zip = zip::ZipWriter::new(tf.open());
        for mapping in &mappings {
            let pb = make_byte_progress_bar(mapping.size);
            zip.start_file(format!("proguard/{}.txt", mapping.uuid),
                           zip::write::FileOptions::default())?;
            copy_with_progress(&pb, &mut fs::File::open(&mapping.path)?, &mut zip)?;
            pb.finish_and_clear();
        }
    }

    if !matches.is_present("no_upload") {
        println!("{} uploading mappings", style("[2/2]").dim());
        let rv = api.upload_dsyms(&org, &project, tf.path())?;
        println!("Uploaded a total of {} new mapping files",
                 style(rv.len()).yellow());
        if rv.len() > 0 {
            println!("Newly uploaded debug symbols:");
            for df in rv {
                println!("  {}", style(&df.uuid).dim());
            }
        }
    } else {
        println!("Skipping upload.");
    }

    // write UUIDs into the mapping file.
    if let Some(p) = matches.value_of("write_properties") {
        let uuids: Vec<_> = mappings.iter().map(|x| x.uuid).collect();
        dump_proguard_uuids_as_properties(p, &uuids)?;
    }

    // update the uuids
    if let Some(android_manifest) = android_manifest {
        api.associate_android_proguard_mappings(
            &org, &project, &android_manifest, all_checksums)?;

    // if values are given associate
    } else if let Some(app_id) = matches.value_of("app_id") {
        api.associate_dsyms(&org, &project, &AssociateDsyms {
            platform: matches.value_of("platform").unwrap_or("android").to_string(),
            checksums: all_checksums,
            name: app_id.to_string(),
            app_id: app_id.to_string(),
            version: matches.value_of("version").unwrap().to_string(),
            build: matches.value_of("version_code").map(|x| x.to_string()),
        })?;
    }

    // If wanted trigger reprocessing
    if !matches.is_present("no_reprocessing") &&
       !matches.is_present("no_upload") {
        if !api.trigger_reprocessing(&org, &project)? {
            println!("Server does not support reprocessing. Not triggering.");
        }
    } else {
        println!("Skipped reprocessing.");
    }

    Ok(())
}
<|endoftext|>


<commit_before>use analysis::rust_type::Result;
use env::Env;
use gobjects::GStatus;
use library;
use nameutil::module_name;

pub fn ffi_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let type_ = env.library.type_(type_id);
    match type_ {
        &Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("gboolean"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("c_int"),      //maybe dependent on target system
                UInt => ok("c_uint"),     //maybe dependent on target system

                Float => ok("c_float"),
                Double => ok("c_double"),

                Utf8 => ok("*const c_char"),

                Type => ok("GType"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        &Enumeration(ref enum_) => Ok(format!("enums::{}", enum_.name)),
        &Interface(ref interface) => to_mut_ptr(fix_external_name(env, type_id, &interface.glib_type_name)),
        &Class(ref klass) => to_mut_ptr(fix_external_name(env, type_id, &klass.glib_type_name)),
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name() )),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

fn fix_external_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::MAIN_NAMESPACE || type_id.ns_id == library::INTERNAL_NAMESPACE {
        Ok(name.into())
    } else {
        let name_with_prefix = format!("{}_ffi::{}",
            module_name(&env.library.namespace(type_id.ns_id).name), name);
        if env.type_status(&type_id.full_name(&env.library)) == GStatus::Ignore {
            Err(name_with_prefix.into())
        } else {
            Ok(name_with_prefix)
        }
    }
}

fn to_mut_ptr(res: Result) -> Result {
    match res {
        Ok(s) => Ok(format!("*mut {}", s)),
        Err(s) => Err(format!("*mut {}", s)),
    }
}
<commit_msg>Fix external type names<commit_after>use analysis::rust_type::Result;
use env::Env;
use gobjects::GStatus;
use library;
use nameutil::module_name;

pub fn ffi_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let type_ = env.library.type_(type_id);
    match type_ {
        &Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("gboolean"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("c_int"),      //maybe dependent on target system
                UInt => ok("c_uint"),     //maybe dependent on target system

                Float => ok("c_float"),
                Double => ok("c_double"),

                Utf8 => ok("*const c_char"),

                Type => ok("GType"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        &Enumeration(ref enum_) => Ok(format!("enums::{}", enum_.name)),
        &Interface(ref interface) => to_mut_ptr(fix_external_name(env, type_id, &interface.glib_type_name)),
        &Class(ref klass) => to_mut_ptr(fix_external_name(env, type_id, &klass.glib_type_name)),
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name() )),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

fn fix_external_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::MAIN_NAMESPACE || type_id.ns_id == library::INTERNAL_NAMESPACE {
        Ok(name.into())
    } else {
        let name_with_prefix = format!("{}_ffi::{}",
            fix_namespace(env, type_id), name);
        if env.type_status(&type_id.full_name(&env.library)) == GStatus::Ignore {
            Err(name_with_prefix.into())
        } else {
            Ok(name_with_prefix)
        }
    }
}

fn to_mut_ptr(res: Result) -> Result {
    match res {
        Ok(s) => Ok(format!("*mut {}", s)),
        Err(s) => Err(format!("*mut {}", s)),
    }
}

//TODO: check if need to use in non sys codegen
fn fix_namespace(env: &Env, type_id: library::TypeId) -> String {
    let mut name: &str = &module_name(&env.library.namespace(type_id.ns_id).name);
    name = match name {
        "gdk_pixbuf" => "gdk",
        "gio" => "glib",
        "gobject" => "glib",
        _ => name,
    };
    name.into()
}
<|endoftext|>


<commit_before>// TODO: use Impls https://github.com/iron/iron/blob/master/src/iron.rs

extern crate collections;

use collections::dlist::DList;
use collections::Deque;

use std::cmp;
use std::mem;

use std::rc::{mod, Rc};

static CHB_MIN_SIZE:uint = 32u;

// TODO: move to utils
fn blit<T:Clone>(src: &[T], dst: &mut [T], dst_ofs: uint) {
    let len = src.len();
    let sd = dst.slice_mut(dst_ofs, dst_ofs + len);
    if len > sd.len() {
        fail!("blit: source larger than destination");
    }

    let _ = sd.clone_from_slice(src);
}

/// Chained buffer of bytes.
/// Consists of linked list of nodes.
pub struct Chain {
    head: DList<Node>,
    length: uint
}

impl Chain {
    pub fn new() -> Chain {
        Chain{
            head: DList::new(),
            length: 0
        }
    }

    pub fn from_foreign(src: Chain) -> Chain {
        let mut ch = Chain::new();
        ch.concat(src);
        ch
    }

    pub fn len(&self) -> uint {
        self.length
    }

    // XXX: maybe DEDUP append/prepend?
    pub fn append_bytes(&mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.back() {
            Some(nd) => {
                (nd.room() < size) || !rc::is_unique(&nd.dh)
            }
            None => {
                true
            }
        };
        // We either not the only owner of DH or don't have enough room
        if should_create {
            let nsize = if size < CHB_MIN_SIZE { size << 1 } else { size };
            let node = Node::new(DataHolder::new(nsize));
            self.add_node_tail(node);
        }
        // infailable: added node above
        let node = self.head.back_mut().unwrap();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6268
        // XXX: we need additional var and scope only to fight borrow checker
        {
            let node_end = node.end;
            // we should be sole owner of data holder inside node here
            let dh = rc::get_mut(&mut node.dh).unwrap();
            dh.copy_data_from(data, node_end);
        }
        node.end += size;
        self.length += size;
    }

    pub fn prepend_bytes(&mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.front() {
            Some(nd) => {
                (size > nd.start || !rc::is_unique(&nd.dh))
            }
            None => {
                true
            }
        };
        if should_create {
            let nsize = if size < CHB_MIN_SIZE { size << 1 } else { size };
            let mut node = Node::new(DataHolder::new(nsize)); // Box<Node>
            let r = node.room();
            node.start = r;
            node.end = r;
            self.add_node_head(node);
        }
        // See comments in `append_bytes`
        let node = self.head.front_mut().unwrap();
        {
            let node_start = node.start;
            let dh = rc::get_mut(&mut node.dh).unwrap();
            dh.copy_data_from(data, node_start - size);
        }
        node.start -= size;
        self.length += size;
    }

    pub fn pullup(&mut self, size: uint) -> Option<&[u8]> {
        if size == 0 || size > self.len() {
            return None
        }
        // could not fail, because self.size() > 0 => has node
        if self.head.front().unwrap().size() >= size {
            let node = self.head.front().unwrap();
            return Some(node.data(size));
        }

        let mut newn = Node::new(DataHolder::new(size));
        // XXX: we need this scope to be able to move newn inside our list
        {
            let mut msize = size;
            while msize > 0 {
                {

                    let node = self.head.front_mut().unwrap();
                    let csize = cmp::min(node.size(), msize);
                    // XXX: we need this scope only to beat borrow checker
                    {
                        let node_end = newn.end;
                        // we just created new data holder, so we have unique ownership
                        let dh = rc::get_mut(&mut newn.dh).unwrap();
                        dh.copy_data_from(node.data(csize), node_end);
                    }
                    newn.end += csize;

                    if node.size() > msize {
                        node.start += msize;
                        self.length -= msize;
                        break
                    }
                }
                // infailable
                let n = self.head.pop_front().unwrap();
                self.length -= n.size();
                msize -= n.size();
            }
        }
        self.add_node_head(newn);
        // Now first node.size >= size, so we recurse
        return self.pullup(size)
    }

    pub fn concat(&mut self, src: Chain) {
        self.length += src.length;
        self.head.append(src.head);
        // No need to cleanup `src`, because it has moved and cannot be used
    }

    // XXX: chb_drop; `drop` is the sole method of built-in Drop trait,
    // so use another name
    pub fn reset(&mut self) {
        self.head = DList::new();
        self.length = 0;
    }

    pub fn append(&mut self, src: &Chain) {
        for node in src.head.iter() {
            self.add_node_tail(node.clone());
        }
    }

    // XXX: deprecated & experimental

    // TODO: maybe we do not need this method?
    // TODO: `concat` better express move semantics
    // XXX: to delete...
    pub fn concat_ptr(&mut self, src: &mut Chain) {
        self.length += src.length;
        let sh = mem::replace(&mut src.head, DList::new());
        self.head.append(sh);
        src.length = 0;
    }

    // XXX: private
    fn add_node_tail(&mut self, node: Node) {
        self.length += node.size();
        self.head.push(node);
    }

    fn add_node_head(&mut self, node: Node) {
        self.length += node.size();
        self.head.push_front(node);
    }
}

/// Node of chain buffer.
/// Owned by Chain.
struct Node {
    dh: Rc<DataHolder>,
    start: uint,
    end: uint
}

impl Node {
    fn new(dh: Rc<DataHolder>) -> Node {
        Node {
            dh: dh,
            start: 0,
            end: 0
        }
    }

    fn size(&self) -> uint {
        self.end - self.start
    }

    fn room(&self) -> uint {
        self.dh.size - self.end
    }

    fn data(&self, size:uint) -> &[u8] {
        self.dh.data.slice(self.start, self.start + size)
    }

}

impl Clone for Node {
    fn clone(&self) -> Node {
        let mut newn = Node::new(self.dh.clone());
        newn.start = self.start;
        newn.end = self.end;
        newn
    }
}

/// Refcounted data holder
/// TODO: can be shared among different chains
/// TODO: implement other storages: shmem, mmap
struct DataHolder{
    size: uint,
    data: Vec<u8>
}

impl DataHolder {
    fn new(size: uint) -> Rc<DataHolder> {
        Rc::new(DataHolder {
            size: size,
            data: Vec::from_elem(size, 0)
        })
    }

    fn copy_data_from(&mut self, src: &[u8], dst_offs: uint) {
        blit(src,
             self.data.as_mut_slice(), dst_offs);
    }
}

#[cfg(test)]
mod test {
    use super::Chain;
    use std::rand::{task_rng, Rng};

    #[test]
    fn test_append_bytes_changes_length() {
        let mut chain = Chain::new();
        let s = "HelloWorld";
        let ls = s.len();
        chain.append_bytes(s.as_bytes());
        assert_eq!(chain.len(), ls);
    }

    #[test]
    fn test_prepend_bytes_changes_length() {
        let mut chain = Chain::new();
        let s = "HelloWorld".as_bytes();
        let ls = s.len();
        chain.prepend_bytes(s);
        assert_eq!(chain.len(), ls);
    }

    #[test]
    fn test_from_foreign_moves_all_data() {
        let mut orig = Chain::new();
        orig.append_bytes("HelloWorld".as_bytes());
        let sz = orig.len();
        let new = Chain::from_foreign(orig);
        assert_eq!(new.len(), sz);
    }

    #[test]
    fn test_pullup_return_none_on_empty_chain() {
        let mut chain = Chain::new();
        assert!(chain.pullup(1).is_none());
    }

    #[test]
    fn test_pullup_return_what_has_been_appended() {
        let mut chain = Chain::new();
        let s = "HelloWorld".as_bytes();
        let ls = s.len();
        chain.append_bytes(s);
        let res = chain.pullup(ls);
        assert!(res.is_some());
        assert_eq!(res.unwrap(), s);
    }

    #[test]
    fn test_pullup_does_not_change_length() {
        let mut chain = Chain::new();
        let s = "HelloWorld".as_bytes();
        let ls = s.len();
        chain.append_bytes(s);
        let olds = chain.len();
        chain.pullup(ls / 2);
        assert_eq!(chain.len(), olds);
    }

    #[test]
    fn test_pullup_works_on_large_sequences() {
        let mut chain = Chain::new();
        let total = 2048u;
        let mut t = total;
        let one_seq = 128u;
        let mut buf = Vec::new();
        while t > 0 {
            let s:String = task_rng().gen_ascii_chars().take(one_seq).collect();
            let b = s.as_bytes();
            buf = buf.append(b);
            chain.append_bytes(b);
            t -= one_seq;
        }
        {
            let ret = chain.pullup(total);
            assert!(ret.is_some());
            assert_eq!(ret.unwrap(), buf.as_slice());
        }
        assert_eq!(chain.len(), total);
    }

    #[test]
    fn test_concat_increase_dst_length() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        chain1.append_bytes("HelloWorld".as_bytes());
        let l1 = chain1.len();
        chain2.append_bytes("HelloWorld".as_bytes());
        let l2 = chain2.len();
        chain1.concat(chain2);
        assert_eq!(chain1.len(), l1+l2);
    }

    #[test]
    fn test_concat_appends_content() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        let b = "HelloWorld".as_bytes();
        let bl = b.len();
        chain2.append_bytes(b);
        chain1.concat(chain2);
        let res = chain1.pullup(bl);

        assert!(res.is_some());
        assert_eq!(res.unwrap(), b);
    }

    #[test]
    fn test_reset_empties_chain() {
        let mut chain = Chain::new();
        chain.append_bytes("HelloWorld".as_bytes());
        chain.reset();
        assert!(chain.pullup(1).is_none());
        assert_eq!(chain.len(), 0);
    }

    #[test]
    fn test_append_copies_data() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        let s = "HelloWorld";
        let b = s.as_bytes();
        let lb = b.len();
        let ss = String::from_str(s).append(s);
        chain1.append_bytes(b);
        chain2.append_bytes(b);
        chain1.append(&mut chain2);
        {
            let res = chain1.pullup(2 * lb);
            assert!(res.is_some());
            assert_eq!(res.unwrap(), ss.as_bytes());
        }
        assert_eq!(chain1.len(), 2 * lb);
    }

    // XXX: do not need to test it for `concat`, because it is done for us
    // XXX: by type-system.
    #[test]
    fn test_concat_ptr_destroy_src() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        chain2.append_bytes("HelloWorld".as_bytes());
        chain1.concat_ptr(&mut chain2);
        assert_eq!(chain2.len(), 0);
    }

}
<commit_msg>[minor] cleanup<commit_after>extern crate collections;

use collections::dlist::DList;
use collections::Deque;

use std::cmp;
use std::mem;

use std::rc::{mod, Rc};

static CHB_MIN_SIZE:uint = 32u;

// TODO: move to utils
fn blit<T:Clone>(src: &[T], dst: &mut [T], dst_ofs: uint) {
    let len = src.len();
    let sd = dst.slice_mut(dst_ofs, dst_ofs + len);
    if len > sd.len() {
        fail!("blit: source larger than destination");
    }

    let _ = sd.clone_from_slice(src);
}

/// Chained buffer of bytes.
/// Consists of linked list of nodes.
pub struct Chain {
    head: DList<Node>,
    length: uint
}

impl Chain {
    pub fn new() -> Chain {
        Chain{
            head: DList::new(),
            length: 0
        }
    }

    pub fn from_foreign(src: Chain) -> Chain {
        let mut ch = Chain::new();
        ch.concat(src);
        ch
    }

    pub fn len(&self) -> uint {
        self.length
    }

    // XXX: maybe DEDUP append/prepend?
    pub fn append_bytes(&mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.back() {
            Some(nd) => {
                (nd.room() < size) || !rc::is_unique(&nd.dh)
            }
            None => {
                true
            }
        };
        // We either not the only owner of DH or don't have enough room
        if should_create {
            let nsize = if size < CHB_MIN_SIZE { size << 1 } else { size };
            let node = Node::new(DataHolder::new(nsize));
            self.add_node_tail(node);
        }
        // infailable: added node above
        let node = self.head.back_mut().unwrap();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6268
        // XXX: we need additional var and scope only to fight borrow checker
        {
            let node_end = node.end;
            // we should be sole owner of data holder inside node here
            let dh = rc::get_mut(&mut node.dh).unwrap();
            dh.copy_data_from(data, node_end);
        }
        node.end += size;
        self.length += size;
    }

    pub fn prepend_bytes(&mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.front() {
            Some(nd) => {
                (size > nd.start || !rc::is_unique(&nd.dh))
            }
            None => {
                true
            }
        };
        if should_create {
            let nsize = if size < CHB_MIN_SIZE { size << 1 } else { size };
            let mut node = Node::new(DataHolder::new(nsize)); // Box<Node>
            let r = node.room();
            node.start = r;
            node.end = r;
            self.add_node_head(node);
        }
        // See comments in `append_bytes`
        let node = self.head.front_mut().unwrap();
        {
            let node_start = node.start;
            let dh = rc::get_mut(&mut node.dh).unwrap();
            dh.copy_data_from(data, node_start - size);
        }
        node.start -= size;
        self.length += size;
    }

    pub fn pullup(&mut self, size: uint) -> Option<&[u8]> {
        if size == 0 || size > self.len() {
            return None
        }
        // could not fail, because self.size() > 0 => has node
        if self.head.front().unwrap().size() >= size {
            let node = self.head.front().unwrap();
            return Some(node.data(size));
        }

        let mut newn = Node::new(DataHolder::new(size));
        // XXX: we need this scope to be able to move newn inside our list
        {
            let mut msize = size;
            while msize > 0 {
                {

                    let node = self.head.front_mut().unwrap();
                    let csize = cmp::min(node.size(), msize);
                    // XXX: we need this scope only to beat borrow checker
                    {
                        let node_end = newn.end;
                        // we just created new data holder, so we have unique ownership
                        let dh = rc::get_mut(&mut newn.dh).unwrap();
                        dh.copy_data_from(node.data(csize), node_end);
                    }
                    newn.end += csize;

                    if node.size() > msize {
                        node.start += msize;
                        self.length -= msize;
                        break
                    }
                }
                // infailable
                let n = self.head.pop_front().unwrap();
                self.length -= n.size();
                msize -= n.size();
            }
        }
        self.add_node_head(newn);
        // Now first node.size >= size, so we recurse
        return self.pullup(size)
    }

    pub fn concat(&mut self, src: Chain) {
        self.length += src.length;
        self.head.append(src.head);
        // No need to cleanup `src`, because it has moved and cannot be used
    }

    // XXX: chb_drop; `drop` is the sole method of built-in Drop trait,
    // so use another name
    pub fn reset(&mut self) {
        self.head = DList::new();
        self.length = 0;
    }

    pub fn append(&mut self, src: &Chain) {
        for node in src.head.iter() {
            self.add_node_tail(node.clone());
        }
    }

    // XXX: deprecated & experimental

    // TODO: maybe we do not need this method?
    // TODO: `concat` better express move semantics
    // XXX: to delete...
    pub fn concat_ptr(&mut self, src: &mut Chain) {
        self.length += src.length;
        let sh = mem::replace(&mut src.head, DList::new());
        self.head.append(sh);
        src.length = 0;
    }

    // XXX: private
    fn add_node_tail(&mut self, node: Node) {
        self.length += node.size();
        self.head.push(node);
    }

    fn add_node_head(&mut self, node: Node) {
        self.length += node.size();
        self.head.push_front(node);
    }
}

/// Node of chain buffer.
/// Owned by Chain.
struct Node {
    dh: Rc<DataHolder>,
    start: uint,
    end: uint
}

impl Node {
    fn new(dh: Rc<DataHolder>) -> Node {
        Node {
            dh: dh,
            start: 0,
            end: 0
        }
    }

    fn size(&self) -> uint {
        self.end - self.start
    }

    fn room(&self) -> uint {
        self.dh.size - self.end
    }

    fn data(&self, size:uint) -> &[u8] {
        self.dh.data.slice(self.start, self.start + size)
    }

}

impl Clone for Node {
    fn clone(&self) -> Node {
        let mut newn = Node::new(self.dh.clone());
        newn.start = self.start;
        newn.end = self.end;
        newn
    }
}

/// Refcounted data holder
/// TODO: can be shared among different chains
/// TODO: implement other storages: shmem, mmap
struct DataHolder{
    size: uint,
    data: Vec<u8>
}

impl DataHolder {
    fn new(size: uint) -> Rc<DataHolder> {
        Rc::new(DataHolder {
            size: size,
            data: Vec::from_elem(size, 0)
        })
    }

    fn copy_data_from(&mut self, src: &[u8], dst_offs: uint) {
        blit(src,
             self.data.as_mut_slice(), dst_offs);
    }
}

#[cfg(test)]
mod test {
    use super::Chain;
    use std::rand::{task_rng, Rng};

    #[test]
    fn test_append_bytes_changes_length() {
        let mut chain = Chain::new();
        let s = "HelloWorld";
        let ls = s.len();
        chain.append_bytes(s.as_bytes());
        assert_eq!(chain.len(), ls);
    }

    #[test]
    fn test_prepend_bytes_changes_length() {
        let mut chain = Chain::new();
        let s = "HelloWorld".as_bytes();
        let ls = s.len();
        chain.prepend_bytes(s);
        assert_eq!(chain.len(), ls);
    }

    #[test]
    fn test_from_foreign_moves_all_data() {
        let mut orig = Chain::new();
        orig.append_bytes("HelloWorld".as_bytes());
        let sz = orig.len();
        let new = Chain::from_foreign(orig);
        assert_eq!(new.len(), sz);
    }

    #[test]
    fn test_pullup_return_none_on_empty_chain() {
        let mut chain = Chain::new();
        assert!(chain.pullup(1).is_none());
    }

    #[test]
    fn test_pullup_return_what_has_been_appended() {
        let mut chain = Chain::new();
        let s = "HelloWorld".as_bytes();
        let ls = s.len();
        chain.append_bytes(s);
        let res = chain.pullup(ls);
        assert!(res.is_some());
        assert_eq!(res.unwrap(), s);
    }

    #[test]
    fn test_pullup_does_not_change_length() {
        let mut chain = Chain::new();
        let s = "HelloWorld".as_bytes();
        let ls = s.len();
        chain.append_bytes(s);
        let olds = chain.len();
        chain.pullup(ls / 2);
        assert_eq!(chain.len(), olds);
    }

    #[test]
    fn test_pullup_works_on_large_sequences() {
        let mut chain = Chain::new();
        let total = 2048u;
        let mut t = total;
        let one_seq = 128u;
        let mut buf = Vec::new();
        while t > 0 {
            let s:String = task_rng().gen_ascii_chars().take(one_seq).collect();
            let b = s.as_bytes();
            buf = buf.append(b);
            chain.append_bytes(b);
            t -= one_seq;
        }
        {
            let ret = chain.pullup(total);
            assert!(ret.is_some());
            assert_eq!(ret.unwrap(), buf.as_slice());
        }
        assert_eq!(chain.len(), total);
    }

    #[test]
    fn test_concat_increase_dst_length() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        chain1.append_bytes("HelloWorld".as_bytes());
        let l1 = chain1.len();
        chain2.append_bytes("HelloWorld".as_bytes());
        let l2 = chain2.len();
        chain1.concat(chain2);
        assert_eq!(chain1.len(), l1+l2);
    }

    #[test]
    fn test_concat_appends_content() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        let b = "HelloWorld".as_bytes();
        let bl = b.len();
        chain2.append_bytes(b);
        chain1.concat(chain2);
        let res = chain1.pullup(bl);

        assert!(res.is_some());
        assert_eq!(res.unwrap(), b);
    }

    #[test]
    fn test_reset_empties_chain() {
        let mut chain = Chain::new();
        chain.append_bytes("HelloWorld".as_bytes());
        chain.reset();
        assert!(chain.pullup(1).is_none());
        assert_eq!(chain.len(), 0);
    }

    #[test]
    fn test_append_copies_data() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        let s = "HelloWorld";
        let b = s.as_bytes();
        let lb = b.len();
        let ss = String::from_str(s).append(s);
        chain1.append_bytes(b);
        chain2.append_bytes(b);
        chain1.append(&mut chain2);
        {
            let res = chain1.pullup(2 * lb);
            assert!(res.is_some());
            assert_eq!(res.unwrap(), ss.as_bytes());
        }
        assert_eq!(chain1.len(), 2 * lb);
    }

    // XXX: do not need to test it for `concat`, because it is done for us
    // XXX: by type-system.
    #[test]
    fn test_concat_ptr_destroy_src() {
        let mut chain1 = Chain::new();
        let mut chain2 = Chain::new();
        chain2.append_bytes("HelloWorld".as_bytes());
        chain1.concat_ptr(&mut chain2);
        assert_eq!(chain2.len(), 0);
    }

}
<|endoftext|>


<commit_before>use crate::cmp;
use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::ptr;
use crate::sys::os;
use crate::time::Duration;

use crate::sys_common::thread::*;

pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;

pub struct Thread {
    id: libc::pthread_t,
}

// Some platforms may have pthread_t as a pointer in which case we still want
// a thread to be Send/Sync
unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}

// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc,
// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS.
unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
                                    stack_size: libc::size_t) -> libc::c_int {
    libc::pthread_attr_setstacksize(attr, stack_size)
}

impl Thread {
    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>)
                          -> io::Result<Thread> {
        let p = box p;
        let mut native: libc::pthread_t = mem::zeroed();
        let mut attr: libc::pthread_attr_t = mem::zeroed();
        assert_eq!(libc::pthread_attr_init(&mut attr), 0);

        let stack_size = cmp::max(stack, min_stack_size(&attr));

        match pthread_attr_setstacksize(&mut attr,
                                        stack_size) {
            0 => {}
            n => {
                assert_eq!(n, libc::EINVAL);
                // EINVAL means |stack_size| is either too small or not a
                // multiple of the system page size.  Because it's definitely
                // >= PTHREAD_STACK_MIN, it must be an alignment issue.
                // Round up to the nearest page and try again.
                let page_size = os::page_size();
                let stack_size = (stack_size + page_size - 1) &
                                 (-(page_size as isize - 1) as usize - 1);
                assert_eq!(libc::pthread_attr_setstacksize(&mut attr,
                                                           stack_size), 0);
            }
        };

        let ret = libc::pthread_create(&mut native, &attr, thread_start,
                                       &*p as *const _ as *mut _);
        assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);

        return if ret != 0 {
            Err(io::Error::from_raw_os_error(ret))
        } else {
            mem::forget(p); // ownership passed to pthread_create
            Ok(Thread { id: native })
        };

        extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
            unsafe { start_thread(main as *mut u8); }
            ptr::null_mut()
        }
    }

    pub fn yield_now() {
        let ret = unsafe { libc::sched_yield() };
        debug_assert_eq!(ret, 0);
    }

    pub fn set_name(_name: &CStr) {
        assert!(false, "FIXME: set_name");
    }

    pub fn sleep(dur: Duration) {
        let mut secs = dur.as_secs();
        let mut nsecs = dur.subsec_nanos() as _;

        // If we're awoken with a signal then the return value will be -1 and
        // nanosleep will fill in `ts` with the remaining time.
        unsafe {
            while secs > 0 || nsecs > 0 {
                let mut ts = libc::timespec {
                    tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
                    tv_nsec: nsecs,
                };
                secs -= ts.tv_sec as u64;
                if libc::nanosleep(&ts, &mut ts) == -1 {
                    assert_eq!(os::errno(), libc::EINTR);
                    secs += ts.tv_sec as u64;
                    nsecs = ts.tv_nsec;
                } else {
                    nsecs = 0;
                }
            }
        }
    }

    pub fn join(self) {
        unsafe {
            let ret = libc::pthread_join(self.id, ptr::null_mut());
            mem::forget(self);
            assert!(ret == 0,
                    "failed to join thread: {}", io::Error::from_raw_os_error(ret));
        }
    }

    pub fn id(&self) -> libc::pthread_t { self.id }

    pub fn into_id(self) -> libc::pthread_t {
        let id = self.id;
        mem::forget(self);
        id
    }
}

impl Drop for Thread {
    fn drop(&mut self) {
        let ret = unsafe { libc::pthread_detach(self.id) };
        debug_assert_eq!(ret, 0);
    }
}

#[cfg_attr(test, allow(dead_code))]
pub mod guard {
    use crate::ops::Range;
    pub type Guard = Range<usize>;
    pub unsafe fn current() -> Option<Guard> { None }
    pub unsafe fn init() -> Option<Guard> { None }
    pub unsafe fn deinit() {}
}

fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
    libc::PTHREAD_STACK_MIN
}
<commit_msg>VxWorks does not provide a way to set the task name except at creation time<commit_after>use crate::cmp;
use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::ptr;
use crate::sys::os;
use crate::time::Duration;

use crate::sys_common::thread::*;

pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;

pub struct Thread {
    id: libc::pthread_t,
}

// Some platforms may have pthread_t as a pointer in which case we still want
// a thread to be Send/Sync
unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}

// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc,
// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS.
unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
                                    stack_size: libc::size_t) -> libc::c_int {
    libc::pthread_attr_setstacksize(attr, stack_size)
}

impl Thread {
    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>)
                          -> io::Result<Thread> {
        let p = box p;
        let mut native: libc::pthread_t = mem::zeroed();
        let mut attr: libc::pthread_attr_t = mem::zeroed();
        assert_eq!(libc::pthread_attr_init(&mut attr), 0);

        let stack_size = cmp::max(stack, min_stack_size(&attr));

        match pthread_attr_setstacksize(&mut attr,
                                        stack_size) {
            0 => {}
            n => {
                assert_eq!(n, libc::EINVAL);
                // EINVAL means |stack_size| is either too small or not a
                // multiple of the system page size.  Because it's definitely
                // >= PTHREAD_STACK_MIN, it must be an alignment issue.
                // Round up to the nearest page and try again.
                let page_size = os::page_size();
                let stack_size = (stack_size + page_size - 1) &
                                 (-(page_size as isize - 1) as usize - 1);
                assert_eq!(libc::pthread_attr_setstacksize(&mut attr,
                                                           stack_size), 0);
            }
        };

        let ret = libc::pthread_create(&mut native, &attr, thread_start,
                                       &*p as *const _ as *mut _);
        assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);

        return if ret != 0 {
            Err(io::Error::from_raw_os_error(ret))
        } else {
            mem::forget(p); // ownership passed to pthread_create
            Ok(Thread { id: native })
        };

        extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
            unsafe { start_thread(main as *mut u8); }
            ptr::null_mut()
        }
    }

    pub fn yield_now() {
        let ret = unsafe { libc::sched_yield() };
        debug_assert_eq!(ret, 0);
    }

    pub fn set_name(_name: &CStr) {
        // VxWorks does not provide a way to set the task name except at creation time
    }

    pub fn sleep(dur: Duration) {
        let mut secs = dur.as_secs();
        let mut nsecs = dur.subsec_nanos() as _;

        // If we're awoken with a signal then the return value will be -1 and
        // nanosleep will fill in `ts` with the remaining time.
        unsafe {
            while secs > 0 || nsecs > 0 {
                let mut ts = libc::timespec {
                    tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
                    tv_nsec: nsecs,
                };
                secs -= ts.tv_sec as u64;
                if libc::nanosleep(&ts, &mut ts) == -1 {
                    assert_eq!(os::errno(), libc::EINTR);
                    secs += ts.tv_sec as u64;
                    nsecs = ts.tv_nsec;
                } else {
                    nsecs = 0;
                }
            }
        }
    }

    pub fn join(self) {
        unsafe {
            let ret = libc::pthread_join(self.id, ptr::null_mut());
            mem::forget(self);
            assert!(ret == 0,
                    "failed to join thread: {}", io::Error::from_raw_os_error(ret));
        }
    }

    pub fn id(&self) -> libc::pthread_t { self.id }

    pub fn into_id(self) -> libc::pthread_t {
        let id = self.id;
        mem::forget(self);
        id
    }
}

impl Drop for Thread {
    fn drop(&mut self) {
        let ret = unsafe { libc::pthread_detach(self.id) };
        debug_assert_eq!(ret, 0);
    }
}

#[cfg_attr(test, allow(dead_code))]
pub mod guard {
    use crate::ops::Range;
    pub type Guard = Range<usize>;
    pub unsafe fn current() -> Option<Guard> { None }
    pub unsafe fn init() -> Option<Guard> { None }
    pub unsafe fn deinit() {}
}

fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
    libc::PTHREAD_STACK_MIN
}
<|endoftext|>