<commit_before>use std::{
    collections::HashMap,
    ops::Deref,
    sync::{
        atomic::{
            AtomicUsize,
            Ordering::{Acquire, Relaxed, Release},
        },
        Arc, Mutex, RwLock,
    },
};

use pagecache::PagePtr;

use super::*;

/// The `sled` embedded database!
pub struct Db {
    config: Config,
    pages: Arc<PageCache<BLinkMaterializer, Frag, Recovery>>,
    idgen: Arc<AtomicUsize>,
    idgen_persists: Arc<AtomicUsize>,
    idgen_persist_mu: Arc<Mutex<()>>,
    tenants: Arc<RwLock<HashMap<Vec<u8>, Arc<Tree>>>>,
    default: Arc<Tree>,
    transactions: Arc<Tree>,
    was_recovered: bool,
}

#[cfg(feature = "event_log")]
impl Drop for Db {
    fn drop(&mut self) {
        let guard = pin();

        self.config.event_log.meta_before_restart(
            self.meta(&guard)
                .expect("should get meta under test")
                .clone(),
        );
    }
}

// We can assume we always have a default Tree.
impl Deref for Db {
    type Target = Tree;

    fn deref(&self) -> &Tree {
        let tree_arc: &Arc<Tree> = &self.default;
        let tree_ref: &Tree = &*tree_arc;
        let tp = tree_ref as *const Tree;
        unsafe {
            // lol
            &*tp
        }
    }
}

impl Db {
    /// Load existing or create a new `Db` with a default configuration.
    pub fn start_default<P: AsRef<std::path::Path>>(
        path: P,
    ) -> Result<Db, ()> {
        let config = ConfigBuilder::new().path(path).build();
        Self::start(config)
    }

    /// Load existing or create a new `Db`.
    pub fn start(config: Config) -> Result<Db, ()> {
        let _measure = Measure::new(&M.tree_start);

        #[cfg(any(test, feature = "check_snapshot_integrity"))]
        match config
            .verify_snapshot::<BLinkMaterializer, Frag, Recovery>()
        {
            Ok(_) => {}
            #[cfg(feature = "failpoints")]
            Err(Error::FailPoint) => {}
            other => panic!("failed to verify snapshot: {:?}", other),
        }

        let pages = PageCache::start(config.clone())?;

        let recovery: Recovery =
            pages.recovered_state().unwrap_or_default();
        let idgen_recovery =
            recovery.counter + (2 * config.idgen_persist_interval);
        let idgen_persists = recovery.counter
            / config.idgen_persist_interval
            * config.idgen_persist_interval;

        let guard = pin();

        let was_recovered: bool;

        if pages
            .get(META_PID, &guard)
            .map_err(|e| e.danger_cast())?
            .is_unallocated()
        {
            was_recovered = false;

            // set up meta
            let meta_id = pages.allocate(&guard)?;

            assert_eq!(
                meta_id,
                META_PID,
                "we expect the meta page to have pid {}, but it had pid {} instead",
                META_PID,
                meta_id,
            );

            let meta = Frag::Meta(Meta::default());
            pages
                .replace(meta_id, PagePtr::allocated(), meta, &guard)
                .map_err(|e| e.danger_cast())?;

            // set up idgen
            let counter_id = pages.allocate(&guard)?;

            assert_eq!(
                counter_id,
                COUNTER_PID,
                "we expect the counter to have pid {}, but it had pid {} instead",
                COUNTER_PID,
                counter_id,
            );

            let counter = Frag::Counter(0);
            pages
                .replace(
                    counter_id,
                    PagePtr::allocated(),
                    counter,
                    &guard,
                )
                .map_err(|e| e.danger_cast())?;
        } else {
            was_recovered = true;
        };

        let ret = Db {
            pages: Arc::new(pages),
            config,
            idgen: Arc::new(AtomicUsize::new(idgen_recovery)),
            idgen_persists: Arc::new(AtomicUsize::new(
                idgen_persists,
            )),
            idgen_persist_mu: Arc::new(Mutex::new(())),
            tenants: unsafe { std::mem::uninitialized() },
            default: unsafe { std::mem::uninitialized() },
            transactions: unsafe { std::mem::uninitialized() },
            was_recovered,
        };

        #[cfg(feature = "event_log")]
        ret.config.event_log.meta_after_restart(
            ret.meta(&guard)
                .expect("should be able to get meta under test")
                .clone(),
        );

        if meta::pid_for_name(&*ret.pages, DEFAULT_TREE_ID, &guard)?
            .is_none()
        {
            // set up initial tree

            ret.open_tree(DEFAULT_TREE_ID.to_vec(), &guard)?;
            assert_eq!(
                meta::pid_for_name(
                    &*ret.pages,
                    DEFAULT_TREE_ID,
                    &guard
                )?,
                Some(DEFAULT_TREE_PID)
            );
        }

        if meta::pid_for_name(&*ret.pages, TX_TREE_ID, &guard)?
            .is_none()
        {
            // set up initial tree

            ret.open_tree(TX_TREE_ID.to_vec(), &guard)?;
            assert_eq!(
                meta::pid_for_name(&*ret.pages, TX_TREE_ID, &guard)?,
                Some(TX_TREE_PID)
            );
        }

        let mut tenants = HashMap::new();

        for (id, _root) in
            meta::meta(&*ret.pages, &guard)?.tenants().into_iter()
        {
            let tree = Tree {
                subscriptions: Arc::new(Subscriptions::default()),
                config: ret.config.clone(),
                pages: ret.pages.clone(),
            };
            tenants.insert(id, Arc::new(tree));
        }

        let mut ret = ret;

        ret.tenants = Arc::new(RwLock::new(tenants));
        ret.default =
            ret.open_tree(DEFAULT_TREE_ID.to_vec(), &guard)?;
        ret.transactions =
            ret.open_tree(TX_TREE_ID.to_vec(), &guard)?;

        Ok(ret)
    }

    /// Generate a monotonic ID. Not guaranteed to be
    /// contiguous. Written to disk every `idgen_persist_interval`
    /// operations, followed by a blocking flush. During recovery, we
    /// take the last recovered generated ID and add 2x
    /// the `idgen_persist_interval` to it. While persisting, if the
    /// previous persisted counter wasn't synced to disk yet, we will do
    /// a blocking flush to fsync the latest counter, ensuring
    /// that we will never give out the same counter twice.
    pub fn generate_id(&self) -> Result<usize, ()> {
        let ret = self.idgen.fetch_add(1, Relaxed);

        let interval = self.config.idgen_persist_interval;
        let necessary_persists = ret / interval * interval;
        let mut persisted = self.idgen_persists.load(Acquire);

        while persisted < necessary_persists {
            let _mu = self.idgen_persist_mu.lock().unwrap();
            persisted = self.idgen_persists.load(Acquire);
            if persisted < necessary_persists {
                // it's our responsibility to persist up to our ID
                let guard = pin();
                let (current, key) = self
                    .pages
                    .get(COUNTER_PID, &guard)
                    .map_err(|e| e.danger_cast())?
                    .unwrap();

                if let Frag::Counter(current) = current {
                    assert_eq!(*current, persisted);
                } else {
                    panic!(
                        "counter pid contained non-Counter: {:?}",
                        current
                    );
                }

                let counter_frag = Frag::Counter(necessary_persists);

                let old = self
                    .idgen_persists
                    .swap(necessary_persists, Release);
                assert_eq!(old, persisted);

                self.pages
                    .replace(
                        COUNTER_PID,
                        key.clone(),
                        counter_frag,
                        &guard,
                    )
                    .map_err(|e| e.danger_cast())?;

                // during recovery we add 2x the interval. we only
                // need to block if the last one wasn't stable yet.
                if key.last_lsn() > self.pages.stable_lsn() {
                    self.pages.make_stable(key.last_lsn())?;
                }

                guard.flush();
            }
        }

        Ok(ret)
    }

    /// Open or create a new disk-backed Tree with its own keyspace,
    /// accessible from the `Db` via the provided identifier.
    pub fn open_tree<'a>(
        &self,
        name: Vec<u8>,
        guard: &'a Guard,
    ) -> Result<Arc<Tree>, ()> {
        let tenants = self.tenants.read().unwrap();
        if let Some(tree) = tenants.get(&name) {
            return Ok(tree.clone());
        }
        // drop reader lock
        drop(tenants);

        // set up empty leaf
        let leaf_id = self.pages.allocate(&guard)?;
        trace!("allocated pid {} for leaf in new_tree for namespace {:?}", leaf_id, name);

        let leaf = Frag::Base(Node {
            id: leaf_id,
            data: Data::Leaf(vec![]),
            next: None,
            lo: vec![],
            hi: vec![],
        });

        self.pages
            .replace(leaf_id, PagePtr::allocated(), leaf, &guard)
            .map_err(|e| e.danger_cast())?;

        // set up root index
        let root_id = self.pages.allocate(&guard)?;

        debug!(
            "allocated pid {} for root of new_tree {:?}",
            root_id, name
        );

        // vec![0] represents a prefix-encoded empty prefix
        let root_index_vec = vec![(vec![0], leaf_id)];

        let root = Frag::Base(Node {
            id: root_id,
            data: Data::Index(root_index_vec),
            next: None,
            lo: vec![],
            hi: vec![],
        });

        self.pages
            .replace(root_id, PagePtr::allocated(), root, &guard)
            .map_err(|e| e.danger_cast())?;

        meta::cas_root(
            &*self.pages,
            name.clone(),
            None,
            root_id,
            guard,
        )
        .map_err(|e| e.danger_cast())?;

        let mut tenants = self.tenants.write().unwrap();
        let tree = Arc::new(Tree {
            subscriptions: Arc::new(Subscriptions::default()),
            config: self.config.clone(),
            pages: self.pages.clone(),
        });
        tenants.insert(name, tree.clone());

        Ok(tree)
    }

    /// Returns `true` if the database was
    /// recovered from a previous process.
    /// Note that database state is only
    /// guaranteed to be present up to the
    /// last call to `flush`! Otherwise state
    /// is synced to disk periodically if the
    /// `sync_every_ms` configuration option
    /// is set to `Some(number_of_ms_between_syncs)`
    /// or if the IO buffer gets filled to
    /// capacity before being rotated.
    pub fn was_recovered(&self) -> bool {
        self.was_recovered
    }

    /// Record a set of pages as being involved in
    /// a transaction that is about to be written to
    /// one of the structures in the Db. Do this
    /// before performing transactions in case
    /// the system crashes before all changes
    /// can be written. During recovery,
    /// you can call `possibly_dirty_pages`
    /// to get the set of pages that may have
    /// transactional state written to them.
    pub fn mark_pending_tx(
        &self,
        pages: Vec<PageId>,
        txid: u64,
    ) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being aborted. This
    /// ensures that during recovery we know that
    /// this transaction is not the last one that
    /// should be recovered, because the process
    /// finished the transaction, but we may need
    /// to clean up its pending state still.
    pub fn abort_tx(&self, txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being completed. This
    /// ensures that during recovery we know that
    /// this transaction completed, and that we
    /// can safely remove its entry in the
    /// transaction table.
    pub fn commit_tx(&self, txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Remove entries associated with this transaction
    /// in the transaction table, because we have
    /// completed the transaction and finished
    /// applying or removing pending state, and do
    /// not need to know about it during crash recovery.
    pub fn cleanup_tx(&self, txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Call this during recovery to retrieve the set of
    /// pages that were involved in a transaction
    /// that was in a pending or aborted (but not
    /// yet fully cleaned up) state during process
    /// termination.
    pub fn possibly_dirty_pages(
        &self,
    ) -> Result<Vec<(u64, Vec<PageId>)>, ()> {
        unimplemented!()
    }
}
<commit_msg>Small cleanup<commit_after>use std::{
    collections::HashMap,
    ops::Deref,
    sync::{
        atomic::{
            AtomicUsize,
            Ordering::{Acquire, Relaxed, Release},
        },
        Arc, Mutex, RwLock,
    },
};

use pagecache::PagePtr;

use super::*;

/// The `sled` embedded database!
pub struct Db {
    config: Config,
    pages: Arc<PageCache<BLinkMaterializer, Frag, Recovery>>,
    idgen: Arc<AtomicUsize>,
    idgen_persists: Arc<AtomicUsize>,
    idgen_persist_mu: Arc<Mutex<()>>,
    tenants: Arc<RwLock<HashMap<Vec<u8>, Arc<Tree>>>>,
    default: Arc<Tree>,
    transactions: Arc<Tree>,
    was_recovered: bool,
}

#[cfg(feature = "event_log")]
impl Drop for Db {
    fn drop(&mut self) {
        let guard = pin();

        self.config.event_log.meta_before_restart(
            meta::meta(&*self.pages, &guard)
                .expect("should get meta under test")
                .clone(),
        );
    }
}

// We can assume we always have a default Tree.
impl Deref for Db {
    type Target = Tree;

    fn deref(&self) -> &Tree {
        let tree_arc: &Arc<Tree> = &self.default;
        let tree_ref: &Tree = &*tree_arc;
        let tp = tree_ref as *const Tree;
        unsafe {
            // lol
            &*tp
        }
    }
}

impl Db {
    /// Load existing or create a new `Db` with a default configuration.
    pub fn start_default<P: AsRef<std::path::Path>>(
        path: P,
    ) -> Result<Db, ()> {
        let config = ConfigBuilder::new().path(path).build();
        Self::start(config)
    }

    /// Load existing or create a new `Db`.
    pub fn start(config: Config) -> Result<Db, ()> {
        let _measure = Measure::new(&M.tree_start);

        #[cfg(any(test, feature = "check_snapshot_integrity"))]
        match config
            .verify_snapshot::<BLinkMaterializer, Frag, Recovery>()
        {
            Ok(_) => {}
            #[cfg(feature = "failpoints")]
            Err(Error::FailPoint) => {}
            other => panic!("failed to verify snapshot: {:?}", other),
        }

        let pages = PageCache::start(config.clone())?;

        let recovery: Recovery =
            pages.recovered_state().unwrap_or_default();
        let idgen_recovery =
            recovery.counter + (2 * config.idgen_persist_interval);
        let idgen_persists = recovery.counter
            / config.idgen_persist_interval
            * config.idgen_persist_interval;

        let guard = pin();

        let was_recovered: bool;

        if pages
            .get(META_PID, &guard)
            .map_err(|e| e.danger_cast())?
            .is_unallocated()
        {
            was_recovered = false;

            // set up meta
            let meta_id = pages.allocate(&guard)?;

            assert_eq!(
                meta_id,
                META_PID,
                "we expect the meta page to have pid {}, but it had pid {} instead",
                META_PID,
                meta_id,
            );

            let meta = Frag::Meta(Meta::default());
            pages
                .replace(meta_id, PagePtr::allocated(), meta, &guard)
                .map_err(|e| e.danger_cast())?;

            // set up idgen
            let counter_id = pages.allocate(&guard)?;

            assert_eq!(
                counter_id,
                COUNTER_PID,
                "we expect the counter to have pid {}, but it had pid {} instead",
                COUNTER_PID,
                counter_id,
            );

            let counter = Frag::Counter(0);
            pages
                .replace(
                    counter_id,
                    PagePtr::allocated(),
                    counter,
                    &guard,
                )
                .map_err(|e| e.danger_cast())?;
        } else {
            was_recovered = true;
        };

        let ret = Db {
            pages: Arc::new(pages),
            config,
            idgen: Arc::new(AtomicUsize::new(idgen_recovery)),
            idgen_persists: Arc::new(AtomicUsize::new(
                idgen_persists,
            )),
            idgen_persist_mu: Arc::new(Mutex::new(())),
            tenants: unsafe { std::mem::uninitialized() },
            default: unsafe { std::mem::uninitialized() },
            transactions: unsafe { std::mem::uninitialized() },
            was_recovered,
        };

        #[cfg(feature = "event_log")]
        ret.config.event_log.meta_after_restart(
            meta::meta(&*ret.pages, &guard)
                .expect("should be able to get meta under test")
                .clone(),
        );

        if meta::pid_for_name(&*ret.pages, DEFAULT_TREE_ID, &guard)?
            .is_none()
        {
            // set up initial tree

            ret.open_tree(DEFAULT_TREE_ID.to_vec(), &guard)?;
            assert_eq!(
                meta::pid_for_name(
                    &*ret.pages,
                    DEFAULT_TREE_ID,
                    &guard
                )?,
                Some(DEFAULT_TREE_PID)
            );
        }

        if meta::pid_for_name(&*ret.pages, TX_TREE_ID, &guard)?
            .is_none()
        {
            // set up initial tree

            ret.open_tree(TX_TREE_ID.to_vec(), &guard)?;
            assert_eq!(
                meta::pid_for_name(&*ret.pages, TX_TREE_ID, &guard)?,
                Some(TX_TREE_PID)
            );
        }

        let mut tenants = HashMap::new();

        for (id, _root) in
            meta::meta(&*ret.pages, &guard)?.tenants().into_iter()
        {
            let tree = Tree {
                subscriptions: Arc::new(Subscriptions::default()),
                config: ret.config.clone(),
                pages: ret.pages.clone(),
            };
            tenants.insert(id, Arc::new(tree));
        }

        let mut ret = ret;

        ret.tenants = Arc::new(RwLock::new(tenants));
        ret.default =
            ret.open_tree(DEFAULT_TREE_ID.to_vec(), &guard)?;
        ret.transactions =
            ret.open_tree(TX_TREE_ID.to_vec(), &guard)?;

        Ok(ret)
    }

    /// Generate a monotonic ID. Not guaranteed to be
    /// contiguous. Written to disk every `idgen_persist_interval`
    /// operations, followed by a blocking flush. During recovery, we
    /// take the last recovered generated ID and add 2x
    /// the `idgen_persist_interval` to it. While persisting, if the
    /// previous persisted counter wasn't synced to disk yet, we will do
    /// a blocking flush to fsync the latest counter, ensuring
    /// that we will never give out the same counter twice.
    pub fn generate_id(&self) -> Result<usize, ()> {
        let ret = self.idgen.fetch_add(1, Relaxed);

        let interval = self.config.idgen_persist_interval;
        let necessary_persists = ret / interval * interval;
        let mut persisted = self.idgen_persists.load(Acquire);

        while persisted < necessary_persists {
            let _mu = self.idgen_persist_mu.lock().unwrap();
            persisted = self.idgen_persists.load(Acquire);
            if persisted < necessary_persists {
                // it's our responsibility to persist up to our ID
                let guard = pin();
                let (current, key) = self
                    .pages
                    .get(COUNTER_PID, &guard)
                    .map_err(|e| e.danger_cast())?
                    .unwrap();

                if let Frag::Counter(current) = current {
                    assert_eq!(*current, persisted);
                } else {
                    panic!(
                        "counter pid contained non-Counter: {:?}",
                        current
                    );
                }

                let counter_frag = Frag::Counter(necessary_persists);

                let old = self
                    .idgen_persists
                    .swap(necessary_persists, Release);
                assert_eq!(old, persisted);

                self.pages
                    .replace(
                        COUNTER_PID,
                        key.clone(),
                        counter_frag,
                        &guard,
                    )
                    .map_err(|e| e.danger_cast())?;

                // during recovery we add 2x the interval. we only
                // need to block if the last one wasn't stable yet.
                if key.last_lsn() > self.pages.stable_lsn() {
                    self.pages.make_stable(key.last_lsn())?;
                }

                guard.flush();
            }
        }

        Ok(ret)
    }

    /// Open or create a new disk-backed Tree with its own keyspace,
    /// accessible from the `Db` via the provided identifier.
    pub fn open_tree<'a>(
        &self,
        name: Vec<u8>,
        guard: &'a Guard,
    ) -> Result<Arc<Tree>, ()> {
        let tenants = self.tenants.read().unwrap();
        if let Some(tree) = tenants.get(&name) {
            return Ok(tree.clone());
        }
        // drop reader lock
        drop(tenants);

        // set up empty leaf
        let leaf_id = self.pages.allocate(&guard)?;
        trace!("allocated pid {} for leaf in new_tree for namespace {:?}", leaf_id, name);

        let leaf = Frag::Base(Node {
            id: leaf_id,
            data: Data::Leaf(vec![]),
            next: None,
            lo: vec![],
            hi: vec![],
        });

        self.pages
            .replace(leaf_id, PagePtr::allocated(), leaf, &guard)
            .map_err(|e| e.danger_cast())?;

        // set up root index
        let root_id = self.pages.allocate(&guard)?;

        debug!(
            "allocated pid {} for root of new_tree {:?}",
            root_id, name
        );

        // vec![0] represents a prefix-encoded empty prefix
        let root_index_vec = vec![(vec![0], leaf_id)];

        let root = Frag::Base(Node {
            id: root_id,
            data: Data::Index(root_index_vec),
            next: None,
            lo: vec![],
            hi: vec![],
        });

        self.pages
            .replace(root_id, PagePtr::allocated(), root, &guard)
            .map_err(|e| e.danger_cast())?;

        meta::cas_root(
            &*self.pages,
            name.clone(),
            None,
            root_id,
            guard,
        )
        .map_err(|e| e.danger_cast())?;

        let mut tenants = self.tenants.write().unwrap();
        let tree = Arc::new(Tree {
            subscriptions: Arc::new(Subscriptions::default()),
            config: self.config.clone(),
            pages: self.pages.clone(),
        });
        tenants.insert(name, tree.clone());

        Ok(tree)
    }

    /// Returns `true` if the database was
    /// recovered from a previous process.
    /// Note that database state is only
    /// guaranteed to be present up to the
    /// last call to `flush`! Otherwise state
    /// is synced to disk periodically if the
    /// `sync_every_ms` configuration option
    /// is set to `Some(number_of_ms_between_syncs)`
    /// or if the IO buffer gets filled to
    /// capacity before being rotated.
    pub fn was_recovered(&self) -> bool {
        self.was_recovered
    }

    /// Record a set of pages as being involved in
    /// a transaction that is about to be written to
    /// one of the structures in the Db. Do this
    /// before performing transactions in case
    /// the system crashes before all changes
    /// can be written. During recovery,
    /// you can call `possibly_dirty_pages`
    /// to get the set of pages that may have
    /// transactional state written to them.
    pub fn mark_pending_tx(
        &self,
        _pages: Vec<PageId>,
        _txid: u64,
    ) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being aborted. This
    /// ensures that during recovery we know that
    /// this transaction is not the last one that
    /// should be recovered, because the process
    /// finished the transaction, but we may need
    /// to clean up its pending state still.
    pub fn abort_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being completed. This
    /// ensures that during recovery we know that
    /// this transaction completed, and that we
    /// can safely remove its entry in the
    /// transaction table.
    pub fn commit_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Remove entries associated with this transaction
    /// in the transaction table, because we have
    /// completed the transaction and finished
    /// applying or removing pending state, and do
    /// not need to know about it during crash recovery.
    pub fn cleanup_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Call this during recovery to retrieve the set of
    /// pages that were involved in a transaction
    /// that was in a pending or aborted (but not
    /// yet fully cleaned up) state during process
    /// termination.
    pub fn possibly_dirty_pages(
        &self,
    ) -> Result<Vec<(u64, Vec<PageId>)>, ()> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>/// TODO: submit upstream

use std::io::{IoResult, Seek, SeekStyle};
use std::io::{MemReader, MemWriter};

/// Writes to an owned, growable byte vector but also implements read with fail-on-call methods.
struct MemWriterFakeStream(MemWriter);

impl MemWriterFakeStream {
    pub fn new() -> MemWriterFakeStream { MemWriterFakeStream(MemWriter::new()) }

    pub fn get_ref(&self) -> &[u8] {
        let &MemWriterFakeStream(ref s) = self;
        s.get_ref()
    }
}

impl Writer for MemWriterFakeStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.flush()
    }
}

impl Reader for MemWriterFakeStream {
    fn read(&mut self, _buf: &mut [u8]) -> IoResult<uint> {
        panic!("Uh oh, you didn't aught to call MemWriterFakeStream.read()!")
    }
}

/// Reads from an owned byte vector, but also implements write with fail-on-call methods.
pub struct MemReaderFakeStream(MemReader);

impl MemReaderFakeStream {
    pub fn new(buf: Vec<u8>) -> MemReaderFakeStream { MemReaderFakeStream(MemReader::new(buf)) }
}

impl Reader for MemReaderFakeStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.read(buf)
    }
}

impl Seek for MemReaderFakeStream {
    fn tell(&self) -> IoResult<u64> {
        let &MemReaderFakeStream(ref s) = self;
        s.tell()
    }

    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.seek(pos, style)
    }
}

impl Writer for MemReaderFakeStream {
    fn write(&mut self, _buf: &[u8]) -> IoResult<()> {
        panic!("Uh oh, you didn't aught to call MemReaderFakeStream.write()!")
    }
    fn flush(&mut self) -> IoResult<()> {
        panic!("Uh oh, you didn't aught to call MemReaderFakeStream.flush()!")
    }
}

#[cfg(test)]
mod test {
    use super::{MemReaderFakeStream, MemWriterFakeStream};

    #[test]
    fn test_mem_writer_fake_stream() {
        let mut writer = MemWriterFakeStream::new();
        assert_eq!(writer.get_ref(),            [][]);
        assert_eq!(writer.write(&[0]),          Ok(()));
        assert_eq!(writer.get_ref(),            [0][]);
        assert_eq!(writer.write(&[1, 2, 3]),    Ok(()));
        assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(()));
        assert_eq!(writer.get_ref(),            [0, 1, 2, 3, 4, 5, 6, 7][]);
    }

    #[test]
    fn test_mem_reader_fake_stream() {
        let mut reader = MemReaderFakeStream::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
        let mut buf = vec![];
        assert_eq!(reader.read(buf[mut]),      Ok(0));
        assert_eq!(reader.tell(),              Ok(0));
        let mut buf = vec![0];
        assert_eq!(reader.read(buf[mut]),      Ok(1));
        assert_eq!(reader.tell(),              Ok(1));
        assert_eq!(buf,                        vec![0]);
        let mut buf = vec![0, 0, 0, 0];
        assert_eq!(reader.read(buf[mut]),      Ok(4));
        assert_eq!(reader.tell(),              Ok(5));
        assert_eq!(buf,                        vec![1, 2, 3, 4]);
        assert_eq!(reader.read(buf[mut]),      Ok(3));
        assert_eq!(buf[0..3],                  [5, 6, 7][]);
        assert_eq!(reader.read(buf[mut]).ok(), None);
    }
}
<commit_msg>fix tests<commit_after>/// TODO: submit upstream

use std::io::{IoResult, Seek, SeekStyle};
use std::io::{MemReader, MemWriter};

/// Writes to an owned, growable byte vector but also implements read with fail-on-call methods.
struct MemWriterFakeStream(MemWriter);

impl MemWriterFakeStream {
    pub fn new() -> MemWriterFakeStream { MemWriterFakeStream(MemWriter::new()) }

    pub fn get_ref(&self) -> &[u8] {
        let &MemWriterFakeStream(ref s) = self;
        s.get_ref()
    }
}

impl Writer for MemWriterFakeStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.flush()
    }
}

impl Reader for MemWriterFakeStream {
    fn read(&mut self, _buf: &mut [u8]) -> IoResult<uint> {
        panic!("Uh oh, you didn't aught to call MemWriterFakeStream.read()!")
    }
}

/// Reads from an owned byte vector, but also implements write with fail-on-call methods.
pub struct MemReaderFakeStream(MemReader);

impl MemReaderFakeStream {
    pub fn new(buf: Vec<u8>) -> MemReaderFakeStream { MemReaderFakeStream(MemReader::new(buf)) }
}

impl Reader for MemReaderFakeStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.read(buf)
    }
}

impl Seek for MemReaderFakeStream {
    fn tell(&self) -> IoResult<u64> {
        let &MemReaderFakeStream(ref s) = self;
        s.tell()
    }

    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.seek(pos, style)
    }
}

impl Writer for MemReaderFakeStream {
    fn write(&mut self, _buf: &[u8]) -> IoResult<()> {
        panic!("Uh oh, you didn't aught to call MemReaderFakeStream.write()!")
    }
    fn flush(&mut self) -> IoResult<()> {
        panic!("Uh oh, you didn't aught to call MemReaderFakeStream.flush()!")
    }
}

#[cfg(test)]
mod test {
    use super::{MemReaderFakeStream, MemWriterFakeStream};

    #[test]
    fn test_mem_writer_fake_stream() {
        let mut writer = MemWriterFakeStream::new();
        assert_eq!(writer.get_ref(),            [][]);
        assert_eq!(writer.write(&[0]),          Ok(()));
        assert_eq!(writer.get_ref(),            [0][]);
        assert_eq!(writer.write(&[1, 2, 3]),    Ok(()));
        assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(()));
        assert_eq!(writer.get_ref(),            [0, 1, 2, 3, 4, 5, 6, 7][]);
    }

    #[test]
    fn test_mem_reader_fake_stream() {
        let mut reader = MemReaderFakeStream::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
        let mut buf = vec![];
        assert_eq!(reader.read(buf.as_mut_slice()),      Ok(0));
        assert_eq!(reader.tell(),              Ok(0));
        let mut buf = vec![0];
        assert_eq!(reader.read(buf.as_mut_slice()),      Ok(1));
        assert_eq!(reader.tell(),              Ok(1));
        assert_eq!(buf,                        vec![0]);
        let mut buf = vec![0, 0, 0, 0];
        assert_eq!(reader.read(buf.as_mut_slice()),      Ok(4));
        assert_eq!(reader.tell(),              Ok(5));
        assert_eq!(buf,                        vec![1, 2, 3, 4]);
        assert_eq!(reader.read(buf.as_mut_slice()),      Ok(3));
        assert_eq!(buf[0..3],                  [5, 6, 7][]);
        assert_eq!(reader.read(buf.as_mut_slice()).ok(), None);
    }
}
<|endoftext|>


<commit_before>use alloc::borrow::Cow;
use alloc::string::String;
use core::fmt::{self, Debug, Display};
use core::marker::{PhantomData, PhantomPinned};
use core::slice;
use core::str::{self, Utf8Error};

extern "C" {
    #[link_name = "cxxbridge05$cxx_string$data"]
    fn string_data(this: &CxxString) -> *const u8;
    #[link_name = "cxxbridge05$cxx_string$length"]
    fn string_length(this: &CxxString) -> usize;
    #[link_name = "cxxbridge05$cxx_string$push"]
    fn string_push(this: &mut CxxString, ptr: *const u8, len: usize);
}

/// Binding to C++ `std::string`.
///
/// # Invariants
///
/// As an invariant of this API and the static analysis of the cxx::bridge
/// macro, in Rust code we can never obtain a `CxxString` by value. C++'s string
/// requires a move constructor and may hold internal pointers, which is not
/// compatible with Rust's move behavior. Instead in Rust code we will only ever
/// look at a CxxString through a reference or smart pointer, as in `&CxxString`
/// or `UniquePtr<CxxString>`.
#[repr(C)]
pub struct CxxString {
    _private: [u8; 0],
    _pinned: PhantomData<PhantomPinned>,
}

impl CxxString {
    /// Returns the length of the string in bytes.
    ///
    /// Matches the behavior of C++ [std::string::size][size].
    ///
    /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
    pub fn len(&self) -> usize {
        unsafe { string_length(self) }
    }

    /// Returns true if `self` has a length of zero bytes.
    ///
    /// Matches the behavior of C++ [std::string::empty][empty].
    ///
    /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a byte slice of this string's contents.
    pub fn as_bytes(&self) -> &[u8] {
        let data = self.as_ptr();
        let len = self.len();
        unsafe { slice::from_raw_parts(data, len) }
    }

    /// Produces a pointer to the first character of the string.
    ///
    /// Matches the behavior of C++ [std::string::data][data].
    ///
    /// Note that the return type may look like `const char *` but is not a
    /// `const char *` in the typical C sense, as C++ strings may contain
    /// internal null bytes. As such, the returned pointer only makes sense as a
    /// string in combination with the length returned by [`len()`][len].
    ///
    /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
    /// [len]: #method.len
    pub fn as_ptr(&self) -> *const u8 {
        unsafe { string_data(self) }
    }

    /// Validates that the C++ string contains UTF-8 data and produces a view of
    /// it as a Rust &str, otherwise an error.
    pub fn to_str(&self) -> Result<&str, Utf8Error> {
        str::from_utf8(self.as_bytes())
    }

    /// If the contents of the C++ string are valid UTF-8, this function returns
    /// a view as a Cow::Borrowed &str. Otherwise replaces any invalid UTF-8
    /// sequences with the U+FFFD [replacement character] and returns a
    /// Cow::Owned String.
    ///
    /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
    pub fn to_string_lossy(&self) -> Cow<str> {
        String::from_utf8_lossy(self.as_bytes())
    }

    /// Appends a given string slice onto the end of this C++ string.
    pub fn push_str(&mut self, s: &str) {
        self.push_bytes(s.as_bytes());
    }

    /// Appends arbitrary bytes onto the end of this C++ string.
    pub fn push_bytes(&mut self, bytes: &[u8]) {
        unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
    }
}

impl Display for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl Debug for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl PartialEq for CxxString {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<CxxString> for str {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<str> for CxxString {
    fn eq(&self, other: &str) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}
<commit_msg>Require Pin<&mut CxxString> for push<commit_after>use alloc::borrow::Cow;
use alloc::string::String;
use core::fmt::{self, Debug, Display};
use core::marker::{PhantomData, PhantomPinned};
use core::pin::Pin;
use core::slice;
use core::str::{self, Utf8Error};

extern "C" {
    #[link_name = "cxxbridge05$cxx_string$data"]
    fn string_data(this: &CxxString) -> *const u8;
    #[link_name = "cxxbridge05$cxx_string$length"]
    fn string_length(this: &CxxString) -> usize;
    #[link_name = "cxxbridge05$cxx_string$push"]
    fn string_push(this: &mut CxxString, ptr: *const u8, len: usize);
}

/// Binding to C++ `std::string`.
///
/// # Invariants
///
/// As an invariant of this API and the static analysis of the cxx::bridge
/// macro, in Rust code we can never obtain a `CxxString` by value. C++'s string
/// requires a move constructor and may hold internal pointers, which is not
/// compatible with Rust's move behavior. Instead in Rust code we will only ever
/// look at a CxxString through a reference or smart pointer, as in `&CxxString`
/// or `UniquePtr<CxxString>`.
#[repr(C)]
pub struct CxxString {
    _private: [u8; 0],
    _pinned: PhantomData<PhantomPinned>,
}

impl CxxString {
    /// Returns the length of the string in bytes.
    ///
    /// Matches the behavior of C++ [std::string::size][size].
    ///
    /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
    pub fn len(&self) -> usize {
        unsafe { string_length(self) }
    }

    /// Returns true if `self` has a length of zero bytes.
    ///
    /// Matches the behavior of C++ [std::string::empty][empty].
    ///
    /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a byte slice of this string's contents.
    pub fn as_bytes(&self) -> &[u8] {
        let data = self.as_ptr();
        let len = self.len();
        unsafe { slice::from_raw_parts(data, len) }
    }

    /// Produces a pointer to the first character of the string.
    ///
    /// Matches the behavior of C++ [std::string::data][data].
    ///
    /// Note that the return type may look like `const char *` but is not a
    /// `const char *` in the typical C sense, as C++ strings may contain
    /// internal null bytes. As such, the returned pointer only makes sense as a
    /// string in combination with the length returned by [`len()`][len].
    ///
    /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
    /// [len]: #method.len
    pub fn as_ptr(&self) -> *const u8 {
        unsafe { string_data(self) }
    }

    /// Validates that the C++ string contains UTF-8 data and produces a view of
    /// it as a Rust &str, otherwise an error.
    pub fn to_str(&self) -> Result<&str, Utf8Error> {
        str::from_utf8(self.as_bytes())
    }

    /// If the contents of the C++ string are valid UTF-8, this function returns
    /// a view as a Cow::Borrowed &str. Otherwise replaces any invalid UTF-8
    /// sequences with the U+FFFD [replacement character] and returns a
    /// Cow::Owned String.
    ///
    /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
    pub fn to_string_lossy(&self) -> Cow<str> {
        String::from_utf8_lossy(self.as_bytes())
    }

    /// Appends a given string slice onto the end of this C++ string.
    pub fn push_str(self: Pin<&mut Self>, s: &str) {
        self.push_bytes(s.as_bytes());
    }

    /// Appends arbitrary bytes onto the end of this C++ string.
    pub fn push_bytes(self: Pin<&mut Self>, bytes: &[u8]) {
        unsafe {
            let this = Pin::into_inner_unchecked(self);
            string_push(this, bytes.as_ptr(), bytes.len())
        }
    }
}

impl Display for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl Debug for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl PartialEq for CxxString {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<CxxString> for str {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<str> for CxxString {
    fn eq(&self, other: &str) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::Path;
use super::{Result,time_adjusted,calculate_time_difference};
use error::ProbeError;

#[derive(Debug,PartialEq)]
pub struct DiskStatsMeasurement {
    pub precise_time_ns: u64,
    pub stats: HashMap<String, DiskStat>
}

impl DiskStatsMeasurement {
    /// Calculate the disk stats per minute based on this measurement and a measurement in the
    /// future. It is advisable to make the next measurement roughly a minute from this one for the
    /// most reliable result.
    pub fn calculate_per_minute(&self, next_measurement: &DiskStatsMeasurement) -> Result<DiskStatsPerMinute> {
        let time_difference = try!(calculate_time_difference(self.precise_time_ns, next_measurement.precise_time_ns));

        let mut stats = HashMap::new();

        for (name, stat) in self.stats.iter() {
            let next_stat = match next_measurement.stats.get(name) {
                Some(stat) => stat,
                None => return Err(ProbeError::UnexpectedContent(format!("{} is not present in the next measurement", name)))
            };
            stats.insert(
                name.to_owned(),
                DiskStat {
                    reads_completed_successfully: try!(time_adjusted(next_stat.reads_completed_successfully, stat.reads_completed_successfully, time_difference)),
                    reads_merged: try!(time_adjusted(next_stat.reads_merged, stat.reads_merged, time_difference)),
                    sectors_read: try!(time_adjusted(next_stat.sectors_read, stat.sectors_read, time_difference)),
                    time_spent_reading_ms: try!(time_adjusted(next_stat.time_spent_reading_ms, stat.time_spent_reading_ms, time_difference)),
                    writes_completed: try!(time_adjusted(next_stat.writes_completed, stat.writes_completed, time_difference)),
                    writes_merged: try!(time_adjusted(next_stat.writes_merged, stat.writes_merged, time_difference)),
                    sectors_written: try!(time_adjusted(next_stat.sectors_written, stat.sectors_written, time_difference)),
                    time_spent_writing_ms: try!(time_adjusted(next_stat.time_spent_writing_ms, stat.time_spent_writing_ms, time_difference)),
                    ios_currently_in_progress: try!(time_adjusted(next_stat.ios_currently_in_progress, stat.ios_currently_in_progress, time_difference)),
                    time_spent_doing_ios_ms: try!(time_adjusted(next_stat.time_spent_doing_ios_ms, stat.time_spent_doing_ios_ms, time_difference)),
                    weighted_time_spent_doing_ios_ms: try!(time_adjusted(next_stat.weighted_time_spent_doing_ios_ms, stat.weighted_time_spent_doing_ios_ms, time_difference))
                }
            );
        }

        Ok(DiskStatsPerMinute {
            stats: stats
        })
    }
}

#[derive(Debug,PartialEq)]
pub struct DiskStat {
    pub reads_completed_successfully: u64,
    pub reads_merged: u64,
    pub sectors_read: u64,
    pub time_spent_reading_ms: u64,
    pub writes_completed: u64,
    pub writes_merged: u64,
    pub sectors_written: u64,
    pub time_spent_writing_ms: u64,
    pub ios_currently_in_progress: u64,
    pub time_spent_doing_ios_ms: u64,
    pub weighted_time_spent_doing_ios_ms: u64
}

#[derive(Debug,PartialEq)]
pub struct DiskStatsPerMinute {
   pub stats: HashMap<String, DiskStat>
}

#[cfg(target_os = "linux")]
pub fn read() -> Result<DiskStatsMeasurement> {
    os::read_and_parse_proc_diskstats(&Path::new("/proc/diskstats"))
}

#[cfg(target_os = "linux")]
mod os {
    use std::io::BufRead;
    use std::path::Path;
    use std::collections::HashMap;
    use time;
    use super::{DiskStatsMeasurement,DiskStat};
    use super::super::{file_to_buf_reader,parse_u64,Result,ProbeError};

    #[inline]
    pub fn read_and_parse_proc_diskstats(path: &Path) -> Result<DiskStatsMeasurement> {
        let reader = try!(file_to_buf_reader(path));

        let mut out = DiskStatsMeasurement {
            precise_time_ns: time::precise_time_ns(),
            stats: HashMap::new()
        };

        for line_result in reader.lines() {
            let line = try!(line_result);
            let segments: Vec<&str> = line.split_whitespace().collect();

            if segments.len() != 14 {
                return Err(ProbeError::UnexpectedContent("Incorrect number of segments".to_owned()))
            }

            let disk_stat = DiskStat {
                reads_completed_successfully: try!(parse_u64(segments[3])),
                reads_merged: try!(parse_u64(segments[4])),
                sectors_read: try!(parse_u64(segments[5])),
                time_spent_reading_ms: try!(parse_u64(segments[6])),
                writes_completed: try!(parse_u64(segments[7])),
                writes_merged: try!(parse_u64(segments[8])),
                sectors_written: try!(parse_u64(segments[9])),
                time_spent_writing_ms: try!(parse_u64(segments[10])),
                ios_currently_in_progress: try!(parse_u64(segments[11])),
                time_spent_doing_ios_ms: try!(parse_u64(segments[12])),
                weighted_time_spent_doing_ios_ms: try!(parse_u64(segments[13]))
            };
            out.stats.insert(segments[2].to_owned(), disk_stat);
        }

        Ok(out)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::path::Path;
    use error::ProbeError;
    use super::{DiskStat,DiskStatsMeasurement};
    use super::os::read_and_parse_proc_diskstats;

    #[test]
    fn test_read_disk_stats() {
        assert!(super::read().is_ok());
    }

    #[test]
    fn test_read_and_parse_proc_diskstats() {
        let measurement = read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats")).unwrap();

        assert!(measurement.precise_time_ns > 0);

        assert_eq!(2, measurement.stats.len());

        let sda = measurement.stats.get("sda").unwrap();
        assert_eq!(6185, sda.reads_completed_successfully);
        assert_eq!(9367, sda.reads_merged);
        assert_eq!(403272, sda.sectors_read);
        assert_eq!(22160, sda.time_spent_reading_ms);
        assert_eq!(2591, sda.writes_completed);
        assert_eq!(8251, sda.writes_merged);
        assert_eq!(84452, sda.sectors_written);
        assert_eq!(2860, sda.time_spent_writing_ms);
        assert_eq!(0, sda.ios_currently_in_progress);
        assert_eq!(8960, sda.time_spent_doing_ios_ms);
        assert_eq!(24990, sda.weighted_time_spent_doing_ios_ms);

        let sda1 = measurement.stats.get("sda1").unwrap();
        assert_eq!(483, sda1.reads_completed_successfully);
        assert_eq!(4782, sda1.reads_merged);
        assert_eq!(41466, sda1.sectors_read);
        assert_eq!(1100, sda1.time_spent_reading_ms);
        assert_eq!(7, sda1.writes_completed);
        assert_eq!(1, sda1.writes_merged);
        assert_eq!(28, sda1.sectors_written);
        assert_eq!(40, sda1.time_spent_writing_ms);
        assert_eq!(0, sda1.ios_currently_in_progress);
        assert_eq!(930, sda1.time_spent_doing_ios_ms);
        assert_eq!(1140, sda1.weighted_time_spent_doing_ios_ms);
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_incomplete() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_incomplete")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_garbage() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_garbage")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_full_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let mut measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let mut measurement2 = DiskStatsMeasurement {
            precise_time_ns: 120_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 120);
        assert_eq!(sda1.reads_merged, 120);
        assert_eq!(sda1.sectors_read, 120);
        assert_eq!(sda1.time_spent_reading_ms, 120);
        assert_eq!(sda1.writes_completed, 120);
        assert_eq!(sda1.writes_merged, 120);
        assert_eq!(sda1.sectors_written, 120);
        assert_eq!(sda1.time_spent_writing_ms, 120);
        assert_eq!(sda1.ios_currently_in_progress, 120);
        assert_eq!(sda1.time_spent_doing_ios_ms, 120);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 120);
    }

    #[test]
    fn test_calculate_per_minute_partial_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let mut measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let mut measurement2 = DiskStatsMeasurement {
            precise_time_ns: 90_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 60);
        assert_eq!(sda1.reads_merged, 60);
        assert_eq!(sda1.sectors_read, 60);
        assert_eq!(sda1.time_spent_reading_ms, 60);
        assert_eq!(sda1.writes_completed, 60);
        assert_eq!(sda1.writes_merged, 60);
        assert_eq!(sda1.sectors_written, 60);
        assert_eq!(sda1.time_spent_writing_ms, 60);
        assert_eq!(sda1.ios_currently_in_progress, 60);
        assert_eq!(sda1.time_spent_doing_ios_ms, 60);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 60);
    }

    #[test]
    fn test_calculate_per_minute_wrong_times() {
        let mut measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: HashMap::new()
        };
        let mut measurement2 = DiskStatsMeasurement {
            precise_time_ns: 300,
            stats: HashMap::new()
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::InvalidInput(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_values_lower() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let mut measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(400));
        let mut measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_different_disks() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let mut measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda2".to_owned(), helpers::disk_stat(600));
        let mut measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    mod helpers {
        use super::super::DiskStat;

        pub fn disk_stat(value: u64) -> DiskStat {
            DiskStat {
                reads_completed_successfully: value,
                reads_merged: value,
                sectors_read: value,
                time_spent_reading_ms: value,
                writes_completed: value,
                writes_merged: value,
                sectors_written: value,
                time_spent_writing_ms: value,
                ios_currently_in_progress: value,
                time_spent_doing_ios_ms: value,
                weighted_time_spent_doing_ios_ms: value
            }
        }
    }
}
<commit_msg>Fix warnings<commit_after>use std::collections::HashMap;
use std::path::Path;
use super::{Result,time_adjusted,calculate_time_difference};
use error::ProbeError;

#[derive(Debug,PartialEq)]
pub struct DiskStatsMeasurement {
    pub precise_time_ns: u64,
    pub stats: HashMap<String, DiskStat>
}

impl DiskStatsMeasurement {
    /// Calculate the disk stats per minute based on this measurement and a measurement in the
    /// future. It is advisable to make the next measurement roughly a minute from this one for the
    /// most reliable result.
    pub fn calculate_per_minute(&self, next_measurement: &DiskStatsMeasurement) -> Result<DiskStatsPerMinute> {
        let time_difference = try!(calculate_time_difference(self.precise_time_ns, next_measurement.precise_time_ns));

        let mut stats = HashMap::new();

        for (name, stat) in self.stats.iter() {
            let next_stat = match next_measurement.stats.get(name) {
                Some(stat) => stat,
                None => return Err(ProbeError::UnexpectedContent(format!("{} is not present in the next measurement", name)))
            };
            stats.insert(
                name.to_owned(),
                DiskStat {
                    reads_completed_successfully: try!(time_adjusted(next_stat.reads_completed_successfully, stat.reads_completed_successfully, time_difference)),
                    reads_merged: try!(time_adjusted(next_stat.reads_merged, stat.reads_merged, time_difference)),
                    sectors_read: try!(time_adjusted(next_stat.sectors_read, stat.sectors_read, time_difference)),
                    time_spent_reading_ms: try!(time_adjusted(next_stat.time_spent_reading_ms, stat.time_spent_reading_ms, time_difference)),
                    writes_completed: try!(time_adjusted(next_stat.writes_completed, stat.writes_completed, time_difference)),
                    writes_merged: try!(time_adjusted(next_stat.writes_merged, stat.writes_merged, time_difference)),
                    sectors_written: try!(time_adjusted(next_stat.sectors_written, stat.sectors_written, time_difference)),
                    time_spent_writing_ms: try!(time_adjusted(next_stat.time_spent_writing_ms, stat.time_spent_writing_ms, time_difference)),
                    ios_currently_in_progress: try!(time_adjusted(next_stat.ios_currently_in_progress, stat.ios_currently_in_progress, time_difference)),
                    time_spent_doing_ios_ms: try!(time_adjusted(next_stat.time_spent_doing_ios_ms, stat.time_spent_doing_ios_ms, time_difference)),
                    weighted_time_spent_doing_ios_ms: try!(time_adjusted(next_stat.weighted_time_spent_doing_ios_ms, stat.weighted_time_spent_doing_ios_ms, time_difference))
                }
            );
        }

        Ok(DiskStatsPerMinute {
            stats: stats
        })
    }
}

#[derive(Debug,PartialEq)]
pub struct DiskStat {
    pub reads_completed_successfully: u64,
    pub reads_merged: u64,
    pub sectors_read: u64,
    pub time_spent_reading_ms: u64,
    pub writes_completed: u64,
    pub writes_merged: u64,
    pub sectors_written: u64,
    pub time_spent_writing_ms: u64,
    pub ios_currently_in_progress: u64,
    pub time_spent_doing_ios_ms: u64,
    pub weighted_time_spent_doing_ios_ms: u64
}

#[derive(Debug,PartialEq)]
pub struct DiskStatsPerMinute {
   pub stats: HashMap<String, DiskStat>
}

#[cfg(target_os = "linux")]
pub fn read() -> Result<DiskStatsMeasurement> {
    os::read_and_parse_proc_diskstats(&Path::new("/proc/diskstats"))
}

#[cfg(target_os = "linux")]
mod os {
    use std::io::BufRead;
    use std::path::Path;
    use std::collections::HashMap;
    use time;
    use super::{DiskStatsMeasurement,DiskStat};
    use super::super::{file_to_buf_reader,parse_u64,Result,ProbeError};

    #[inline]
    pub fn read_and_parse_proc_diskstats(path: &Path) -> Result<DiskStatsMeasurement> {
        let reader = try!(file_to_buf_reader(path));

        let mut out = DiskStatsMeasurement {
            precise_time_ns: time::precise_time_ns(),
            stats: HashMap::new()
        };

        for line_result in reader.lines() {
            let line = try!(line_result);
            let segments: Vec<&str> = line.split_whitespace().collect();

            if segments.len() != 14 {
                return Err(ProbeError::UnexpectedContent("Incorrect number of segments".to_owned()))
            }

            let disk_stat = DiskStat {
                reads_completed_successfully: try!(parse_u64(segments[3])),
                reads_merged: try!(parse_u64(segments[4])),
                sectors_read: try!(parse_u64(segments[5])),
                time_spent_reading_ms: try!(parse_u64(segments[6])),
                writes_completed: try!(parse_u64(segments[7])),
                writes_merged: try!(parse_u64(segments[8])),
                sectors_written: try!(parse_u64(segments[9])),
                time_spent_writing_ms: try!(parse_u64(segments[10])),
                ios_currently_in_progress: try!(parse_u64(segments[11])),
                time_spent_doing_ios_ms: try!(parse_u64(segments[12])),
                weighted_time_spent_doing_ios_ms: try!(parse_u64(segments[13]))
            };
            out.stats.insert(segments[2].to_owned(), disk_stat);
        }

        Ok(out)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::path::Path;
    use error::ProbeError;
    use super::DiskStatsMeasurement;
    use super::os::read_and_parse_proc_diskstats;

    #[test]
    fn test_read_disk_stats() {
        assert!(super::read().is_ok());
    }

    #[test]
    fn test_read_and_parse_proc_diskstats() {
        let measurement = read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats")).unwrap();

        assert!(measurement.precise_time_ns > 0);

        assert_eq!(2, measurement.stats.len());

        let sda = measurement.stats.get("sda").unwrap();
        assert_eq!(6185, sda.reads_completed_successfully);
        assert_eq!(9367, sda.reads_merged);
        assert_eq!(403272, sda.sectors_read);
        assert_eq!(22160, sda.time_spent_reading_ms);
        assert_eq!(2591, sda.writes_completed);
        assert_eq!(8251, sda.writes_merged);
        assert_eq!(84452, sda.sectors_written);
        assert_eq!(2860, sda.time_spent_writing_ms);
        assert_eq!(0, sda.ios_currently_in_progress);
        assert_eq!(8960, sda.time_spent_doing_ios_ms);
        assert_eq!(24990, sda.weighted_time_spent_doing_ios_ms);

        let sda1 = measurement.stats.get("sda1").unwrap();
        assert_eq!(483, sda1.reads_completed_successfully);
        assert_eq!(4782, sda1.reads_merged);
        assert_eq!(41466, sda1.sectors_read);
        assert_eq!(1100, sda1.time_spent_reading_ms);
        assert_eq!(7, sda1.writes_completed);
        assert_eq!(1, sda1.writes_merged);
        assert_eq!(28, sda1.sectors_written);
        assert_eq!(40, sda1.time_spent_writing_ms);
        assert_eq!(0, sda1.ios_currently_in_progress);
        assert_eq!(930, sda1.time_spent_doing_ios_ms);
        assert_eq!(1140, sda1.weighted_time_spent_doing_ios_ms);
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_incomplete() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_incomplete")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_garbage() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_garbage")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_full_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 120_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 120);
        assert_eq!(sda1.reads_merged, 120);
        assert_eq!(sda1.sectors_read, 120);
        assert_eq!(sda1.time_spent_reading_ms, 120);
        assert_eq!(sda1.writes_completed, 120);
        assert_eq!(sda1.writes_merged, 120);
        assert_eq!(sda1.sectors_written, 120);
        assert_eq!(sda1.time_spent_writing_ms, 120);
        assert_eq!(sda1.ios_currently_in_progress, 120);
        assert_eq!(sda1.time_spent_doing_ios_ms, 120);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 120);
    }

    #[test]
    fn test_calculate_per_minute_partial_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 90_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 60);
        assert_eq!(sda1.reads_merged, 60);
        assert_eq!(sda1.sectors_read, 60);
        assert_eq!(sda1.time_spent_reading_ms, 60);
        assert_eq!(sda1.writes_completed, 60);
        assert_eq!(sda1.writes_merged, 60);
        assert_eq!(sda1.sectors_written, 60);
        assert_eq!(sda1.time_spent_writing_ms, 60);
        assert_eq!(sda1.ios_currently_in_progress, 60);
        assert_eq!(sda1.time_spent_doing_ios_ms, 60);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 60);
    }

    #[test]
    fn test_calculate_per_minute_wrong_times() {
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: HashMap::new()
        };
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 300,
            stats: HashMap::new()
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::InvalidInput(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_values_lower() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(400));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_different_disks() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda2".to_owned(), helpers::disk_stat(600));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    mod helpers {
        use super::super::DiskStat;

        pub fn disk_stat(value: u64) -> DiskStat {
            DiskStat {
                reads_completed_successfully: value,
                reads_merged: value,
                sectors_read: value,
                time_spent_reading_ms: value,
                writes_completed: value,
                writes_merged: value,
                sectors_written: value,
                time_spent_writing_ms: value,
                ios_currently_in_progress: value,
                time_spent_doing_ios_ms: value,
                weighted_time_spent_doing_ios_ms: value
            }
        }
    }
}
<|endoftext|>


<commit_before>pub mod cmd;

use std::mem;
use std::cell::RefCell;
use std::collections::HashMap;

use codegen::codegen::CodeGen;
use mem::CodeMemory;
use parser::ast::ctxt::Context;
use error::diag::Diagnostic;
use error::msg::Msg;

use parser::ast::{self, Function};
use parser::Parser;
use parser::lexer::position::Position;
use semck;
use sym::*;

pub fn compile() -> i32 {
    let args = cmd::parse();

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut parser = match Parser::from_file(&args.arg_file) {
        Err(_) => {
            println!("unable to read file `{}`", &args.arg_file);
            return 1;
        }

        Ok(parser) => parser
    };

    let (ast, interner) = match parser.parse() {
        Ok(ret) => ret,

        Err(error) => {
            error.print();
            return 1;
        }
    };

    if args.flag_emit_ast {
        ast::dump::dump(&ast, &interner);
    }

    let ast_map = ast::map::build(&ast, &interner);
    let ctxt = Context::new(&args, &interner, &ast_map, &ast);

    semck::check(&ctxt);

    let main = find_main(&ctxt);

    if ctxt.diag.borrow().has_errors() {
        ctxt.diag.borrow().dump();

        println!("{} error(s) found", ctxt.diag.borrow().errors().len());
        return 1;
    }

    let main = main.unwrap();

    let mut cg = CodeGen::new(&ctxt, main);
    let buffer = cg.generate().finish();

    if args.flag_emit_asm {
        dump_asm(&buffer, &ctxt.interner.str(main.name));
    }

    let code = CodeMemory::new(&buffer);

    let fct : extern "C" fn() -> i32 = unsafe { mem::transmute(code.ptr()) };
    let res = fct();

    // main-fct without return value exits with status 0
    if main.return_type.is_none() {
        0

    // use return value of main for exit status
    } else {
        res
    }
}

fn dump_asm(buf: &[u8], name: &str) {
    use capstone::*;

    let engine = Engine::new(Arch::X86, MODE_64).expect("cannot create capstone engine");
    let instrs = engine.disasm(buf, 0, buf.len()).expect("could not disassemble code");

    println!("fn {}", name);
    for instr in instrs {
        println!("  {:#06x}: {} {}", instr.addr, instr.mnemonic, instr.op_str);
    }
}

fn find_main<'a, 'ast>(ctxt: &Context<'a, 'ast>) -> Option<&'ast Function> where 'a: 'ast {
    let name = ctxt.interner.intern("main");
    let fctid = match ctxt.sym.borrow().get_function(name) {
        Some(id) => id,
        None => {
            ctxt.diag.borrow_mut().report(Position::new(1, 1), Msg::MainNotFound);
            return None;
        }
    };

    let fct_infos = ctxt.fct_infos.borrow();
    let fct = fct_infos.get(fctid.0).unwrap();
    let return_type = fct.return_type;

    if (return_type != BuiltinType::Unit && return_type != BuiltinType::Int) ||
        fct.params_types.len() > 0 {
        ctxt.diag.borrow_mut().report(fct.ast.unwrap().pos, Msg::WrongMainDefinition);
        return None;
    }

    Some(fct.ast.unwrap())
}
<commit_msg>use AT&T syntax for disasm<commit_after>pub mod cmd;

use std::mem;
use std::cell::RefCell;
use std::collections::HashMap;

use codegen::codegen::CodeGen;
use mem::CodeMemory;
use parser::ast::ctxt::Context;
use error::diag::Diagnostic;
use error::msg::Msg;

use parser::ast::{self, Function};
use parser::Parser;
use parser::lexer::position::Position;
use semck;
use sym::*;

pub fn compile() -> i32 {
    let args = cmd::parse();

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut parser = match Parser::from_file(&args.arg_file) {
        Err(_) => {
            println!("unable to read file `{}`", &args.arg_file);
            return 1;
        }

        Ok(parser) => parser
    };

    let (ast, interner) = match parser.parse() {
        Ok(ret) => ret,

        Err(error) => {
            error.print();
            return 1;
        }
    };

    if args.flag_emit_ast {
        ast::dump::dump(&ast, &interner);
    }

    let ast_map = ast::map::build(&ast, &interner);
    let ctxt = Context::new(&args, &interner, &ast_map, &ast);

    semck::check(&ctxt);

    let main = find_main(&ctxt);

    if ctxt.diag.borrow().has_errors() {
        ctxt.diag.borrow().dump();

        println!("{} error(s) found", ctxt.diag.borrow().errors().len());
        return 1;
    }

    let main = main.unwrap();

    let mut cg = CodeGen::new(&ctxt, main);
    let buffer = cg.generate().finish();

    if args.flag_emit_asm {
        dump_asm(&buffer, &ctxt.interner.str(main.name));
    }

    let code = CodeMemory::new(&buffer);

    let fct : extern "C" fn() -> i32 = unsafe { mem::transmute(code.ptr()) };
    let res = fct();

    // main-fct without return value exits with status 0
    if main.return_type.is_none() {
        0

    // use return value of main for exit status
    } else {
        res
    }
}

fn dump_asm(buf: &[u8], name: &str) {
    use capstone::*;

    let engine = Engine::new(Arch::X86, MODE_64).expect("cannot create capstone engine");
    engine.set_option(Opt::Syntax, 2); // switch to AT&T syntax
    let instrs = engine.disasm(buf, 0, buf.len()).expect("could not disassemble code");

    println!("fn {}", name);
    for instr in instrs {
        println!("  {:#06x}: {}\t\t{}", instr.addr, instr.mnemonic, instr.op_str);
    }
}

fn find_main<'a, 'ast>(ctxt: &Context<'a, 'ast>) -> Option<&'ast Function> where 'a: 'ast {
    let name = ctxt.interner.intern("main");
    let fctid = match ctxt.sym.borrow().get_function(name) {
        Some(id) => id,
        None => {
            ctxt.diag.borrow_mut().report(Position::new(1, 1), Msg::MainNotFound);
            return None;
        }
    };

    let fct_infos = ctxt.fct_infos.borrow();
    let fct = fct_infos.get(fctid.0).unwrap();
    let return_type = fct.return_type;

    if (return_type != BuiltinType::Unit && return_type != BuiltinType::Int) ||
        fct.params_types.len() > 0 {
        ctxt.diag.borrow_mut().report(fct.ast.unwrap().pos, Msg::WrongMainDefinition);
        return None;
    }

    Some(fct.ast.unwrap())
}
<|endoftext|>


<commit_before>/*
Module: c_vec

Library to interface with chunks of memory allocated in C.

It is often desirable to safely interface with memory allocated from C,
encapsulating the unsafety into allocation and destruction time.  Indeed,
allocating memory externally is currently the only way to give Rust shared
mutable state with C programs that keep their own references; vectors are
unsuitable because they could be reallocated or moved at any time, and
importing C memory into a vector takes a one-time snapshot of the memory.

This module simplifies the usage of such external blocks of memory.  Memory
is encapsulated into an opaque object after creation; the lifecycle of the
memory can be optionally managed by Rust, if an appropriate destructor
closure is provided.  Safety is ensured by bounds-checking accesses, which
are marshalled through get and set functions.

There are three unsafe functions: the two introduction forms, and the
pointer elimination form.  The introduction forms are unsafe for the obvious
reason (they act on a pointer that cannot be checked inside the method), but
the elimination form is somewhat more subtle in its unsafety.  By using a
pointer taken from a c_vec::t without keeping a reference to the c_vec::t
itself around, the c_vec could be garbage collected, and the memory within
could be destroyed.  There are legitimate uses for the pointer elimination
form -- for instance, to pass memory back into C -- but great care must be
taken to ensure that a reference to the c_vec::t is still held if needed.

 */

export t;
export create, create_with_dtor;
export get, set;
export size;
export ptr;

/*
 Type: t

 The type representing a native chunk of memory.  Wrapped in a tag for
 opacity; FIXME #818 when it is possible to have truly opaque types, this
 should be revisited.
 */

tag t<T> {
    t({ base: *T, size: uint, rsrc: @dtor_res});
}

resource dtor_res(dtor: option::t<fn@()>) {
    alt dtor {
      option::none. { }
      option::some(f) { f(); }
    }
}

/*
 Section: Introduction forms
 */

unsafe fn create<T>(base: *T, size: uint) -> t<T> {
    ret t({base: base,
           size: size,
           rsrc: @dtor_res(option::none)
          });
}

unsafe fn create_with_dtor<T>(base: *T, size: uint, dtor: fn@()) -> t<T> {
    ret t({base: base,
           size: size,
           rsrc: @dtor_res(option::some(dtor))
          });
}

/*
 Section: Operations
 */

fn get<copy T>(t: t<T>, ofs: uint) -> T {
    assert ofs < (*t).size;
    ret unsafe { *ptr::offset((*t).base, ofs) };
}

fn set<copy T>(t: t<T>, ofs: uint, v: T) {
    assert ofs < (*t).size;
    unsafe { *(ptr::offset((*t).base, ofs) as *mutable T) = v };
}

/*
 Section: Elimination forms
 */

fn size<T>(t: t<T>) -> uint {
    ret (*t).size;
}

unsafe fn ptr<T>(t: t<T>) -> *T {
    ret (*t).base;
}
<commit_msg>c_vec: Remove the mutable cast be forcing the pointer to be mutable throughout (discussion in #1217).<commit_after>/*
Module: c_vec

Library to interface with chunks of memory allocated in C.

It is often desirable to safely interface with memory allocated from C,
encapsulating the unsafety into allocation and destruction time.  Indeed,
allocating memory externally is currently the only way to give Rust shared
mutable state with C programs that keep their own references; vectors are
unsuitable because they could be reallocated or moved at any time, and
importing C memory into a vector takes a one-time snapshot of the memory.

This module simplifies the usage of such external blocks of memory.  Memory
is encapsulated into an opaque object after creation; the lifecycle of the
memory can be optionally managed by Rust, if an appropriate destructor
closure is provided.  Safety is ensured by bounds-checking accesses, which
are marshalled through get and set functions.

There are three unsafe functions: the two introduction forms, and the
pointer elimination form.  The introduction forms are unsafe for the obvious
reason (they act on a pointer that cannot be checked inside the method), but
the elimination form is somewhat more subtle in its unsafety.  By using a
pointer taken from a c_vec::t without keeping a reference to the c_vec::t
itself around, the c_vec could be garbage collected, and the memory within
could be destroyed.  There are legitimate uses for the pointer elimination
form -- for instance, to pass memory back into C -- but great care must be
taken to ensure that a reference to the c_vec::t is still held if needed.

 */

export t;
export create, create_with_dtor;
export get, set;
export size;
export ptr;

/*
 Type: t

 The type representing a native chunk of memory.  Wrapped in a tag for
 opacity; FIXME #818 when it is possible to have truly opaque types, this
 should be revisited.
 */

tag t<T> {
    t({ base: *mutable T, size: uint, rsrc: @dtor_res});
}

resource dtor_res(dtor: option::t<fn@()>) {
    alt dtor {
      option::none. { }
      option::some(f) { f(); }
    }
}

/*
 Section: Introduction forms
 */

unsafe fn create<T>(base: *mutable T, size: uint) -> t<T> {
    ret t({base: base,
           size: size,
           rsrc: @dtor_res(option::none)
          });
}

unsafe fn create_with_dtor<T>(base: *mutable T, size: uint, dtor: fn@())
  -> t<T> {
    ret t({base: base,
           size: size,
           rsrc: @dtor_res(option::some(dtor))
          });
}

/*
 Section: Operations
 */

fn get<copy T>(t: t<T>, ofs: uint) -> T {
    assert ofs < (*t).size;
    ret unsafe { *ptr::mut_offset((*t).base, ofs) };
}

fn set<copy T>(t: t<T>, ofs: uint, v: T) {
    assert ofs < (*t).size;
    unsafe { *ptr::mut_offset((*t).base, ofs) = v };
}

/*
 Section: Elimination forms
 */

fn size<T>(t: t<T>) -> uint {
    ret (*t).size;
}

unsafe fn ptr<T>(t: t<T>) -> *mutable T {
    ret (*t).base;
}
<|endoftext|>


<commit_before>//! This crate provides Serde's two derive macros.
//!
//! ```edition2018
//! # use serde_derive::{Serialize, Deserialize};
//! #
//! #[derive(Serialize, Deserialize)]
//! # struct S;
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html

#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.114")]
#![allow(unknown_lints, bare_trait_objects)]
#![deny(clippy::all, clippy::pedantic)]
// Ignored clippy lints
#![allow(
    clippy::cognitive_complexity,
    clippy::enum_variant_names,
    clippy::needless_pass_by_value,
    clippy::too_many_arguments,
    clippy::trivially_copy_pass_by_ref,
    clippy::used_underscore_binding,
    clippy::wildcard_in_or_patterns,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
    clippy::unnested_or_patterns,
)]
// Ignored clippy_pedantic lints
#![allow(
    clippy::cast_possible_truncation,
    clippy::checked_conversions,
    clippy::doc_markdown,
    clippy::enum_glob_use,
    clippy::filter_map,
    clippy::indexing_slicing,
    clippy::items_after_statements,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::must_use_candidate,
    clippy::option_if_let_else,
    clippy::similar_names,
    clippy::single_match_else,
    clippy::struct_excessive_bools,
    clippy::too_many_lines,
    clippy::unseparated_literal_suffix,
    clippy::use_self,
    clippy::wildcard_imports
)]
// The `quote!` macro requires deep recursion.
#![recursion_limit = "512"]

#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;

extern crate proc_macro;
extern crate proc_macro2;

mod internals;

use proc_macro::TokenStream;
use syn::DeriveInput;

#[macro_use]
mod bound;
#[macro_use]
mod fragment;

mod de;
mod dummy;
mod pretend;
mod ser;
mod try;

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    ser::expand_derive_serialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
    quote!(#(#compile_errors)*)
}
<commit_msg>Suppress match_like_matches_macro clippy lint<commit_after>//! This crate provides Serde's two derive macros.
//!
//! ```edition2018
//! # use serde_derive::{Serialize, Deserialize};
//! #
//! #[derive(Serialize, Deserialize)]
//! # struct S;
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html

#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.114")]
#![allow(unknown_lints, bare_trait_objects)]
#![deny(clippy::all, clippy::pedantic)]
// Ignored clippy lints
#![allow(
    clippy::cognitive_complexity,
    clippy::enum_variant_names,
    clippy::match_like_matches_macro,
    clippy::needless_pass_by_value,
    clippy::too_many_arguments,
    clippy::trivially_copy_pass_by_ref,
    clippy::used_underscore_binding,
    clippy::wildcard_in_or_patterns,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
    clippy::unnested_or_patterns,
)]
// Ignored clippy_pedantic lints
#![allow(
    clippy::cast_possible_truncation,
    clippy::checked_conversions,
    clippy::doc_markdown,
    clippy::enum_glob_use,
    clippy::filter_map,
    clippy::indexing_slicing,
    clippy::items_after_statements,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::must_use_candidate,
    clippy::option_if_let_else,
    clippy::similar_names,
    clippy::single_match_else,
    clippy::struct_excessive_bools,
    clippy::too_many_lines,
    clippy::unseparated_literal_suffix,
    clippy::use_self,
    clippy::wildcard_imports
)]
// The `quote!` macro requires deep recursion.
#![recursion_limit = "512"]

#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;

extern crate proc_macro;
extern crate proc_macro2;

mod internals;

use proc_macro::TokenStream;
use syn::DeriveInput;

#[macro_use]
mod bound;
#[macro_use]
mod fragment;

mod de;
mod dummy;
mod pretend;
mod ser;
mod try;

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    ser::expand_derive_serialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
    quote!(#(#compile_errors)*)
}
<|endoftext|>