<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{
    clone::Clone,
    collections::HashMap,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::{Device, DmNameBuf};

#[cfg(test)]
use crate::engine::strat_engine::cleanup::teardown_pools;

use crate::{
    engine::{
        engine::Eventable,
        event::get_engine_listener_list,
        shared::create_pool_idempotent_or_err,
        strat_engine::{
            backstore::{find_all, get_metadata, identify_block_device},
            cmd::verify_binaries,
            devlinks,
            dm::{get_dm, get_dm_init},
            names::validate_name,
            pool::StratPool,
        },
        structures::Table,
        types::{CreateAction, DeleteAction, RenameAction},
        Engine, EngineEvent, Name, Pool, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

const REQUIRED_DM_MINOR_VERSION: u32 = 37;

#[derive(Debug)]
pub struct StratEngine {
    pools: Table<StratPool>,

    // Map of stratis devices that have been found but one or more stratis block devices are missing
    // which prevents the associated pools from being setup.
    incomplete_pools: HashMap<PoolUuid, HashMap<Device, PathBuf>>,

    // Maps name of DM devices we are watching to the most recent event number
    // we've handled for each
    watched_dev_last_event_nrs: HashMap<DmNameBuf, u32>,
}

impl StratEngine {
    /// Setup a StratEngine.
    /// 1. Verify the existence of Stratis /dev directory.
    /// 2. Setup all the pools belonging to the engine.
    ///    a. Places any devices which belong to a pool, but are not complete
    ///       in the incomplete pools data structure.
    ///
    /// Returns an error if the kernel doesn't support required DM features.
    /// Returns an error if there was an error reading device nodes.
    /// Returns an error if the binaries on which it depends can not be found.
    pub fn initialize() -> StratisResult<StratEngine> {
        let dm = get_dm_init()?;
        verify_binaries()?;
        let minor_dm_version = dm.version()?.1;
        if minor_dm_version < REQUIRED_DM_MINOR_VERSION {
            let err_msg = format!(
                "Requires DM minor version {} but kernel only supports {}",
                REQUIRED_DM_MINOR_VERSION, minor_dm_version
            );
            return Err(StratisError::Engine(ErrorEnum::Error, err_msg));
        }

        devlinks::setup_dev_path()?;

        let mut engine = StratEngine {
            pools: Table::default(),
            incomplete_pools: HashMap::new(),
            watched_dev_last_event_nrs: HashMap::new(),
        };

        for (pool_uuid, devices) in find_all()? {
            engine.try_setup_pool(pool_uuid, devices);
        }

        devlinks::cleanup_devlinks(engine.pools().iter());

        Ok(engine)
    }

    // Given a set of devices, try to set up a pool. If the setup fails,
    // insert the devices into incomplete_pools.
    fn try_setup_pool(&mut self, pool_uuid: PoolUuid, devices: HashMap<Device, PathBuf>) {
        /// Setup a pool from constituent devices in the context of some already
        /// setup pools. Return an error on anything that prevents the pool
        /// being set up.
        /// Precondition: every device in devices has already been determined to belong
        /// to the pool with pool_uuid.
        fn setup_pool(
            pool_uuid: PoolUuid,
            devices: &HashMap<Device, PathBuf>,
            pools: &Table<StratPool>,
        ) -> StratisResult<(Name, StratPool)> {
            // FIXME: In this method, various errors are assembled from various
            // sources and combined into strings, so that they
            // can be printed as log messages if necessary. Instead, some kind of
            // error-chaining should be used here and if it is necessary
            // to log the error, the log code should be able to reduce the error
            // chain to something that can be sensibly logged.
            let info_string = || {
                let dev_paths = devices
                    .values()
                    .map(|p| p.to_str().expect("Unix is utf-8"))
                    .collect::<Vec<&str>>()
                    .join(" ,");
                format!("(pool UUID: {}, devnodes: {})", pool_uuid, dev_paths)
            };

            let (timestamp, metadata) = get_metadata(pool_uuid, devices)?.ok_or_else(|| {
                let err_msg = format!("no metadata found for {}", info_string());
                StratisError::Engine(ErrorEnum::NotFound, err_msg)
            })?;

            if pools.contains_name(&metadata.name) {
                let err_msg = format!(
                    "pool with name \"{}\" set up; metadata specifies same name for {}",
                    &metadata.name,
                    info_string()
                );
                return Err(StratisError::Engine(ErrorEnum::AlreadyExists, err_msg));
            }

            StratPool::setup(pool_uuid, devices, timestamp, &metadata).or_else(|e| {
                let err_msg = format!(
                    "failed to set up pool for {}: reason: {:?}",
                    info_string(),
                    e
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            })
        }

        match setup_pool(pool_uuid, &devices, &self.pools) {
            Ok((pool_name, pool)) => {
                devlinks::setup_pool_devlinks(&pool_name, &pool);
                self.pools.insert(pool_name, pool_uuid, pool);
            }
            Err(err) => {
                warn!("no pool set up, reason: {:?}", err);
                self.incomplete_pools.insert(pool_uuid, devices);
            }
        }
    }

    /// Teardown Stratis, preparatory to a shutdown.
    #[cfg(test)]
    pub fn teardown(self) -> StratisResult<()> {
        teardown_pools(self.pools)
    }

    /// Given a udev database entry, process the entry.
    ///
    /// If all the devices are present in the pool and the pool isn't already
    /// up and running, it will get setup and the newly created pool and UUID
    /// will be returned.
    ///
    /// Logs a warning if the block devices appears to be a Stratis block
    /// device and no pool is set up.
    fn block_evaluate(&mut self, device: &libudev::Device) -> Option<(PoolUuid, &mut dyn Pool)> {
        identify_block_device(device).and_then(move |(pool_uuid, _, device, dev_node)| {
            if self.pools.contains_uuid(pool_uuid) {
                None
            } else {
                let mut devices = self
                    .incomplete_pools
                    .remove(&pool_uuid)
                    .unwrap_or_else(HashMap::new);
                devices.insert(device, dev_node);
                self.try_setup_pool(pool_uuid, devices);
                self.pools
                    .get_mut_by_uuid(pool_uuid)
                    .map(|(_, pool)| (pool_uuid, pool as &mut dyn Pool))
            }
        })
    }
}

impl Engine for StratEngine {
    fn handle_event(&mut self, event: &libudev::Event) -> Option<(PoolUuid, &mut dyn Pool)> {
        let event_type = event.event_type();
        if event_type == libudev::EventType::Add || event_type == libudev::EventType::Change {
            self.block_evaluate(event.device())
        } else {
            None
        }
    }

    fn create_pool(
        &mut self,
        name: &str,
        blockdev_paths: &[&Path],
        redundancy: Option<u16>,
    ) -> StratisResult<CreateAction<PoolUuid>> {
        let redundancy = calculate_redundancy!(redundancy);

        validate_name(name)?;

        match self.pools.get_by_name(name) {
            Some((_, pool)) => create_pool_idempotent_or_err(pool, name, blockdev_paths),
            None => {
                let (uuid, pool) = StratPool::initialize(name, blockdev_paths, redundancy)?;

                let name = Name::new(name.to_owned());
                devlinks::pool_added(&name);
                self.pools.insert(name, uuid, pool);
                Ok(CreateAction::Created(uuid))
            }
        }
    }

    fn destroy_pool(&mut self, uuid: PoolUuid) -> StratisResult<DeleteAction<PoolUuid>> {
        if let Some((_, pool)) = self.pools.get_by_uuid(uuid) {
            if pool.has_filesystems() {
                return Err(StratisError::Engine(
                    ErrorEnum::Busy,
                    "filesystems remaining on pool".into(),
                ));
            };
        } else {
            return Ok(DeleteAction::Identity);
        }

        let (pool_name, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        if let Err(err) = pool.destroy() {
            self.pools.insert(pool_name, uuid, pool);
            Err(err)
        } else {
            devlinks::pool_removed(&pool_name);
            Ok(DeleteAction::Deleted(uuid))
        }
    }

    fn rename_pool(
        &mut self,
        uuid: PoolUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<PoolUuid>> {
        validate_name(new_name)?;
        let old_name = rename_pool_pre_idem!(self; uuid; new_name);

        let (_, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        let new_name = Name::new(new_name.to_owned());
        if let Err(err) = pool.write_metadata(&new_name) {
            self.pools.insert(old_name, uuid, pool);
            Err(err)
        } else {
            get_engine_listener_list().notify(&EngineEvent::PoolRenamed {
                dbus_path: pool.get_dbus_path(),
                from: &*old_name,
                to: &*new_name,
            });

            self.pools.insert(new_name.clone(), uuid, pool);
            devlinks::pool_renamed(&old_name, &new_name);
            Ok(RenameAction::Renamed(uuid))
        }
    }

    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &dyn Pool)> {
        get_pool!(self; uuid)
    }

    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut dyn Pool)> {
        get_mut_pool!(self; uuid)
    }

    fn configure_simulator(&mut self, _denominator: u32) -> StratisResult<()> {
        Ok(()) // we're not the simulator and not configurable, so just say ok
    }

    fn pools(&self) -> Vec<(Name, PoolUuid, &dyn Pool)> {
        self.pools
            .iter()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &dyn Pool))
            .collect()
    }

    fn pools_mut(&mut self) -> Vec<(Name, PoolUuid, &mut dyn Pool)> {
        self.pools
            .iter_mut()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &mut dyn Pool))
            .collect()
    }

    fn get_eventable(&self) -> Option<&'static dyn Eventable> {
        Some(get_dm())
    }

    fn evented(&mut self) -> StratisResult<()> {
        let device_list: HashMap<_, _> = get_dm()
            .list_devices()?
            .into_iter()
            .map(|(dm_name, _, event_nr)| {
                (
                    dm_name,
                    event_nr.expect("Supported DM versions always provide a value"),
                )
            })
            .collect();

        for (pool_name, pool_uuid, pool) in &mut self.pools {
            for dm_name in pool.get_eventing_dev_names(*pool_uuid) {
                if device_list.get(&dm_name) > self.watched_dev_last_event_nrs.get(&dm_name) {
                    pool.event_on(*pool_uuid, pool_name, &dm_name)?;
                }
            }
        }

        self.watched_dev_last_event_nrs = device_list;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::fs::remove_dir_all;

    use crate::engine::engine::DEV_PATH;

    use crate::engine::strat_engine::tests::{loopbacked, real};

    use crate::engine::types::EngineAction;

    use super::*;

    /// Verify that a pool rename causes the pool metadata to get the new name.
    fn test_pool_rename(paths: &[&Path]) {
        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let action = engine.rename_pool(uuid1, name2).unwrap();

        assert_eq!(action, RenameAction::Renamed(uuid1));
        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();
        let pool_name: String = engine.get_pool(uuid1).unwrap().0.to_owned();
        assert_eq!(pool_name, name2);
    }

    #[test]
    pub fn loop_test_pool_rename() {
        loopbacked::test_with_spec(
            &loopbacked::DeviceLimits::Range(1, 3, None),
            test_pool_rename,
        );
    }

    #[test]
    pub fn real_test_pool_rename() {
        real::test_with_spec(
            &real::DeviceLimits::AtLeast(1, None, None),
            test_pool_rename,
        );
    }

    /// Test engine setup.
    /// 1. Create two pools.
    /// 2. Verify that both exist.
    /// 3. Teardown the engine.
    /// 4. Initialize the engine.
    /// 5. Verify that pools can be found again.
    /// 6. Teardown the engine and remove "/stratis".
    /// 7. Initialize the engine one more time.
    /// 8. Verify that both pools are found and that there are no incomplete pools.
    fn test_setup(paths: &[&Path]) {
        assert!(paths.len() > 1);

        let (paths1, paths2) = paths.split_at(paths.len() / 2);

        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths1, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let uuid2 = engine
            .create_pool(name2, paths2, None)
            .unwrap()
            .changed()
            .unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
        remove_dir_all(DEV_PATH).unwrap();

        let engine = StratEngine::initialize().unwrap();
        assert_eq!(engine.incomplete_pools, HashMap::new());

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
    }

    #[test]
    pub fn loop_test_setup() {
        loopbacked::test_with_spec(&loopbacked::DeviceLimits::Range(2, 3, None), test_setup);
    }

    #[test]
    pub fn real_test_setup() {
        real::test_with_spec(&real::DeviceLimits::AtLeast(2, None, None), test_setup);
    }
}
<commit_msg>Use setup_pool methods to distinguish among reasons for no setup<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{
    clone::Clone,
    collections::HashMap,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::{Device, DmNameBuf};

#[cfg(test)]
use crate::engine::strat_engine::cleanup::teardown_pools;

use crate::{
    engine::{
        engine::Eventable,
        event::get_engine_listener_list,
        shared::create_pool_idempotent_or_err,
        strat_engine::{
            backstore::{find_all, get_metadata, identify_block_device},
            cmd::verify_binaries,
            devlinks,
            dm::{get_dm, get_dm_init},
            names::validate_name,
            pool::StratPool,
        },
        structures::Table,
        types::{CreateAction, DeleteAction, RenameAction},
        Engine, EngineEvent, Name, Pool, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

const REQUIRED_DM_MINOR_VERSION: u32 = 37;

#[derive(Debug)]
pub struct StratEngine {
    pools: Table<StratPool>,

    // Map of stratis devices that have been found but one or more stratis block devices are missing
    // which prevents the associated pools from being setup.
    incomplete_pools: HashMap<PoolUuid, HashMap<Device, PathBuf>>,

    // Maps name of DM devices we are watching to the most recent event number
    // we've handled for each
    watched_dev_last_event_nrs: HashMap<DmNameBuf, u32>,
}

impl StratEngine {
    /// Setup a StratEngine.
    /// 1. Verify the existence of Stratis /dev directory.
    /// 2. Setup all the pools belonging to the engine.
    ///    a. Places any devices which belong to a pool, but are not complete
    ///       in the incomplete pools data structure.
    ///
    /// Returns an error if the kernel doesn't support required DM features.
    /// Returns an error if there was an error reading device nodes.
    /// Returns an error if the binaries on which it depends can not be found.
    pub fn initialize() -> StratisResult<StratEngine> {
        let dm = get_dm_init()?;
        verify_binaries()?;
        let minor_dm_version = dm.version()?.1;
        if minor_dm_version < REQUIRED_DM_MINOR_VERSION {
            let err_msg = format!(
                "Requires DM minor version {} but kernel only supports {}",
                REQUIRED_DM_MINOR_VERSION, minor_dm_version
            );
            return Err(StratisError::Engine(ErrorEnum::Error, err_msg));
        }

        devlinks::setup_dev_path()?;

        let mut engine = StratEngine {
            pools: Table::default(),
            incomplete_pools: HashMap::new(),
            watched_dev_last_event_nrs: HashMap::new(),
        };

        for (pool_uuid, devices) in find_all()? {
            engine.try_setup_pool(pool_uuid, devices);
        }

        devlinks::cleanup_devlinks(engine.pools().iter());

        Ok(engine)
    }

    // Given a set of devices, try to set up a pool. If the setup fails,
    // insert the devices into incomplete_pools.
    fn try_setup_pool(&mut self, pool_uuid: PoolUuid, devices: HashMap<Device, PathBuf>) {
        // Setup a pool from constituent devices in the context of some already
        // setup pools.
        // Return None if the pool's metadata was not found. This is a
        // legitimate non-error condition, which may result if only a subset
        // of the pool's devices are in the set of devices being used.
        // Return an error on all other errors. Note that any one of these
        // errors could represent a temporary condition, that could be changed
        // by finding another device. So it is reasonable to treat them all
        // as loggable at the warning level, but not at the error level.
        // Precondition: every device in devices has already been determined to belong
        // to the pool with pool_uuid.
        fn setup_pool(
            pool_uuid: PoolUuid,
            devices: &HashMap<Device, PathBuf>,
            pools: &Table<StratPool>,
        ) -> Result<Option<(Name, StratPool)>, String> {
            let (timestamp, metadata) = match get_metadata(pool_uuid, devices) {
                Err(err) => return Err(format!(
                        "There was an error encountered when reading the metadata for the devices found for pool with UUID {}: {}",
                        pool_uuid.to_simple_ref(),
                        err)),
                Ok(None) => return Ok(None),
                Ok(Some((timestamp, metadata))) => (timestamp, metadata),
            };

            if let Some((uuid, _)) = pools.get_by_name(&metadata.name) {
                return Err(format!(
                        "There is a pool name conflict. The devices currently being processed have been identified as belonging to the pool with UUID {} and name {}, but a pool with the same name and UUID {} is already active",
                        pool_uuid.to_simple_ref(),
                        &metadata.name,
                        uuid.to_simple_ref()));
            }

            StratPool::setup(pool_uuid, devices, timestamp, &metadata)
                .map_err(|err| {
                    format!(
                        "An attempt to set up pool with UUID {} from the assembled devices failed: {}",
                        pool_uuid.to_simple_ref(),
                        err
                    )
                })
                .map(Some)
        }

        let result = setup_pool(pool_uuid, &devices, &self.pools);

        if let Err(err) = &result {
            warn!("{}", err);
        }

        match result {
            Ok(Some((pool_name, pool))) => {
                devlinks::setup_pool_devlinks(&pool_name, &pool);
                self.pools.insert(pool_name, pool_uuid, pool);
            }
            _ => {
                self.incomplete_pools.insert(pool_uuid, devices);
            }
        }
    }

    /// Teardown Stratis, preparatory to a shutdown.
    #[cfg(test)]
    pub fn teardown(self) -> StratisResult<()> {
        teardown_pools(self.pools)
    }

    /// Given a udev database entry, process the entry.
    ///
    /// If all the devices are present in the pool and the pool isn't already
    /// up and running, it will get setup and the newly created pool and UUID
    /// will be returned.
    ///
    /// Logs a warning if the block devices appears to be a Stratis block
    /// device and no pool is set up.
    fn block_evaluate(&mut self, device: &libudev::Device) -> Option<(PoolUuid, &mut dyn Pool)> {
        identify_block_device(device).and_then(move |(pool_uuid, _, device, dev_node)| {
            if self.pools.contains_uuid(pool_uuid) {
                None
            } else {
                let mut devices = self
                    .incomplete_pools
                    .remove(&pool_uuid)
                    .unwrap_or_else(HashMap::new);
                devices.insert(device, dev_node);
                self.try_setup_pool(pool_uuid, devices);
                self.pools
                    .get_mut_by_uuid(pool_uuid)
                    .map(|(_, pool)| (pool_uuid, pool as &mut dyn Pool))
            }
        })
    }
}

impl Engine for StratEngine {
    fn handle_event(&mut self, event: &libudev::Event) -> Option<(PoolUuid, &mut dyn Pool)> {
        let event_type = event.event_type();
        if event_type == libudev::EventType::Add || event_type == libudev::EventType::Change {
            self.block_evaluate(event.device())
        } else {
            None
        }
    }

    fn create_pool(
        &mut self,
        name: &str,
        blockdev_paths: &[&Path],
        redundancy: Option<u16>,
    ) -> StratisResult<CreateAction<PoolUuid>> {
        let redundancy = calculate_redundancy!(redundancy);

        validate_name(name)?;

        match self.pools.get_by_name(name) {
            Some((_, pool)) => create_pool_idempotent_or_err(pool, name, blockdev_paths),
            None => {
                let (uuid, pool) = StratPool::initialize(name, blockdev_paths, redundancy)?;

                let name = Name::new(name.to_owned());
                devlinks::pool_added(&name);
                self.pools.insert(name, uuid, pool);
                Ok(CreateAction::Created(uuid))
            }
        }
    }

    fn destroy_pool(&mut self, uuid: PoolUuid) -> StratisResult<DeleteAction<PoolUuid>> {
        if let Some((_, pool)) = self.pools.get_by_uuid(uuid) {
            if pool.has_filesystems() {
                return Err(StratisError::Engine(
                    ErrorEnum::Busy,
                    "filesystems remaining on pool".into(),
                ));
            };
        } else {
            return Ok(DeleteAction::Identity);
        }

        let (pool_name, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        if let Err(err) = pool.destroy() {
            self.pools.insert(pool_name, uuid, pool);
            Err(err)
        } else {
            devlinks::pool_removed(&pool_name);
            Ok(DeleteAction::Deleted(uuid))
        }
    }

    fn rename_pool(
        &mut self,
        uuid: PoolUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<PoolUuid>> {
        validate_name(new_name)?;
        let old_name = rename_pool_pre_idem!(self; uuid; new_name);

        let (_, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        let new_name = Name::new(new_name.to_owned());
        if let Err(err) = pool.write_metadata(&new_name) {
            self.pools.insert(old_name, uuid, pool);
            Err(err)
        } else {
            get_engine_listener_list().notify(&EngineEvent::PoolRenamed {
                dbus_path: pool.get_dbus_path(),
                from: &*old_name,
                to: &*new_name,
            });

            self.pools.insert(new_name.clone(), uuid, pool);
            devlinks::pool_renamed(&old_name, &new_name);
            Ok(RenameAction::Renamed(uuid))
        }
    }

    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &dyn Pool)> {
        get_pool!(self; uuid)
    }

    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut dyn Pool)> {
        get_mut_pool!(self; uuid)
    }

    fn configure_simulator(&mut self, _denominator: u32) -> StratisResult<()> {
        Ok(()) // we're not the simulator and not configurable, so just say ok
    }

    fn pools(&self) -> Vec<(Name, PoolUuid, &dyn Pool)> {
        self.pools
            .iter()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &dyn Pool))
            .collect()
    }

    fn pools_mut(&mut self) -> Vec<(Name, PoolUuid, &mut dyn Pool)> {
        self.pools
            .iter_mut()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &mut dyn Pool))
            .collect()
    }

    fn get_eventable(&self) -> Option<&'static dyn Eventable> {
        Some(get_dm())
    }

    fn evented(&mut self) -> StratisResult<()> {
        let device_list: HashMap<_, _> = get_dm()
            .list_devices()?
            .into_iter()
            .map(|(dm_name, _, event_nr)| {
                (
                    dm_name,
                    event_nr.expect("Supported DM versions always provide a value"),
                )
            })
            .collect();

        for (pool_name, pool_uuid, pool) in &mut self.pools {
            for dm_name in pool.get_eventing_dev_names(*pool_uuid) {
                if device_list.get(&dm_name) > self.watched_dev_last_event_nrs.get(&dm_name) {
                    pool.event_on(*pool_uuid, pool_name, &dm_name)?;
                }
            }
        }

        self.watched_dev_last_event_nrs = device_list;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::fs::remove_dir_all;

    use crate::engine::engine::DEV_PATH;

    use crate::engine::strat_engine::tests::{loopbacked, real};

    use crate::engine::types::EngineAction;

    use super::*;

    /// Verify that a pool rename causes the pool metadata to get the new name.
    fn test_pool_rename(paths: &[&Path]) {
        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let action = engine.rename_pool(uuid1, name2).unwrap();

        assert_eq!(action, RenameAction::Renamed(uuid1));
        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();
        let pool_name: String = engine.get_pool(uuid1).unwrap().0.to_owned();
        assert_eq!(pool_name, name2);
    }

    #[test]
    pub fn loop_test_pool_rename() {
        loopbacked::test_with_spec(
            &loopbacked::DeviceLimits::Range(1, 3, None),
            test_pool_rename,
        );
    }

    #[test]
    pub fn real_test_pool_rename() {
        real::test_with_spec(
            &real::DeviceLimits::AtLeast(1, None, None),
            test_pool_rename,
        );
    }

    /// Test engine setup.
    /// 1. Create two pools.
    /// 2. Verify that both exist.
    /// 3. Teardown the engine.
    /// 4. Initialize the engine.
    /// 5. Verify that pools can be found again.
    /// 6. Teardown the engine and remove "/stratis".
    /// 7. Initialize the engine one more time.
    /// 8. Verify that both pools are found and that there are no incomplete pools.
    fn test_setup(paths: &[&Path]) {
        assert!(paths.len() > 1);

        let (paths1, paths2) = paths.split_at(paths.len() / 2);

        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths1, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let uuid2 = engine
            .create_pool(name2, paths2, None)
            .unwrap()
            .changed()
            .unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
        remove_dir_all(DEV_PATH).unwrap();

        let engine = StratEngine::initialize().unwrap();
        assert_eq!(engine.incomplete_pools, HashMap::new());

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
    }

    #[test]
    pub fn loop_test_setup() {
        loopbacked::test_with_spec(&loopbacked::DeviceLimits::Range(2, 3, None), test_setup);
    }

    #[test]
    pub fn real_test_setup() {
        real::test_with_spec(&real::DeviceLimits::AtLeast(2, None, None), test_setup);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
use slog;

use std::mem;
use std::fmt;

use store::{TxPtr, BlockHeaderPtr};
use store::FlatFilePtr;

use store::flatfileset::FlatFileSet;

use store::spent_tree::SpendingError;
use store::spent_tree::BlockPtr;

use store::flatfile::INITIAL_WRITEPOS;
use super::SpentTreeStats;

use super::params;

/// A record is a 16 byte structure that points to either a
/// * blockheader
/// * transaction
/// * transaction-output
///
/// The skips point to other Records; at least the previous.
///
/// The exact format is still in work-in-progress.
///
#[derive(Debug, Copy, Clone)]
pub enum Record {

    OrphanBlock {
        file_number: i16,
        file_offset: u32,
    },
    Block {
        file_number: i16,
        file_offset: u32,
        prev:        u64
    },
    Transaction {
        file_number: i16,
        file_offset: u32,
        skips:       [i16; params::SKIP_FIELDS]
    },
    OutputLarge {
        file_number:  i16,
        file_offset:  u32,
        output_index: u32

    },
    Output {
        output_index: u8,
        file_number:  i16,
        file_offset:  u32,
        skips:       [i16; params::SKIP_FIELDS]
    },

    // If a referenced output doesn't exist at the time of block-insertion,
    // the record references an input instead
    UnmatchedInput

}

impl Record {
    pub fn new_unmatched_input() -> Record {
        Record::UnmatchedInput
    }

    pub fn new_transaction(tx_ptr: TxPtr) -> Record {

        Record::Transaction {
            file_number: tx_ptr.get_file_number(),
            file_offset: tx_ptr.get_file_offset() as u32,
            skips: [-1; params::SKIP_FIELDS]
        }
    }

    pub fn new_orphan_block(block_header_ptr: BlockHeaderPtr) -> Record {
        Record::OrphanBlock {
            file_number: block_header_ptr.get_file_number(),
            file_offset: block_header_ptr.get_file_offset() as u32

        }

    }

    pub fn new_block(previous: BlockPtr, orphan_block_record: Record) -> Record {

        match orphan_block_record {
            Record::OrphanBlock { file_number: n, file_offset: o } =>
                Record::Block { file_number: n, file_offset: o, prev: previous.start.to_index() + previous.length - 1 },

            _ => panic!("Expecting orphan block record. Tried to link {:?} to prev {:?} ", orphan_block_record, previous)
        }

    }

    pub fn new_output(txptr: TxPtr, output_index: u32) -> Record {
        if output_index <= u8::max_value() as u32 {
            Record::Output {
                output_index: output_index as u8,
                file_number:  txptr.get_file_number(),
                file_offset:  txptr.get_file_offset() as u32,
                skips:        [-1; params::SKIP_FIELDS]
            }
        }
            else {
                Record::OutputLarge {
                    output_index: output_index ,
                    file_number:  txptr.get_file_number(),
                    file_offset:  txptr.get_file_offset()  as u32,
                }
            }

    }

    pub fn is_transaction(&self) -> bool {
        match self {
            Record::Transaction { ..}  => true,
            _ => false
        }
    }

    pub fn get_transaction_ptr(self) -> TxPtr {
        match self {
            Record::Transaction { file_number: n, file_offset: o, .. } => TxPtr::new(n,o as u64),
            _ => panic!("get_transaction_ptr transaction record expected, got  {:?}", self)

        }
    }

    pub fn get_block_header_ptr(self) -> BlockHeaderPtr {
        match self {
            Record::Block       { file_number: n, file_offset: o, .. } => BlockHeaderPtr::new(n,o as u64),
            Record::OrphanBlock { file_number: n, file_offset: o, .. } => BlockHeaderPtr::new(n,o as u64),
            _ => panic!("transaction record expected")

        }
    }

    pub fn is_output(&self) -> bool {
        match self {
            Record::Output { .. }      => true,
            Record::OutputLarge {..} => true,
            _ => false
        }

    }

    pub fn is_block(&self) -> bool {
        match self {
            Record::Block { .. }      => true,
            Record::OrphanBlock {..} => true,
            _ => false
        }

    }

    pub fn is_unmatched_input(&self) -> bool {
        match self {
            Record::UnmatchedInput { .. } => true,
            _ => false
        }
    }

    fn filenumber_and_pos(&self) -> i64 {
        match self {
            Record::Transaction { file_number: n, file_offset: f, .. } => ((n as i64) << 32)  |  f as i64,
            Record::Output      { file_number: n, file_offset: f, .. } => ((n as i64) << 32)  |  f as i64,
            Record::OutputLarge { file_number: n, file_offset: f, .. } => ((n as i64) << 32)  |  f as i64,
            _ => unreachable!()
        }
    }

    fn output_index(self) -> u32 {
        match self {
            Record::Output      { output_index: x, .. } => x as u32,
            Record::OutputLarge { output_index: x, .. } => x as u32,
            _ => unreachable!()
        }
    }

    // test only as normally it makes no sense to treat file_offsets from different record-types
    // as a single value
    #[cfg(test)]
    pub fn get_file_offset(self) -> u32 {
        match self {
            Record::OrphanBlock { file_offset: f, .. } => f,
            Record::Block       { file_offset: f, .. } => f,
            Record::Transaction { file_offset: f, .. } => f,
            Record::Output      { file_offset: f, .. } => f,
            Record::OutputLarge { file_offset: f, .. } => f,
            _ => unimplemented!()
        }
    }

}

/// A filepointer that points to a record in the SpentTree
#[derive(PartialEq, Copy, Clone)]
pub struct RecordPtr(u64);

impl FlatFilePtr for RecordPtr {
    fn new(file_number: i16, file_offset: u64) -> RecordPtr {
        assert_eq!(file_number, 0); // can only have one spent-tree file

        RecordPtr((file_offset as u64 - INITIAL_WRITEPOS)
                      / mem::size_of::<Record>() as u64)
    }

    fn get_file_number(self) -> i16 { 0 }
    fn get_file_offset(self) -> u64 {
        INITIAL_WRITEPOS + self.0 * mem::size_of::<Record>() as u64
    }

}

impl fmt::Debug for RecordPtr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:?}", self.0)
    }
}

impl RecordPtr {
    pub fn new(ptr: u64) -> Self {
        RecordPtr (ptr )
    }

    pub fn to_index(&self) -> u64 {
        self.0
    }

/*
    pub fn iter(self, fileset: &mut FlatFileSet) -> RecordBlockIterator {
        RecordBlockIterator {
            cur_ptr: self.next_in_block(),
            fileset: fileset
        }
    }
    */
}
/*
pub struct RecordBlockIterator<'a> {
    cur_ptr: RecordPtr,
    //fileset: &'a mut FlatFileSet<P
}

/// Browsing backwards over the entire tree
impl<'a> Iterator for RecordBlockIterator<'a> {
    type Item = RecordPtr;

    fn next(&mut self) -> Option<RecordPtr> {
        let rec: Record = *self.fileset.read_fixed::<Record>(self.cur_ptr.ptr);

        if rec.ptr.is_blockheader() {
            None
        } else {
            let result = self.cur_ptr;
            self.cur_ptr = self.cur_ptr.next_in_block();

            Some(result)
        }
    }
}
*/

impl Record {
    /*
    pub fn previous(&self, fileset: &mut FlatFileSet) -> Option<&Record> {
        match self.bits(0,2) {
            0 => Some( fileset.read_fixed(self.skips_as_fileptr()) ),
            1 => Some( self.before_in_memory(1) ),
            2 => Some( self.before_in_memory( self.skips_bit_3_to_16())),
            3 => None,
            _ => unreachable!()
        }
    }
    */

    pub fn seek(&self) -> Option<&Record> {
        None
    }

    /// This is a preliminary new. To set the proper skip pointers
    /// we must now where we are in the file so we do this aferwards in set_skips
    /*pub fn new(content: FilePtr) -> Self {
        Record {
            ptr: content,
            skips: [0; params::SKIP_FIELDS]
        }
    }*/

/*
    fn set_ptr_in_skips(&mut self, ptr: RecordPtr) {
        let cv: [u64; 1] = [ptr.ptr.to_u64()];
        self.skips = unsafe { mem::transmute(cv) };
    }

    fn get_ptr_from_skips(&self) -> RecordPtr {
        let cv: [u64; 1] = unsafe { mem::transmute(self.skips) };

        RecordPtr::new(FilePtr::from_u64(cv[0]))
    }
*/

    pub fn seek_and_set(
        &mut self,
        seek_idx: usize,
        records: &[Record],
        logger: &slog::Logger) -> Result<SpentTreeStats, SpendingError>

    {

        trace!(logger, format!("FL# Start search for {:?} {:?} {:?}", self, seek_idx, records[seek_idx]));

        let mut stats: SpentTreeStats = Default::default();

        // maybe we would like to set skips but there is nothing to actually verify
        if self.is_transaction() { return Ok(stats); }

        stats.inputs += 1;

        // cur  will walk through the skip-list, starting from
        // the previous record
        let mut cur_idx = seek_idx - 1;

        // this is the transaction position we seek (the filepos stripped of the output-index metadata)
        let seek_filenr_pos = self.filenumber_and_pos();
        let seek_output_index = self.output_index();

        // all filenr_pos we've passed are at least this high
        let mut minimal_filenr_pos = seek_filenr_pos;

        // these are the pointers that will be stored in rec. By default, they just point to the
        // previous
        let mut seek_skips = [-1; params::SKIP_FIELDS];

        // for lack of a better name, `skip_r` traces which of the four skips of seek_rec are still
        // "following" the jumps. Initially all are (which will cause seek_rec.skips to be all set
        // to -1 again), and once seek_plus[0] is cur_filenr_pos is too small, skip_r will increase
        let mut skip_r = 0;

        let seek_plus:  Vec<i64> = params::DELTA.iter().map(|n| seek_filenr_pos + n).collect();
        let seek_minus: Vec<i64> = params::DELTA.iter().map(|n| seek_filenr_pos - n).collect();

        loop {
            let cur_rec: Record = records[cur_idx];
            stats.seeks += 1;
            trace!(logger, format!("FL# Seek now at {:?} ", cur_idx));

            match cur_rec {
                Record::OrphanBlock { .. } => {
                    // this must mean we've reached the end of the line as we wouldn't find an orphan
                    // block while connecting somewhere in the middle
                    return Err(SpendingError::OutputNotFound);
                },

                Record::Block { prev: p, .. } => {
                    stats.jumps += 1;

                    cur_idx = p as usize;
                }

                Record::Transaction { file_number: f, file_offset: o, .. } => {
                    let cur_filenr_pos = ((f as i64) << 32) | o as i64;

                    if cur_filenr_pos == seek_filenr_pos {
                        // we've found the transaction of the output before we
                        // found the same output. So we're all good
                        return Ok(stats);
                    } else {

                        if minimal_filenr_pos > cur_filenr_pos {
                            minimal_filenr_pos = cur_filenr_pos;
                        }
                        cur_idx -= 1;
                    }
                },

                Record::Output { file_number: f, file_offset: o, output_index: x, .. } => {
                    let cur_filenr_pos = ((f as i64) << 32) | o as i64;

                    if cur_filenr_pos == seek_filenr_pos  && x as u32 == seek_output_index {
                        trace!(logger, format!("FL# Already spent {:?} {:?}={:?}", cur_idx, x, seek_output_index));
                        return Err(SpendingError::OutputAlreadySpent);
                    }

                    if minimal_filenr_pos > cur_filenr_pos {
                        minimal_filenr_pos = cur_filenr_pos;
                    }

                    cur_idx -= 1;
                },

                Record::OutputLarge { .. } => {
                    unimplemented!()
                },

                _ => {
                    panic!("Unexpected record type {:?} during set_seek", cur_rec);
                }
            }

            // See if there are skip-values we need to update in the record were seeking
            if skip_r < params::DELTA.len() {
                let diff: i64 = cur_idx as i64 - seek_idx as i64;
                if diff > i16::min_value() as i64 && diff < i16::max_value() as i64 {
                    for n in skip_r..params::DELTA.len() {
                        seek_skips[n] = diff as i16;
                    }
                }
                /*else {
                    println!("Proc Diff too large {:?}", diff);
                }*/

                while skip_r < params::SKIP_FIELDS && seek_minus[skip_r] >= minimal_filenr_pos {
                    skip_r += 1;
                }
            }

            /*
            let mut skip = -1;
            for n in (0..params::SKIP_FIELDS).rev() {
                if seek_plus[n] < cur_filenr_pos {
                    skip = cur_rec.skips[n];

                    stats.total_move += (skip as i64).abs();
                    stats.use_diff[n] += 1;

                    if minimal_filenr_pos > cur_filenr_pos - params::DELTA[n] {
                        minimal_filenr_pos = cur_filenr_pos - params::DELTA[n];
                    }
                    break;
                }
            }*/
        }

    }
}

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

    #[test]
    fn test_size_of_record() {
        assert_eq!(::std::mem::size_of::<Record>(), 16);

    }
}
<commit_msg>Fix build error<commit_after>#[macro_use]
use slog;

use std::mem;
use std::fmt;

use store::{TxPtr, BlockHeaderPtr};
use store::FlatFilePtr;

use store::flatfileset::FlatFileSet;

use store::spent_tree::SpendingError;
use store::spent_tree::BlockPtr;

use store::flatfile::INITIAL_WRITEPOS;
use super::SpentTreeStats;

use super::params;

/// A record is a 16 byte structure that points to either a
/// * blockheader
/// * transaction
/// * transaction-output
///
/// The skips point to other Records; at least the previous.
///
/// The exact format is still in work-in-progress.
///
#[derive(Debug, Copy, Clone)]
pub enum Record {

    OrphanBlock {
        file_number: i16,
        file_offset: u32,
    },
    Block {
        file_number: i16,
        file_offset: u32,
        prev:        u64
    },
    Transaction {
        file_number: i16,
        file_offset: u32,
        skips:       [i16; params::SKIP_FIELDS]
    },
    OutputLarge {
        file_number:  i16,
        file_offset:  u32,
        output_index: u32

    },
    Output {
        output_index: u8,
        file_number:  i16,
        file_offset:  u32,
        skips:       [i16; params::SKIP_FIELDS]
    },

    // If a referenced output doesn't exist at the time of block-insertion,
    // the record references an input instead
    UnmatchedInput

}

impl Record {
    pub fn new_unmatched_input() -> Record {
        Record::UnmatchedInput
    }

    pub fn new_transaction(tx_ptr: TxPtr) -> Record {

        Record::Transaction {
            file_number: tx_ptr.get_file_number(),
            file_offset: tx_ptr.get_file_offset() as u32,
            skips: [-1; params::SKIP_FIELDS]
        }
    }

    pub fn new_orphan_block(block_header_ptr: BlockHeaderPtr) -> Record {
        Record::OrphanBlock {
            file_number: block_header_ptr.get_file_number(),
            file_offset: block_header_ptr.get_file_offset() as u32

        }

    }

    pub fn new_block(previous: BlockPtr, orphan_block_record: Record) -> Record {

        match orphan_block_record {
            Record::OrphanBlock { file_number: n, file_offset: o } =>
                Record::Block { file_number: n, file_offset: o, prev: previous.start.to_index() + previous.length - 1 },

            _ => panic!("Expecting orphan block record. Tried to link {:?} to prev {:?} ", orphan_block_record, previous)
        }

    }

    pub fn new_output(txptr: TxPtr, output_index: u32) -> Record {
        if output_index <= u8::max_value() as u32 {
            Record::Output {
                output_index: output_index as u8,
                file_number:  txptr.get_file_number(),
                file_offset:  txptr.get_file_offset() as u32,
                skips:        [-1; params::SKIP_FIELDS]
            }
        }
            else {
                Record::OutputLarge {
                    output_index: output_index ,
                    file_number:  txptr.get_file_number(),
                    file_offset:  txptr.get_file_offset()  as u32,
                }
            }

    }

    pub fn is_transaction(self) -> bool {
        match self {
            Record::Transaction { ..}  => true,
            _ => false
        }
    }

    pub fn get_transaction_ptr(self) -> TxPtr {
        match self {
            Record::Transaction { file_number: n, file_offset: o, .. } => TxPtr::new(n,o as u64),
            _ => panic!("get_transaction_ptr transaction record expected, got  {:?}", self)

        }
    }

    pub fn get_block_header_ptr(self) -> BlockHeaderPtr {
        match self {
            Record::Block       { file_number: n, file_offset: o, .. } => BlockHeaderPtr::new(n,o as u64),
            Record::OrphanBlock { file_number: n, file_offset: o, .. } => BlockHeaderPtr::new(n,o as u64),
            _ => panic!("transaction record expected")

        }
    }

    pub fn is_output(self) -> bool {
        match self {
            Record::Output { .. }      => true,
            Record::OutputLarge {..} => true,
            _ => false
        }

    }

    pub fn is_block(self) -> bool {
        match self {
            Record::Block { .. }      => true,
            Record::OrphanBlock {..} => true,
            _ => false
        }

    }

    pub fn is_unmatched_input(self) -> bool {
        match self {
            Record::UnmatchedInput { .. } => true,
            _ => false
        }
    }

    fn filenumber_and_pos(self) -> i64 {
        match self {
            Record::Transaction { file_number: n, file_offset: f, .. } => ((n as i64) << 32)  |  f as i64,
            Record::Output      { file_number: n, file_offset: f, .. } => ((n as i64) << 32)  |  f as i64,
            Record::OutputLarge { file_number: n, file_offset: f, .. } => ((n as i64) << 32)  |  f as i64,
            _ => unreachable!()
        }
    }

    fn output_index(self) -> u32 {
        match self {
            Record::Output      { output_index: x, .. } => x as u32,
            Record::OutputLarge { output_index: x, .. } => x as u32,
            _ => unreachable!()
        }
    }

    // test only as normally it makes no sense to treat file_offsets from different record-types
    // as a single value
    #[cfg(test)]
    pub fn get_file_offset(self) -> u32 {
        match self {
            Record::OrphanBlock { file_offset: f, .. } => f,
            Record::Block       { file_offset: f, .. } => f,
            Record::Transaction { file_offset: f, .. } => f,
            Record::Output      { file_offset: f, .. } => f,
            Record::OutputLarge { file_offset: f, .. } => f,
            _ => unimplemented!()
        }
    }

}

/// A filepointer that points to a record in the SpentTree
#[derive(PartialEq, Copy, Clone)]
pub struct RecordPtr(u64);

impl FlatFilePtr for RecordPtr {
    fn new(file_number: i16, file_offset: u64) -> RecordPtr {
        assert_eq!(file_number, 0); // can only have one spent-tree file

        RecordPtr((file_offset as u64 - INITIAL_WRITEPOS)
                      / mem::size_of::<Record>() as u64)
    }

    fn get_file_number(self) -> i16 { 0 }
    fn get_file_offset(self) -> u64 {
        INITIAL_WRITEPOS + self.0 * mem::size_of::<Record>() as u64
    }

}

impl fmt::Debug for RecordPtr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:?}", self.0)
    }
}

impl RecordPtr {
    pub fn new(ptr: u64) -> Self {
        RecordPtr (ptr )
    }

    pub fn to_index(self) -> u64 {
        self.0
    }

/*
    pub fn iter(self, fileset: &mut FlatFileSet) -> RecordBlockIterator {
        RecordBlockIterator {
            cur_ptr: self.next_in_block(),
            fileset: fileset
        }
    }
    */
}
/*
pub struct RecordBlockIterator<'a> {
    cur_ptr: RecordPtr,
    //fileset: &'a mut FlatFileSet<P
}

/// Browsing backwards over the entire tree
impl<'a> Iterator for RecordBlockIterator<'a> {
    type Item = RecordPtr;

    fn next(&mut self) -> Option<RecordPtr> {
        let rec: Record = *self.fileset.read_fixed::<Record>(self.cur_ptr.ptr);

        if rec.ptr.is_blockheader() {
            None
        } else {
            let result = self.cur_ptr;
            self.cur_ptr = self.cur_ptr.next_in_block();

            Some(result)
        }
    }
}
*/

impl Record {
    /*
    pub fn previous(&self, fileset: &mut FlatFileSet) -> Option<&Record> {
        match self.bits(0,2) {
            0 => Some( fileset.read_fixed(self.skips_as_fileptr()) ),
            1 => Some( self.before_in_memory(1) ),
            2 => Some( self.before_in_memory( self.skips_bit_3_to_16())),
            3 => None,
            _ => unreachable!()
        }
    }
    */

    pub fn seek(&self) -> Option<&Record> {
        None
    }

    /// This is a preliminary new. To set the proper skip pointers
    /// we must now where we are in the file so we do this aferwards in set_skips
    /*pub fn new(content: FilePtr) -> Self {
        Record {
            ptr: content,
            skips: [0; params::SKIP_FIELDS]
        }
    }*/

/*
    fn set_ptr_in_skips(&mut self, ptr: RecordPtr) {
        let cv: [u64; 1] = [ptr.ptr.to_u64()];
        self.skips = unsafe { mem::transmute(cv) };
    }

    fn get_ptr_from_skips(&self) -> RecordPtr {
        let cv: [u64; 1] = unsafe { mem::transmute(self.skips) };

        RecordPtr::new(FilePtr::from_u64(cv[0]))
    }
*/

    pub fn seek_and_set(
        &mut self,
        seek_idx: usize,
        records: &[Record],
        logger: &slog::Logger) -> Result<SpentTreeStats, SpendingError>

    {

        trace!(logger, format!("FL# Start search for {:?} {:?} {:?}", self, seek_idx, records[seek_idx]));

        let mut stats: SpentTreeStats = Default::default();

        // maybe we would like to set skips but there is nothing to actually verify
        if self.is_transaction() { return Ok(stats); }

        stats.inputs += 1;

        // cur  will walk through the skip-list, starting from
        // the previous record
        let mut cur_idx = seek_idx - 1;

        // this is the transaction position we seek (the filepos stripped of the output-index metadata)
        let seek_filenr_pos = self.filenumber_and_pos();
        let seek_output_index = self.output_index();

        // all filenr_pos we've passed are at least this high
        let mut minimal_filenr_pos = seek_filenr_pos;

        // these are the pointers that will be stored in rec. By default, they just point to the
        // previous
        let mut seek_skips = [-1; params::SKIP_FIELDS];

        // for lack of a better name, `skip_r` traces which of the four skips of seek_rec are still
        // "following" the jumps. Initially all are (which will cause seek_rec.skips to be all set
        // to -1 again), and once seek_plus[0] is cur_filenr_pos is too small, skip_r will increase
        let mut skip_r = 0;

        let seek_plus:  Vec<i64> = params::DELTA.iter().map(|n| seek_filenr_pos + n).collect();
        let seek_minus: Vec<i64> = params::DELTA.iter().map(|n| seek_filenr_pos - n).collect();

        loop {
            let cur_rec: Record = records[cur_idx];
            stats.seeks += 1;
            trace!(logger, format!("FL# Seek now at {:?} ", cur_idx));

            match cur_rec {
                Record::OrphanBlock { .. } => {
                    // this must mean we've reached the end of the line as we wouldn't find an orphan
                    // block while connecting somewhere in the middle
                    return Err(SpendingError::OutputNotFound);
                },

                Record::Block { prev: p, .. } => {
                    stats.jumps += 1;

                    cur_idx = p as usize;
                }

                Record::Transaction { file_number: f, file_offset: o, .. } => {
                    let cur_filenr_pos = ((f as i64) << 32) | o as i64;

                    if cur_filenr_pos == seek_filenr_pos {
                        // we've found the transaction of the output before we
                        // found the same output. So we're all good
                        return Ok(stats);
                    } else {

                        if minimal_filenr_pos > cur_filenr_pos {
                            minimal_filenr_pos = cur_filenr_pos;
                        }
                        cur_idx -= 1;
                    }
                },

                Record::Output { file_number: f, file_offset: o, output_index: x, .. } => {
                    let cur_filenr_pos = ((f as i64) << 32) | o as i64;

                    if cur_filenr_pos == seek_filenr_pos  && x as u32 == seek_output_index {
                        trace!(logger, format!("FL# Already spent {:?} {:?}={:?}", cur_idx, x, seek_output_index));
                        return Err(SpendingError::OutputAlreadySpent);
                    }

                    if minimal_filenr_pos > cur_filenr_pos {
                        minimal_filenr_pos = cur_filenr_pos;
                    }

                    cur_idx -= 1;
                },

                Record::OutputLarge { .. } => {
                    unimplemented!()
                },

                _ => {
                    panic!("Unexpected record type {:?} during set_seek", cur_rec);
                }
            }

            // See if there are skip-values we need to update in the record were seeking
            if skip_r < params::DELTA.len() {
                let diff: i64 = cur_idx as i64 - seek_idx as i64;
                if diff > i16::min_value() as i64 && diff < i16::max_value() as i64 {
                    for n in skip_r..params::DELTA.len() {
                        seek_skips[n] = diff as i16;
                    }
                }
                /*else {
                    println!("Proc Diff too large {:?}", diff);
                }*/

                while skip_r < params::SKIP_FIELDS && seek_minus[skip_r] >= minimal_filenr_pos {
                    skip_r += 1;
                }
            }

            /*
            let mut skip = -1;
            for n in (0..params::SKIP_FIELDS).rev() {
                if seek_plus[n] < cur_filenr_pos {
                    skip = cur_rec.skips[n];

                    stats.total_move += (skip as i64).abs();
                    stats.use_diff[n] += 1;

                    if minimal_filenr_pos > cur_filenr_pos - params::DELTA[n] {
                        minimal_filenr_pos = cur_filenr_pos - params::DELTA[n];
                    }
                    break;
                }
            }*/
        }

    }
}

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

    #[test]
    fn test_size_of_record() {
        assert_eq!(::std::mem::size_of::<Record>(), 16);

    }
}
<|endoftext|>


<commit_before>//! The simplest possible implementation of the TimeSteward API.
//!
//! This implementation is unusably slow on large simulations. Its main use is to cross-check with other TimeSteward implementations to make sure they are implementing the API correctly.
//!
//!

use super::{DeterministicRandomId, SiphashIdGenerator, RowId, TimeId, FieldId, Column,
            ExtendedTime, EventRng, Basics, TimeSteward, FiatEventOperationResult, ValidSince,
            TimeStewardLifetimedMethods};
use std::collections::{HashMap, BTreeMap};
use std::hash::Hash;
// use std::collections::Bound::{Included, Excluded, Unbounded};
use std::any::Any;
use std::borrow::{Borrow, Cow};
use std::rc::Rc;
use rand::Rng;

#[derive (Clone)]
struct Field<B: Basics> {
  last_change: ExtendedTime<B>,
  data: Rc<Any>,
}

#[derive (Clone)]
// struct StewardState<'a, B: Basics> {
struct StewardState<B: Basics> {
  last_change: Option<ExtendedTime<B>>, // B::Time,
  field_states: HashMap<FieldId, Field<B>>,
  // fiat_events: BTreeMap<ExtendedTime<B>, Event<'a, B>>,
  fiat_events: BTreeMap<ExtendedTime<B>, Event<B>>,
}

struct StewardSettings<B: Basics> {
  predictors: Vec<Predictor<B>>,
  constants: B::Constants,
}
#[derive (Clone)]
pub struct Steward<B: Basics> {
  state: StewardState<B>,
  settings: Rc<StewardSettings<B>>,
}
type StewardImpl<B: Basics> = Steward<B>;
// pub struct Steward<'a, B: Basics> {
// steward: Cow<'a, StewardImpl<B>>,
// }
pub struct Snapshot<'a, B: Basics> {
  now: B::Time,
  state: StewardState<B>,
  settings: &'a StewardSettings<B>,
}
pub struct Mutator<'a, B: Basics> {
  now: ExtendedTime<B>,
  steward: &'a mut StewardImpl<B>,
  generator: EventRng,
}
pub struct PredictorAccessor<'a, B: Basics> {
  steward: &'a StewardImpl<B>,
  soonest_prediction: Option<(B::Time, Event<B>)>,
  dependencies_hasher: SiphashIdGenerator,
}
// pub type Event<'a, B: Basics> = super::Event<Mutator<'a, B>>;
pub type Event<B: Basics> = Rc<for<'d, 'e> Fn(&'d mut Mutator<'e, B>)>;
// type Prediction<B: Basics> = super::Prediction<B, Event<B>>;
// it seems impossible to write the lifetimes this way
// type Predictor<'a, B: Basics> = super::Predictor<B, Mutator<'a, B>, PredictorAccessor<'a, B>>;

pub type Predictor<B: Basics> = super::Predictor<PredictorFn<B>>;

type PredictorFn<B: Basics> = Rc<for<'b, 'c> Fn(&'b mut PredictorAccessor<'c, B>, RowId)>;
// type PredictorFn<B: Basics> = Rc<for<'b, 'c> Fn(&'b mut PredictorAccessor<'c, B>, RowId)
//                                              -> Prediction<B>>;
// -> Prediction<B, Event<B>>>;
// -> Prediction<B, Rc<for<'d, 'e> Fn(&'d mut Mutator<'e, B>)>>>;

impl<'a, B: Basics> super::Accessor<B> for Snapshot<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.state.get::<C>(id).map(|P| P.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for Mutator<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|P| P.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for PredictorAccessor<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|P| {
      P.1.id.hash(&mut self.dependencies_hasher);
      P.0
    })
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}

impl<'a, B: Basics> super::MomentaryAccessor<B> for Snapshot<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now
  }
}
impl<'a, B: Basics> super::MomentaryAccessor<B> for Mutator<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now.base
  }
}
impl<'a, B: Basics> super::PredictorAccessor<B> for PredictorAccessor<'a, B> {
  type Event = Event<B>;
  fn predict_immediately(&mut self, event: Event<B>) {
    self.predict_at_time(&self.steward
                           .state
                           .last_change
                           .as_ref()
                           .expect("how can we be calling a predictor when there are no fields \
                                    yet?")
                           .base,
                         event);
  }
  fn predict_at_time(&mut self, time: &B::Time, event: Event<B>) {
    if let Some((ref old_time, _)) = self.soonest_prediction {
      if old_time <= time {
        return;
      }
    }
    self.soonest_prediction = Some((time.clone(), event));
  }
}
impl<'a, B: Basics> super::Snapshot<B> for Snapshot<'a, B> {}

impl<'a, B: Basics> super::Mutator<B> for Mutator<'a, B> {
  // fn get_mut<C: Column>(&mut self, id: RowId) -> Option<&mut C::FieldType>
  // where C::FieldType: Clone
  // {
  // panic!("are we really doing this");
  // }
  fn set<C: Column>(&mut self, id: RowId, data: Option<C::FieldType>) {
    self.steward.state.set_opt::<C>(id, data, &self.now);
  }
  fn rng(&mut self) -> &mut EventRng {
    &mut self.generator
  }
  fn random_id(&mut self) -> RowId {
    RowId { data: [self.generator.gen::<u64>(), self.generator.gen::<u64>()] }
  }
}

// https://github.com/rust-lang/rfcs/issues/1485
trait Filter<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self;
}
impl<T> Filter<T> for Option<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
    self.and_then(|x| if predicate(&x) {
      Some(x)
    } else {
      None
    })
  }
}

impl<B: Basics> StewardState<B> {
  fn get<C: Column>(&self, id: RowId) -> Option<(&C::FieldType, &ExtendedTime<B>)> {
    self.field_states
      .get(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      })
      .map(|something| {
        (something.data
          .downcast_ref::<C::FieldType>()
          .expect("a field had the wrong type for its column")
          .borrow(),
         &something.last_change)
      })
  }
  fn set<C: Column>(&mut self, id: RowId, value: C::FieldType, time: &ExtendedTime<B>) {
    self.field_states
      .insert(FieldId {
                row_id: id,
                column_id: C::column_id(),
              },
              Field {
                data: Rc::new(value),
                last_change: time.clone(),
              });
  }
  fn remove<C: Column>(&mut self, id: RowId) {
    self.field_states
      .remove(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      });
  }
  fn set_opt<C: Column>(&mut self,
                        id: RowId,
                        value_opt: Option<C::FieldType>,
                        time: &ExtendedTime<B>) {
    if let Some(value) = value_opt {
      self.set::<C>(id, value, time);
    } else {
      self.remove::<C>(id);
    }
  }
}
impl<B: Basics> StewardImpl<B> {
  fn next_event(&self) -> Option<(ExtendedTime<B>, Event<B>)> {
    let first_fiat_event_iter =
    // range is unstable
    // https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.range
    // and cannot be enabled in stable rustc
    // https://stackoverflow.com/questions/30975088/use-of-unstable-library-feature-how-can-i-fix-those
    // so use something else...
    //  self.state.fiat_events.range(Excluded(&self.state.last_change), Unbounded).next();
      self.state.fiat_events.iter().map(|ev| (ev.0.clone(), ev.1.clone()));
    let predicted_events_iter = self.settings
      .predictors
      .iter()
      .flat_map(|predictor|
      // TODO change field_states
      // to separate by field type, for efficiency,
      // like the haskell does?
      self.state.field_states.keys().filter_map(move |field_id|
        if field_id.column_id != predictor.column_id {
          None
        } else {
          let mut pa = PredictorAccessor{
              steward: self,
              soonest_prediction: None,
              dependencies_hasher: SiphashIdGenerator::new(),
            };
          (predictor.function)(&mut pa, field_id.row_id);
          let dependencies_hash = pa.dependencies_hasher.generate();
          pa.soonest_prediction.and_then(|(event_base_time, event)|
            super::next_extended_time_of_predicted_event(
              predictor.predictor_id,
              field_id.row_id,
              dependencies_hash,
              event_base_time,
              &self.state.last_change.as_ref().expect ("how can we be calling a predictor when there are no fields yet?")
            ).map(|event_time| (event_time, event)))}));
    let events_iter = first_fiat_event_iter.chain(predicted_events_iter);
    events_iter.min_by_key(|ev| ev.0.clone())
  }

  fn execute_event(&mut self, event_time: ExtendedTime<B>, event: Event<B>) {
    event(&mut Mutator {
      now: event_time.clone(),
      steward: &mut *self,
      generator: super::generator_for_event(event_time.id),
    });
    // if it was a fiat event, clean it up:
    self.state.fiat_events.remove(&event_time);
    self.state.last_change = Some(event_time);
  }

  fn update_until_beginning_of(&mut self, target_time: &B::Time) {
    while let Some(ev) = self.next_event().filter(|ev| ev.0.base < *target_time) {
      let (event_time, event) = ev;
      self.execute_event(event_time, event);
    }
  }
}

// impl<'a, B: Basics> Steward<'a, B> {
impl<B: Basics> Steward<B> {}
impl<'a, B: Basics> TimeStewardLifetimedMethods<'a, B> for Steward<B> {
  type Snapshot = Snapshot<'a, B>;
  fn snapshot_before(&'a mut self, time: &B::Time) -> Option<Self::Snapshot> {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return None;
      }
    }
    self.update_until_beginning_of(time);
    Some(Snapshot {
      now: time.clone(),
      state: self.state.clone(),
      settings: self.settings.as_ref(),
    })
  }
}
impl<B: Basics> TimeSteward<B> for Steward<B> {
  type Event = Event<B>;
  type Predictor = Predictor<B>;

  fn valid_since(&self) -> ValidSince<B::Time> {
    match self.state.last_change {
      None => ValidSince::TheBeginning,
      Some(ref time) => ValidSince::After(time.base.clone()),
    }
  }
  fn new_empty(constants: B::Constants, predictors: Vec<Self::Predictor>) -> Self {
    StewardImpl {
      state: StewardState {
        last_change: None,
        field_states: HashMap::new(),
        fiat_events: BTreeMap::new(),
      },
      settings: Rc::new(StewardSettings {
        predictors: predictors,
        constants: constants,
      }),
    }
  }

  fn insert_fiat_event(&mut self,
                       time: B::Time,
                       id: DeterministicRandomId,
                       event: Self::Event)
                       -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.insert(super::extended_time_of_fiat_event(time, id), event) {
      None => FiatEventOperationResult::Success,
      Some(_) => FiatEventOperationResult::InvalidInput,
    }
  }
  fn erase_fiat_event(&mut self,
                      time: &B::Time,
                      id: DeterministicRandomId)
                      -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.remove(&super::extended_time_of_fiat_event(time.clone(), id)) {
      None => FiatEventOperationResult::InvalidInput,
      Some(_) => FiatEventOperationResult::Success,
    }
  }
}
<commit_msg>lowercase a variable name per rust coding style & warnings<commit_after>//! The simplest possible implementation of the TimeSteward API.
//!
//! This implementation is unusably slow on large simulations. Its main use is to cross-check with other TimeSteward implementations to make sure they are implementing the API correctly.
//!
//!

use super::{DeterministicRandomId, SiphashIdGenerator, RowId, TimeId, FieldId, Column,
            ExtendedTime, EventRng, Basics, TimeSteward, FiatEventOperationResult, ValidSince,
            TimeStewardLifetimedMethods};
use std::collections::{HashMap, BTreeMap};
use std::hash::Hash;
// use std::collections::Bound::{Included, Excluded, Unbounded};
use std::any::Any;
use std::borrow::{Borrow, Cow};
use std::rc::Rc;
use rand::Rng;

#[derive (Clone)]
struct Field<B: Basics> {
  last_change: ExtendedTime<B>,
  data: Rc<Any>,
}

#[derive (Clone)]
// struct StewardState<'a, B: Basics> {
struct StewardState<B: Basics> {
  last_change: Option<ExtendedTime<B>>, // B::Time,
  field_states: HashMap<FieldId, Field<B>>,
  // fiat_events: BTreeMap<ExtendedTime<B>, Event<'a, B>>,
  fiat_events: BTreeMap<ExtendedTime<B>, Event<B>>,
}

struct StewardSettings<B: Basics> {
  predictors: Vec<Predictor<B>>,
  constants: B::Constants,
}
#[derive (Clone)]
pub struct Steward<B: Basics> {
  state: StewardState<B>,
  settings: Rc<StewardSettings<B>>,
}
type StewardImpl<B: Basics> = Steward<B>;
// pub struct Steward<'a, B: Basics> {
// steward: Cow<'a, StewardImpl<B>>,
// }
pub struct Snapshot<'a, B: Basics> {
  now: B::Time,
  state: StewardState<B>,
  settings: &'a StewardSettings<B>,
}
pub struct Mutator<'a, B: Basics> {
  now: ExtendedTime<B>,
  steward: &'a mut StewardImpl<B>,
  generator: EventRng,
}
pub struct PredictorAccessor<'a, B: Basics> {
  steward: &'a StewardImpl<B>,
  soonest_prediction: Option<(B::Time, Event<B>)>,
  dependencies_hasher: SiphashIdGenerator,
}
// pub type Event<'a, B: Basics> = super::Event<Mutator<'a, B>>;
pub type Event<B: Basics> = Rc<for<'d, 'e> Fn(&'d mut Mutator<'e, B>)>;
// type Prediction<B: Basics> = super::Prediction<B, Event<B>>;
// it seems impossible to write the lifetimes this way
// type Predictor<'a, B: Basics> = super::Predictor<B, Mutator<'a, B>, PredictorAccessor<'a, B>>;

pub type Predictor<B: Basics> = super::Predictor<PredictorFn<B>>;

type PredictorFn<B: Basics> = Rc<for<'b, 'c> Fn(&'b mut PredictorAccessor<'c, B>, RowId)>;
// type PredictorFn<B: Basics> = Rc<for<'b, 'c> Fn(&'b mut PredictorAccessor<'c, B>, RowId)
//                                              -> Prediction<B>>;
// -> Prediction<B, Event<B>>>;
// -> Prediction<B, Rc<for<'d, 'e> Fn(&'d mut Mutator<'e, B>)>>>;

impl<'a, B: Basics> super::Accessor<B> for Snapshot<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.state.get::<C>(id).map(|p| p.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for Mutator<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|p| p.0)
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}
impl<'a, B: Basics> super::Accessor<B> for PredictorAccessor<'a, B> {
  fn get<C: Column>(&mut self, id: RowId) -> Option<&C::FieldType> {
    self.steward.state.get::<C>(id).map(|p| {
      p.1.id.hash(&mut self.dependencies_hasher);
      p.0
    })
  }
  fn constants(&self) -> &B::Constants {
    &self.steward.settings.constants
  }
}

impl<'a, B: Basics> super::MomentaryAccessor<B> for Snapshot<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now
  }
}
impl<'a, B: Basics> super::MomentaryAccessor<B> for Mutator<'a, B> {
  fn now(&self) -> &B::Time {
    &self.now.base
  }
}
impl<'a, B: Basics> super::PredictorAccessor<B> for PredictorAccessor<'a, B> {
  type Event = Event<B>;
  fn predict_immediately(&mut self, event: Event<B>) {
    self.predict_at_time(&self.steward
                           .state
                           .last_change
                           .as_ref()
                           .expect("how can we be calling a predictor when there are no fields \
                                    yet?")
                           .base,
                         event);
  }
  fn predict_at_time(&mut self, time: &B::Time, event: Event<B>) {
    if let Some((ref old_time, _)) = self.soonest_prediction {
      if old_time <= time {
        return;
      }
    }
    self.soonest_prediction = Some((time.clone(), event));
  }
}
impl<'a, B: Basics> super::Snapshot<B> for Snapshot<'a, B> {}

impl<'a, B: Basics> super::Mutator<B> for Mutator<'a, B> {
  // fn get_mut<C: Column>(&mut self, id: RowId) -> Option<&mut C::FieldType>
  // where C::FieldType: Clone
  // {
  // panic!("are we really doing this");
  // }
  fn set<C: Column>(&mut self, id: RowId, data: Option<C::FieldType>) {
    self.steward.state.set_opt::<C>(id, data, &self.now);
  }
  fn rng(&mut self) -> &mut EventRng {
    &mut self.generator
  }
  fn random_id(&mut self) -> RowId {
    RowId { data: [self.generator.gen::<u64>(), self.generator.gen::<u64>()] }
  }
}

// https://github.com/rust-lang/rfcs/issues/1485
trait Filter<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self;
}
impl<T> Filter<T> for Option<T> {
  fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
    self.and_then(|x| if predicate(&x) {
      Some(x)
    } else {
      None
    })
  }
}

impl<B: Basics> StewardState<B> {
  fn get<C: Column>(&self, id: RowId) -> Option<(&C::FieldType, &ExtendedTime<B>)> {
    self.field_states
      .get(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      })
      .map(|something| {
        (something.data
          .downcast_ref::<C::FieldType>()
          .expect("a field had the wrong type for its column")
          .borrow(),
         &something.last_change)
      })
  }
  fn set<C: Column>(&mut self, id: RowId, value: C::FieldType, time: &ExtendedTime<B>) {
    self.field_states
      .insert(FieldId {
                row_id: id,
                column_id: C::column_id(),
              },
              Field {
                data: Rc::new(value),
                last_change: time.clone(),
              });
  }
  fn remove<C: Column>(&mut self, id: RowId) {
    self.field_states
      .remove(&FieldId {
        row_id: id,
        column_id: C::column_id(),
      });
  }
  fn set_opt<C: Column>(&mut self,
                        id: RowId,
                        value_opt: Option<C::FieldType>,
                        time: &ExtendedTime<B>) {
    if let Some(value) = value_opt {
      self.set::<C>(id, value, time);
    } else {
      self.remove::<C>(id);
    }
  }
}
impl<B: Basics> StewardImpl<B> {
  fn next_event(&self) -> Option<(ExtendedTime<B>, Event<B>)> {
    let first_fiat_event_iter =
    // range is unstable
    // https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.range
    // and cannot be enabled in stable rustc
    // https://stackoverflow.com/questions/30975088/use-of-unstable-library-feature-how-can-i-fix-those
    // so use something else...
    //  self.state.fiat_events.range(Excluded(&self.state.last_change), Unbounded).next();
      self.state.fiat_events.iter().map(|ev| (ev.0.clone(), ev.1.clone()));
    let predicted_events_iter = self.settings
      .predictors
      .iter()
      .flat_map(|predictor|
      // TODO change field_states
      // to separate by field type, for efficiency,
      // like the haskell does?
      self.state.field_states.keys().filter_map(move |field_id|
        if field_id.column_id != predictor.column_id {
          None
        } else {
          let mut pa = PredictorAccessor{
              steward: self,
              soonest_prediction: None,
              dependencies_hasher: SiphashIdGenerator::new(),
            };
          (predictor.function)(&mut pa, field_id.row_id);
          let dependencies_hash = pa.dependencies_hasher.generate();
          pa.soonest_prediction.and_then(|(event_base_time, event)|
            super::next_extended_time_of_predicted_event(
              predictor.predictor_id,
              field_id.row_id,
              dependencies_hash,
              event_base_time,
              &self.state.last_change.as_ref().expect ("how can we be calling a predictor when there are no fields yet?")
            ).map(|event_time| (event_time, event)))}));
    let events_iter = first_fiat_event_iter.chain(predicted_events_iter);
    events_iter.min_by_key(|ev| ev.0.clone())
  }

  fn execute_event(&mut self, event_time: ExtendedTime<B>, event: Event<B>) {
    event(&mut Mutator {
      now: event_time.clone(),
      steward: &mut *self,
      generator: super::generator_for_event(event_time.id),
    });
    // if it was a fiat event, clean it up:
    self.state.fiat_events.remove(&event_time);
    self.state.last_change = Some(event_time);
  }

  fn update_until_beginning_of(&mut self, target_time: &B::Time) {
    while let Some(ev) = self.next_event().filter(|ev| ev.0.base < *target_time) {
      let (event_time, event) = ev;
      self.execute_event(event_time, event);
    }
  }
}

// impl<'a, B: Basics> Steward<'a, B> {
impl<B: Basics> Steward<B> {}
impl<'a, B: Basics> TimeStewardLifetimedMethods<'a, B> for Steward<B> {
  type Snapshot = Snapshot<'a, B>;
  fn snapshot_before(&'a mut self, time: &B::Time) -> Option<Self::Snapshot> {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return None;
      }
    }
    self.update_until_beginning_of(time);
    Some(Snapshot {
      now: time.clone(),
      state: self.state.clone(),
      settings: self.settings.as_ref(),
    })
  }
}
impl<B: Basics> TimeSteward<B> for Steward<B> {
  type Event = Event<B>;
  type Predictor = Predictor<B>;

  fn valid_since(&self) -> ValidSince<B::Time> {
    match self.state.last_change {
      None => ValidSince::TheBeginning,
      Some(ref time) => ValidSince::After(time.base.clone()),
    }
  }
  fn new_empty(constants: B::Constants, predictors: Vec<Self::Predictor>) -> Self {
    StewardImpl {
      state: StewardState {
        last_change: None,
        field_states: HashMap::new(),
        fiat_events: BTreeMap::new(),
      },
      settings: Rc::new(StewardSettings {
        predictors: predictors,
        constants: constants,
      }),
    }
  }

  fn insert_fiat_event(&mut self,
                       time: B::Time,
                       id: DeterministicRandomId,
                       event: Self::Event)
                       -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.insert(super::extended_time_of_fiat_event(time, id), event) {
      None => FiatEventOperationResult::Success,
      Some(_) => FiatEventOperationResult::InvalidInput,
    }
  }
  fn erase_fiat_event(&mut self,
                      time: &B::Time,
                      id: DeterministicRandomId)
                      -> FiatEventOperationResult {
    if let Some(ref change) = self.state.last_change {
      if change.base >= *time {
        return FiatEventOperationResult::InvalidTime;
      }
    }
    match self.state.fiat_events.remove(&super::extended_time_of_fiat_event(time.clone(), id)) {
      None => FiatEventOperationResult::InvalidInput,
      Some(_) => FiatEventOperationResult::Success,
    }
  }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]

#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(custom_attribute)]
#![feature(nll)]
#![allow(unused_attributes)]
#![allow(dead_code)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]

#![recursion_limit="256"]

extern crate flate2;
#[macro_use]
extern crate log;

#[macro_use]
extern crate rustc;
extern crate rustc_target;
extern crate rustc_metadata;
extern crate rustc_mir;
extern crate rustc_incremental;
extern crate syntax;
extern crate syntax_pos;
#[macro_use] extern crate rustc_data_structures;

use rustc::ty::TyCtxt;

pub mod link;
pub mod codegen_backend;
pub mod symbol_names;
pub mod symbol_names_test;

/// check for the #[rustc_error] annotation, which forces an
/// error in codegen. This is used to write compile-fail tests
/// that actually test that compilation succeeds without
/// reporting an error.
pub fn check_for_rustc_errors_attr(tcx: TyCtxt) {
    if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() {
        let main_def_id = tcx.hir.local_def_id(id);

        if tcx.has_attr(main_def_id, "rustc_error") {
            tcx.sess.span_fatal(span, "compilation successful");
        }
    }
}

__build_diagnostic_array! { librustc_codegen_utils, DIAGNOSTICS }
<commit_msg>[eddyb] rustc_codegen_utils: remove extraneous `#![allow(dead_code)]`.<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]

#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(custom_attribute)]
#![feature(nll)]
#![allow(unused_attributes)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]

#![recursion_limit="256"]

extern crate flate2;
#[macro_use]
extern crate log;

#[macro_use]
extern crate rustc;
extern crate rustc_target;
extern crate rustc_metadata;
extern crate rustc_mir;
extern crate rustc_incremental;
extern crate syntax;
extern crate syntax_pos;
#[macro_use] extern crate rustc_data_structures;

use rustc::ty::TyCtxt;

pub mod link;
pub mod codegen_backend;
pub mod symbol_names;
pub mod symbol_names_test;

/// check for the #[rustc_error] annotation, which forces an
/// error in codegen. This is used to write compile-fail tests
/// that actually test that compilation succeeds without
/// reporting an error.
pub fn check_for_rustc_errors_attr(tcx: TyCtxt) {
    if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() {
        let main_def_id = tcx.hir.local_def_id(id);

        if tcx.has_attr(main_def_id, "rustc_error") {
            tcx.sess.span_fatal(span, "compilation successful");
        }
    }
}

__build_diagnostic_array! { librustc_codegen_utils, DIAGNOSTICS }
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use animation::Animation;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use dom::OpaqueNode;
use euclid::size::TypedSize2D;
use gecko_bindings::bindings::RawServoStyleSet;
use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use media_queries::{Device, MediaType};
use num_cpus;
use parking_lot::RwLock;
use rayon;
use std::cmp;
use std::collections::HashMap;
use std::env;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use style_traits::ViewportPx;
use stylesheets::Stylesheet;
use stylist::Stylist;

pub struct PerDocumentStyleDataImpl {
    /// Rule processor.
    pub stylist: Arc<Stylist>,

    /// List of stylesheets, mirrored from Gecko.
    pub stylesheets: Vec<Arc<Stylesheet>>,

    /// Whether the stylesheets list above has changed since the last restyle.
    pub stylesheets_changed: bool,

    // FIXME(bholley): Hook these up to something.
    pub new_animations_sender: Sender<Animation>,
    pub new_animations_receiver: Receiver<Animation>,
    pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
    pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,

    // FIXME(bholley): This shouldn't be per-document.
    pub work_queue: Option<rayon::ThreadPool>,

    pub num_threads: usize,
}

pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);

lazy_static! {
    pub static ref NUM_THREADS: usize = {
        match env::var("STYLO_THREADS").map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS")) {
            Ok(num) => num,
            _ => cmp::max(num_cpus::get() * 3 / 4, 1),
        }
    };
}

impl PerDocumentStyleData {
    pub fn new() -> Self {
        // FIXME(bholley): Real window size.
        let window_size: TypedSize2D<f32, ViewportPx> = TypedSize2D::new(800.0, 600.0);
        let device = Device::new(MediaType::Screen, window_size);

        let (new_anims_sender, new_anims_receiver) = channel();

        PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
            stylist: Arc::new(Stylist::new(device)),
            stylesheets: vec![],
            stylesheets_changed: true,
            new_animations_sender: new_anims_sender,
            new_animations_receiver: new_anims_receiver,
            running_animations: Arc::new(RwLock::new(HashMap::new())),
            expired_animations: Arc::new(RwLock::new(HashMap::new())),
            work_queue: if *NUM_THREADS <= 1 {
                None
            } else {
                let configuration =
                    rayon::Configuration::new().set_num_threads(*NUM_THREADS);
                rayon::ThreadPool::new(configuration).ok()
            },
            num_threads: *NUM_THREADS,
        }))
    }

    pub fn borrow(&self) -> AtomicRef<PerDocumentStyleDataImpl> {
        self.0.borrow()
    }

    pub fn borrow_mut(&self) -> AtomicRefMut<PerDocumentStyleDataImpl> {
        self.0.borrow_mut()
    }
}

impl PerDocumentStyleDataImpl {
    pub fn flush_stylesheets(&mut self) {
        // The stylist wants to be flushed if either the stylesheets change or the
        // device dimensions change. When we add support for media queries, we'll
        // need to detect the latter case and trigger a flush as well.
        if self.stylesheets_changed {
            let _ = Arc::get_mut(&mut self.stylist).unwrap()
                                                   .update(&self.stylesheets, None, true);
            self.stylesheets_changed = false;
        }
    }
}

unsafe impl HasFFI for PerDocumentStyleData {
    type FFIType = RawServoStyleSet;
}
unsafe impl HasSimpleFFI for PerDocumentStyleData {}
unsafe impl HasBoxFFI for PerDocumentStyleData {}

impl Drop for PerDocumentStyleDataImpl {
    fn drop(&mut self) {
        let _ = self.work_queue.take();
    }
}
<commit_msg>style: Document Gecko's data module.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Data needed to style a Gecko document.

use animation::Animation;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use dom::OpaqueNode;
use euclid::size::TypedSize2D;
use gecko_bindings::bindings::RawServoStyleSet;
use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use media_queries::{Device, MediaType};
use num_cpus;
use parking_lot::RwLock;
use rayon;
use std::cmp;
use std::collections::HashMap;
use std::env;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use style_traits::ViewportPx;
use stylesheets::Stylesheet;
use stylist::Stylist;

/// The container for data that a Servo-backed Gecko document needs to style
/// itself.
pub struct PerDocumentStyleDataImpl {
    /// Rule processor.
    pub stylist: Arc<Stylist>,

    /// List of stylesheets, mirrored from Gecko.
    pub stylesheets: Vec<Arc<Stylesheet>>,

    /// Whether the stylesheets list above has changed since the last restyle.
    pub stylesheets_changed: bool,

    // FIXME(bholley): Hook these up to something.
    /// Unused. Will go away when we actually implement transitions and
    /// animations properly.
    pub new_animations_sender: Sender<Animation>,
    /// Unused. Will go away when we actually implement transitions and
    /// animations properly.
    pub new_animations_receiver: Receiver<Animation>,
    /// Unused. Will go away when we actually implement transitions and
    /// animations properly.
    pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
    /// Unused. Will go away when we actually implement transitions and
    /// animations properly.
    pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,

    /// The worker thread pool.
    /// FIXME(bholley): This shouldn't be per-document.
    pub work_queue: Option<rayon::ThreadPool>,

    /// The number of threads of the work queue.
    pub num_threads: usize,
}

/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
/// and unexpected races while trying to mutate it.
pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);

lazy_static! {
    /// The number of layout threads, computed statically.
    pub static ref NUM_THREADS: usize = {
        match env::var("STYLO_THREADS").map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS")) {
            Ok(num) => num,
            _ => cmp::max(num_cpus::get() * 3 / 4, 1),
        }
    };
}

impl PerDocumentStyleData {
    /// Create a dummy `PerDocumentStyleData`.
    pub fn new() -> Self {
        // FIXME(bholley): Real window size.
        let window_size: TypedSize2D<f32, ViewportPx> = TypedSize2D::new(800.0, 600.0);
        let device = Device::new(MediaType::Screen, window_size);

        let (new_anims_sender, new_anims_receiver) = channel();

        PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
            stylist: Arc::new(Stylist::new(device)),
            stylesheets: vec![],
            stylesheets_changed: true,
            new_animations_sender: new_anims_sender,
            new_animations_receiver: new_anims_receiver,
            running_animations: Arc::new(RwLock::new(HashMap::new())),
            expired_animations: Arc::new(RwLock::new(HashMap::new())),
            work_queue: if *NUM_THREADS <= 1 {
                None
            } else {
                let configuration =
                    rayon::Configuration::new().set_num_threads(*NUM_THREADS);
                rayon::ThreadPool::new(configuration).ok()
            },
            num_threads: *NUM_THREADS,
        }))
    }

    /// Get an immutable reference to this style data.
    pub fn borrow(&self) -> AtomicRef<PerDocumentStyleDataImpl> {
        self.0.borrow()
    }

    /// Get an mutable reference to this style data.
    pub fn borrow_mut(&self) -> AtomicRefMut<PerDocumentStyleDataImpl> {
        self.0.borrow_mut()
    }
}

impl PerDocumentStyleDataImpl {
    /// Recreate the style data if the stylesheets have changed.
    pub fn flush_stylesheets(&mut self) {
        // The stylist wants to be flushed if either the stylesheets change or the
        // device dimensions change. When we add support for media queries, we'll
        // need to detect the latter case and trigger a flush as well.
        if self.stylesheets_changed {
            let _ = Arc::get_mut(&mut self.stylist).unwrap()
                                                   .update(&self.stylesheets, None, true);
            self.stylesheets_changed = false;
        }
    }
}

unsafe impl HasFFI for PerDocumentStyleData {
    type FFIType = RawServoStyleSet;
}
unsafe impl HasSimpleFFI for PerDocumentStyleData {}
unsafe impl HasBoxFFI for PerDocumentStyleData {}

impl Drop for PerDocumentStyleDataImpl {
    fn drop(&mut self) {
        let _ = self.work_queue.take();
    }
}
<|endoftext|>