<commit_before>extern crate fallible_iterator;
extern crate futures;
extern crate postgres_shared;
extern crate postgres_protocol;
extern crate tokio_core;
extern crate tokio_dns;
extern crate tokio_uds;

use fallible_iterator::FallibleIterator;
use futures::{Future, IntoFuture, BoxFuture, Stream, Sink, Poll, StartSend};
use futures::future::Either;
use postgres_protocol::authentication;
use postgres_protocol::message::{backend, frontend};
use postgres_protocol::message::backend::{ErrorResponseBody, ErrorFields};
use postgres_shared::RowData;
use std::collections::HashMap;
use std::fmt;
use std::io;
use tokio_core::reactor::Handle;

#[doc(inline)]
pub use postgres_shared::params;

use error::{ConnectError, Error, DbError};
use params::{ConnectParams, IntoConnectParams};
use stream::PostgresStream;

pub mod error;
mod stream;

#[cfg(test)]
mod test;

#[derive(Debug, Copy, Clone)]
pub struct CancelData {
    pub process_id: i32,
    pub secret_key: i32,
}

struct InnerConnection {
    stream: PostgresStream,
    parameters: HashMap<String, String>,
    cancel_data: CancelData,
}

impl InnerConnection {
    fn read(self) -> BoxFuture<(backend::Message<Vec<u8>>, InnerConnection), (io::Error, InnerConnection)> {
        self.into_future()
            .then(|r| {
                let (m, mut s) = match r {
                    Ok((m, s)) => (m, s),
                    Err((e, s)) => return Either::A(Err((e, s)).into_future()),
                };

                match m {
                    Some(backend::Message::ParameterStatus(body)) => {
                        let name = match body.name() {
                            Ok(name) => name.to_owned(),
                            Err(e) => return Either::A(Err((e, s)).into_future()),
                        };
                        let value = match body.value() {
                            Ok(value) => value.to_owned(),
                            Err(e) => return Either::A(Err((e, s)).into_future()),
                        };
                        s.parameters.insert(name, value);
                        Either::B(s.read())
                    }
                    Some(backend::Message::NoticeResponse(_)) => {
                        // TODO forward the error
                        Either::B(s.read())
                    }
                    Some(m) => Either::A(Ok((m, s)).into_future()),
                    None => {
                        let err = io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF");
                        Either::A(Err((err, s)).into_future())
                    }
                }
            })
            .boxed()
    }
}

impl Stream for InnerConnection {
    type Item = backend::Message<Vec<u8>>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<backend::Message<Vec<u8>>>, io::Error> {
        self.stream.poll()
    }
}

impl Sink for InnerConnection {
    type SinkItem = Vec<u8>;
    type SinkError = io::Error;

    fn start_send(&mut self, item: Vec<u8>) -> StartSend<Vec<u8>, io::Error> {
        self.stream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), io::Error> {
        self.stream.poll_complete()
    }
}

pub struct Connection(InnerConnection);

// FIXME fill out
impl fmt::Debug for Connection {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Connection")
            .finish()
    }
}

impl Connection {
    pub fn connect<T>(params: T, handle: &Handle) -> BoxFuture<Connection, ConnectError>
        where T: IntoConnectParams
    {
        let params = match params.into_connect_params() {
            Ok(params) => params,
            Err(e) => return futures::failed(ConnectError::ConnectParams(e)).boxed(),
        };

        stream::connect(params.host(), params.port(), handle)
            .map_err(ConnectError::Io)
            .map(|s| {
                Connection(InnerConnection {
                    stream: s,
                    parameters: HashMap::new(),
                    cancel_data: CancelData {
                        process_id: 0,
                        secret_key: 0,
                    }
                })
            })
            .and_then(|s| s.startup(params))
            .and_then(|(s, params)| s.handle_auth(params))
            .and_then(|s| s.finish_startup())
            .boxed()
    }

    fn startup(self, params: ConnectParams) -> BoxFuture<(Connection, ConnectParams), ConnectError> {
        let mut buf = vec![];
        let result = {
            let options = [("client_encoding", "UTF8"), ("timezone", "GMT")];
            let options = options.iter().cloned();
            let options = options.chain(params.user().map(|u| ("user", u.name())));
            let options = options.chain(params.database().map(|d| ("database", d)));
            let options = options.chain(params.options().iter().map(|e| (&*e.0, &*e.1)));

            frontend::startup_message(options, &mut buf)
        };

        result
            .into_future()
            .and_then(move |()| self.0.send(buf))
            .and_then(|s| s.flush())
            .map_err(ConnectError::Io)
            .map(move |s| (Connection(s), params))
            .boxed()
    }

    fn handle_auth(self, params: ConnectParams) -> BoxFuture<Connection, ConnectError> {
        self.0.read()
            .map_err(|e| e.0.into())
            .and_then(move |(m, s)| {
                let response = match m {
                    backend::Message::AuthenticationOk => Ok(None),
                    backend::Message::AuthenticationCleartextPassword => {
                        match params.user().and_then(|u| u.password()) {
                            Some(pass) => {
                                let mut buf = vec![];
                                frontend::password_message(pass, &mut buf)
                                    .map(|()| Some(buf))
                                    .map_err(Into::into)
                            }
                            None => {
                                Err(ConnectError::ConnectParams(
                                    "password was required but not provided".into()))
                            }
                        }
                    }
                    backend::Message::AuthenticationMd5Password(body) => {
                        match params.user().and_then(|u| u.password().map(|p| (u.name(), p))) {
                            Some((user, pass)) => {
                                let pass = authentication::md5_hash(user.as_bytes(),
                                                                    pass.as_bytes(),
                                                                    body.salt());
                                let mut buf = vec![];
                                frontend::password_message(&pass, &mut buf)
                                    .map(|()| Some(buf))
                                    .map_err(Into::into)
                            }
                            None => {
                                Err(ConnectError::ConnectParams(
                                    "password was required but not provided".into()))
                            }
                        }
                    }
                    backend::Message::ErrorResponse(body) => Err(connect_err(&mut body.fields())),
                    _ => Err(bad_message()),
                };

                response.map(|m| (m, Connection(s)))
            })
            .and_then(|(m, s)| {
                match m {
                    Some(m) => Either::A(s.handle_auth_response(m)),
                    None => Either::B(Ok(s).into_future())
                }
            })
            .boxed()
    }

    fn handle_auth_response(self, message: Vec<u8>) -> BoxFuture<Connection, ConnectError> {
        self.0.send(message)
            .and_then(|s| s.flush())
            .and_then(|s| s.read().map_err(|e| e.0))
            .map_err(ConnectError::Io)
            .and_then(|(m, s)| {
                match m {
                    backend::Message::AuthenticationOk => Ok(Connection(s)),
                    backend::Message::ErrorResponse(body) => Err(connect_err(&mut body.fields())),
                    _ => Err(bad_message()),
                }
            })
            .boxed()
    }

    fn finish_startup(self) -> BoxFuture<Connection, ConnectError> {
        self.0.read()
            .map_err(|e| ConnectError::Io(e.0))
            .and_then(|(m, mut s)| {
                match m {
                    backend::Message::BackendKeyData(body) => {
                        s.cancel_data.process_id = body.process_id();
                        s.cancel_data.secret_key = body.secret_key();
                        Either::A(Connection(s).finish_startup())
                    }
                    backend::Message::ReadyForQuery(_) => Either::B(Ok(Connection(s)).into_future()),
                    backend::Message::ErrorResponse(body) => {
                        Either::B(Err(connect_err(&mut body.fields())).into_future())
                    }
                    _ => Either::B(Err(bad_message()).into_future()),
                }
            })
            .boxed()
    }

    fn simple_query(self, query: &str) -> BoxFuture<(Vec<RowData>, Connection), Error> {
        let mut buf = vec![];
        frontend::query(query, &mut buf)
            .map(|()| buf)
            .into_future()
            .and_then(move |buf| self.0.send(buf))
            .and_then(|s| s.flush())
            .map_err(Error::Io)
            .and_then(|s| Connection(s).simple_read_rows(vec![]))
            .boxed()
    }

    // This has its own read_rows since it will need to handle multiple query completions
    fn simple_read_rows(self, mut rows: Vec<RowData>) -> BoxFuture<(Vec<RowData>, Connection), Error> {
        self.0.read()
            .map_err(|e| Error::Io(e.0))
            .and_then(|(m, s)| {
                match m {
                    backend::Message::ReadyForQuery(_) => {
                        Ok((rows, Connection(s))).into_future().boxed()
                    }
                    backend::Message::DataRow(body) => {
                        match body.values().collect() {
                            Ok(row) => {
                                rows.push(row);
                                Connection(s).simple_read_rows(rows)
                            }
                            Err(e) => Err(Error::Io(e)).into_future().boxed(),
                        }
                    }
                    backend::Message::EmptyQueryResponse |
                    backend::Message::CommandComplete(_) |
                    backend::Message::RowDescription(_) => Connection(s).simple_read_rows(rows),
                    backend::Message::ErrorResponse(body) => Connection(s).ready_err(body),
                    _ => Err(bad_message()).into_future().boxed(),
                }
            })
            .boxed()
    }

    fn read_rows(self, mut rows: Vec<RowData>) -> BoxFuture<(Vec<RowData>, Connection), Error> {
        self.0.read()
            .map_err(|e| Error::Io(e.0))
            .and_then(|(m, s)| {
                match m {
                    backend::Message::EmptyQueryResponse |
                    backend::Message::CommandComplete(_) => Connection(s).ready(rows).boxed(),
                    backend::Message::DataRow(body) => {
                        match body.values().collect() {
                            Ok(row) => {
                                rows.push(row);
                                Connection(s).read_rows(rows)
                            }
                            Err(e) => Err(Error::Io(e)).into_future().boxed(),
                        }
                    }
                    backend::Message::ErrorResponse(body) => Connection(s).ready_err(body),
                    _ => Err(bad_message()).into_future().boxed(),
                }
            })
            .boxed()
    }

    fn ready<T>(self, t: T) -> BoxFuture<(T, Connection), Error>
        where T: 'static + Send
    {
        self.0.read()
            .map_err(|e| Error::Io(e.0))
            .and_then(|(m, s)| {
                match m {
                    backend::Message::ReadyForQuery(_) => Ok((t, Connection(s))),
                    _ => Err(bad_message())
                }
            })
            .boxed()
    }

    fn ready_err<T>(self, body: ErrorResponseBody<Vec<u8>>) -> BoxFuture<T, Error>
        where T: 'static + Send
    {
        DbError::new(&mut body.fields())
            .map_err(Error::Io)
            .into_future()
            .and_then(|e| self.ready(e))
            .and_then(|(e, s)| Err(Error::Db(Box::new(e), s)))
            .boxed()
    }

    pub fn batch_execute(self, query: &str) -> BoxFuture<Connection, Error> {
        self.simple_query(query).map(|r| r.1).boxed()
    }

    pub fn cancel_data(&self) -> CancelData {
        self.0.cancel_data
    }
}

fn connect_err(fields: &mut ErrorFields) -> ConnectError {
    match DbError::new(fields) {
        Ok(err) => ConnectError::Db(Box::new(err)),
        Err(err) => ConnectError::Io(err),
    }
}

fn bad_message<T>() -> T
    where T: From<io::Error>
{
    io::Error::new(io::ErrorKind::InvalidInput, "unexpected message").into()
}
<commit_msg>Add raw_prepare<commit_after>extern crate fallible_iterator;
extern crate futures;
extern crate postgres_shared;
extern crate postgres_protocol;
extern crate tokio_core;
extern crate tokio_dns;
extern crate tokio_uds;

use fallible_iterator::FallibleIterator;
use futures::{Future, IntoFuture, BoxFuture, Stream, Sink, Poll, StartSend};
use futures::future::Either;
use postgres_protocol::authentication;
use postgres_protocol::message::{backend, frontend};
use postgres_protocol::message::backend::{ErrorResponseBody, ErrorFields};
use postgres_shared::RowData;
use std::collections::HashMap;
use std::fmt;
use std::io;
use tokio_core::reactor::Handle;

#[doc(inline)]
pub use postgres_shared::{params, types};

use error::{ConnectError, Error, DbError};
use params::{ConnectParams, IntoConnectParams};
use stream::PostgresStream;
use types::{Oid, Type};

pub mod error;
mod stream;

#[cfg(test)]
mod test;

#[derive(Debug, Copy, Clone)]
pub struct CancelData {
    pub process_id: i32,
    pub secret_key: i32,
}

struct InnerConnection {
    stream: PostgresStream,
    parameters: HashMap<String, String>,
    cancel_data: CancelData,
}

impl InnerConnection {
    fn read(self) -> BoxFuture<(backend::Message<Vec<u8>>, InnerConnection), io::Error> {
        self.into_future()
            .map_err(|e| e.0)
            .and_then(|(m, mut s)| {
                match m {
                    Some(backend::Message::ParameterStatus(body)) => {
                        let name = match body.name() {
                            Ok(name) => name.to_owned(),
                            Err(e) => return Either::A(Err(e).into_future()),
                        };
                        let value = match body.value() {
                            Ok(value) => value.to_owned(),
                            Err(e) => return Either::A(Err(e).into_future()),
                        };
                        s.parameters.insert(name, value);
                        Either::B(s.read())
                    }
                    Some(backend::Message::NoticeResponse(_)) => {
                        // TODO forward the error
                        Either::B(s.read())
                    }
                    Some(m) => Either::A(Ok((m, s)).into_future()),
                    None => {
                        let err = io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF");
                        Either::A(Err(err).into_future())
                    }
                }
            })
            .boxed()
    }
}

impl Stream for InnerConnection {
    type Item = backend::Message<Vec<u8>>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<backend::Message<Vec<u8>>>, io::Error> {
        self.stream.poll()
    }
}

impl Sink for InnerConnection {
    type SinkItem = Vec<u8>;
    type SinkError = io::Error;

    fn start_send(&mut self, item: Vec<u8>) -> StartSend<Vec<u8>, io::Error> {
        self.stream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), io::Error> {
        self.stream.poll_complete()
    }
}

pub struct Connection(InnerConnection);

// FIXME fill out
impl fmt::Debug for Connection {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Connection")
            .finish()
    }
}

impl Connection {
    pub fn connect<T>(params: T, handle: &Handle) -> BoxFuture<Connection, ConnectError>
        where T: IntoConnectParams
    {
        let params = match params.into_connect_params() {
            Ok(params) => params,
            Err(e) => return futures::failed(ConnectError::ConnectParams(e)).boxed(),
        };

        stream::connect(params.host(), params.port(), handle)
            .map_err(ConnectError::Io)
            .map(|s| {
                Connection(InnerConnection {
                    stream: s,
                    parameters: HashMap::new(),
                    cancel_data: CancelData {
                        process_id: 0,
                        secret_key: 0,
                    }
                })
            })
            .and_then(|s| s.startup(params))
            .and_then(|(s, params)| s.handle_auth(params))
            .and_then(|s| s.finish_startup())
            .boxed()
    }

    fn startup(self, params: ConnectParams) -> BoxFuture<(Connection, ConnectParams), ConnectError> {
        let mut buf = vec![];
        let result = {
            let options = [("client_encoding", "UTF8"), ("timezone", "GMT")];
            let options = options.iter().cloned();
            let options = options.chain(params.user().map(|u| ("user", u.name())));
            let options = options.chain(params.database().map(|d| ("database", d)));
            let options = options.chain(params.options().iter().map(|e| (&*e.0, &*e.1)));

            frontend::startup_message(options, &mut buf)
        };

        result
            .into_future()
            .and_then(move |()| self.0.send(buf))
            .and_then(|s| s.flush())
            .map_err(ConnectError::Io)
            .map(move |s| (Connection(s), params))
            .boxed()
    }

    fn handle_auth(self, params: ConnectParams) -> BoxFuture<Connection, ConnectError> {
        self.0.read()
            .map_err(ConnectError::Io)
            .and_then(move |(m, s)| {
                let response = match m {
                    backend::Message::AuthenticationOk => Ok(None),
                    backend::Message::AuthenticationCleartextPassword => {
                        match params.user().and_then(|u| u.password()) {
                            Some(pass) => {
                                let mut buf = vec![];
                                frontend::password_message(pass, &mut buf)
                                    .map(|()| Some(buf))
                                    .map_err(Into::into)
                            }
                            None => {
                                Err(ConnectError::ConnectParams(
                                    "password was required but not provided".into()))
                            }
                        }
                    }
                    backend::Message::AuthenticationMd5Password(body) => {
                        match params.user().and_then(|u| u.password().map(|p| (u.name(), p))) {
                            Some((user, pass)) => {
                                let pass = authentication::md5_hash(user.as_bytes(),
                                                                    pass.as_bytes(),
                                                                    body.salt());
                                let mut buf = vec![];
                                frontend::password_message(&pass, &mut buf)
                                    .map(|()| Some(buf))
                                    .map_err(Into::into)
                            }
                            None => {
                                Err(ConnectError::ConnectParams(
                                    "password was required but not provided".into()))
                            }
                        }
                    }
                    backend::Message::ErrorResponse(body) => Err(connect_err(&mut body.fields())),
                    _ => Err(bad_message()),
                };

                response.map(|m| (m, Connection(s)))
            })
            .and_then(|(m, s)| {
                match m {
                    Some(m) => Either::A(s.handle_auth_response(m)),
                    None => Either::B(Ok(s).into_future())
                }
            })
            .boxed()
    }

    fn handle_auth_response(self, message: Vec<u8>) -> BoxFuture<Connection, ConnectError> {
        self.0.send(message)
            .and_then(|s| s.flush())
            .and_then(|s| s.read())
            .map_err(ConnectError::Io)
            .and_then(|(m, s)| {
                match m {
                    backend::Message::AuthenticationOk => Ok(Connection(s)),
                    backend::Message::ErrorResponse(body) => Err(connect_err(&mut body.fields())),
                    _ => Err(bad_message()),
                }
            })
            .boxed()
    }

    fn finish_startup(self) -> BoxFuture<Connection, ConnectError> {
        self.0.read()
            .map_err(ConnectError::Io)
            .and_then(|(m, mut s)| {
                match m {
                    backend::Message::BackendKeyData(body) => {
                        s.cancel_data.process_id = body.process_id();
                        s.cancel_data.secret_key = body.secret_key();
                        Either::A(Connection(s).finish_startup())
                    }
                    backend::Message::ReadyForQuery(_) => Either::B(Ok(Connection(s)).into_future()),
                    backend::Message::ErrorResponse(body) => {
                        Either::B(Err(connect_err(&mut body.fields())).into_future())
                    }
                    _ => Either::B(Err(bad_message()).into_future()),
                }
            })
            .boxed()
    }

    fn simple_query(self, query: &str) -> BoxFuture<(Vec<RowData>, Connection), Error> {
        let mut buf = vec![];
        frontend::query(query, &mut buf)
            .map(|()| buf)
            .into_future()
            .and_then(move |buf| self.0.send(buf))
            .and_then(|s| s.flush())
            .map_err(Error::Io)
            .and_then(|s| Connection(s).simple_read_rows(vec![]))
            .boxed()
    }

    // This has its own read_rows since it will need to handle multiple query completions
    fn simple_read_rows(self, mut rows: Vec<RowData>) -> BoxFuture<(Vec<RowData>, Connection), Error> {
        self.0.read()
            .map_err(Error::Io)
            .and_then(|(m, s)| {
                match m {
                    backend::Message::ReadyForQuery(_) => {
                        Ok((rows, Connection(s))).into_future().boxed()
                    }
                    backend::Message::DataRow(body) => {
                        match body.values().collect() {
                            Ok(row) => {
                                rows.push(row);
                                Connection(s).simple_read_rows(rows)
                            }
                            Err(e) => Err(Error::Io(e)).into_future().boxed(),
                        }
                    }
                    backend::Message::EmptyQueryResponse |
                    backend::Message::CommandComplete(_) |
                    backend::Message::RowDescription(_) => Connection(s).simple_read_rows(rows),
                    backend::Message::ErrorResponse(body) => Connection(s).ready_err(body),
                    _ => Err(bad_message()).into_future().boxed(),
                }
            })
            .boxed()
    }

    #[allow(dead_code)]
    fn read_rows(self, mut rows: Vec<RowData>) -> BoxFuture<(Vec<RowData>, Connection), Error> {
        self.0.read()
            .map_err(Error::Io)
            .and_then(|(m, s)| {
                match m {
                    backend::Message::EmptyQueryResponse |
                    backend::Message::CommandComplete(_) => Connection(s).ready(rows).boxed(),
                    backend::Message::DataRow(body) => {
                        match body.values().collect() {
                            Ok(row) => {
                                rows.push(row);
                                Connection(s).read_rows(rows)
                            }
                            Err(e) => Err(Error::Io(e)).into_future().boxed(),
                        }
                    }
                    backend::Message::ErrorResponse(body) => Connection(s).ready_err(body),
                    _ => Err(bad_message()).into_future().boxed(),
                }
            })
            .boxed()
    }

    fn ready<T>(self, t: T) -> BoxFuture<(T, Connection), Error>
        where T: 'static + Send
    {
        self.0.read()
            .map_err(Error::Io)
            .and_then(|(m, s)| {
                match m {
                    backend::Message::ReadyForQuery(_) => Ok((t, Connection(s))),
                    _ => Err(bad_message())
                }
            })
            .boxed()
    }

    fn ready_err<T>(self, body: ErrorResponseBody<Vec<u8>>) -> BoxFuture<T, Error>
        where T: 'static + Send
    {
        DbError::new(&mut body.fields())
            .map_err(Error::Io)
            .into_future()
            .and_then(|e| self.ready(e))
            .and_then(|(e, s)| Err(Error::Db(Box::new(e), s)))
            .boxed()
    }

    pub fn batch_execute(self, query: &str) -> BoxFuture<Connection, Error> {
        self.simple_query(query).map(|r| r.1).boxed()
    }

    fn raw_prepare(self,
                   name: &str,
                   query: &str)
                   -> BoxFuture<(Vec<Type>, Vec<Column>, Connection), Error> {
        let mut parse = vec![];
        let mut describe = vec![];
        let mut sync = vec![];
        frontend::parse(name, query, None, &mut parse)
            .and_then(|()| frontend::describe(b'S', name, &mut describe))
            .and_then(|()| Ok(frontend::sync(&mut sync)))
            .into_future()
            .and_then(move |()| self.0.send(parse))
            .and_then(|s| s.send(describe))
            .and_then(|s| s.send(sync))
            .and_then(|s| s.flush())
            .and_then(|s| s.read())
            .map_err(Error::Io)
            .boxed() // work around nonlinear trans blowup
            .and_then(|(m, s)| {
                match m {
                    backend::Message::ParseComplete => Either::A(Ok(s).into_future()),
                    backend::Message::ErrorResponse(body) => {
                        Either::B(Connection(s).ready_err(body))
                    }
                    _ => Either::A(Err(bad_message()).into_future()),
                }
            })
            .and_then(|s| s.read().map_err(Error::Io))
            .and_then(|(m, s)| {
                match m {
                    backend::Message::ParameterDescription(body) => {
                        body.parameters().collect::<Vec<_>>()
                            .map(|p| (p, s))
                            .map_err(Error::Io)
                    }
                    _ => Err(bad_message()),
                }
            })
            .and_then(|(p, s)| s.read().map(|(m, s)| (p, m, s)).map_err(Error::Io))
            .boxed() // work around nonlinear trans blowup
            .and_then(|(p, m, s)| {
                match m {
                    backend::Message::RowDescription(body) => {
                        body.fields()
                            .map(|f| (f.name().to_owned(), f.type_oid()))
                            .collect::<Vec<_>>()
                            .map(|d| (p, d, s))
                            .map_err(Error::Io)
                    }
                    backend::Message::NoData => Ok((p, vec![], s)),
                    _ => Err(bad_message()),
                }
            })
            .and_then(|(p, r, s)| Connection(s).ready((p, r)))
            .and_then(|((p, r), s)| {
                s.get_types(p.into_iter(), vec![], |&p| p, |_, t| t)
                    .map(|(p, s)| (p, r, s))
            })
            .and_then(|(p, r, s)| {
                s.get_types(r.into_iter(),
                            vec![],
                            |f| f.1,
                            |f, t| Column { name: f.0, type_: t })
                    .map(|(r, s)| (p, r, s))
            })
            .boxed()
    }

    fn get_types<T, U, I, F, G>(self,
                                mut raw: I,
                                mut out: Vec<U>,
                                mut get_oid: F,
                                mut build: G)
                                -> BoxFuture<(Vec<U>, Connection), Error>
        where T: 'static + Send,
              U: 'static + Send,
              I: 'static + Send + Iterator<Item = T>,
              F: 'static + Send + FnMut(&T) -> Oid,
              G: 'static + Send + FnMut(T, Type) -> U
    {
        match raw.next() {
            Some(v) => {
                let oid = get_oid(&v);
                self.get_type(oid)
                    .and_then(move |(ty, s)| {
                        out.push(build(v, ty));
                        s.get_types(raw, out, get_oid, build)
                    })
                    .boxed()
            }
            None => Ok((out, self)).into_future().boxed(),
        }
    }

    fn get_type(self, oid: Oid) -> BoxFuture<(Type, Connection), Error> {
        if let Some(type_) = Type::from_oid(oid) {
            return Ok((type_, self)).into_future().boxed();
        };
        unimplemented!()
    }

    pub fn cancel_data(&self) -> CancelData {
        self.0.cancel_data
    }
}

struct Column {
    name: String,
    type_: Type,
}

fn connect_err(fields: &mut ErrorFields) -> ConnectError {
    match DbError::new(fields) {
        Ok(err) => ConnectError::Db(Box::new(err)),
        Err(err) => ConnectError::Io(err),
    }
}

fn bad_message<T>() -> T
    where T: From<io::Error>
{
    io::Error::new(io::ErrorKind::InvalidInput, "unexpected message").into()
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use glob::Pattern;
use x_lint::prelude::*;

#[derive(Clone, Copy, Debug)]
pub(super) struct EofNewline;

impl Linter for EofNewline {
    fn name(&self) -> &'static str {
        "eof-newline"
    }
}

impl ContentLinter for EofNewline {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };
        if !content.is_empty() && !content.ends_with('\n') {
            out.write(LintLevel::Error, "missing newline at EOF");
        }
        Ok(RunStatus::Executed)
    }
}

#[derive(Clone, Copy, Debug)]
pub(super) struct TrailingWhitespace;

impl Linter for TrailingWhitespace {
    fn name(&self) -> &'static str {
        "trailing-whitespace"
    }
}

impl ContentLinter for TrailingWhitespace {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };

        for (ln, line) in content.lines().enumerate().map(|(ln, line)| (ln + 1, line)) {
            if line.trim_end() != line {
                out.write(
                    LintLevel::Error,
                    format!("trailing whitespace at line {}", ln),
                );
            }
        }

        if content
            .lines()
            .rev()
            .take_while(|line| line.is_empty())
            .count()
            > 0
        {
            out.write(LintLevel::Error, "trailing whitespace at EOF");
        }

        Ok(RunStatus::Executed)
    }
}

fn skip_whitespace_checks<'l>(file: &FileContext<'l>) -> RunStatus<'l> {
    // glob based opt outs
    let patterns = [".github/actions/*/dist/*"];

    if let Some(pattern) = patterns
        .iter()
        .find(|s| Pattern::new(s).unwrap().matches_path(file.file_path()))
    {
        return RunStatus::Skipped(SkipReason::GlobExemption(pattern));
    };

    // extension based opt outs
    #[allow(clippy::single_match)]
    match file.extension() {
        Some("exp") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        Some("errmap") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        _ => (),
    }

    RunStatus::Executed
}
<commit_msg>x: skip whitespace checks for *.md files in developers.libra.org<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use glob::Pattern;
use x_lint::prelude::*;

#[derive(Clone, Copy, Debug)]
pub(super) struct EofNewline;

impl Linter for EofNewline {
    fn name(&self) -> &'static str {
        "eof-newline"
    }
}

impl ContentLinter for EofNewline {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };
        if !content.is_empty() && !content.ends_with('\n') {
            out.write(LintLevel::Error, "missing newline at EOF");
        }
        Ok(RunStatus::Executed)
    }
}

#[derive(Clone, Copy, Debug)]
pub(super) struct TrailingWhitespace;

impl Linter for TrailingWhitespace {
    fn name(&self) -> &'static str {
        "trailing-whitespace"
    }
}

impl ContentLinter for TrailingWhitespace {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };

        for (ln, line) in content.lines().enumerate().map(|(ln, line)| (ln + 1, line)) {
            if line.trim_end() != line {
                out.write(
                    LintLevel::Error,
                    format!("trailing whitespace at line {}", ln),
                );
            }
        }

        if content
            .lines()
            .rev()
            .take_while(|line| line.is_empty())
            .count()
            > 0
        {
            out.write(LintLevel::Error, "trailing whitespace at EOF");
        }

        Ok(RunStatus::Executed)
    }
}

fn skip_whitespace_checks<'l>(file: &FileContext<'l>) -> RunStatus<'l> {
    // glob based opt outs
    // TODO Reevaluate skipping whitespace checks in .md files for the website
    let patterns = [".github/actions/*/dist/*", "developers.libra.org/**/*.md"];

    if let Some(pattern) = patterns
        .iter()
        .find(|s| Pattern::new(s).unwrap().matches_path(file.file_path()))
    {
        return RunStatus::Skipped(SkipReason::GlobExemption(pattern));
    };

    // extension based opt outs
    #[allow(clippy::single_match)]
    match file.extension() {
        Some("exp") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        Some("errmap") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        _ => (),
    }

    RunStatus::Executed
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An engine for handling edits (possibly from async sources) and undo. This
//! module actually implements a mini Conflict-free Replicated Data Type, but
//! is considerably simpler than the usual CRDT implementation techniques,
//! because all operations are serialized in this central engine.

use std::borrow::Cow;
use std::collections::BTreeSet;
use std;

use rope::{Rope, RopeInfo};
use subset::Subset;
use delta::Delta;

pub struct Engine {
    rev_id_counter: usize,
    union_str: Rope,
    revs: Vec<Revision>,
}

struct Revision {
    rev_id: usize,
    deletes_from_union: Subset,
    union_str_len: usize,
    edit: Contents,
}

use self::Contents::*;

enum Contents {
    Edit {
        priority: usize,
        undo_group: usize,
        inserts: Subset,
        deletes: Subset,
    },
    Undo {
        groups: BTreeSet<usize>,  // set of undo_group id's
    }
}

impl Engine {
    pub fn new(initial_contents: Rope) -> Engine {
        let rev = Revision {
            rev_id: 0,
            deletes_from_union: Subset::default(),
            union_str_len: initial_contents.len(),
            edit: Undo { groups: BTreeSet::default() },
        };
        Engine {
            rev_id_counter: 1,
            union_str: initial_contents,
            revs: vec![rev],
        }
    }

    fn get_current_undo(&self) -> Option<&BTreeSet<usize>> {
        for rev in self.revs.iter().rev() {
            if let Undo { ref groups } = rev.edit {
                return Some(groups);
            }
        }
        None
    }

    fn find_rev(&self, rev_id: usize) -> Option<usize> {
        for (i, rev) in self.revs.iter().enumerate().rev() {
            if rev.rev_id == rev_id {
                return Some(i)
            }
        }
        None
    }

    /// Get the contents of the document at a given revision number
    fn get_rev_from_index(&self, rev_index: usize) -> Rope {
        self.get_subset_from_index(rev_index).delete_from(&self.union_str)
    }

    /// Get the Subset to delete from the current union string in order to obtain a revision's content
    fn get_subset_from_index(&self, rev_index: usize) -> Cow<Subset> {
        let mut deletes_from_union = Cow::Borrowed(&self.revs[rev_index].deletes_from_union);
        for rev in &self.revs[rev_index + 1..] {
            if let Edit { ref inserts, .. } = rev.edit {
                if !inserts.is_empty() {
                    deletes_from_union = Cow::Owned(deletes_from_union.transform_union(inserts));
                }
            }
        }
        deletes_from_union
    }

    /// Get revision id of head revision.
    pub fn get_head_rev_id(&self) -> usize {
        self.revs.last().unwrap().rev_id
    }

    /// Get text of head revision.
    pub fn get_head(&self) -> Rope {
        self.get_rev_from_index(self.revs.len() - 1)
    }

    /// Get text of a given revision, if it can be found.
    pub fn get_rev(&self, rev: usize) -> Option<Rope> {
        self.find_rev(rev).map(|rev_index| self.get_rev_from_index(rev_index))
    }

    /// A delta that, when applied to previous head, results in the current head. Panics
    /// if there is not at least one edit.
    pub fn delta_rev_head(&self, base_rev: usize) -> Delta<RopeInfo> {
        let ix = self.find_rev(base_rev).expect("base revision not found");
        let rev = &self.revs[ix];
        let mut prev_from_union = Cow::Borrowed(&rev.deletes_from_union);
        for r in &self.revs[ix + 1..] {
            if let Edit { ref inserts, .. } = r.edit {
                if !inserts.is_empty() {
                    prev_from_union = Cow::Owned(prev_from_union.transform_union(inserts));
                }
            }
        }
        let head_rev = &self.revs.last().unwrap();
        Delta::synthesize(&self.union_str, &prev_from_union, &head_rev.deletes_from_union)
    }

    fn mk_new_rev(&self, new_priority: usize, undo_group: usize,
            base_rev: usize, delta: Delta<RopeInfo>) -> (Revision, Rope) {
        let ix = self.find_rev(base_rev).expect("base revision not found");
        let rev = &self.revs[ix];
        let (ins_delta, deletes) = delta.factor();
        let mut union_ins_delta = ins_delta.transform_expand(&rev.deletes_from_union, rev.union_str_len, true);
        let mut new_deletes = deletes.transform_expand(&rev.deletes_from_union);
        for r in &self.revs[ix + 1..] {
            if let Edit { priority, ref inserts, .. } = r.edit {
                if !inserts.is_empty() {
                    let after = new_priority >= priority;  // should never be ==
                    union_ins_delta = union_ins_delta.transform_expand(inserts, r.union_str_len, after);
                    new_deletes = new_deletes.transform_expand(inserts);
                }
            }
        }
        let new_inserts = union_ins_delta.inserted_subset();
        if !new_inserts.is_empty() {
            new_deletes = new_deletes.transform_expand(&new_inserts);
        }
        let new_union_str = union_ins_delta.apply(&self.union_str);
        let undone = self.get_current_undo().map_or(false, |undos| undos.contains(&undo_group));
        let mut new_from_union = Cow::Borrowed(&self.revs.last().unwrap().deletes_from_union);
        if undone {
            if !new_inserts.is_empty() {
                new_from_union = Cow::Owned(new_from_union.transform_union(&new_inserts));
            }
        } else {
            if !new_inserts.is_empty() {
                new_from_union = Cow::Owned(new_from_union.transform_expand(&new_inserts));
            }
            if !new_deletes.is_empty() {
                new_from_union = Cow::Owned(new_from_union.union(&new_deletes));
            }
        }
        (Revision {
            rev_id: self.rev_id_counter,
            deletes_from_union: new_from_union.into_owned(),
            union_str_len: new_union_str.len(),
            edit: Edit {
                priority: new_priority,
                undo_group: undo_group,
                inserts: new_inserts,
                deletes: new_deletes,
            }
        }, new_union_str)
    }

    pub fn edit_rev(&mut self, priority: usize, undo_group: usize,
            base_rev: usize, delta: Delta<RopeInfo>) {
        let (new_rev, new_union_str) = self.mk_new_rev(priority, undo_group, base_rev, delta);
        self.rev_id_counter += 1;
        self.revs.push(new_rev);
        self.union_str = new_union_str;
    }

    // This computes undo all the way from the beginning. An optimization would be to not
    // recompute the prefix up to where the history diverges, but it's not clear that's
    // even worth the code complexity.
    fn compute_undo(&self, groups: BTreeSet<usize>) -> Revision {
        let mut deletes_from_union = Subset::default();
        for rev in &self.revs {
            if let Edit { ref undo_group, ref inserts, ref deletes, .. } = rev.edit {
                if groups.contains(undo_group) {
                    if !inserts.is_empty() {
                        deletes_from_union = deletes_from_union.transform_union(inserts);
                    }
                } else {
                    if !inserts.is_empty() {
                        deletes_from_union = deletes_from_union.transform_expand(inserts);
                    }
                    if !deletes.is_empty() {
                        deletes_from_union = deletes_from_union.union(deletes);
                    }
                }
            }
        }
        Revision {
            rev_id: self.rev_id_counter,
            deletes_from_union: deletes_from_union,
            union_str_len: self.union_str.len(),
            edit: Undo {
                groups: groups
            }
        }
    }

    pub fn undo(&mut self, groups: BTreeSet<usize>) {
        let new_rev = self.compute_undo(groups);
        self.revs.push(new_rev);
        self.rev_id_counter += 1;
    }

    pub fn is_equivalent_revision(&self, base_rev: usize, other_rev: usize) -> bool {
        let base_subset = self.find_rev(base_rev).map(|rev_index| self.get_subset_from_index(rev_index));
        let other_subset = self.find_rev(other_rev).map(|rev_index| self.get_subset_from_index(rev_index));

        base_subset.is_some() && base_subset == other_subset
    }

    // Note: this function would need some work to handle retaining arbitrary revisions,
    // partly because the reachability calculation would become more complicated (a
    // revision might hold content from an undo group that would otherwise be gc'ed),
    // and partly because you need to retain more undo history, to supply input to the
    // reachability calculation.
    //
    // Thus, it's easiest to defer gc to when all plugins quiesce, but it's certainly
    // possible to fix it so that's not necessary.
    pub fn gc(&mut self, gc_groups: &BTreeSet<usize>) {
        let mut gc_dels = Subset::default();
        // TODO: want to let caller retain more rev_id's.
        let mut retain_revs = BTreeSet::new();
        if let Some(last) = self.revs.last() {
            retain_revs.insert(last.rev_id);
        }
        {
            let cur_undo = self.get_current_undo();
            for rev in &self.revs {
                if let Edit { ref undo_group, ref inserts, ref deletes, .. } = rev.edit {
                    if !retain_revs.contains(&rev.rev_id) && gc_groups.contains(undo_group) {
                        if cur_undo.map_or(false, |undos| undos.contains(undo_group)) {
                            if !inserts.is_empty() {
                                gc_dels = gc_dels.transform_union(inserts);
                            }
                        } else {
                            if !inserts.is_empty() {
                                gc_dels = gc_dels.transform_expand(inserts);
                            }
                            if !deletes.is_empty() {
                                gc_dels = gc_dels.union(deletes);
                            }
                        }
                    } else if !inserts.is_empty() {
                        gc_dels = gc_dels.transform_expand(inserts);
                    }
                }
            }
        }
        if !gc_dels.is_empty() {
            self.union_str = gc_dels.delete_from(&self.union_str);
        }
        let old_revs = std::mem::replace(&mut self.revs, Vec::new());
        for rev in old_revs.into_iter().rev() {
            match rev.edit {
                Edit { priority, undo_group, inserts, deletes } => {
                    let new_gc_dels = if inserts.is_empty() {
                        None
                    } else {
                        Some(inserts.transform_shrink(&gc_dels))
                    };
                    if retain_revs.contains(&rev.rev_id) || !gc_groups.contains(&undo_group) {
                        let (inserts, deletes, deletes_from_union, len) = if gc_dels.is_empty() {
                            (inserts, deletes, rev.deletes_from_union, rev.union_str_len)
                        } else {
                            (gc_dels.transform_shrink(&inserts),
                                gc_dels.transform_shrink(&deletes),
                                gc_dels.transform_shrink(&rev.deletes_from_union),
                                gc_dels.len_after_delete(rev.union_str_len))
                        };
                        self.revs.push(Revision {
                            rev_id: rev.rev_id,
                            deletes_from_union: deletes_from_union,
                            union_str_len: len,
                            edit: Edit {
                                priority: priority,
                                undo_group: undo_group,
                                inserts: inserts,
                                deletes: deletes,
                            }
                        });
                    }
                    if let Some(new_gc_dels) = new_gc_dels {
                        gc_dels = new_gc_dels;
                    }
                }
                Undo { groups } => {
                    // We're super-aggressive about dropping these; after gc, the history
                    // of which undos were used to compute deletes_from_union in edits may be lost.
                    if retain_revs.contains(&rev.rev_id) {
                        let (deletes_from_union, len) = if gc_dels.is_empty() {
                            (rev.deletes_from_union, rev.union_str_len)
                        } else {
                            (gc_dels.transform_shrink(&rev.deletes_from_union),
                                gc_dels.len_after_delete(rev.union_str_len))
                        };
                        self.revs.push(Revision {
                            rev_id: rev.rev_id,
                            deletes_from_union: deletes_from_union,
                            union_str_len: len,
                            edit: Undo {
                                groups: &groups - gc_groups,
                            }
                        })
                    }
                }
            }
        }
        self.revs.reverse();
    }
}
<commit_msg>Rename revision helper functions<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An engine for handling edits (possibly from async sources) and undo. This
//! module actually implements a mini Conflict-free Replicated Data Type, but
//! is considerably simpler than the usual CRDT implementation techniques,
//! because all operations are serialized in this central engine.

use std::borrow::Cow;
use std::collections::BTreeSet;
use std;

use rope::{Rope, RopeInfo};
use subset::Subset;
use delta::Delta;

pub struct Engine {
    rev_id_counter: usize,
    union_str: Rope,
    revs: Vec<Revision>,
}

struct Revision {
    rev_id: usize,
    deletes_from_union: Subset,
    union_str_len: usize,
    edit: Contents,
}

use self::Contents::*;

enum Contents {
    Edit {
        priority: usize,
        undo_group: usize,
        inserts: Subset,
        deletes: Subset,
    },
    Undo {
        groups: BTreeSet<usize>,  // set of undo_group id's
    }
}

impl Engine {
    pub fn new(initial_contents: Rope) -> Engine {
        let rev = Revision {
            rev_id: 0,
            deletes_from_union: Subset::default(),
            union_str_len: initial_contents.len(),
            edit: Undo { groups: BTreeSet::default() },
        };
        Engine {
            rev_id_counter: 1,
            union_str: initial_contents,
            revs: vec![rev],
        }
    }

    fn get_current_undo(&self) -> Option<&BTreeSet<usize>> {
        for rev in self.revs.iter().rev() {
            if let Undo { ref groups } = rev.edit {
                return Some(groups);
            }
        }
        None
    }

    fn find_rev(&self, rev_id: usize) -> Option<usize> {
        for (i, rev) in self.revs.iter().enumerate().rev() {
            if rev.rev_id == rev_id {
                return Some(i)
            }
        }
        None
    }

    /// Get the contents of the document at a given revision number
    fn rev_content_for_index(&self, rev_index: usize) -> Rope {
        self.deletes_from_union_for_index(rev_index).delete_from(&self.union_str)
    }

    /// Get the Subset to delete from the current union string in order to obtain a revision's content
    fn deletes_from_union_for_index(&self, rev_index: usize) -> Cow<Subset> {
        let mut deletes_from_union = Cow::Borrowed(&self.revs[rev_index].deletes_from_union);
        for rev in &self.revs[rev_index + 1..] {
            if let Edit { ref inserts, .. } = rev.edit {
                if !inserts.is_empty() {
                    deletes_from_union = Cow::Owned(deletes_from_union.transform_union(inserts));
                }
            }
        }
        deletes_from_union
    }

    /// Get revision id of head revision.
    pub fn get_head_rev_id(&self) -> usize {
        self.revs.last().unwrap().rev_id
    }

    /// Get text of head revision.
    pub fn get_head(&self) -> Rope {
        self.rev_content_for_index(self.revs.len() - 1)
    }

    /// Get text of a given revision, if it can be found.
    pub fn get_rev(&self, rev: usize) -> Option<Rope> {
        self.find_rev(rev).map(|rev_index| self.rev_content_for_index(rev_index))
    }

    /// A delta that, when applied to previous head, results in the current head. Panics
    /// if there is not at least one edit.
    pub fn delta_rev_head(&self, base_rev: usize) -> Delta<RopeInfo> {
        let ix = self.find_rev(base_rev).expect("base revision not found");
        let rev = &self.revs[ix];
        let mut prev_from_union = Cow::Borrowed(&rev.deletes_from_union);
        for r in &self.revs[ix + 1..] {
            if let Edit { ref inserts, .. } = r.edit {
                if !inserts.is_empty() {
                    prev_from_union = Cow::Owned(prev_from_union.transform_union(inserts));
                }
            }
        }
        let head_rev = &self.revs.last().unwrap();
        Delta::synthesize(&self.union_str, &prev_from_union, &head_rev.deletes_from_union)
    }

    fn mk_new_rev(&self, new_priority: usize, undo_group: usize,
            base_rev: usize, delta: Delta<RopeInfo>) -> (Revision, Rope) {
        let ix = self.find_rev(base_rev).expect("base revision not found");
        let rev = &self.revs[ix];
        let (ins_delta, deletes) = delta.factor();
        let mut union_ins_delta = ins_delta.transform_expand(&rev.deletes_from_union, rev.union_str_len, true);
        let mut new_deletes = deletes.transform_expand(&rev.deletes_from_union);
        for r in &self.revs[ix + 1..] {
            if let Edit { priority, ref inserts, .. } = r.edit {
                if !inserts.is_empty() {
                    let after = new_priority >= priority;  // should never be ==
                    union_ins_delta = union_ins_delta.transform_expand(inserts, r.union_str_len, after);
                    new_deletes = new_deletes.transform_expand(inserts);
                }
            }
        }
        let new_inserts = union_ins_delta.inserted_subset();
        if !new_inserts.is_empty() {
            new_deletes = new_deletes.transform_expand(&new_inserts);
        }
        let new_union_str = union_ins_delta.apply(&self.union_str);
        let undone = self.get_current_undo().map_or(false, |undos| undos.contains(&undo_group));
        let mut new_from_union = Cow::Borrowed(&self.revs.last().unwrap().deletes_from_union);
        if undone {
            if !new_inserts.is_empty() {
                new_from_union = Cow::Owned(new_from_union.transform_union(&new_inserts));
            }
        } else {
            if !new_inserts.is_empty() {
                new_from_union = Cow::Owned(new_from_union.transform_expand(&new_inserts));
            }
            if !new_deletes.is_empty() {
                new_from_union = Cow::Owned(new_from_union.union(&new_deletes));
            }
        }
        (Revision {
            rev_id: self.rev_id_counter,
            deletes_from_union: new_from_union.into_owned(),
            union_str_len: new_union_str.len(),
            edit: Edit {
                priority: new_priority,
                undo_group: undo_group,
                inserts: new_inserts,
                deletes: new_deletes,
            }
        }, new_union_str)
    }

    pub fn edit_rev(&mut self, priority: usize, undo_group: usize,
            base_rev: usize, delta: Delta<RopeInfo>) {
        let (new_rev, new_union_str) = self.mk_new_rev(priority, undo_group, base_rev, delta);
        self.rev_id_counter += 1;
        self.revs.push(new_rev);
        self.union_str = new_union_str;
    }

    // This computes undo all the way from the beginning. An optimization would be to not
    // recompute the prefix up to where the history diverges, but it's not clear that's
    // even worth the code complexity.
    fn compute_undo(&self, groups: BTreeSet<usize>) -> Revision {
        let mut deletes_from_union = Subset::default();
        for rev in &self.revs {
            if let Edit { ref undo_group, ref inserts, ref deletes, .. } = rev.edit {
                if groups.contains(undo_group) {
                    if !inserts.is_empty() {
                        deletes_from_union = deletes_from_union.transform_union(inserts);
                    }
                } else {
                    if !inserts.is_empty() {
                        deletes_from_union = deletes_from_union.transform_expand(inserts);
                    }
                    if !deletes.is_empty() {
                        deletes_from_union = deletes_from_union.union(deletes);
                    }
                }
            }
        }
        Revision {
            rev_id: self.rev_id_counter,
            deletes_from_union: deletes_from_union,
            union_str_len: self.union_str.len(),
            edit: Undo {
                groups: groups
            }
        }
    }

    pub fn undo(&mut self, groups: BTreeSet<usize>) {
        let new_rev = self.compute_undo(groups);
        self.revs.push(new_rev);
        self.rev_id_counter += 1;
    }

    pub fn is_equivalent_revision(&self, base_rev: usize, other_rev: usize) -> bool {
        let base_subset = self.find_rev(base_rev).map(|rev_index| self.deletes_from_union_for_index(rev_index));
        let other_subset = self.find_rev(other_rev).map(|rev_index| self.deletes_from_union_for_index(rev_index));

        base_subset.is_some() && base_subset == other_subset
    }

    // Note: this function would need some work to handle retaining arbitrary revisions,
    // partly because the reachability calculation would become more complicated (a
    // revision might hold content from an undo group that would otherwise be gc'ed),
    // and partly because you need to retain more undo history, to supply input to the
    // reachability calculation.
    //
    // Thus, it's easiest to defer gc to when all plugins quiesce, but it's certainly
    // possible to fix it so that's not necessary.
    pub fn gc(&mut self, gc_groups: &BTreeSet<usize>) {
        let mut gc_dels = Subset::default();
        // TODO: want to let caller retain more rev_id's.
        let mut retain_revs = BTreeSet::new();
        if let Some(last) = self.revs.last() {
            retain_revs.insert(last.rev_id);
        }
        {
            let cur_undo = self.get_current_undo();
            for rev in &self.revs {
                if let Edit { ref undo_group, ref inserts, ref deletes, .. } = rev.edit {
                    if !retain_revs.contains(&rev.rev_id) && gc_groups.contains(undo_group) {
                        if cur_undo.map_or(false, |undos| undos.contains(undo_group)) {
                            if !inserts.is_empty() {
                                gc_dels = gc_dels.transform_union(inserts);
                            }
                        } else {
                            if !inserts.is_empty() {
                                gc_dels = gc_dels.transform_expand(inserts);
                            }
                            if !deletes.is_empty() {
                                gc_dels = gc_dels.union(deletes);
                            }
                        }
                    } else if !inserts.is_empty() {
                        gc_dels = gc_dels.transform_expand(inserts);
                    }
                }
            }
        }
        if !gc_dels.is_empty() {
            self.union_str = gc_dels.delete_from(&self.union_str);
        }
        let old_revs = std::mem::replace(&mut self.revs, Vec::new());
        for rev in old_revs.into_iter().rev() {
            match rev.edit {
                Edit { priority, undo_group, inserts, deletes } => {
                    let new_gc_dels = if inserts.is_empty() {
                        None
                    } else {
                        Some(inserts.transform_shrink(&gc_dels))
                    };
                    if retain_revs.contains(&rev.rev_id) || !gc_groups.contains(&undo_group) {
                        let (inserts, deletes, deletes_from_union, len) = if gc_dels.is_empty() {
                            (inserts, deletes, rev.deletes_from_union, rev.union_str_len)
                        } else {
                            (gc_dels.transform_shrink(&inserts),
                                gc_dels.transform_shrink(&deletes),
                                gc_dels.transform_shrink(&rev.deletes_from_union),
                                gc_dels.len_after_delete(rev.union_str_len))
                        };
                        self.revs.push(Revision {
                            rev_id: rev.rev_id,
                            deletes_from_union: deletes_from_union,
                            union_str_len: len,
                            edit: Edit {
                                priority: priority,
                                undo_group: undo_group,
                                inserts: inserts,
                                deletes: deletes,
                            }
                        });
                    }
                    if let Some(new_gc_dels) = new_gc_dels {
                        gc_dels = new_gc_dels;
                    }
                }
                Undo { groups } => {
                    // We're super-aggressive about dropping these; after gc, the history
                    // of which undos were used to compute deletes_from_union in edits may be lost.
                    if retain_revs.contains(&rev.rev_id) {
                        let (deletes_from_union, len) = if gc_dels.is_empty() {
                            (rev.deletes_from_union, rev.union_str_len)
                        } else {
                            (gc_dels.transform_shrink(&rev.deletes_from_union),
                                gc_dels.len_after_delete(rev.union_str_len))
                        };
                        self.revs.push(Revision {
                            rev_id: rev.rev_id,
                            deletes_from_union: deletes_from_union,
                            union_str_len: len,
                            edit: Undo {
                                groups: &groups - gc_groups,
                            }
                        })
                    }
                }
            }
        }
        self.revs.reverse();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;
extern crate dotenv;

use futures::future::Future;
use std::env;
use std::io;

use actix::prelude::Addr;
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use dotenv::dotenv;
use uuid::Uuid;

#[derive(Deserialize)]
pub struct CacheableSecret {
    a: String,
}

#[derive(Deserialize)]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: u32,
}

#[derive(Serialize)]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

struct AppState {
    redis: Addr<RedisActor>,
}

fn create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();
    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();
    println!("Writing: {:?}: {:?}", id.to_string(), expiration);

    state
        .redis
        .send(Command(resp_array![
            "SETEX",
            id.to_string(),
            expiration,
            body.content
        ]))
        .map_err(actix_web::Error::from)
        .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }))
}

fn get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.to_string();
    println!("Fetching: {:?}", secret_id);

    state
        .redis
        .send(Command(resp_array!["GET", secret_id]))
        .map_err(actix_web::Error::from)
        .map(|res: Result<RespValue, actix_redis::Error>| match res {
            Ok(RespValue::BulkString(vec)) => {
                let str = String::from_utf8(vec).unwrap();

                HttpResponse::Ok().body(str)
            }
            _ => HttpResponse::NotFound().body("not found"),
        })
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    HttpServer::new(move || {
        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(web::resource("/secret").route(web::post().to_async(create_secret)))
            .service(web::resource("/secret/{secretid}").route(web::get().to_async(get_secret)))
    })
    .bind("127.0.0.1:8080")?
    .run()
}
<commit_msg>Minimal input validation<commit_after>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;
extern crate dotenv;

use futures::future;
use futures::future::Future;
use std::io;

use actix::prelude::Addr;
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpResponse, HttpServer};
use dotenv::dotenv;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
pub struct CacheableSecret {
    content: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: u32,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
enum GetSecretResponse {
    Success(CacheableSecret),
    Error(String),
}

struct AppState {
    redis: Addr<RedisActor>,
    max_secret_length: usize,
}

fn create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();

    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();

    let validation = future::result(if body.content.len() > state.max_secret_length {
        Err("content too large")
    } else if body.expiration_seconds > 9000 {
        Err("expiration too long")
    } else {
        Ok(())
    })
    .map_err(actix_web::error::ErrorBadRequest);

    println!("Writing: {:?}: {:?}", id.to_string(), expiration);

    let redis_fetch = state
        .redis
        .send(Command(resp_array![
            "SETEX",
            id.to_string(),
            expiration,
            body.content
        ]))
        .map_err(actix_web::Error::from)
        .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }));

    validation.and_then(|_| redis_fetch)
}

fn get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.to_string();
    println!("Fetching: {:?}", secret_id);

    state
        .redis
        .send(Command(resp_array!["GET", secret_id]))
        .map_err(actix_web::Error::from)
        .map(|res: Result<RespValue, actix_redis::Error>| match res {
            Ok(RespValue::BulkString(bytes)) => {
                let str = String::from_utf8(bytes).expect("non-utf8 string in redis");
                HttpResponse::Ok()
                    .json(GetSecretResponse::Success(CacheableSecret { content: str }))
            }
            _ => HttpResponse::NotFound().json(GetSecretResponse::Error("not found".to_string())),
        })
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    HttpServer::new(move || {
        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
            max_secret_length: std::env::var("MAX_SECRET_LENGTH")
                .unwrap_or("4096".to_string())
                .parse()
                .expect("unvalid integer value"),
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(web::resource("/secret").route(web::post().to_async(create_secret)))
            .service(web::resource("/secret/{secretid}").route(web::get().to_async(get_secret)))
    })
    .bind("127.0.0.1:8080")?
    .run()
}
<|endoftext|>


<commit_before>use core::error::FromError;
use std::collections::HashMap;
use std::old_io::{BufferedReader, File, IoError};

use architecture::*;

#[derive(Debug)]
pub enum AssmError { IoError(IoError), ParseError(String) }

impl FromError<IoError> for AssmError {
  fn from_error(err: IoError) -> AssmError {
    AssmError::IoError(err)
  }
}

pub type Label = String;

pub type LInsn = InsnGen<Label, Label>;

#[derive(Debug)]
pub enum Assm {
  LABEL(Label),
  Insn(LInsn),
  RET,
  LEA(RName, Label),
  LC(RName, Label),
  CODE,
  DATA,
  ADDR(UIMM16),
  FALIGN,
  FILL(IMM16),
  STRINGZ(String),
  BLKW(UIMM16),
  LCONST(Label, IMM16),
  LUCONST(Label, UIMM16)
}

peg_file! lc4_grammar("grammar/lc4.pegjs");

fn read_assembly_line(line: String) -> Result<Assm, AssmError> {
  match lc4_grammar::assm(&line.trim()[..]) {
    Err(err) => Err(AssmError::ParseError(err)),
    Ok(assm) => Ok(assm)
  }
}

pub fn read_assembly_file(filename: &str) -> Result<Vec<Assm>, AssmError> {
  let file = try!(File::open(&Path::new(filename)));
  let mut reader = BufferedReader::new(file);
  let mut assms = Vec::new();
  for line in reader.lines() {
    assms.push(try!(read_assembly_line(try!(line))))
  }
  Ok(assms)
}

#[derive(Copy, Debug, Eq, PartialEq)]
pub enum Section { CODE, DATA }

#[derive(Clone, Copy, Debug)]
pub enum Mem {
  CODE(Insn),
  DATA(i16)
}

pub struct AssmData {
  pub memory: Box<[Mem; 0x10000]>,
  pub labels: HashMap<Label, (Section, u16)>,
  pub heap: u16
}

pub fn pad16(addr: u16) -> u16 {
  let mut padded = addr & 0xFFF0;
  if padded < addr { padded += 0x10; }
  padded
}

pub fn assemble(assm_lines: Vec<Assm>) -> AssmData {

  let mut section: Section = Section::CODE;

  let mut code_addr: u16 = 0;
  let mut data_addr: u16 = 0;

  let mut addr_labels: HashMap<Label, (Section, u16)> = HashMap::new();
  let mut value_labels: HashMap<Label, i16> = HashMap::new();

  /* First-pass, setup labels */

  for &ref assm in assm_lines.iter() {
    match assm {

      // Instructions and Pseudo-Instructions
      &Assm::Insn(_) => {
        assert!(section == Section::CODE);
        code_addr += 1
      },
      &Assm::RET => {
        assert!(section == Section::CODE);
        code_addr += 1
      },
      &Assm::LEA(_, _) => {
        assert!(section == Section::CODE);
        code_addr += 1
      },
      &Assm::LC(_, _) => {
        assert!(section == Section::CODE);
        code_addr += 1
      },

      // Assembler Directives
      &Assm::LABEL(ref l) => {
        if addr_labels.contains_key(l) {
          panic!("Cannot have duplicate labels")
        }
        addr_labels.insert(l.clone(), (section, code_addr));
      },

      &Assm::CODE => section = Section::CODE,
      &Assm::DATA => section = Section::DATA,

      &Assm::ADDR(ref u) =>
        match section {
          Section::CODE => code_addr = u.value,
          Section::DATA => data_addr = u.value
        },

      &Assm::FALIGN => {
        match section {
          Section::CODE => code_addr = pad16(code_addr),
          Section::DATA => data_addr = pad16(data_addr)
        }
      },

      &Assm::FILL(_) => {
        assert!(section == Section::DATA);
        data_addr += 1
      },
      &Assm::STRINGZ(ref s) => {
        assert!(section == Section::DATA);
        data_addr += s.len() as u16
      },

      &Assm::BLKW(ref u) => {
        match section {
          Section::CODE => code_addr += u.value,
          Section::DATA => data_addr += u.value
        }
      },

      &Assm::LCONST(ref l, ref i) => {
        if value_labels.contains_key(l) {
          panic!("Cannot have duplicate labels")
        }
        value_labels.insert(l.clone(), i.value);
      }
      &Assm::LUCONST(ref l, ref u) => {
        if value_labels.contains_key(l) {
          panic!("Cannot have duplicate labels")
        }
        value_labels.insert(l.clone(), u.value as i16);
      }
    }
  }

  /* Second pass, write to memory */

  let mut memory = box [Mem::DATA(0); 0x10000];
  let base_data_addr = pad16(code_addr);
  let base_heap_addr = pad16(base_data_addr + data_addr);
  let mut addr: u16 = 0;

  for &ref assm in assm_lines.iter() {
    match assm {

      &Assm::LABEL(ref target) => {
        let (label_section, label_addr) = addr_labels[target.clone()];
        match label_section {
          Section::CODE => addr = label_addr,
          Section::DATA => addr = label_addr + base_data_addr
        }
      },

      &Assm::Insn(InsnGen::BR(cc, ref target)) => {
        let (section, label_addr) = addr_labels[target.clone()];
        assert!(section == Section::CODE);
        memory[addr as usize] = Mem::CODE(InsnGen::BR(cc, IMM9{value: (label_addr - (addr + 1)) as i16}));
        addr += 1
      },
      &Assm::Insn(InsnGen::JSR(ref target)) => {
        let (section, label_addr) = addr_labels[target.clone()];
        assert!(section == Section::CODE);
        memory[addr as usize] = Mem::CODE(InsnGen::JSR(IMM11{value: (label_addr - (addr & 0x8000)) as i16 >> 4}));
        addr += 1
      },
      &Assm::Insn(InsnGen::JMP(ref target)) => {
        let (section, label_addr) = addr_labels[target.clone()];
        assert!(section == Section::CODE);
        memory[addr as usize] = Mem::CODE(InsnGen::JMP(IMM11{value: (label_addr - (addr + 1)) as i16}));
        addr += 1
      },

      &Assm::Insn(ref insn) => {
        memory[addr as usize] = Mem::CODE(partial_cast(insn));
        addr += 1
      }

      &Assm::RET => {
        memory[addr as usize] = Mem::CODE(InsnGen::JMPr(R7));
        addr += 1
      },

      &Assm::LEA(rd, ref target) => {
        let (section, label_addr) = addr_labels[target.clone()];
        let label_addr = match section {
          Section::CODE => label_addr,
          Section::DATA => label_addr + base_data_addr
        };
        let low = IMM9{value: label_addr as i16 & 0x01FF};
        let high = UIMM8{value: label_addr >> 8};
        memory[addr as usize] = Mem::CODE(InsnGen::CONST(rd, low));
        memory[addr as usize + 1] = Mem::CODE(InsnGen::HICONST(rd, high));
        addr += 2
      },

      &Assm::LC(rd, ref target) => {
        let label_value = value_labels[target.clone()];
        let low = IMM9{value: label_value & 0x01FF};
        let high = UIMM8{value: label_value as u16 >> 8};
        memory[addr as usize] = Mem::CODE(InsnGen::CONST(rd, low));
        memory[addr as usize + 1] = Mem::CODE(InsnGen::HICONST(rd, high));
        addr += 2
      }

      &Assm::CODE => (),
      &Assm::DATA => (),
      &Assm::ADDR(_) => (),
      &Assm::FALIGN => (),

      &Assm::FILL(i) => {
        memory[addr as usize] = Mem::DATA(i.value);
        addr += 1
      },

      &Assm::STRINGZ(_) => panic!("Not implemented"),

      &Assm::BLKW(_) => (),
      &Assm::LCONST(_,_) => (),
      &Assm::LUCONST(_,_) => ()
    }
  }

  AssmData{
    memory: memory,
    labels: addr_labels,
    heap: base_heap_addr
  }
}

pub fn encode_word(mem: Mem) -> i16 {
  match mem {
    case CODE(insn) => panic!("Not implemented"),
    case DATA(i) => i
  }
}<commit_msg>starting on encoding object files<commit_after>use core::error::FromError;
use std::collections::HashMap;
use std::old_io::{BufferedReader, File, IoError};

use architecture::*;

#[derive(Debug)]
pub enum AssmError { IoError(IoError), ParseError(String) }

impl FromError<IoError> for AssmError {
  fn from_error(err: IoError) -> AssmError {
    AssmError::IoError(err)
  }
}

pub type Label = String;

pub type LInsn = InsnGen<Label, Label>;

#[derive(Debug)]
pub enum Assm {
  LABEL(Label),
  Insn(LInsn),
  RET,
  LEA(RName, Label),
  LC(RName, Label),
  CODE,
  DATA,
  ADDR(UIMM16),
  FALIGN,
  FILL(IMM16),
  STRINGZ(String),
  BLKW(UIMM16),
  LCONST(Label, IMM16),
  LUCONST(Label, UIMM16)
}

peg_file! lc4_grammar("grammar/lc4.pegjs");

fn read_assembly_line(line: String) -> Result<Assm, AssmError> {
  match lc4_grammar::assm(&line.trim()[..]) {
    Err(err) => Err(AssmError::ParseError(err)),
    Ok(assm) => Ok(assm)
  }
}

pub fn read_assembly_file(filename: &str) -> Result<Vec<Assm>, AssmError> {
  let file = try!(File::open(&Path::new(filename)));
  let mut reader = BufferedReader::new(file);
  let mut assms = Vec::new();
  for line in reader.lines() {
    assms.push(try!(read_assembly_line(try!(line))))
  }
  Ok(assms)
}

#[derive(Copy, Debug, Eq, PartialEq)]
pub enum Section { CODE, DATA }

#[derive(Clone, Copy, Debug)]
pub enum Mem {
  CODE(Insn),
  DATA(i16)
}

pub struct AssmData {
  pub memory: Box<[Mem; 0x10000]>,
  pub labels: HashMap<Label, (Section, u16)>,
  pub heap: u16
}

pub fn pad16(addr: u16) -> u16 {
  let mut padded = addr & 0xFFF0;
  if padded < addr { padded += 0x10; }
  padded
}

pub fn assemble(assm_lines: Vec<Assm>) -> AssmData {

  let mut section: Section = Section::CODE;

  let mut code_addr: u16 = 0;
  let mut data_addr: u16 = 0;

  let mut addr_labels: HashMap<Label, (Section, u16)> = HashMap::new();
  let mut value_labels: HashMap<Label, i16> = HashMap::new();

  /* First-pass, setup labels */

  for &ref assm in assm_lines.iter() {
    match assm {

      // Instructions and Pseudo-Instructions
      &Assm::Insn(_) => {
        assert!(section == Section::CODE);
        code_addr += 1
      },
      &Assm::RET => {
        assert!(section == Section::CODE);
        code_addr += 1
      },
      &Assm::LEA(_, _) => {
        assert!(section == Section::CODE);
        code_addr += 1
      },
      &Assm::LC(_, _) => {
        assert!(section == Section::CODE);
        code_addr += 1
      },

      // Assembler Directives
      &Assm::LABEL(ref l) => {
        if addr_labels.contains_key(l) {
          panic!("Cannot have duplicate labels")
        }
        addr_labels.insert(l.clone(), (section, code_addr));
      },

      &Assm::CODE => section = Section::CODE,
      &Assm::DATA => section = Section::DATA,

      &Assm::ADDR(ref u) =>
        match section {
          Section::CODE => code_addr = u.value,
          Section::DATA => data_addr = u.value
        },

      &Assm::FALIGN => {
        match section {
          Section::CODE => code_addr = pad16(code_addr),
          Section::DATA => data_addr = pad16(data_addr)
        }
      },

      &Assm::FILL(_) => {
        assert!(section == Section::DATA);
        data_addr += 1
      },
      &Assm::STRINGZ(ref s) => {
        assert!(section == Section::DATA);
        data_addr += s.len() as u16
      },

      &Assm::BLKW(ref u) => {
        match section {
          Section::CODE => code_addr += u.value,
          Section::DATA => data_addr += u.value
        }
      },

      &Assm::LCONST(ref l, ref i) => {
        if value_labels.contains_key(l) {
          panic!("Cannot have duplicate labels")
        }
        value_labels.insert(l.clone(), i.value);
      }
      &Assm::LUCONST(ref l, ref u) => {
        if value_labels.contains_key(l) {
          panic!("Cannot have duplicate labels")
        }
        value_labels.insert(l.clone(), u.value as i16);
      }
    }
  }

  /* Second pass, write to memory */

  let mut memory = box [Mem::DATA(0); 0x10000];
  let base_data_addr = pad16(code_addr);
  let base_heap_addr = pad16(base_data_addr + data_addr);
  let mut addr: u16 = 0;

  for &ref assm in assm_lines.iter() {
    match assm {

      &Assm::LABEL(ref target) => {
        let (label_section, label_addr) = addr_labels[target.clone()];
        match label_section {
          Section::CODE => addr = label_addr,
          Section::DATA => addr = label_addr + base_data_addr
        }
      },

      &Assm::Insn(InsnGen::BR(cc, ref target)) => {
        let (section, label_addr) = addr_labels[target.clone()];
        assert!(section == Section::CODE);
        memory[addr as usize] = Mem::CODE(InsnGen::BR(cc, IMM9{value: (label_addr - (addr + 1)) as i16}));
        addr += 1
      },
      &Assm::Insn(InsnGen::JSR(ref target)) => {
        let (section, label_addr) = addr_labels[target.clone()];
        assert!(section == Section::CODE);
        memory[addr as usize] = Mem::CODE(InsnGen::JSR(IMM11{value: (label_addr - (addr & 0x8000)) as i16 >> 4}));
        addr += 1
      },
      &Assm::Insn(InsnGen::JMP(ref target)) => {
        let (section, label_addr) = addr_labels[target.clone()];
        assert!(section == Section::CODE);
        memory[addr as usize] = Mem::CODE(InsnGen::JMP(IMM11{value: (label_addr - (addr + 1)) as i16}));
        addr += 1
      },

      &Assm::Insn(ref insn) => {
        memory[addr as usize] = Mem::CODE(partial_cast(insn));
        addr += 1
      }

      &Assm::RET => {
        memory[addr as usize] = Mem::CODE(InsnGen::JMPr(R7));
        addr += 1
      },

      &Assm::LEA(rd, ref target) => {
        let (section, label_addr) = addr_labels[target.clone()];
        let label_addr = match section {
          Section::CODE => label_addr,
          Section::DATA => label_addr + base_data_addr
        };
        let low = IMM9{value: label_addr as i16 & 0x01FF};
        let high = UIMM8{value: label_addr >> 8};
        memory[addr as usize] = Mem::CODE(InsnGen::CONST(rd, low));
        memory[addr as usize + 1] = Mem::CODE(InsnGen::HICONST(rd, high));
        addr += 2
      },

      &Assm::LC(rd, ref target) => {
        let label_value = value_labels[target.clone()];
        let low = IMM9{value: label_value & 0x01FF};
        let high = UIMM8{value: label_value as u16 >> 8};
        memory[addr as usize] = Mem::CODE(InsnGen::CONST(rd, low));
        memory[addr as usize + 1] = Mem::CODE(InsnGen::HICONST(rd, high));
        addr += 2
      }

      &Assm::CODE => (),
      &Assm::DATA => (),
      &Assm::ADDR(_) => (),
      &Assm::FALIGN => (),

      &Assm::FILL(i) => {
        memory[addr as usize] = Mem::DATA(i.value);
        addr += 1
      },

      &Assm::STRINGZ(_) => panic!("Not implemented"),

      &Assm::BLKW(_) => (),
      &Assm::LCONST(_,_) => (),
      &Assm::LUCONST(_,_) => ()
    }
  }

  AssmData{
    memory: memory,
    labels: addr_labels,
    heap: base_heap_addr
  }
}

pub fn encode_insn(insn: Insn) -> i16 {
  match insn {
    InsnGen::NOP => 0x0000,
    _ => panic!("Not imeplemented")
  }
}

pub fn encode_word(mem: Mem) -> i16 {
  match mem {
    Mem::CODE(insn) => encode_insn(insn),
    Mem::DATA(i) => i
  }
}<|endoftext|>


<commit_before>use super::{Open, Sink};
extern crate rodio;
use std::io;
use std::process::exit;

pub struct RodioSink {
    rodio_sink: rodio::Sink,
}

fn list_outputs() {
    println!("Default Audio Device:\n  {:?}", rodio::default_output_device().map(|e| e.name()));

    println!("Available Audio Devices:");
    for device in rodio::output_devices() {
        println!("- {}", device.name());
        // Output formats
        if let Ok(fmt) = device.default_output_format() {
            println!("  Default format:\n    {:?}", fmt);
        }
        let mut output_formats = match device.supported_output_formats() {
            Ok(f) => f.peekable(),
            Err(e) => {
                println!("Error: {:?}", e);
                continue;
            },
        };
        if output_formats.peek().is_some() {
            println!("  All formats:");
            for format in output_formats {
                println!("    {:?}", format);
            }
        }
    }
}

impl Open for RodioSink {
    fn open(device: Option<String>) -> RodioSink {
        info!("Using rodio sink");

        let mut rodio_device = rodio::default_output_device().expect("no output device available");
        if device.is_some() {
            let device_name = device.unwrap();

            if device_name == "?".to_string() {
                list_outputs();
                exit(0)
            }
            let mut found = false;
            for d in rodio::output_devices() {
                if d.name() == device_name {
                    rodio_device = d;
                    found = true;
                    break;
                }
            }
            if !found {
                println!("No output sink matching '{}' found.", device_name);
                exit(0)
            }
        }
        let sink = rodio::Sink::new(&rodio_device);

        RodioSink {
            rodio_sink: sink,
        }
    }
}

impl Sink for RodioSink {
    fn start(&mut self) -> io::Result<()> {
        self.rodio_sink.play();
        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        self.rodio_sink.stop();
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        let source = rodio::buffer::SamplesBuffer::new(2, 44100, data);
        self.rodio_sink.append(source);
        Ok(())
    }
}
<commit_msg>Sink.play is not the opposite of Sink.stop in Rodio<commit_after>use super::{Open, Sink};
extern crate rodio;
use std::io;
use std::process::exit;

pub struct RodioSink {
    rodio_sink: rodio::Sink,
}

fn list_outputs() {
    println!("Default Audio Device:\n  {:?}", rodio::default_output_device().map(|e| e.name()));

    println!("Available Audio Devices:");
    for device in rodio::output_devices() {
        println!("- {}", device.name());
        // Output formats
        if let Ok(fmt) = device.default_output_format() {
            println!("  Default format:\n    {:?}", fmt);
        }
        let mut output_formats = match device.supported_output_formats() {
            Ok(f) => f.peekable(),
            Err(e) => {
                println!("Error: {:?}", e);
                continue;
            },
        };
        if output_formats.peek().is_some() {
            println!("  All formats:");
            for format in output_formats {
                println!("    {:?}", format);
            }
        }
    }
}

impl Open for RodioSink {
    fn open(device: Option<String>) -> RodioSink {
        info!("Using rodio sink");

        let mut rodio_device = rodio::default_output_device().expect("no output device available");
        if device.is_some() {
            let device_name = device.unwrap();

            if device_name == "?".to_string() {
                list_outputs();
                exit(0)
            }
            let mut found = false;
            for d in rodio::output_devices() {
                if d.name() == device_name {
                    rodio_device = d;
                    found = true;
                    break;
                }
            }
            if !found {
                println!("No output sink matching '{}' found.", device_name);
                exit(0)
            }
        }
        let sink = rodio::Sink::new(&rodio_device);

        RodioSink {
            rodio_sink: sink,
        }
    }
}

impl Sink for RodioSink {
    fn start(&mut self) -> io::Result<()> {
        // More similar to an "unpause" than "play". Doesn't undo "stop".
        // self.rodio_sink.play();
        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        // This will immediately stop playback, but the sink is then unusable.
        // We just have to let the current buffer play till the end.
        // self.rodio_sink.stop();
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        let source = rodio::buffer::SamplesBuffer::new(2, 44100, data);
        self.rodio_sink.append(source);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::io::{BufRead, BufReader, BufWriter, Write};
use std::fs::File;
use std::slice;
use std::mem;

fn main() {
    println!("usage: parse <source> <target>");
    println!("takes sorted input whose lines look like:");
    println!("<src> <dst>");
    println!("will overwrite <target>.offsets and <target>.targets");
    let source = std::env::args().skip(1).next().unwrap();
    let target = std::env::args().skip(2).next().unwrap();

    let graph = read_edges(&source);
    digest_graph_vector(&_extract_fragment(graph.iter().map(|x| *x)), &target);
}

fn read_edges(filename: &str) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for readline in file.lines() {
        let line = readline.ok().expect("read error");
        if !line.starts_with('#') {
            let elts: Vec<&str> = line[..].split_whitespace().collect();
            let src: u32 = elts[0].parse().ok().expect("malformed src");
            let dst: u32 = elts[1].parse().ok().expect("malformed dst");
            graph.push((src, dst))
        }
    }

    println!("graph data loaded; {:?} edges", graph.len());
    return graph;
}

fn _extract_fragment<I: Iterator<Item=(u32, u32)>>(graph: I) -> (Vec<u64>, Vec<u32>) {
    let mut nodes = Vec::new();
    let mut edges = Vec::new();

    for (src, dst) in graph {
        while src + 1 >= nodes.len() as u32 { nodes.push(0); }
        while dst + 1 >= nodes.len() as u32 { nodes.push(0); } // allows unsafe access to nodes

        nodes[src as usize + 1] += 1;
        edges.push(dst);
    }

    for index in (1..nodes.len()) {
        nodes[index] += nodes[index - 1];
    }

    return (nodes, edges);
}

fn digest_graph_vector(graph: &(Vec<u64>, Vec<u32>), output_prefix: &str) {
    let mut edge_writer = BufWriter::new(File::create(format!("{}.targets", output_prefix)).unwrap());
    let mut node_writer = BufWriter::new(File::create(format!("{}.offsets", output_prefix)).unwrap());
    node_writer.write_all(unsafe { _typed_as_byte_slice(&graph.0[..]) }).unwrap();

    let mut slice = unsafe { _typed_as_byte_slice(&graph.1[..]) };
    while slice.len() > 0 {
        let to_write = if slice.len() < 1000000 { slice.len() } else { 1000000 };
        edge_writer.write_all(&slice[..to_write]).unwrap();
        println!("wrote some; remaining: {}", slice.len());
        slice = &slice[to_write..];
    }
}

unsafe fn _typed_as_byte_slice<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}
<commit_msg>reduce allocations<commit_after>use std::io::{BufRead, BufReader, BufWriter, Write};
use std::fs::File;
use std::slice;
use std::mem;

fn main() {
    println!("usage: parse <source> <target>");
    println!("takes sorted input whose lines look like:");
    println!("<src> <dst>");
    println!("will overwrite <target>.offsets and <target>.targets");
    let source = std::env::args().skip(1).next().unwrap();
    let target = std::env::args().skip(2).next().unwrap();

    let graph = read_edges(&source);
    digest_graph_vector(&_extract_fragment(graph.iter().map(|x| *x)), &target);
}

fn read_edges(filename: &str) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for readline in file.lines() {
        let line = readline.ok().expect("read error");
        if !line.starts_with('#') {
            let mut elts = line[..].split_whitespace();
            let src: u32 = elts.next().unwrap().parse().ok().expect("malformed src");
            let dst: u32 = elts.next().unwrap().parse().ok().expect("malformed dst");
            graph.push((src, dst))
        }
    }

    println!("graph data loaded; {:?} edges", graph.len());
    return graph;
}

fn _extract_fragment<I: Iterator<Item=(u32, u32)>>(graph: I) -> (Vec<u64>, Vec<u32>) {
    let mut nodes = Vec::new();
    let mut edges = Vec::new();

    for (src, dst) in graph {
        while src + 1 >= nodes.len() as u32 { nodes.push(0); }
        while dst + 1 >= nodes.len() as u32 { nodes.push(0); } // allows unsafe access to nodes

        nodes[src as usize + 1] += 1;
        edges.push(dst);
    }

    for index in (1..nodes.len()) {
        nodes[index] += nodes[index - 1];
    }

    return (nodes, edges);
}

fn digest_graph_vector(graph: &(Vec<u64>, Vec<u32>), output_prefix: &str) {
    let mut edge_writer = BufWriter::new(File::create(format!("{}.targets", output_prefix)).unwrap());
    let mut node_writer = BufWriter::new(File::create(format!("{}.offsets", output_prefix)).unwrap());
    node_writer.write_all(unsafe { _typed_as_byte_slice(&graph.0[..]) }).unwrap();

    let mut slice = unsafe { _typed_as_byte_slice(&graph.1[..]) };
    while slice.len() > 0 {
        let to_write = if slice.len() < 1000000 { slice.len() } else { 1000000 };
        edge_writer.write_all(&slice[..to_write]).unwrap();
        slice = &slice[to_write..];
    }
}

unsafe fn _typed_as_byte_slice<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}
<|endoftext|>