<commit_before>extern crate lmdb;
extern crate lmdb_sys;

#[cfg(test)]
extern crate tempdir;

use std::{result, str};
use std::path::Path;
use lmdb::{Cursor, DUP_SORT, INTEGER_KEY};
use lmdb::Transaction as LmdbTransaction;

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Error {
    DbCreateError,
    DbOpenError,
    DbWriteError,
    DbCorruptError,
    ParseError,
    TransactionError,
    PathError,
    NotFoundError,
    DuplicateEntryError,
}

pub type Result<T> = result::Result<T, Error>;

pub struct LmdbAdapter {
    env: lmdb::Environment,
    nodes: lmdb::Database,
    entries: lmdb::Database,
}

pub struct RwTransaction<'a>(lmdb::RwTransaction<'a>);
pub struct RoTransaction<'a>(lmdb::RoTransaction<'a>);

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct Id(u64);

#[derive(Debug, Eq, PartialEq)]
pub struct Node<'a> {
    pub id: Id,
    pub parent_id: Id,
    pub name: &'a str,
}

#[derive(Debug, Eq, PartialEq)]
pub struct Entry<'a> {
    pub node: Node<'a>,
    pub objectclass: &'a str,
}

// Ids are 64-bit integers in host-native byte order
// LMDB has special optimizations for host-native integers as keys
impl Id {
    fn root() -> Id {
        Id(0)
    }

    fn from_bytes(bytes: &[u8]) -> Result<Id> {
        if bytes.len() != 8 {
            return Err(Error::ParseError);
        }

        let mut id = [0u8; 8];
        id.copy_from_slice(&bytes[0..8]);

        Ok(Id(unsafe { std::mem::transmute(id) }))
    }

    fn as_bytes(self) -> [u8; 8] {
        let Id(id) = self;
        unsafe { std::mem::transmute(id) }
    }

    fn next(self) -> Id {
        let Id(id) = self;
        Id(id + 1)
    }
}

impl<'a> Node<'a> {
    fn root() -> Node<'a> {
        Node {
            id: Id::root(),
            parent_id: Id::root(),
            name: "/",
        }
    }

    fn from_parent_id_and_bytes(parent_id: Id, bytes: &[u8]) -> Result<Node> {
        if bytes.len() < 8 {
            return Err(Error::DbCorruptError);
        }

        let id = try!(Id::from_bytes(&bytes[0..8]));
        let name = try!(std::str::from_utf8(&bytes[8..]).map_err(|_err| Error::DbCorruptError));

        Ok(Node {
            id: id,
            parent_id: parent_id,
            name: name,
        })
    }

    fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(8 + self.name.len());
        bytes.extend_from_slice(&self.id.as_bytes());
        bytes.extend_from_slice(self.name.as_bytes());
        bytes
    }
}

impl LmdbAdapter {
    pub fn create_database(path: &Path) -> Result<LmdbAdapter> {
        let env = try!(lmdb::Environment::new()
                           .set_max_dbs(8)
                           .open_with_permissions(&path, 0o600)
                           .map_err(|_err| Error::DbCreateError));

        let nodes = try!(env.create_db(Some("nodes"), INTEGER_KEY | DUP_SORT)
                            .map_err(|_err| Error::DbCreateError));

        let entries = try!(env.create_db(Some("entries"), INTEGER_KEY)
                              .map_err(|_err| Error::DbCreateError));

        Ok(LmdbAdapter {
            env: env,
            nodes: nodes,
            entries: entries,
        })
    }

    pub fn open_database(path: &Path) -> Result<LmdbAdapter> {
        let env = try!(lmdb::Environment::new().open(&path).map_err(|_err| Error::DbOpenError));

        let nodes = try!(env.open_db(Some("nodes")).map_err(|_err| Error::DbOpenError));

        let entries = try!(env.open_db(Some("entries")).map_err(|_err| Error::DbOpenError));

        Ok(LmdbAdapter {
            env: env,
            nodes: nodes,
            entries: entries,
        })
    }

    pub fn rw_transaction(&self) -> Result<RwTransaction> {
        match self.env.begin_rw_txn() {
            Ok(txn) => Ok(RwTransaction(txn)),
            Err(_) => Err(Error::TransactionError),
        }
    }

    pub fn ro_transaction(&self) -> Result<RoTransaction> {
        match self.env.begin_ro_txn() {
            Ok(txn) => Ok(RoTransaction(txn)),
            Err(_) => Err(Error::TransactionError),
        }
    }

    pub fn next_available_id<T: Transaction>(&self, txn: &T) -> Result<Id> {
        let cursor = try!(txn.open_ro_cursor(self.nodes)
                             .map_err(|_err| Error::TransactionError));

        let last_id = match cursor.get(None, None, lmdb_sys::MDB_LAST) {
            Ok((id, _)) => Id::from_bytes(id.unwrap()).unwrap(),
            Err(_) => Id::root(),
        };

        Ok(last_id.next())
    }

    pub fn create_entry<'a>(&'a self,
                            txn: &'a mut RwTransaction,
                            id: Id,
                            parent_id: Id,
                            name: &'a str,
                            objectclass: &'a str)
                            -> Result<Entry> {
        if txn.get(self.entries, &id.as_bytes()) != Err(Error::NotFoundError) {
            return Err(Error::DuplicateEntryError);
        }

        if txn.get(self.nodes, &parent_id.as_bytes()) != Err(Error::NotFoundError) &&
           self.find_child_node(txn, parent_id, name) != Err(Error::NotFoundError) {
            return Err(Error::DuplicateEntryError);
        }

        let node = Node {
            id: id,
            parent_id: parent_id,
            name: name,
        };

        try!(txn.put(self.nodes, &parent_id.as_bytes(), &node.to_bytes())
                .map_err(|_err| Error::DbWriteError));

        try!(txn.put(self.entries, &id.as_bytes(), &objectclass.as_bytes())
                .map_err(|_err| Error::DbWriteError));

        Ok(Entry {
            node: node,
            objectclass: objectclass,
        })
    }

    pub fn find_entry<'a, T: Transaction>(&'a self, txn: &'a T, path: &str) -> Result<Entry> {
        let node = try!(self.find_node(txn, path));

        let entry_bytes = try!(txn.get(self.entries, &node.id.as_bytes())
                                  .map_err(|_err| Error::DbCorruptError));

        let objectclass = try!(std::str::from_utf8(&entry_bytes)
                                   .map_err(|_err| Error::DbCorruptError));

        Ok(Entry {
            node: node,
            objectclass: objectclass,
        })
    }

    fn find_node<T: Transaction>(&self, txn: &T, path: &str) -> Result<Node> {
        let all_components: Vec<&str> = path.split("/").collect();

        if all_components.is_empty() {
            return Err(Error::PathError);
        }

        let (prefix, components) = all_components.split_first().unwrap();

        // Does the path start with something other than "/"?
        if !prefix.is_empty() {
            return Err(Error::PathError);
        }

        components.iter().fold(Ok(Node::root()), |parent_node, component| {
            self.find_child_node(txn, try!(parent_node).id, component)
        })
    }

    // TODO: since LMDB is ordered, we could e.g. perform a binary search
    fn find_child_node<T: Transaction>(&self, txn: &T, parent_id: Id, name: &str) -> Result<Node> {
        let mut cursor = try!(txn.open_ro_cursor(self.nodes)
                                 .map_err(|_err| Error::TransactionError));

        let mut child_node = None;

        for (id, node_bytes) in cursor.iter_from(parent_id.as_bytes()) {
            if id != parent_id.as_bytes() {
                return Err(Error::NotFoundError);
            }

            let node = try!(Node::from_parent_id_and_bytes(parent_id, node_bytes));

            if node.name == name {
                child_node = Some(node);
                break;
            }
        }

        child_node.ok_or(Error::NotFoundError)
    }
}

pub trait Transaction {
    fn get(&self, database: lmdb::Database, key: &[u8]) -> Result<&[u8]>;
    fn commit(self) -> Result<()>;
    fn open_ro_cursor(&self, db: lmdb::Database) -> lmdb::Result<lmdb::RoCursor>;
}

macro_rules! impl_transaction (($newtype:ident) => (
    impl<'a> Transaction for $newtype<'a> {
        fn get(&self, database: lmdb::Database, key: &[u8]) -> Result<&[u8]> {
            self.0.get(database, &key).map_err(|_err| Error::NotFoundError)
        }

        fn commit(self) -> Result<()> {
            self.0.commit().map_err(|_err| Error::TransactionError)
        }

        fn open_ro_cursor(&self, db: lmdb::Database) -> lmdb::Result<lmdb::RoCursor> {
            self.0.open_ro_cursor(db)
        }
    }
));

impl_transaction!(RwTransaction);
impl_transaction!(RoTransaction);

impl<'a> RwTransaction<'a> {
    fn put(&mut self, database: lmdb::Database, key: &[u8], data: &[u8]) -> Result<()> {
        self.0
            .put(database, &key, &data, lmdb::WriteFlags::empty())
            .map_err(|_err| Error::TransactionError)
    }
}

#[cfg(test)]
mod tests {
    use {LmdbAdapter, Id, Transaction, Error};
    use tempdir::TempDir;

    fn create_database() -> LmdbAdapter {
        let dir = TempDir::new("ithos-test").unwrap();
        LmdbAdapter::create_database(dir.path()).unwrap()
    }

    #[test]
    fn test_entry_lookup() {
        let adapter = create_database();

        {
            let mut txn = adapter.rw_transaction().unwrap();

            let domain_id = adapter.next_available_id(&txn).unwrap();
            adapter.create_entry(&mut txn, domain_id, Id::root(), "example.com", "domain").unwrap();

            let hosts_id = domain_id.next();
            adapter.create_entry(&mut txn, hosts_id, domain_id, "hosts", "ou").unwrap();

            let host_id = hosts_id.next();
            adapter.create_entry(&mut txn, host_id, hosts_id, "master.example.com", "host")
                   .unwrap();

            txn.commit().unwrap();
        }

        {
            let txn = adapter.ro_transaction().unwrap();

            {
                let entry = adapter.find_entry(&txn, "/example.com/hosts/master.example.com")
                                   .unwrap();

                assert_eq!(entry.node.name, "master.example.com");
                assert_eq!(entry.objectclass, "host");
            }

            txn.commit().unwrap();
        }
    }

    #[test]
    fn test_duplicate_entry_id() {
        let adapter = create_database();

        let mut txn = adapter.rw_transaction().unwrap();

        let domain_id = adapter.next_available_id(&txn).unwrap();
        adapter.create_entry(&mut txn, domain_id, Id::root(), "example.com", "domain").unwrap();

        assert_eq!(adapter.create_entry(&mut txn, domain_id, Id::root(), "another.com", "domain"),
                   Err(Error::DuplicateEntryError));
    }

    #[test]
    fn test_duplicate_entry_name() {
        let adapter = create_database();

        let mut txn = adapter.rw_transaction().unwrap();

        let domain_id = adapter.next_available_id(&txn).unwrap();
        adapter.create_entry(&mut txn, domain_id, Id::root(), "example.com", "domain").unwrap();

        assert_eq!(adapter.create_entry(&mut txn,
                                        domain_id.next(),
                                        Id::root(),
                                        "example.com",
                                        "domain"),
                   Err(Error::DuplicateEntryError));
    }
}
<commit_msg>Abstract away cursors with "find"<commit_after>extern crate lmdb;
extern crate lmdb_sys;

#[cfg(test)]
extern crate tempdir;

use std::{result, str};
use std::path::Path;
use lmdb::{Cursor, DUP_SORT, INTEGER_KEY};
use lmdb::Transaction as LmdbTransaction;

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Error {
    DbCreateError,
    DbOpenError,
    DbWriteError,
    DbCorruptError,
    ParseError,
    TransactionError,
    PathError,
    NotFoundError,
    DuplicateEntryError,
}

pub type Result<T> = result::Result<T, Error>;

pub struct LmdbAdapter {
    env: lmdb::Environment,
    nodes: lmdb::Database,
    entries: lmdb::Database,
}

pub struct RwTransaction<'a>(lmdb::RwTransaction<'a>);
pub struct RoTransaction<'a>(lmdb::RoTransaction<'a>);

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct Id(u64);

#[derive(Debug, Eq, PartialEq)]
pub struct Node<'a> {
    pub id: Id,
    pub parent_id: Id,
    pub name: &'a str,
}

#[derive(Debug, Eq, PartialEq)]
pub struct Entry<'a> {
    pub node: Node<'a>,
    pub objectclass: &'a str,
}

// Ids are 64-bit integers in host-native byte order
// LMDB has special optimizations for host-native integers as keys
impl Id {
    fn root() -> Id {
        Id(0)
    }

    fn from_bytes(bytes: &[u8]) -> Result<Id> {
        if bytes.len() != 8 {
            return Err(Error::ParseError);
        }

        let mut id = [0u8; 8];
        id.copy_from_slice(&bytes[0..8]);

        Ok(Id(unsafe { std::mem::transmute(id) }))
    }

    fn as_bytes(self) -> [u8; 8] {
        let Id(id) = self;
        unsafe { std::mem::transmute(id) }
    }

    fn next(self) -> Id {
        let Id(id) = self;
        Id(id + 1)
    }
}

impl<'a> Node<'a> {
    fn root() -> Node<'a> {
        Node {
            id: Id::root(),
            parent_id: Id::root(),
            name: "/",
        }
    }

    fn from_parent_id_and_bytes(parent_id: Id, bytes: &[u8]) -> Result<Node> {
        if bytes.len() < 8 {
            return Err(Error::DbCorruptError);
        }

        let id = try!(Id::from_bytes(&bytes[0..8]));
        let name = try!(std::str::from_utf8(&bytes[8..]).map_err(|_err| Error::DbCorruptError));

        Ok(Node {
            id: id,
            parent_id: parent_id,
            name: name,
        })
    }

    fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(8 + self.name.len());
        bytes.extend_from_slice(&self.id.as_bytes());
        bytes.extend_from_slice(self.name.as_bytes());
        bytes
    }
}

impl LmdbAdapter {
    pub fn create_database(path: &Path) -> Result<LmdbAdapter> {
        let env = try!(lmdb::Environment::new()
                           .set_max_dbs(8)
                           .open_with_permissions(&path, 0o600)
                           .map_err(|_err| Error::DbCreateError));

        let nodes = try!(env.create_db(Some("nodes"), INTEGER_KEY | DUP_SORT)
                            .map_err(|_err| Error::DbCreateError));

        let entries = try!(env.create_db(Some("entries"), INTEGER_KEY)
                              .map_err(|_err| Error::DbCreateError));

        Ok(LmdbAdapter {
            env: env,
            nodes: nodes,
            entries: entries,
        })
    }

    pub fn open_database(path: &Path) -> Result<LmdbAdapter> {
        let env = try!(lmdb::Environment::new().open(&path).map_err(|_err| Error::DbOpenError));

        let nodes = try!(env.open_db(Some("nodes")).map_err(|_err| Error::DbOpenError));

        let entries = try!(env.open_db(Some("entries")).map_err(|_err| Error::DbOpenError));

        Ok(LmdbAdapter {
            env: env,
            nodes: nodes,
            entries: entries,
        })
    }

    pub fn rw_transaction(&self) -> Result<RwTransaction> {
        match self.env.begin_rw_txn() {
            Ok(txn) => Ok(RwTransaction(txn)),
            Err(_) => Err(Error::TransactionError),
        }
    }

    pub fn ro_transaction(&self) -> Result<RoTransaction> {
        match self.env.begin_ro_txn() {
            Ok(txn) => Ok(RoTransaction(txn)),
            Err(_) => Err(Error::TransactionError),
        }
    }

    pub fn next_available_id(&self, txn: &RwTransaction) -> Result<Id> {
        let cursor = try!(txn.0
                             .open_ro_cursor(self.nodes)
                             .map_err(|_err| Error::TransactionError));

        let last_id = match cursor.get(None, None, lmdb_sys::MDB_LAST) {
            Ok((id, _)) => Id::from_bytes(id.unwrap()).unwrap(),
            Err(_) => Id::root(),
        };

        Ok(last_id.next())
    }

    pub fn create_entry<'a>(&'a self,
                            txn: &'a mut RwTransaction,
                            id: Id,
                            parent_id: Id,
                            name: &'a str,
                            objectclass: &'a str)
                            -> Result<Entry> {
        if txn.get(self.entries, &id.as_bytes()) != Err(Error::NotFoundError) {
            return Err(Error::DuplicateEntryError);
        }

        if txn.get(self.nodes, &parent_id.as_bytes()) != Err(Error::NotFoundError) &&
           self.find_child_node(txn, parent_id, name) != Err(Error::NotFoundError) {
            return Err(Error::DuplicateEntryError);
        }

        let node = Node {
            id: id,
            parent_id: parent_id,
            name: name,
        };

        try!(txn.put(self.nodes, &parent_id.as_bytes(), &node.to_bytes())
                .map_err(|_err| Error::DbWriteError));

        try!(txn.put(self.entries, &id.as_bytes(), &objectclass.as_bytes())
                .map_err(|_err| Error::DbWriteError));

        Ok(Entry {
            node: node,
            objectclass: objectclass,
        })
    }

    pub fn find_entry<'a, T: Transaction>(&'a self, txn: &'a T, path: &str) -> Result<Entry> {
        let node = try!(self.find_node(txn, path));

        let entry_bytes = try!(txn.get(self.entries, &node.id.as_bytes())
                                  .map_err(|_err| Error::DbCorruptError));

        let objectclass = try!(std::str::from_utf8(&entry_bytes)
                                   .map_err(|_err| Error::DbCorruptError));

        Ok(Entry {
            node: node,
            objectclass: objectclass,
        })
    }

    fn find_node<'a, T: Transaction>(&'a self, txn: &'a T, path: &str) -> Result<Node> {
        let all_components: Vec<&str> = path.split("/").collect();

        if all_components.is_empty() {
            return Err(Error::PathError);
        }

        let (prefix, components) = all_components.split_first().unwrap();

        // Does the path start with something other than "/"?
        if !prefix.is_empty() {
            return Err(Error::PathError);
        }

        components.iter().fold(Ok(Node::root()), |parent_node, component| {
            self.find_child_node(txn, try!(parent_node).id, component)
        })
    }

    fn find_child_node<'a, T: Transaction>(&'a self,
                                           txn: &'a T,
                                           parent_id: Id,
                                           name: &str)
                                           -> Result<Node> {
        let node_bytes = try!(txn.find(self.nodes, &parent_id.as_bytes(), |node_bytes| {
            match Node::from_parent_id_and_bytes(parent_id, node_bytes) {
                Ok(node) => node.name == name,
                _ => false,
            }
        }));

        Node::from_parent_id_and_bytes(parent_id, node_bytes)
    }
}

pub trait Transaction {
    fn get(&self, database: lmdb::Database, key: &[u8]) -> Result<&[u8]>;
    fn find<P>(&self, db: lmdb::Database, key: &[u8], predicate: P) -> Result<&[u8]>
        where P: Fn(&[u8]) -> bool;
    fn commit(self) -> Result<()>;
}

macro_rules! impl_transaction (($newtype:ident) => (
    impl<'a> Transaction for $newtype<'a> {
        fn get(&self, database: lmdb::Database, key: &[u8]) -> Result<&[u8]> {
            self.0.get(database, &key).map_err(|_err| Error::NotFoundError)
        }

        // TODO: since LMDB is ordered, we could e.g. perform a binary search
        fn find<P>(&self, db: lmdb::Database, key: &[u8], predicate: P) -> Result<&[u8]>
            where P: Fn(&[u8]) -> bool
        {
            let mut cursor = try!(self.0
                                      .open_ro_cursor(db)
                                      .map_err(|_err| Error::TransactionError));

            let mut result = None;

            for (cursor_key, value) in cursor.iter_from(key) {
                if cursor_key != key {
                    return Err(Error::NotFoundError);
                }

                if predicate(value) {
                    result = Some(value);
                    break;
                }
            }

            result.ok_or(Error::NotFoundError)
        }

        fn commit(self) -> Result<()> {
            self.0.commit().map_err(|_err| Error::TransactionError)
        }
    }
));

impl_transaction!(RwTransaction);
impl_transaction!(RoTransaction);

impl<'a> RwTransaction<'a> {
    fn put(&mut self, database: lmdb::Database, key: &[u8], data: &[u8]) -> Result<()> {
        self.0
            .put(database, &key, &data, lmdb::WriteFlags::empty())
            .map_err(|_err| Error::TransactionError)
    }
}

#[cfg(test)]
mod tests {
    use {LmdbAdapter, Id, Transaction, Error};
    use tempdir::TempDir;

    fn create_database() -> LmdbAdapter {
        let dir = TempDir::new("ithos-test").unwrap();
        LmdbAdapter::create_database(dir.path()).unwrap()
    }

    #[test]
    fn test_entry_lookup() {
        let adapter = create_database();

        {
            let mut txn = adapter.rw_transaction().unwrap();

            let domain_id = adapter.next_available_id(&txn).unwrap();
            adapter.create_entry(&mut txn, domain_id, Id::root(), "example.com", "domain").unwrap();

            let hosts_id = domain_id.next();
            adapter.create_entry(&mut txn, hosts_id, domain_id, "hosts", "ou").unwrap();

            let host_id = hosts_id.next();
            adapter.create_entry(&mut txn, host_id, hosts_id, "master.example.com", "host")
                   .unwrap();

            txn.commit().unwrap();
        }

        {
            let txn = adapter.ro_transaction().unwrap();

            {
                let entry = adapter.find_entry(&txn, "/example.com/hosts/master.example.com")
                                   .unwrap();

                assert_eq!(entry.node.name, "master.example.com");
                assert_eq!(entry.objectclass, "host");
            }

            txn.commit().unwrap();
        }
    }

    #[test]
    fn test_duplicate_entry_id() {
        let adapter = create_database();

        let mut txn = adapter.rw_transaction().unwrap();

        let domain_id = adapter.next_available_id(&txn).unwrap();
        adapter.create_entry(&mut txn, domain_id, Id::root(), "example.com", "domain").unwrap();

        assert_eq!(adapter.create_entry(&mut txn, domain_id, Id::root(), "another.com", "domain"),
                   Err(Error::DuplicateEntryError));
    }

    #[test]
    fn test_duplicate_entry_name() {
        let adapter = create_database();

        let mut txn = adapter.rw_transaction().unwrap();

        let domain_id = adapter.next_available_id(&txn).unwrap();
        adapter.create_entry(&mut txn, domain_id, Id::root(), "example.com", "domain").unwrap();

        assert_eq!(adapter.create_entry(&mut txn,
                                        domain_id.next(),
                                        Id::root(),
                                        "example.com",
                                        "domain"),
                   Err(Error::DuplicateEntryError));
    }
}
<|endoftext|>


<commit_before>//! Actix web is a small, pragmatic, and extremely fast web framework
//! for Rust.
//!
//! ```rust
//! use actix_web::{server, App, Path};
//! # use std::thread;
//!
//! fn index(info: Path<(String, u32)>) -> String {
//!     format!("Hello {}! id:{}", info.0, info.1)
//! }
//!
//! fn main() {
//!     # thread::spawn(|| {
//!     server::new(|| {
//!         App::new().resource("/{name}/{id}/index.html", |r| r.with(index))
//!     }).bind("127.0.0.1:8080")
//!         .unwrap()
//!         .run();
//!     # });
//! }
//! ```
//!
//! ## Documentation & community resources
//!
//! Besides the API documentation (which you are currently looking
//! at!), several other resources are available:
//!
//! * [User Guide](https://actix.rs/docs/)
//! * [Chat on gitter](https://gitter.im/actix/actix)
//! * [GitHub repository](https://github.com/actix/actix-web)
//! * [Cargo package](https://crates.io/crates/actix-web)
//!
//! To get started navigating the API documentation you may want to
//! consider looking at the following pages:
//!
//! * [App](struct.App.html): This struct represents an actix-web
//!   application and is used to configure routes and other common
//!   settings.
//!
//! * [HttpServer](server/struct.HttpServer.html): This struct
//!   represents an HTTP server instance and is used to instantiate and
//!   configure servers.
//!
//! * [HttpRequest](struct.HttpRequest.html) and
//!   [HttpResponse](struct.HttpResponse.html): These structs
//!   represent HTTP requests and responses and expose various methods
//!   for inspecting, creating and otherwise utilizing them.
//!
//! ## Features
//!
//! * Supported *HTTP/1.x* and *HTTP/2.0* protocols
//! * Streaming and pipelining
//! * Keep-alive and slow requests handling
//! * `WebSockets` server/client
//! * Transparent content compression/decompression (br, gzip, deflate)
//! * Configurable request routing
//! * Graceful server shutdown
//! * Multipart streams
//! * SSL support with OpenSSL or `native-tls`
//! * Middlewares (`Logger`, `Session`, `CORS`, `CSRF`, `DefaultHeaders`)
//! * Built on top of [Actix actor framework](https://github.com/actix/actix)
//! * Supported Rust version: 1.26 or later
//!
//! ## Package feature
//!
//! * `tls` - enables ssl support via `native-tls` crate
//! * `alpn` - enables ssl support via `openssl` crate, require for `http/2`
//!    support
//! * `session` - enables session support, includes `ring` crate as
//!   dependency
//! * `brotli` - enables `brotli` compression support, requires `c`
//!   compiler
//! * `flate-c` - enables `gzip`, `deflate` compression support, requires
//!   `c` compiler
//! * `flate-rust` - experimental rust based implementation for
//!   `gzip`, `deflate` compression.
//!
#![cfg_attr(actix_nightly, feature(
    specialization, // for impl ErrorResponse for std::error::Error
))]
#![cfg_attr(
    feature = "cargo-clippy",
    allow(decimal_literal_representation, suspicious_arithmetic_impl)
)]

#[macro_use]
extern crate log;
extern crate base64;
extern crate byteorder;
extern crate bytes;
extern crate regex;
extern crate sha1;
extern crate time;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate futures;
extern crate cookie;
extern crate futures_cpupool;
extern crate http as modhttp;
extern crate http_range;
extern crate httparse;
extern crate language_tags;
extern crate libc;
extern crate mime;
extern crate mime_guess;
extern crate mio;
extern crate net2;
extern crate rand;
extern crate slab;
extern crate tokio;
extern crate tokio_io;
extern crate tokio_reactor;
extern crate tokio_tcp;
extern crate tokio_timer;
extern crate url;
#[macro_use]
extern crate serde;
#[cfg(feature = "brotli")]
extern crate brotli2;
extern crate encoding;
#[cfg(feature = "flate2")]
extern crate flate2;
extern crate h2 as http2;
extern crate num_cpus;
extern crate percent_encoding;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate smallvec;
#[macro_use]
pub extern crate actix as actix_inner;

#[cfg(test)]
#[macro_use]
extern crate serde_derive;

#[cfg(feature = "tls")]
extern crate native_tls;
#[cfg(feature = "tls")]
extern crate tokio_tls;

#[cfg(feature = "openssl")]
extern crate openssl;
#[cfg(feature = "openssl")]
extern crate tokio_openssl;

mod application;
mod body;
mod context;
mod de;
mod extractor;
mod handler;
mod header;
mod helpers;
mod httpcodes;
mod httpmessage;
mod httprequest;
mod httpresponse;
mod info;
mod json;
mod param;
mod payload;
mod pipeline;
mod resource;
mod route;
mod router;
mod scope;
mod uri;
mod with;

pub mod client;
pub mod error;
pub mod fs;
pub mod middleware;
pub mod multipart;
pub mod pred;
pub mod server;
pub mod test;
pub mod ws;
pub use application::App;
pub use body::{Binary, Body};
pub use context::HttpContext;
pub use error::{Error, ResponseError, Result};
pub use extractor::{Form, Path, Query};
pub use handler::{
    AsyncResponder, Either, FromRequest, FutureResponse, Responder, State,
};
pub use httpmessage::HttpMessage;
pub use httprequest::HttpRequest;
pub use httpresponse::HttpResponse;
pub use json::Json;
pub use scope::Scope;

pub mod actix {
    //! Re-exports [actix's](https://docs.rs/actix) prelude

    pub use actix_inner::actors::resolver;
    pub use actix_inner::actors::signal;
    pub use actix_inner::fut;
    pub use actix_inner::msgs;
    pub use actix_inner::prelude::*;
}

#[cfg(feature = "openssl")]
pub(crate) const HAS_OPENSSL: bool = true;
#[cfg(not(feature = "openssl"))]
pub(crate) const HAS_OPENSSL: bool = false;

#[cfg(feature = "tls")]
pub(crate) const HAS_TLS: bool = true;
#[cfg(not(feature = "tls"))]
pub(crate) const HAS_TLS: bool = false;

pub mod dev {
    //! The `actix-web` prelude for library developers
    //!
    //! The purpose of this module is to alleviate imports of many common actix
    //! traits by adding a glob import to the top of actix heavy modules:
    //!
    //! ```
    //! # #![allow(unused_imports)]
    //! use actix_web::dev::*;
    //! ```

    pub use body::BodyStream;
    pub use context::Drain;
    pub use extractor::{FormConfig, PayloadConfig};
    pub use handler::{AsyncResult, Handler};
    pub use httpmessage::{MessageBody, UrlEncoded};
    pub use httpresponse::HttpResponseBuilder;
    pub use info::ConnectionInfo;
    pub use json::{JsonBody, JsonConfig};
    pub use param::{FromParam, Params};
    pub use resource::ResourceHandler;
    pub use route::Route;
    pub use router::{Resource, ResourceType, Router};
    pub use with::ExtractorConfig;
}

pub mod http {
    //! Various HTTP related types

    // re-exports
    pub use modhttp::{Method, StatusCode, Version};

    #[doc(hidden)]
    pub use modhttp::{uri, Error, Extensions, HeaderMap, HttpTryFrom, Uri};

    pub use cookie::{Cookie, CookieBuilder};
    pub use http_range::HttpRange;

    pub use helpers::NormalizePath;

    pub mod header {
        pub use header::*;
    }
    pub use header::ContentEncoding;
    pub use httpresponse::ConnectionType;
}
<commit_msg>Add warning for missing API docs<commit_after>//! Actix web is a small, pragmatic, and extremely fast web framework
//! for Rust.
//!
//! ```rust
//! use actix_web::{server, App, Path};
//! # use std::thread;
//!
//! fn index(info: Path<(String, u32)>) -> String {
//!     format!("Hello {}! id:{}", info.0, info.1)
//! }
//!
//! fn main() {
//!     # thread::spawn(|| {
//!     server::new(|| {
//!         App::new().resource("/{name}/{id}/index.html", |r| r.with(index))
//!     }).bind("127.0.0.1:8080")
//!         .unwrap()
//!         .run();
//!     # });
//! }
//! ```
//!
//! ## Documentation & community resources
//!
//! Besides the API documentation (which you are currently looking
//! at!), several other resources are available:
//!
//! * [User Guide](https://actix.rs/docs/)
//! * [Chat on gitter](https://gitter.im/actix/actix)
//! * [GitHub repository](https://github.com/actix/actix-web)
//! * [Cargo package](https://crates.io/crates/actix-web)
//!
//! To get started navigating the API documentation you may want to
//! consider looking at the following pages:
//!
//! * [App](struct.App.html): This struct represents an actix-web
//!   application and is used to configure routes and other common
//!   settings.
//!
//! * [HttpServer](server/struct.HttpServer.html): This struct
//!   represents an HTTP server instance and is used to instantiate and
//!   configure servers.
//!
//! * [HttpRequest](struct.HttpRequest.html) and
//!   [HttpResponse](struct.HttpResponse.html): These structs
//!   represent HTTP requests and responses and expose various methods
//!   for inspecting, creating and otherwise utilizing them.
//!
//! ## Features
//!
//! * Supported *HTTP/1.x* and *HTTP/2.0* protocols
//! * Streaming and pipelining
//! * Keep-alive and slow requests handling
//! * `WebSockets` server/client
//! * Transparent content compression/decompression (br, gzip, deflate)
//! * Configurable request routing
//! * Graceful server shutdown
//! * Multipart streams
//! * SSL support with OpenSSL or `native-tls`
//! * Middlewares (`Logger`, `Session`, `CORS`, `CSRF`, `DefaultHeaders`)
//! * Built on top of [Actix actor framework](https://github.com/actix/actix)
//! * Supported Rust version: 1.26 or later
//!
//! ## Package feature
//!
//! * `tls` - enables ssl support via `native-tls` crate
//! * `alpn` - enables ssl support via `openssl` crate, require for `http/2`
//!    support
//! * `session` - enables session support, includes `ring` crate as
//!   dependency
//! * `brotli` - enables `brotli` compression support, requires `c`
//!   compiler
//! * `flate-c` - enables `gzip`, `deflate` compression support, requires
//!   `c` compiler
//! * `flate-rust` - experimental rust based implementation for
//!   `gzip`, `deflate` compression.
//!
#![cfg_attr(actix_nightly, feature(
    specialization, // for impl ErrorResponse for std::error::Error
))]
#![cfg_attr(
    feature = "cargo-clippy",
    allow(decimal_literal_representation, suspicious_arithmetic_impl)
)]
#![warn(missing_docs)]

#[macro_use]
extern crate log;
extern crate base64;
extern crate byteorder;
extern crate bytes;
extern crate regex;
extern crate sha1;
extern crate time;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate futures;
extern crate cookie;
extern crate futures_cpupool;
extern crate http as modhttp;
extern crate http_range;
extern crate httparse;
extern crate language_tags;
extern crate libc;
extern crate mime;
extern crate mime_guess;
extern crate mio;
extern crate net2;
extern crate rand;
extern crate slab;
extern crate tokio;
extern crate tokio_io;
extern crate tokio_reactor;
extern crate tokio_tcp;
extern crate tokio_timer;
extern crate url;
#[macro_use]
extern crate serde;
#[cfg(feature = "brotli")]
extern crate brotli2;
extern crate encoding;
#[cfg(feature = "flate2")]
extern crate flate2;
extern crate h2 as http2;
extern crate num_cpus;
extern crate percent_encoding;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate smallvec;
#[macro_use]
pub extern crate actix as actix_inner;

#[cfg(test)]
#[macro_use]
extern crate serde_derive;

#[cfg(feature = "tls")]
extern crate native_tls;
#[cfg(feature = "tls")]
extern crate tokio_tls;

#[cfg(feature = "openssl")]
extern crate openssl;
#[cfg(feature = "openssl")]
extern crate tokio_openssl;

mod application;
mod body;
mod context;
mod de;
mod extractor;
mod handler;
mod header;
mod helpers;
mod httpcodes;
mod httpmessage;
mod httprequest;
mod httpresponse;
mod info;
mod json;
mod param;
mod payload;
mod pipeline;
mod resource;
mod route;
mod router;
mod scope;
mod uri;
mod with;

pub mod client;
pub mod error;
pub mod fs;
pub mod middleware;
pub mod multipart;
pub mod pred;
pub mod server;
pub mod test;
pub mod ws;
pub use application::App;
pub use body::{Binary, Body};
pub use context::HttpContext;
pub use error::{Error, ResponseError, Result};
pub use extractor::{Form, Path, Query};
pub use handler::{
    AsyncResponder, Either, FromRequest, FutureResponse, Responder, State,
};
pub use httpmessage::HttpMessage;
pub use httprequest::HttpRequest;
pub use httpresponse::HttpResponse;
pub use json::Json;
pub use scope::Scope;

pub mod actix {
    //! Re-exports [actix's](https://docs.rs/actix) prelude

    pub use actix_inner::actors::resolver;
    pub use actix_inner::actors::signal;
    pub use actix_inner::fut;
    pub use actix_inner::msgs;
    pub use actix_inner::prelude::*;
}

#[cfg(feature = "openssl")]
pub(crate) const HAS_OPENSSL: bool = true;
#[cfg(not(feature = "openssl"))]
pub(crate) const HAS_OPENSSL: bool = false;

#[cfg(feature = "tls")]
pub(crate) const HAS_TLS: bool = true;
#[cfg(not(feature = "tls"))]
pub(crate) const HAS_TLS: bool = false;

pub mod dev {
    //! The `actix-web` prelude for library developers
    //!
    //! The purpose of this module is to alleviate imports of many common actix
    //! traits by adding a glob import to the top of actix heavy modules:
    //!
    //! ```
    //! # #![allow(unused_imports)]
    //! use actix_web::dev::*;
    //! ```

    pub use body::BodyStream;
    pub use context::Drain;
    pub use extractor::{FormConfig, PayloadConfig};
    pub use handler::{AsyncResult, Handler};
    pub use httpmessage::{MessageBody, UrlEncoded};
    pub use httpresponse::HttpResponseBuilder;
    pub use info::ConnectionInfo;
    pub use json::{JsonBody, JsonConfig};
    pub use param::{FromParam, Params};
    pub use resource::ResourceHandler;
    pub use route::Route;
    pub use router::{Resource, ResourceType, Router};
    pub use with::ExtractorConfig;
}

pub mod http {
    //! Various HTTP related types

    // re-exports
    pub use modhttp::{Method, StatusCode, Version};

    #[doc(hidden)]
    pub use modhttp::{uri, Error, Extensions, HeaderMap, HttpTryFrom, Uri};

    pub use cookie::{Cookie, CookieBuilder};
    pub use http_range::HttpRange;

    pub use helpers::NormalizePath;

    pub mod header {
        pub use header::*;
    }
    pub use header::ContentEncoding;
    pub use httpresponse::ConnectionType;
}
<|endoftext|>


<commit_before>//!Readline for Rust
//!
//!This implementation is based on [Antirez's Linenoise](https://github.com/antirez/linenoise)
//!
//!# Example
//!
//!Usage
//!
//!```
//!let readline = rustyline::readline(">> ");
//!match readline {
//!     Ok(line) => println!("Line: {:?}",line),
//!     Err(_)   => println!("No input"),
//! }
//!```
extern crate libc;
extern crate nix;

#[allow(non_camel_case_types)]
pub mod consts;
pub mod error;

use std::result;
use std::io;
use std::str;
use std::io::{Write, Read};
use nix::errno::Errno;
use nix::sys::termios;
use nix::sys::termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON, OPOST, CS8, ECHO, ICANON, IEXTEN, ISIG, VMIN, VTIME};
use consts::{KeyPress, u8_to_key_press};

/// The error type for I/O and Linux Syscalls (Errno)
pub type Result<T> = result::Result<T, error::ReadlineError>;

/// Maximum buffer size for the line read
static MAX_LINE: usize = 4096;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb","cons25","emacs"];

/// Check to see if STDIN is a TTY
fn is_a_tty() -> bool {
    let isatty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
    isatty
}

/// Check to see if the current `TERM` is unsupported
fn is_unsupported_term() -> bool {
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = term == *iter
            }
            unsupported
        }
        Err(_) => false
    }
}

/// Enable raw mode for the TERM
fn enable_raw_mode() -> Result<termios::Termios> {
    if !is_a_tty() {
        Err(error::ReadlineError
                          ::from(nix::Error
                                    ::from_errno(Errno::ENOTTY)))
    } else {
        let original_term = try!(termios::tcgetattr(libc::STDIN_FILENO));
        let mut raw = original_term;
        raw.c_iflag = raw.c_iflag   & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        raw.c_oflag = raw.c_oflag   & !(OPOST);
        raw.c_cflag = raw.c_cflag   | (CS8);
        raw.c_lflag = raw.c_lflag   & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1;
        raw.c_cc[VTIME] = 0;
        try!(termios::tcsetattr(libc::STDIN_FILENO, termios::TCSAFLUSH, &raw));
        Ok(original_term)
    }
}

/// Disable Raw mode for the term
fn disable_raw_mode(original_termios: termios::Termios) -> Result<()> {
    try!(termios::tcsetattr(libc::STDIN_FILENO,
                             termios::TCSAFLUSH,
                             &original_termios));
    Ok(())
}

/// Handles reading and editting the readline buffer.
/// It will also handle special inputs in an appropriate fashion
/// (e.g., C-c will exit readline)
fn readline_edit() -> Result<String> {
    let mut buffer = String::with_capacity(MAX_LINE);
    let mut input: [u8; 4] = [0; 4]; // UTF-8 can be max 4 bytes
    loop {
        if io::stdin().read(&mut input).is_ok()
        {
            match u8_to_key_press(input[0]) {
                KeyPress::CTRL_A => print!("Pressed C-a"),
                KeyPress::CTRL_B => print!("Pressed C-b"),
                KeyPress::CTRL_C => print!("Pressed C-c"),
                KeyPress::CTRL_D => print!("Pressed C-d"),
                KeyPress::CTRL_E => print!("Pressed C-e"),
                KeyPress::CTRL_F => print!("Pressed C-f"),
                KeyPress::CTRL_H => print!("Pressed C-h"),
                KeyPress::CTRL_K => print!("Pressed C-k"),
                KeyPress::CTRL_L => print!("Pressed C-l"),
                KeyPress::CTRL_N => print!("Pressed C-n"),
                KeyPress::CTRL_P => print!("Pressed C-p"),
                KeyPress::CTRL_T => print!("Pressed C-t"),
                KeyPress::CTRL_U => print!("Pressed C-u"),
                KeyPress::CTRL_W => print!("Pressed C-w"),
                KeyPress::ESC    => print!("Pressed esc"),
                KeyPress::ENTER  => break,
                _      => {
                    match str::from_utf8(&input) {
                        Ok(s) => buffer.push_str(s) ,
                        Err(_) => panic!("Invalid UTF-8 Character"),
                    }
                }
            }
        }

    }
    Ok(buffer)
}

/// Readline method that will enable RAW mode, call the ```readline_edit()```
/// method and disable raw mode
fn readline_raw() -> Result<String> {
    if is_a_tty() {
        let original_termios = try!(enable_raw_mode());
        let user_input = readline_edit();
        try!(disable_raw_mode(original_termios));
        user_input
    } else {
        let mut line = String::new();
        try!(io::stdin().read_line(&mut line));
        Ok(line)
    }
}

/// This method will read a line from STDIN and will display a `prompt`
pub fn readline(prompt: &'static str) -> Result<String> {
    // Write prompt and flush it to stdout
    let mut stdout = io::stdout();
    try!(stdout.write(prompt.as_bytes()));
    try!(stdout.flush());

    if is_unsupported_term() {
        let mut line = String::new();
        try!(io::stdin().read_line(&mut line));
        Ok(line)
    } else {
        readline_raw()
    }
}
<commit_msg>Convert bytes read into UTF-8 Character<commit_after>//!Readline for Rust
//!
//!This implementation is based on [Antirez's Linenoise](https://github.com/antirez/linenoise)
//!
//!# Example
//!
//!Usage
//!
//!```
//!let readline = rustyline::readline(">> ");
//!match readline {
//!     Ok(line) => println!("Line: {:?}",line),
//!     Err(_)   => println!("No input"),
//! }
//!```
extern crate libc;
extern crate nix;

#[allow(non_camel_case_types)]
pub mod consts;
pub mod error;

use std::result;
use std::io;
use std::str;
use std::io::{Write, Read};
use nix::errno::Errno;
use nix::sys::termios;
use nix::sys::termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON, OPOST, CS8, ECHO, ICANON, IEXTEN, ISIG, VMIN, VTIME};
use consts::{KeyPress, u8_to_key_press};

/// The error type for I/O and Linux Syscalls (Errno)
pub type Result<T> = result::Result<T, error::ReadlineError>;

/// Maximum buffer size for the line read
static MAX_LINE: usize = 4096;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb","cons25","emacs"];

/// Check to see if STDIN is a TTY
fn is_a_tty() -> bool {
    let isatty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
    isatty
}

/// Check to see if the current `TERM` is unsupported
fn is_unsupported_term() -> bool {
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = term == *iter
            }
            unsupported
        }
        Err(_) => false
    }
}

/// Enable raw mode for the TERM
fn enable_raw_mode() -> Result<termios::Termios> {
    if !is_a_tty() {
        Err(error::ReadlineError
            ::from(nix::Error
                   ::from_errno(Errno::ENOTTY)))
    } else {
        let original_term = try!(termios::tcgetattr(libc::STDIN_FILENO));
        let mut raw = original_term;
        raw.c_iflag = raw.c_iflag   & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        raw.c_oflag = raw.c_oflag   & !(OPOST);
        raw.c_cflag = raw.c_cflag   | (CS8);
        raw.c_lflag = raw.c_lflag   & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1;
        raw.c_cc[VTIME] = 0;
        try!(termios::tcsetattr(libc::STDIN_FILENO, termios::TCSAFLUSH, &raw));
        Ok(original_term)
    }
}

/// Disable Raw mode for the term
fn disable_raw_mode(original_termios: termios::Termios) -> Result<()> {
    try!(termios::tcsetattr(libc::STDIN_FILENO,
                            termios::TCSAFLUSH,
                            &original_termios));
    Ok(())
}

/// Handles reading and editting the readline buffer.
/// It will also handle special inputs in an appropriate fashion
/// (e.g., C-c will exit readline)
fn readline_edit() -> Result<String> {
    let mut buffer = String::with_capacity(MAX_LINE);
    let mut input: [u8; 4] = [0; 4]; // UTF-8 can be max 4 bytes
    loop {
        let read_result = io::stdin().read(&mut input);
        if read_result.is_ok()
        {
            match u8_to_key_press(input[0]) {
                KeyPress::ENTER     => return Ok(buffer),
                KeyPress::BACKSPACE => { buffer.pop(); },
                //KeyPress::CTRL_A      => print!("Pressed C-a"),
                //KeyPress::CTRL_B    => print!("Pressed C-b"),
                //KeyPress::CTRL_C    => print!("Pressed C-c"),
                //KeyPress::CTRL_D    => print!("Pressed C-d"),
                //KeyPress::CTRL_E    => print!("Pressed C-e"),
                //KeyPress::CTRL_F    => print!("Pressed C-f"),
                //KeyPress::CTRL_H    => print!("Pressed C-h"),
                //KeyPress::CTRL_K    => print!("Pressed C-k"),
                //KeyPress::CTRL_L    => print!("Pressed C-l"),
                //KeyPress::CTRL_N    => print!("Pressed C-n"),
                //KeyPress::CTRL_P    => print!("Pressed C-p"),
                //KeyPress::CTRL_T    => print!("Pressed C-t"),
                //KeyPress::CTRL_U    => print!("Pressed C-u"),
                //KeyPress::CTRL_W    => print!("Pressed C-w"),
                //KeyPress::ESC       => print!("Pressed esc"),
                _                   => {
                    read_result.map_err(error::ReadlineError::Io)
                               .and_then(|bytes| str::from_utf8(&input[0..bytes])
                                                        .map_err(|_| error::ReadlineError::InvalidUTF8))
                               .ok()
                               .and_then(|c| Some(buffer.push_str(c)));
                }
            }
        }
    }
}

/// Readline method that will enable RAW mode, call the ```readline_edit()```
/// method and disable raw mode
fn readline_raw() -> Result<String> {
    if is_a_tty() {
        let original_termios = try!(enable_raw_mode());
        let user_input = readline_edit();
        try!(disable_raw_mode(original_termios));
        user_input
    } else {
        let mut line = String::new();
        try!(io::stdin().read_line(&mut line));
        Ok(line)
    }
}

/// This method will read a line from STDIN and will display a `prompt`
pub fn readline(prompt: &'static str) -> Result<String> {
    // Write prompt and flush it to stdout
    let mut stdout = io::stdout();
    try!(stdout.write(prompt.as_bytes()));
    try!(stdout.flush());

    if is_unsupported_term() {
        let mut line = String::new();
        try!(io::stdin().read_line(&mut line));
        Ok(line)
    } else {
        readline_raw()
    }
}
<|endoftext|>


<commit_before>/*!
Simple persistent generic HashMap/Key-value store.

Basic usage:

```
extern crate kv_cab;

use kv_cab::{ KV, Value };

fn main() {
    let mut test_store = KV::<String, Value>::new("./db.cab").unwrap();

    let _ = test_store.insert("key".to_string(), Value::String("value".to_string()));
    println!("{:?}", test_store.get("key".to_string()));
    let _ = test_store.remove("key".to_string());
}
```

Usage with user defined Key and Value types:

```
extern crate kv_cab;
extern crate rustc_serialize;

use kv_cab::KV;

#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)]
enum MyKey {
    String(String),
    Int(i32),
}

#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
enum MyValue {
    String(String),
    Int(i32),
}

fn main() {
    let mut test_store = KV::<MyKey, MyValue>::new("./db.cab").unwrap();

    let _ = test_store.insert(MyKey::Int(1i32), MyValue::String("value".to_string()));
    println!("{:?}", test_store.get(MyKey::Int(1i32)));
    let _ = test_store.remove(MyKey::Int(1i32));
}
```

*/

extern crate bincode;
extern crate rustc_serialize;
#[macro_use]
extern crate log;

use std::collections::HashMap;
use std::fs;
use std::fs::{ File, OpenOptions };
use std::io::prelude::*;
use std::io::SeekFrom;
use std::hash::Hash;

use bincode::SizeLimit;
use bincode::rustc_serialize::{ encode, decode };

use rustc_serialize::{ Encodable, Decodable };

/// The maximum number of retries the cab will make
const MAX_RETRIES:i32 = 10;

/// Definition of a macro for retrying an operation
macro_rules! retry {
    ($i:ident, $b:block) => (
        for $i in 0..MAX_RETRIES {
            if $i != 0 {
                std::thread::park_timeout(std::time::Duration::new(0u64, 1000u32));
            }

            $b
        }
    )
}

/// A default value type to use with KV
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
pub enum Value {
    String(String),
    Int(i32),
    Float(f32),
    Map(HashMap<String, Value>),
}

/// Type alias for results from KV
type KVResult = Result<bool, KVError>;

/// Errors that KV might have
#[derive(Debug, PartialEq)]
pub enum KVError {
    /// Cab did not contain anything on disk
    CabEmpty,
    /// Could not decode the cab from disk
    CouldntDecode,
    /// Cab was either not readable from disk or doesn't exist
    DoesntExistOrNotReadable,
    /// Couldn't encode the hashmap for writing to disk
    CouldntEncode,
    /// Could not write to the cab on disk
    CouldntWrite,
    /// Could not set the access permissions for the cab
    CouldntSetPermissions,
    /// Could get the permissions that the cab has on disk
    CoudlntGetPermissions,
    /// Failed to read the cab from disk
    FailedToRead,
    /// Could not sync the metadata with the disk
    CouldntSyncMetadata,
    /// Could not flush the data to disk
    CouldntFlush,
    /// Could not open the file from disk
    CouldntOpen,
    /// Could not load the file from disk
    CouldntLoad,
}

/// The type that represents the key-value store
pub struct KV<K,V> {
    cab: HashMap<K, V>,
    file: File,
}

impl<K: Clone + Encodable + Decodable + Eq + Hash, V: Clone + Encodable + Decodable> KV<K,V> {
    /// Creates a new instance of the KV store
    pub fn new(p:&'static str) -> Result<KV<K,V>, KVError> {
        // create the KV instance
        let mut store = KV {
            cab: HashMap::new(),
            file: KV::<K, V>::retry_get_file(p).unwrap(),
        };

        // lock the cab for writes
        if let Err(e) = store.lock_cab(true) {
            return Err(e);
        }

        match store.load_from_persist() {
            Ok(f) => trace!("{}", f),
            Err(e) => {
                if e != KVError::CabEmpty {
                    return Err(e);
                }
                warn!("{:?}", e);
            },
        };

        Ok(store)
    }

    /// Sets file permission for a path to not be readonly
    fn set_path_permission(p: &'static str) -> KVResult {
        match fs::metadata(p) {
            Ok(f) => {
                let mut perms = f.permissions();
                perms.set_readonly(false);

                match fs::set_permissions(p, perms) {
                    Ok(_) => return Ok(true),
                    Err(e) => {
                        error!("{}", e);
                        return Err(KVError::CouldntSetPermissions);
                    }
                }
            },
            Err(e) => {
                if e.kind() != std::io::ErrorKind::NotFound {
                    error!("{}", e);
                    return Err(KVError::CoudlntGetPermissions);
                }
                return Ok(true);
            },
        }
    }

    /// Retry to get a reference to the cab file at path p and create if doesn't exist
    fn retry_get_file(p:&'static str) -> Result<File, KVError> {
        retry!(i, {
            match OpenOptions::new().read(true).write(true).create(true).open(p) {
                Ok(f) => { return Ok(f); },
                Err(e) => {
                    error!("{}", e);
                    if i >= MAX_RETRIES - 1 {
                        return Err(KVError::CouldntOpen);
                    }

                    if let Err(e) = KV::<K, V>::set_path_permission(p) {
                        if i >= MAX_RETRIES - 1 {
                            return Err(e);
                        }
                    }
                    continue;
                }
            };

        });

        Err(KVError::CouldntOpen)
    }

    /// Inserta a key, value pair into the key-value store
    pub fn insert(&mut self, key: K, value: V) -> KVResult {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // insert into the HashMap
        self.cab.insert(key, value);
        // persist
        self.write_to_persist()
    }

    /// Get the value from a key
    pub fn get(&mut self, key: K) -> Result<Option<V>, KVError> {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // get the value from the cab
        match self.cab.get(&key) {
            Some(v) => Ok(Some((*v).clone())),
            None => Ok(None),
        }
    }

    /// Removes a key and associated value from the key-value Store
    pub fn remove(&mut self, key: K) -> KVResult {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // remove from the HashMap
        self.cab.remove(&key);
        // persist
        self.write_to_persist()
    }

    /// get all the keys contained in the KV Store
    pub fn keys(&mut self) -> Result<Vec<K>, KVError> {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // create a vec from the cabs keys
        Ok(self.cab.keys().map(|k| k.clone()).collect())
    }

    /// Locks/unlocks cab for writing purposes
    fn lock_cab(&mut self, readonly:bool) -> KVResult {
        retry!(i, {
            // set not readonly while writing
            let mut perms = match self.file.metadata() {
                Ok(f) => f.permissions(),
                Err(e) => {
                    error!("{}", e);
                    if i >= MAX_RETRIES - 1 {
                        return Err(KVError::CoudlntGetPermissions);
                    }
                    continue;
                },
            };
            perms.set_readonly(readonly);

            match self.file.set_permissions(perms) {
                Ok(_) => {
                    if let Err(e) = self.file.sync_all() {
                        error!("{}", e);
                        return Err(KVError::CouldntSyncMetadata);
                    }
                    break;
                },
                Err(e) => {
                    error!("{}", e);
                    if i >= MAX_RETRIES - 1 {
                        return Err(KVError::CouldntSetPermissions);
                    }
                    continue;
                },
            }
        });

        Ok(true)
    }

    /// Waits for the cab to become free
    fn wait_for_free(&mut self, lock:bool) -> KVResult {
        loop {
            // check if the cab is being written to
            if let Err(e) = self.file.sync_all() {
                error!("{}", e);
                return Err(KVError::CouldntSyncMetadata);
            }
            let metadata = match self.file.metadata() {
                Ok(m) => m,
                Err(_) => return Err(KVError::DoesntExistOrNotReadable),
            };

            if metadata.permissions().readonly() {
                if lock {
                    if let Err(e) = self.lock_cab(false) {
                        return Err(e);
                    }
                }
                // make sure it reads from start
                if let Err(_) = self.file.seek(SeekFrom::Start(0)) {
                    return Err(KVError::FailedToRead);
                }
                break;
            }
        }

        Ok(true)
    }

    /// Writes the key-value Store to file
    fn write_to_persist(&mut self) -> KVResult {
        // encode the cab as a u8 vec
        let byte_vec: Vec<u8> = match encode(&mut self.cab, SizeLimit::Infinite) {
            Ok(bv) => bv,
            Err(e) => {
                error!("encode: {}", e);
                return Err(KVError::CouldntEncode);
            },
        };

        if !self.wait_for_free(true).is_ok() {
            return Err(KVError::DoesntExistOrNotReadable);
        }
        // attempt to write to the cab
        retry!(i, {
            // write the bytes to it
            match self.file.write_all(byte_vec.as_slice()) {
                Ok(_) => (),
                Err(e) => {
                    error!("file.write_all/retry: {}", e);
                    if i >= MAX_RETRIES - 1 {
                        return Err(KVError::CouldntWrite);
                    }
                    continue;
                },
            }

            // flush to disk
            if let Err(e) = self.file.flush() {
                error!("{}", e);
                return Err(KVError::CouldntFlush);
            }
            if let Err(e) = self.lock_cab(true) {
                error!("{:?}", e);
                if i >= MAX_RETRIES - 1 {
                    return Err(e);
                }
            }
            return Ok(true);
        });

        Err(KVError::CouldntWrite)
    }

    /// Loads key-value store from file
    fn load_from_persist(&mut self) -> KVResult {
        retry!(i, {
            // byte vec to read into
            let mut byte_vec = Vec::new();

            // wait/lock the cab and read the bytes
            if !self.wait_for_free(false).is_ok() {
                return Err(KVError::DoesntExistOrNotReadable);
            }

            // read the file into the buffer
            match self.file.read_to_end(&mut byte_vec) {
                Ok(count) => {
                    if count == 0 {
                        // don't attempt to decode as empty
                        return Ok(true);
                    }
                },
                Err(e) => {
                    error!("{}", e);
                    return Err(KVError::FailedToRead);
                },
            }

            // decode u8 vec back into HashMap
            match decode(byte_vec.as_slice()) {
                Ok(f) => {
                    // assign read HashMap back to self
                    self.cab = f;
                    return Ok(true);
                },
                Err(e) => {
                    error!("{}", e);
                    if i >= MAX_RETRIES - 1 {
                        return Err(KVError::CouldntDecode);
                    }
                    continue;
                },
            };
        });

        Err(KVError::CouldntLoad)
    }
}
<commit_msg>Added last_retry macro to tidy up<commit_after>/*!
Simple persistent generic HashMap/Key-value store.

Basic usage:

```
extern crate kv_cab;

use kv_cab::{ KV, Value };

fn main() {
    let mut test_store = KV::<String, Value>::new("./db.cab").unwrap();

    let _ = test_store.insert("key".to_string(), Value::String("value".to_string()));
    println!("{:?}", test_store.get("key".to_string()));
    let _ = test_store.remove("key".to_string());
}
```

Usage with user defined Key and Value types:

```
extern crate kv_cab;
extern crate rustc_serialize;

use kv_cab::KV;

#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)]
enum MyKey {
    String(String),
    Int(i32),
}

#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
enum MyValue {
    String(String),
    Int(i32),
}

fn main() {
    let mut test_store = KV::<MyKey, MyValue>::new("./db.cab").unwrap();

    let _ = test_store.insert(MyKey::Int(1i32), MyValue::String("value".to_string()));
    println!("{:?}", test_store.get(MyKey::Int(1i32)));
    let _ = test_store.remove(MyKey::Int(1i32));
}
```

*/

extern crate bincode;
extern crate rustc_serialize;
#[macro_use]
extern crate log;

use std::collections::HashMap;
use std::fs;
use std::fs::{ File, OpenOptions };
use std::io::prelude::*;
use std::io::SeekFrom;
use std::hash::Hash;

use bincode::SizeLimit;
use bincode::rustc_serialize::{ encode, decode };

use rustc_serialize::{ Encodable, Decodable };

/// The maximum number of retries the cab will make
const MAX_RETRIES:i32 = 10;

/// Definition of a macro for retrying an operation
macro_rules! retry {
    ($i:ident, $b:block) => (
        for $i in 0..MAX_RETRIES {
            if $i != 0 {
                std::thread::park_timeout(std::time::Duration::new(0u64, 1000u32));
            }

            $b
        }
    )
}

/// Macro for what to do on last retry
macro_rules! last_retry {
    ($i:ident, $b:stmt) => (
        if $i >= MAX_RETRIES - 1 {
            $b;
        }
    )
}

/// A default value type to use with KV
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
pub enum Value {
    String(String),
    Int(i32),
    Float(f32),
    Map(HashMap<String, Value>),
}

/// Type alias for results from KV
type KVResult = Result<bool, KVError>;

/// Errors that KV might have
#[derive(Debug, PartialEq)]
pub enum KVError {
    /// Cab did not contain anything on disk
    CabEmpty,
    /// Could not decode the cab from disk
    CouldntDecode,
    /// Cab was either not readable from disk or doesn't exist
    DoesntExistOrNotReadable,
    /// Couldn't encode the hashmap for writing to disk
    CouldntEncode,
    /// Could not write to the cab on disk
    CouldntWrite,
    /// Could not set the access permissions for the cab
    CouldntSetPermissions,
    /// Could get the permissions that the cab has on disk
    CoudlntGetPermissions,
    /// Failed to read the cab from disk
    FailedToRead,
    /// Could not sync the metadata with the disk
    CouldntSyncMetadata,
    /// Could not flush the data to disk
    CouldntFlush,
    /// Could not open the file from disk
    CouldntOpen,
    /// Could not load the file from disk
    CouldntLoad,
}

/// The type that represents the key-value store
pub struct KV<K,V> {
    cab: HashMap<K, V>,
    file: File,
}

impl<K: Clone + Encodable + Decodable + Eq + Hash, V: Clone + Encodable + Decodable> KV<K,V> {
    /// Creates a new instance of the KV store
    pub fn new(p:&'static str) -> Result<KV<K,V>, KVError> {
        // create the KV instance
        let mut store = KV {
            cab: HashMap::new(),
            file: KV::<K, V>::retry_get_file(p).unwrap(),
        };

        // lock the cab for writes
        if let Err(e) = store.lock_cab(true) {
            return Err(e);
        }

        match store.load_from_persist() {
            Ok(f) => trace!("{}", f),
            Err(e) => {
                if e != KVError::CabEmpty {
                    return Err(e);
                }
                warn!("{:?}", e);
            },
        };

        Ok(store)
    }

    /// Sets file permission for a path to not be readonly
    fn set_path_permission(p: &'static str) -> KVResult {
        match fs::metadata(p) {
            Ok(f) => {
                let mut perms = f.permissions();
                perms.set_readonly(false);

                match fs::set_permissions(p, perms) {
                    Ok(_) => return Ok(true),
                    Err(e) => {
                        error!("{}", e);
                        return Err(KVError::CouldntSetPermissions);
                    }
                }
            },
            Err(e) => {
                if e.kind() != std::io::ErrorKind::NotFound {
                    error!("{}", e);
                    return Err(KVError::CoudlntGetPermissions);
                }
                return Ok(true);
            },
        }
    }

    /// Retry to get a reference to the cab file at path p and create if doesn't exist
    fn retry_get_file(p:&'static str) -> Result<File, KVError> {
        retry!(i, {
            match OpenOptions::new().read(true).write(true).create(true).open(p) {
                Ok(f) => { return Ok(f); },
                Err(e) => {
                    error!("{}", e);
                    last_retry!(i, return Err(KVError::CouldntOpen));

                    if let Err(e) = KV::<K, V>::set_path_permission(p) {
                        last_retry!(i, return Err(e));
                    }
                    continue;
                }
            };

        });

        Err(KVError::CouldntOpen)
    }

    /// Inserta a key, value pair into the key-value store
    pub fn insert(&mut self, key: K, value: V) -> KVResult {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // insert into the HashMap
        self.cab.insert(key, value);
        // persist
        self.write_to_persist()
    }

    /// Get the value from a key
    pub fn get(&mut self, key: K) -> Result<Option<V>, KVError> {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // get the value from the cab
        match self.cab.get(&key) {
            Some(v) => Ok(Some((*v).clone())),
            None => Ok(None),
        }
    }

    /// Removes a key and associated value from the key-value Store
    pub fn remove(&mut self, key: K) -> KVResult {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // remove from the HashMap
        self.cab.remove(&key);
        // persist
        self.write_to_persist()
    }

    /// get all the keys contained in the KV Store
    pub fn keys(&mut self) -> Result<Vec<K>, KVError> {
        // make sure mem version up to date
        if let Err(e) = self.load_from_persist() {
            return Err(e);
        }
        // create a vec from the cabs keys
        Ok(self.cab.keys().map(|k| k.clone()).collect())
    }

    /// Locks/unlocks cab for writing purposes
    fn lock_cab(&mut self, readonly:bool) -> KVResult {
        retry!(i, {
            // sync the metadata before doing anything
            if let Err(e) = self.file.sync_all() {
                error!("{}", e);
                return Err(KVError::CouldntSyncMetadata);
            }

            // set not readonly while writing
            let mut perms = match self.file.metadata() {
                Ok(f) => f.permissions(),
                Err(e) => {
                    error!("{}", e);
                    last_retry!(i, return Err(KVError::CoudlntGetPermissions));
                    continue;
                },
            };
            perms.set_readonly(readonly);

            match self.file.set_permissions(perms) {
                Ok(_) => {
                    break;
                },
                Err(e) => {
                    error!("{}", e);
                    last_retry!(i, return Err(KVError::CouldntSetPermissions));
                    continue;
                },
            }
        });

        Ok(true)
    }

    /// Waits for the cab to become free
    fn wait_for_free(&mut self, lock:bool) -> KVResult {
        loop {
            // check if the cab is being written to
            if let Err(e) = self.file.sync_all() {
                error!("{}", e);
                return Err(KVError::CouldntSyncMetadata);
            }
            let metadata = match self.file.metadata() {
                Ok(m) => m,
                Err(_) => return Err(KVError::DoesntExistOrNotReadable),
            };

            if metadata.permissions().readonly() {
                if lock {
                    if let Err(e) = self.lock_cab(false) {
                        return Err(e);
                    }
                }
                // make sure it reads from start
                if let Err(_) = self.file.seek(SeekFrom::Start(0)) {
                    return Err(KVError::FailedToRead);
                }
                break;
            }
        }

        Ok(true)
    }

    /// Writes the key-value Store to file
    fn write_to_persist(&mut self) -> KVResult {
        // encode the cab as a u8 vec
        let byte_vec: Vec<u8> = match encode(&mut self.cab, SizeLimit::Infinite) {
            Ok(bv) => bv,
            Err(e) => {
                error!("encode: {}", e);
                return Err(KVError::CouldntEncode);
            },
        };

        // attempt to write to the cab
        retry!(i, {
            // check that can write to the cab
            if !self.wait_for_free(true).is_ok() {
                return Err(KVError::DoesntExistOrNotReadable);
            }

            // write the bytes to it
            match self.file.write_all(byte_vec.as_slice()) {
                Ok(_) => (),
                Err(e) => {
                    error!("file.write_all/retry: {}", e);
                    last_retry!(i, return Err(KVError::CouldntWrite));
                    continue;
                },
            }

            // flush to disk
            if let Err(e) = self.file.flush() {
                error!("{}", e);
                return Err(KVError::CouldntFlush);
            }
            if let Err(e) = self.lock_cab(true) {
                error!("{:?}", e);
                last_retry!(i, return Err(e));
            }
            return Ok(true);
        });

        Err(KVError::CouldntWrite)
    }

    /// Loads key-value store from file
    fn load_from_persist(&mut self) -> KVResult {
        retry!(i, {
            // byte vec to read into
            let mut byte_vec = Vec::new();

            // wait/lock the cab and read the bytes
            if !self.wait_for_free(false).is_ok() {
                return Err(KVError::DoesntExistOrNotReadable);
            }

            // read the file into the buffer
            match self.file.read_to_end(&mut byte_vec) {
                Ok(count) => {
                    if count == 0 {
                        // don't attempt to decode as empty
                        return Ok(true);
                    }
                },
                Err(e) => {
                    error!("{}", e);
                    return Err(KVError::FailedToRead);
                },
            }

            // decode u8 vec back into HashMap
            match decode(byte_vec.as_slice()) {
                Ok(f) => {
                    // assign read HashMap back to self
                    self.cab = f;
                    return Ok(true);
                },
                Err(e) => {
                    error!("{}", e);
                    last_retry!(i, return Err(KVError::CouldntDecode));
                    continue;
                },
            };
        });

        Err(KVError::CouldntLoad)
    }
}
<|endoftext|>


<commit_before>//! # libgoblin
//!
//! ![say the right
//! words](https://s-media-cache-ak0.pinimg.com/736x/1b/6a/aa/1b6aaa2bae005e2fed84b1a7c32ecb1b.jpg)
//!
//! `libgoblin` is a cross-platform trifecta of binary parsing and loading fun.  It supports:
//!
//! * An ELF32/64 parser, and raw C structs
//! * A 32/64-bit, zero-copy, endian aware, Mach-o parser, and raw C structs
//! * A PE32/PE32+ (64-bit) parser, and raw C structs
//! * A Unix archive parser and loader
//!
//! Goblin _should_ require at least `rustc` 1.16, but is developed on stable.
//!
//! Goblin primarily supports the following important use cases:
//!
//! 1. Core, std-free `#[repr(C)]` structs, tiny compile time, 32/64 (or both) at your leisure
//!
//! 2. Type punning. Define a function once on a type, but have it work on 32 or 64-bit variants - without really changing anything, and no macros! See `examples/automagic.rs` for a basic example.
//!
//! 3. `std` mode. This throws in read and write impls via `Pread` and `Pwrite`, reading from file, convenience allocations, extra methods, etc. This is for clients who can allocate and want to read binaries off disk.
//!
//! 4. `Endian_fd`. A truly terrible name :laughing: this is for binary analysis like in [panopticon](https://github.com/das-labor/panopticon) which needs to read binaries of foreign endianness, _or_ as a basis for constructing cross platform foreign architecture binutils, e.g. [cargo-sym](https://github.com/m4b/cargo-sym) and [bingrep](https://github.com/m4b/bingrep) are simple examples of this, but the sky is the limit.
//!
//! # Example
//!
//! ```rust
//! use goblin::{error, Object};
//! use std::path::Path;
//! use std::env;
//! use std::fs::File;
//! use std::io::Read;
//!
//! fn run () -> error::Result<()> {
//!     for (i, arg) in env::args().enumerate() {
//!         if i == 1 {
//!             let path = Path::new(arg.as_str());
//!             let mut fd = File::open(path)?;
//!             let mut buffer = Vec::new();
//!             fd.read_to_end(&mut buffer)?;
//!             match goblin::parse(&buffer)? {
//!                 Object::Elf(elf) => {
//!                     println!("elf: {:#?}", &elf);
//!                 },
//!                 Object::PE(pe) => {
//!                     println!("pe: {:#?}", &pe);
//!                 },
//!                 Object::Mach(mach) => {
//!                     println!("mach: {:#?}", &mach);
//!                 },
//!                 Object::Archive(archive) => {
//!                     println!("archive: {:#?}", &archive);
//!                 },
//!                 Object::Unknown(magic) => { println!("unknown magic: {:#x}", magic) }
//!             }
//!         }
//!     }
//!     Ok(())
//! }
//! ```
//!
//! # Feature Usage
//!
//! `libgoblin` is engineered to be tailored towards very different use-case scenarios, for example:
//!
//! * a no-std mode; just simply set default features to false
//! * a endian aware parsing and reading
//! * for binary loaders which don't require this, simply use `elf32` and `elf64` (and `std` of course)
//!
//! For example, if you are writing a 64-bit kernel, or just want a barebones C-like
//! header interface which defines the structures, just select `elf64`, `--cfg
//! feature=\"elf64\"`, which will compile without `std`.
//!
//! Similarly, if you want to use host endianness loading via the various `from_fd` methods, `--cfg
//! feature=\"std\"`, which will not use the `byteorder` extern crate, and read the bytes
//! from disk in the endianness of the host machine.
//!
//! If you want endian aware reading, and you don't use `default`, then you need to opt in as normal
//! via `endian_fd`

#![cfg_attr(not(feature = "std"), no_std)]

extern crate plain;
extern crate scroll;

#[cfg(feature = "std")]
#[macro_use] extern crate log;

#[cfg(feature = "std")]
extern crate core;

#[cfg(feature = "std")]
#[macro_use] extern crate scroll_derive;

#[cfg(feature = "std")]
pub mod error;

pub mod strtab;

/////////////////////////
// Misc/Helper Modules
/////////////////////////

macro_rules! if_std {
    ($($i:item)*) => ($(
        #[cfg(feature = "std")]
        $i
    )*)
}

/// Binary container size information and byte-order context
pub mod container {
    use scroll;
    pub use scroll::Endian as Endian;

    #[derive(Debug, Copy, Clone, PartialEq)]
    /// The size of a binary container
    pub enum Container {
        Little,
        Big,
    }

    impl Container {
        /// Is this a 64-bit container or not?
        pub fn is_big(&self) -> bool {
            *self == Container::Big
        }
    }

    #[cfg(not(target_pointer_width = "64"))]
    /// The default binary container size - either `Big` or `Little`, depending on whether the host machine's pointer size is 64 or not
    pub const CONTAINER: Container =  Container::Little;

    #[cfg(target_pointer_width = "64")]
    /// The default binary container size - either `Big` or `Little`, depending on whether the host machine's pointer size is 64 or not
    pub const CONTAINER: Container =  Container::Big;

    impl Default for Container {
        #[inline]
        fn default() -> Self {
            CONTAINER
        }
    }

    #[derive(Debug, Copy, Clone, PartialEq)]
    /// A binary parsing context, including the container size and underlying byte endianness
    pub struct Ctx {
        pub container: Container,
        pub le: scroll::Endian,
    }

    impl Ctx {
        /// Whether this binary container context is "big" or not
        pub fn is_big(&self) -> bool {
            self.container.is_big()
        }
        /// Whether this binary container context is little endian or not
        pub fn is_little_endian(&self) -> bool {
            self.le.is_little()
        }
        /// Create a new binary container context
        pub fn new (container: Container, le: scroll::Endian) -> Self {
            Ctx { container: container, le: le }
        }
        /// Return a dubious pointer/address byte size for the container
        pub fn size(&self) -> usize {
            match self.container {
                // TODO: require pointer size initialization/setting or default to container size with these values, e.g., avr pointer width will be smaller iirc
                Container::Little => 4,
                Container::Big    => 8,
            }
        }
    }

    impl From<Container> for Ctx {
        fn from(container: Container) -> Self {
            Ctx { container: container, le: scroll::Endian::default() }
        }
    }

    impl From<scroll::Endian> for Ctx {
        fn from(le: scroll::Endian) -> Self {
            Ctx { container: CONTAINER, le: le }
        }
    }

    impl Default for Ctx {
        #[inline]
        fn default() -> Self {
            Ctx { container: Container::default(), le: scroll::Endian::default() }
        }
    }
}

if_std! {

    #[derive(Debug, Default)]
    /// Information obtained from a peek `Hint`
    pub struct HintData {
        pub is_lsb: bool,
        pub is_64: Option<bool>,
    }

    #[derive(Debug)]
    /// A hint at the underlying binary format for 16 bytes of arbitrary data
    pub enum Hint {
        Elf(HintData),
        Mach(HintData),
        MachFat(usize),
        PE,
        Archive,
        Unknown(u64),
    }

    /// Peeks at `bytes`, and returns a `Hint`
    #[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
    pub fn peek_bytes(bytes: &[u8; 16]) -> error::Result<Hint> {
        use scroll::{Pread, BE};
        if &bytes[0..elf::header::SELFMAG] == elf::header::ELFMAG {
            let class = bytes[elf::header::EI_CLASS];
            let is_lsb = bytes[elf::header::EI_DATA] == elf::header::ELFDATA2LSB;
            let is_64 =
                if class == elf::header::ELFCLASS64 {
                    Some (true)
                } else if class == elf::header::ELFCLASS32 {
                    Some (false)
                } else { None };

            Ok(Hint::Elf(HintData { is_lsb: is_lsb, is_64: is_64 }))
        } else if &bytes[0..archive::SIZEOF_MAGIC] == archive::MAGIC {
            Ok(Hint::Archive)
        } else if (&bytes[0..2]).pread::<u16>(0)? == pe::header::DOS_MAGIC {
            Ok(Hint::PE)
        } else {
            use mach::{fat, header};
            let magic = mach::peek(bytes, 0)?;
            match magic {
                fat::FAT_MAGIC => {
                    // should probably verify this is always Big Endian...
                    let narchitectures = bytes.pread_with::<u32>(4, BE)? as usize;
                    Ok(Hint::MachFat(narchitectures))
                },
                header::MH_CIGAM_64 | header::MH_CIGAM | header::MH_MAGIC_64 | header::MH_MAGIC => {
                    let is_lsb = magic == header::MH_CIGAM || magic == header::MH_CIGAM_64;
                    let is_64 = magic == header::MH_MAGIC_64 || magic == header::MH_CIGAM_64;
                    Ok(Hint::Mach(HintData { is_lsb: is_lsb, is_64: Some(is_64) }))
                },
                // its something else
                _ => Ok(Hint::Unknown(bytes.pread::<u64>(0)?))
            }
        }
    }

    /// Peeks at the underlying Read object. Requires the underlying bytes to have at least 16 byte length. Resets the seek to `Start` after reading.
    #[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
    pub fn peek<R: ::std::io::Read + ::std::io::Seek>(fd: &mut R) -> error::Result<Hint> {
        use std::io::SeekFrom;
        let mut bytes = [0u8; 16];
        fd.seek(SeekFrom::Start(0))?;
        fd.read_exact(&mut bytes)?;
        fd.seek(SeekFrom::Start(0))?;
        peek_bytes(&bytes)
    }
}

#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
#[derive(Debug)]
/// A parseable object that goblin understands
pub enum Object<'a> {
    /// An ELF32/ELF64!
    Elf(elf::Elf<'a>),
    /// A PE32/PE32+!
    PE(pe::PE<'a>),
    /// A 32/64-bit Mach-o binary _OR_ it is a multi-architecture binary container!
    Mach(mach::Mach<'a>),
    /// A Unix archive
    Archive(archive::Archive<'a>),
    /// None of the above, with the given magic value
    Unknown(u64),
}

#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
impl<'a> Object<'a> {
    /// Tries to parse an `Object` from `bytes`
    pub fn parse(bytes: &[u8]) -> error::Result<Object> {
        use std::io::Cursor;
        match peek(&mut Cursor::new(&bytes))? {
            Hint::Elf(_) => Ok(Object::Elf(elf::Elf::parse(bytes)?)),
            Hint::Mach(_) | Hint::MachFat(_) => Ok(Object::Mach(mach::Mach::parse(bytes)?)),
            Hint::Archive => Ok(Object::Archive(archive::Archive::parse(bytes)?)),
            Hint::PE => Ok(Object::PE(pe::PE::parse(bytes)?)),
            Hint::Unknown(magic) => Ok(Object::Unknown(magic))
        }
    }
}

#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
/// Parses an `Object` from `bytes`.
#[deprecated(since = "0.0.11", note = "use `goblin::Object::parse` instead; this will be removed in 0.0.12")]
pub fn parse(bytes: &[u8]) -> error::Result<Object> {
    Object::parse(bytes)
}

/////////////////////////
// Binary Modules
/////////////////////////

#[cfg(any(feature = "elf64", feature = "elf32"))]
#[macro_use]
pub mod elf;

#[cfg(feature = "elf32")]
/// The ELF 32-bit struct definitions and associated values, re-exported for easy "type-punning"
pub mod elf32 {
    pub use elf::header::header32 as header;
    pub use elf::program_header::program_header32 as program_header;
    pub use elf::section_header::section_header32 as section_header;
    pub use elf::dyn::dyn32 as dyn;
    pub use elf::sym::sym32 as sym;
    pub use elf::reloc::reloc32 as reloc;
    pub use elf::note::Nhdr32 as Note;

    pub mod gnu_hash {
        elf_gnu_hash_impl!(u32);
    }
}

#[cfg(feature = "elf64")]
/// The ELF 64-bit struct definitions and associated values, re-exported for easy "type-punning"
pub mod elf64 {
    pub use elf::header::header64 as header;
    pub use elf::program_header::program_header64 as program_header;
    pub use elf::section_header::section_header64 as section_header;
    pub use elf::dyn::dyn64 as dyn;
    pub use elf::sym::sym64 as sym;
    pub use elf::reloc::reloc64 as reloc;
    pub use elf::note::Nhdr64 as Note;

    pub mod gnu_hash {
        elf_gnu_hash_impl!(u64);
    }
}

#[cfg(all(feature = "mach32", feature = "mach64", feature = "endian_fd"))]
pub mod mach;

#[cfg(all(feature = "pe32", feature = "pe64", feature = "endian_fd"))]
pub mod pe;

#[cfg(all(feature = "archive", feature = "std"))]
pub mod archive;
<commit_msg>Remove the use of `goblin::parse` in `lib.rs` example<commit_after>//! # libgoblin
//!
//! ![say the right
//! words](https://s-media-cache-ak0.pinimg.com/736x/1b/6a/aa/1b6aaa2bae005e2fed84b1a7c32ecb1b.jpg)
//!
//! `libgoblin` is a cross-platform trifecta of binary parsing and loading fun.  It supports:
//!
//! * An ELF32/64 parser, and raw C structs
//! * A 32/64-bit, zero-copy, endian aware, Mach-o parser, and raw C structs
//! * A PE32/PE32+ (64-bit) parser, and raw C structs
//! * A Unix archive parser and loader
//!
//! Goblin _should_ require at least `rustc` 1.16, but is developed on stable.
//!
//! Goblin primarily supports the following important use cases:
//!
//! 1. Core, std-free `#[repr(C)]` structs, tiny compile time, 32/64 (or both) at your leisure
//!
//! 2. Type punning. Define a function once on a type, but have it work on 32 or 64-bit variants - without really changing anything, and no macros! See `examples/automagic.rs` for a basic example.
//!
//! 3. `std` mode. This throws in read and write impls via `Pread` and `Pwrite`, reading from file, convenience allocations, extra methods, etc. This is for clients who can allocate and want to read binaries off disk.
//!
//! 4. `Endian_fd`. A truly terrible name :laughing: this is for binary analysis like in [panopticon](https://github.com/das-labor/panopticon) which needs to read binaries of foreign endianness, _or_ as a basis for constructing cross platform foreign architecture binutils, e.g. [cargo-sym](https://github.com/m4b/cargo-sym) and [bingrep](https://github.com/m4b/bingrep) are simple examples of this, but the sky is the limit.
//!
//! # Example
//!
//! ```rust
//! use goblin::{error, Object};
//! use std::path::Path;
//! use std::env;
//! use std::fs::File;
//! use std::io::Read;
//!
//! fn run () -> error::Result<()> {
//!     for (i, arg) in env::args().enumerate() {
//!         if i == 1 {
//!             let path = Path::new(arg.as_str());
//!             let mut fd = File::open(path)?;
//!             let mut buffer = Vec::new();
//!             fd.read_to_end(&mut buffer)?;
//!             match Object::parse(&buffer)? {
//!                 Object::Elf(elf) => {
//!                     println!("elf: {:#?}", &elf);
//!                 },
//!                 Object::PE(pe) => {
//!                     println!("pe: {:#?}", &pe);
//!                 },
//!                 Object::Mach(mach) => {
//!                     println!("mach: {:#?}", &mach);
//!                 },
//!                 Object::Archive(archive) => {
//!                     println!("archive: {:#?}", &archive);
//!                 },
//!                 Object::Unknown(magic) => { println!("unknown magic: {:#x}", magic) }
//!             }
//!         }
//!     }
//!     Ok(())
//! }
//! ```
//!
//! # Feature Usage
//!
//! `libgoblin` is engineered to be tailored towards very different use-case scenarios, for example:
//!
//! * a no-std mode; just simply set default features to false
//! * a endian aware parsing and reading
//! * for binary loaders which don't require this, simply use `elf32` and `elf64` (and `std` of course)
//!
//! For example, if you are writing a 64-bit kernel, or just want a barebones C-like
//! header interface which defines the structures, just select `elf64`, `--cfg
//! feature=\"elf64\"`, which will compile without `std`.
//!
//! Similarly, if you want to use host endianness loading via the various `from_fd` methods, `--cfg
//! feature=\"std\"`, which will not use the `byteorder` extern crate, and read the bytes
//! from disk in the endianness of the host machine.
//!
//! If you want endian aware reading, and you don't use `default`, then you need to opt in as normal
//! via `endian_fd`

#![cfg_attr(not(feature = "std"), no_std)]

extern crate plain;
extern crate scroll;

#[cfg(feature = "std")]
#[macro_use] extern crate log;

#[cfg(feature = "std")]
extern crate core;

#[cfg(feature = "std")]
#[macro_use] extern crate scroll_derive;

#[cfg(feature = "std")]
pub mod error;

pub mod strtab;

/////////////////////////
// Misc/Helper Modules
/////////////////////////

macro_rules! if_std {
    ($($i:item)*) => ($(
        #[cfg(feature = "std")]
        $i
    )*)
}

/// Binary container size information and byte-order context
pub mod container {
    use scroll;
    pub use scroll::Endian as Endian;

    #[derive(Debug, Copy, Clone, PartialEq)]
    /// The size of a binary container
    pub enum Container {
        Little,
        Big,
    }

    impl Container {
        /// Is this a 64-bit container or not?
        pub fn is_big(&self) -> bool {
            *self == Container::Big
        }
    }

    #[cfg(not(target_pointer_width = "64"))]
    /// The default binary container size - either `Big` or `Little`, depending on whether the host machine's pointer size is 64 or not
    pub const CONTAINER: Container =  Container::Little;

    #[cfg(target_pointer_width = "64")]
    /// The default binary container size - either `Big` or `Little`, depending on whether the host machine's pointer size is 64 or not
    pub const CONTAINER: Container =  Container::Big;

    impl Default for Container {
        #[inline]
        fn default() -> Self {
            CONTAINER
        }
    }

    #[derive(Debug, Copy, Clone, PartialEq)]
    /// A binary parsing context, including the container size and underlying byte endianness
    pub struct Ctx {
        pub container: Container,
        pub le: scroll::Endian,
    }

    impl Ctx {
        /// Whether this binary container context is "big" or not
        pub fn is_big(&self) -> bool {
            self.container.is_big()
        }
        /// Whether this binary container context is little endian or not
        pub fn is_little_endian(&self) -> bool {
            self.le.is_little()
        }
        /// Create a new binary container context
        pub fn new (container: Container, le: scroll::Endian) -> Self {
            Ctx { container: container, le: le }
        }
        /// Return a dubious pointer/address byte size for the container
        pub fn size(&self) -> usize {
            match self.container {
                // TODO: require pointer size initialization/setting or default to container size with these values, e.g., avr pointer width will be smaller iirc
                Container::Little => 4,
                Container::Big    => 8,
            }
        }
    }

    impl From<Container> for Ctx {
        fn from(container: Container) -> Self {
            Ctx { container: container, le: scroll::Endian::default() }
        }
    }

    impl From<scroll::Endian> for Ctx {
        fn from(le: scroll::Endian) -> Self {
            Ctx { container: CONTAINER, le: le }
        }
    }

    impl Default for Ctx {
        #[inline]
        fn default() -> Self {
            Ctx { container: Container::default(), le: scroll::Endian::default() }
        }
    }
}

if_std! {

    #[derive(Debug, Default)]
    /// Information obtained from a peek `Hint`
    pub struct HintData {
        pub is_lsb: bool,
        pub is_64: Option<bool>,
    }

    #[derive(Debug)]
    /// A hint at the underlying binary format for 16 bytes of arbitrary data
    pub enum Hint {
        Elf(HintData),
        Mach(HintData),
        MachFat(usize),
        PE,
        Archive,
        Unknown(u64),
    }

    /// Peeks at `bytes`, and returns a `Hint`
    #[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
    pub fn peek_bytes(bytes: &[u8; 16]) -> error::Result<Hint> {
        use scroll::{Pread, BE};
        if &bytes[0..elf::header::SELFMAG] == elf::header::ELFMAG {
            let class = bytes[elf::header::EI_CLASS];
            let is_lsb = bytes[elf::header::EI_DATA] == elf::header::ELFDATA2LSB;
            let is_64 =
                if class == elf::header::ELFCLASS64 {
                    Some (true)
                } else if class == elf::header::ELFCLASS32 {
                    Some (false)
                } else { None };

            Ok(Hint::Elf(HintData { is_lsb: is_lsb, is_64: is_64 }))
        } else if &bytes[0..archive::SIZEOF_MAGIC] == archive::MAGIC {
            Ok(Hint::Archive)
        } else if (&bytes[0..2]).pread::<u16>(0)? == pe::header::DOS_MAGIC {
            Ok(Hint::PE)
        } else {
            use mach::{fat, header};
            let magic = mach::peek(bytes, 0)?;
            match magic {
                fat::FAT_MAGIC => {
                    // should probably verify this is always Big Endian...
                    let narchitectures = bytes.pread_with::<u32>(4, BE)? as usize;
                    Ok(Hint::MachFat(narchitectures))
                },
                header::MH_CIGAM_64 | header::MH_CIGAM | header::MH_MAGIC_64 | header::MH_MAGIC => {
                    let is_lsb = magic == header::MH_CIGAM || magic == header::MH_CIGAM_64;
                    let is_64 = magic == header::MH_MAGIC_64 || magic == header::MH_CIGAM_64;
                    Ok(Hint::Mach(HintData { is_lsb: is_lsb, is_64: Some(is_64) }))
                },
                // its something else
                _ => Ok(Hint::Unknown(bytes.pread::<u64>(0)?))
            }
        }
    }

    /// Peeks at the underlying Read object. Requires the underlying bytes to have at least 16 byte length. Resets the seek to `Start` after reading.
    #[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
    pub fn peek<R: ::std::io::Read + ::std::io::Seek>(fd: &mut R) -> error::Result<Hint> {
        use std::io::SeekFrom;
        let mut bytes = [0u8; 16];
        fd.seek(SeekFrom::Start(0))?;
        fd.read_exact(&mut bytes)?;
        fd.seek(SeekFrom::Start(0))?;
        peek_bytes(&bytes)
    }
}

#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
#[derive(Debug)]
/// A parseable object that goblin understands
pub enum Object<'a> {
    /// An ELF32/ELF64!
    Elf(elf::Elf<'a>),
    /// A PE32/PE32+!
    PE(pe::PE<'a>),
    /// A 32/64-bit Mach-o binary _OR_ it is a multi-architecture binary container!
    Mach(mach::Mach<'a>),
    /// A Unix archive
    Archive(archive::Archive<'a>),
    /// None of the above, with the given magic value
    Unknown(u64),
}

#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
impl<'a> Object<'a> {
    /// Tries to parse an `Object` from `bytes`
    pub fn parse(bytes: &[u8]) -> error::Result<Object> {
        use std::io::Cursor;
        match peek(&mut Cursor::new(&bytes))? {
            Hint::Elf(_) => Ok(Object::Elf(elf::Elf::parse(bytes)?)),
            Hint::Mach(_) | Hint::MachFat(_) => Ok(Object::Mach(mach::Mach::parse(bytes)?)),
            Hint::Archive => Ok(Object::Archive(archive::Archive::parse(bytes)?)),
            Hint::PE => Ok(Object::PE(pe::PE::parse(bytes)?)),
            Hint::Unknown(magic) => Ok(Object::Unknown(magic))
        }
    }
}

#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
/// Parses an `Object` from `bytes`.
#[deprecated(since = "0.0.11", note = "use `goblin::Object::parse` instead; this will be removed in 0.0.12")]
pub fn parse(bytes: &[u8]) -> error::Result<Object> {
    Object::parse(bytes)
}

/////////////////////////
// Binary Modules
/////////////////////////

#[cfg(any(feature = "elf64", feature = "elf32"))]
#[macro_use]
pub mod elf;

#[cfg(feature = "elf32")]
/// The ELF 32-bit struct definitions and associated values, re-exported for easy "type-punning"
pub mod elf32 {
    pub use elf::header::header32 as header;
    pub use elf::program_header::program_header32 as program_header;
    pub use elf::section_header::section_header32 as section_header;
    pub use elf::dyn::dyn32 as dyn;
    pub use elf::sym::sym32 as sym;
    pub use elf::reloc::reloc32 as reloc;
    pub use elf::note::Nhdr32 as Note;

    pub mod gnu_hash {
        elf_gnu_hash_impl!(u32);
    }
}

#[cfg(feature = "elf64")]
/// The ELF 64-bit struct definitions and associated values, re-exported for easy "type-punning"
pub mod elf64 {
    pub use elf::header::header64 as header;
    pub use elf::program_header::program_header64 as program_header;
    pub use elf::section_header::section_header64 as section_header;
    pub use elf::dyn::dyn64 as dyn;
    pub use elf::sym::sym64 as sym;
    pub use elf::reloc::reloc64 as reloc;
    pub use elf::note::Nhdr64 as Note;

    pub mod gnu_hash {
        elf_gnu_hash_impl!(u64);
    }
}

#[cfg(all(feature = "mach32", feature = "mach64", feature = "endian_fd"))]
pub mod mach;

#[cfg(all(feature = "pe32", feature = "pe64", feature = "endian_fd"))]
pub mod pe;

#[cfg(all(feature = "archive", feature = "std"))]
pub mod archive;
<|endoftext|>