<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::{check_metadata, StratPool},
        },
        structures::Table,
        types::{CreateAction, DeleteAction, RenameAction},
        Engine, EngineEvent, Name, Pool, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

const REQUIRED_DM_MINOR_VERSION: u32 = 37;

/// 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));
    }

    check_metadata(&metadata)
        .or_else(|e| {
            let err_msg = format!(
                "inconsistent metadata for {}: reason: {:?}",
                info_string(),
                e
            );
            Err(StratisError::Engine(ErrorEnum::Error, err_msg))
        })
        .and_then(|_| {
            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))
            })
        })
        .and_then(|(pool_name, pool)| {
            devlinks::setup_pool_devlinks(&pool_name, &pool);
            Ok((pool_name, pool))
        })
}

#[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 pools = find_all()?;

        let mut table = Table::default();
        let mut incomplete_pools = HashMap::new();
        for (pool_uuid, devices) in pools {
            match setup_pool(pool_uuid, &devices, &table) {
                Ok((pool_name, pool)) => {
                    table.insert(pool_name, pool_uuid, pool);
                }
                Err(err) => {
                    warn!("no pool set up, reason: {:?}", err);
                    incomplete_pools.insert(pool_uuid, devices);
                }
            }
        }

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

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

        Ok(engine)
    }

    /// 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);
                match setup_pool(pool_uuid, &devices, &self.pools) {
                    Ok((pool_name, pool)) => {
                        self.pools.insert(pool_name, pool_uuid, pool);
                        Some((
                            pool_uuid,
                            self.get_mut_pool(pool_uuid)
                                .expect("pool was just inserted")
                                .1,
                        ))
                    }
                    Err(err) => {
                        warn!("no pool set up, reason: {:?}", err);
                        self.incomplete_pools.insert(pool_uuid, devices);
                        None
                    }
                }
            }
        })
    }
}

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>Get rid of an unnecessary expect<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::{check_metadata, StratPool},
        },
        structures::Table,
        types::{CreateAction, DeleteAction, RenameAction},
        Engine, EngineEvent, Name, Pool, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

const REQUIRED_DM_MINOR_VERSION: u32 = 37;

/// 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));
    }

    check_metadata(&metadata)
        .or_else(|e| {
            let err_msg = format!(
                "inconsistent metadata for {}: reason: {:?}",
                info_string(),
                e
            );
            Err(StratisError::Engine(ErrorEnum::Error, err_msg))
        })
        .and_then(|_| {
            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))
            })
        })
        .and_then(|(pool_name, pool)| {
            devlinks::setup_pool_devlinks(&pool_name, &pool);
            Ok((pool_name, pool))
        })
}

#[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 pools = find_all()?;

        let mut table = Table::default();
        let mut incomplete_pools = HashMap::new();
        for (pool_uuid, devices) in pools {
            match setup_pool(pool_uuid, &devices, &table) {
                Ok((pool_name, pool)) => {
                    table.insert(pool_name, pool_uuid, pool);
                }
                Err(err) => {
                    warn!("no pool set up, reason: {:?}", err);
                    incomplete_pools.insert(pool_uuid, devices);
                }
            }
        }

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

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

        Ok(engine)
    }

    /// 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);
                match setup_pool(pool_uuid, &devices, &self.pools) {
                    Ok((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);
                    }
                };
                self.get_mut_pool(pool_uuid)
                    .map(|(_, pool)| (pool_uuid, 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>use std::fmt;
use std::vec::IntoIter;

use rand::{Rand, Rng};

#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
pub struct Point2D {
    pub x: f64,
    pub y: f64
}

impl Point2D {
    pub fn new(x: f64, y: f64) -> Point2D {
        Point2D { x: x, y: y }
    }

    pub fn distance_to<T: AsRef<Point2D>>(&self, that: T) -> f64 {
        self.distance_squared_to(that).sqrt()
    }

    pub fn distance_squared_to<T: AsRef<Point2D>>(&self, that: T) -> f64 {
        (self.x - that.as_ref().x).powi(2) + (self.y - that.as_ref().y).powi(2)
    }
}

impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

impl AsRef<Point2D> for Point2D {
    fn as_ref(&self) -> &Point2D {
        &self
    }
}

impl Rand for Point2D {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        Point2D {
            x: rng.next_f64(),
            y: rng.next_f64()
        }
    }
}

#[test]
fn test_point2d() {
    let p1 = Point2D::new(0.0, 0.0);
    let p2 = Point2D::new(1.0, 1.0);

    // maybe bad :(
    assert_eq!(p1.distance_to(p2), (2.0f64).sqrt());
}

#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
/// Implementation of 2D axis-aligned rectangle
pub struct RectHV {
    pub xmin: f64,
    pub ymin: f64,
    pub xmax: f64,
    pub ymax: f64
}

impl RectHV {
    pub fn new(xmin: f64, ymin: f64, xmax: f64, ymax: f64) -> RectHV {
        RectHV { xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax }
    }

    pub fn width(&self) -> f64 {
        self.xmax - self.xmin
    }

    pub fn height(&self) -> f64 {
        self.ymax - self.ymin
    }

    pub fn contains<T: AsRef<Point2D>>(&self, p: T) -> bool {
        let p = p.as_ref();
        p.x >= self.xmin && p.y >= self.ymin &&
            p.x <= self.xmax && p.y <= self.ymax
    }

    /// does this axis-aligned rectangle intersect that one?
    pub fn intersects<T: AsRef<RectHV>>(&self, that: T) -> bool {
        let that = that.as_ref();
        self.xmax >= that.xmin && self.ymax >= that.ymin &&
            that.xmax >= self.xmin && that.ymax >= self.ymin
    }

    /// distance from p to closest point on this axis-aligned rectangle
    pub fn distance_to<T: AsRef<Point2D>>(&self, p: T) -> f64 {
        self.distance_squared_to(p).sqrt()
    }

    /// distance squared from p to closest point on this axis-aligned rectangle
    pub fn distance_squared_to<T: AsRef<Point2D>>(&self, p: T) -> f64 {
        let p = p.as_ref();
        let mut dx = 0.0;
        let mut dy = 0.0;
        if p.x < self.xmin {
            dx = p.x - self.xmin;
        } else if p.x > self.xmax {
            dx = p.x - self.xmax;
        }
        if p.y < self.ymin {
            dy = p.y - self.ymin;
        } else if p.y > self.ymax {
            dy = p.y - self.ymax;
        }
        dx.powi(2) + dy.powi(2)
    }
}

impl fmt::Display for RectHV {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}, {}] x [{}, {}]", self.xmin, self.xmax, self.ymin, self.ymax)
    }
}

impl AsRef<RectHV> for RectHV {
    fn as_ref(&self) -> &RectHV {
        &self
    }
}

#[test]
fn test_rect() {
    let r1 = RectHV::new(0.0, 0.0, 1.1, 1.1);
    let r2 = RectHV::new(1.2, 2.0, 3.1, 4.1);
    assert!(!r1.intersects(r2));
}

pub struct PointSet {
    ps: Vec<Point2D>
}

impl PointSet {
    pub fn new() -> PointSet {
        PointSet { ps: Vec::new() }
    }

    pub fn size(&self) -> usize {
        self.ps.len()
    }

    pub fn insert(&mut self, p: Point2D) {
        if !self.ps.contains(&p) {
            self.ps.push(p)
        }
    }

    pub fn contains<T: AsRef<Point2D>>(&self, p: T) -> bool {
        self.ps.contains(p.as_ref())
    }

    pub fn range<T: AsRef<RectHV>>(&self, rect: T) -> IntoIter<&Point2D> {
        let mut result = Vec::new();
        for p in self.ps.iter() {
            if rect.as_ref().contains(p) {
                result.push(p);
            }
        }
        result.into_iter()
    }

    pub fn nearest<T: AsRef<Point2D>>(&self, p: T) -> Option<&Point2D> {
        // Ord :(
        self.ps.iter().min_by(|q| (q.distance_squared_to(p.as_ref()) * 10000.0) as u64)
    }
}

#[test]
fn test_point_set() {
    use rand::thread_rng;

    let mut rng = thread_rng();
    let mut ps = PointSet::new();
    for _ in 0 .. 100 {
        ps.insert(rng.gen())
    }

    assert!(ps.nearest(Point2D::new(0.5, 0.5)).is_some());
    assert!(ps.range(RectHV::new(0.4, 0.4, 0.6, 0.6)).count() > 0);
}
<commit_msg>geometric_search/primitive: rename range() to range_search()<commit_after>use std::fmt;
use std::vec::IntoIter;

use rand::{Rand, Rng};

#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
pub struct Point2D {
    pub x: f64,
    pub y: f64
}

impl Point2D {
    pub fn new(x: f64, y: f64) -> Point2D {
        Point2D { x: x, y: y }
    }

    pub fn distance_to<T: AsRef<Point2D>>(&self, that: T) -> f64 {
        self.distance_squared_to(that).sqrt()
    }

    pub fn distance_squared_to<T: AsRef<Point2D>>(&self, that: T) -> f64 {
        (self.x - that.as_ref().x).powi(2) + (self.y - that.as_ref().y).powi(2)
    }
}

impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

impl AsRef<Point2D> for Point2D {
    fn as_ref(&self) -> &Point2D {
        &self
    }
}

impl Rand for Point2D {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        Point2D {
            x: rng.next_f64(),
            y: rng.next_f64()
        }
    }
}

#[test]
fn test_point2d() {
    let p1 = Point2D::new(0.0, 0.0);
    let p2 = Point2D::new(1.0, 1.0);

    // maybe bad :(
    assert_eq!(p1.distance_to(p2), (2.0f64).sqrt());
}

#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
/// Implementation of 2D axis-aligned rectangle
pub struct RectHV {
    pub xmin: f64,
    pub ymin: f64,
    pub xmax: f64,
    pub ymax: f64
}

impl RectHV {
    pub fn new(xmin: f64, ymin: f64, xmax: f64, ymax: f64) -> RectHV {
        RectHV { xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax }
    }

    pub fn width(&self) -> f64 {
        self.xmax - self.xmin
    }

    pub fn height(&self) -> f64 {
        self.ymax - self.ymin
    }

    pub fn contains<T: AsRef<Point2D>>(&self, p: T) -> bool {
        let p = p.as_ref();
        p.x >= self.xmin && p.y >= self.ymin &&
            p.x <= self.xmax && p.y <= self.ymax
    }

    /// does this axis-aligned rectangle intersect that one?
    pub fn intersects<T: AsRef<RectHV>>(&self, that: T) -> bool {
        let that = that.as_ref();
        self.xmax >= that.xmin && self.ymax >= that.ymin &&
            that.xmax >= self.xmin && that.ymax >= self.ymin
    }

    /// distance from p to closest point on this axis-aligned rectangle
    pub fn distance_to<T: AsRef<Point2D>>(&self, p: T) -> f64 {
        self.distance_squared_to(p).sqrt()
    }

    /// distance squared from p to closest point on this axis-aligned rectangle
    pub fn distance_squared_to<T: AsRef<Point2D>>(&self, p: T) -> f64 {
        let p = p.as_ref();
        let mut dx = 0.0;
        let mut dy = 0.0;
        if p.x < self.xmin {
            dx = p.x - self.xmin;
        } else if p.x > self.xmax {
            dx = p.x - self.xmax;
        }
        if p.y < self.ymin {
            dy = p.y - self.ymin;
        } else if p.y > self.ymax {
            dy = p.y - self.ymax;
        }
        dx.powi(2) + dy.powi(2)
    }
}

impl fmt::Display for RectHV {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}, {}] x [{}, {}]", self.xmin, self.xmax, self.ymin, self.ymax)
    }
}

impl AsRef<RectHV> for RectHV {
    fn as_ref(&self) -> &RectHV {
        &self
    }
}

#[test]
fn test_rect() {
    let r1 = RectHV::new(0.0, 0.0, 1.1, 1.1);
    let r2 = RectHV::new(1.2, 2.0, 3.1, 4.1);
    assert!(!r1.intersects(r2));
}

pub struct PointSet {
    ps: Vec<Point2D>
}

impl PointSet {
    pub fn new() -> PointSet {
        PointSet { ps: Vec::new() }
    }

    pub fn size(&self) -> usize {
        self.ps.len()
    }

    pub fn insert(&mut self, p: Point2D) {
        if !self.ps.contains(&p) {
            self.ps.push(p)
        }
    }

    pub fn contains<T: AsRef<Point2D>>(&self, p: T) -> bool {
        self.ps.contains(p.as_ref())
    }

    pub fn range_search<T: AsRef<RectHV>>(&self, rect: T) -> IntoIter<&Point2D> {
        let mut result = Vec::new();
        for p in self.ps.iter() {
            if rect.as_ref().contains(p) {
                result.push(p);
            }
        }
        result.into_iter()
    }

    pub fn nearest<T: AsRef<Point2D>>(&self, p: T) -> Option<&Point2D> {
        // Ord :(
        self.ps.iter().min_by(|q| (q.distance_squared_to(p.as_ref()) * 10000.0) as u64)
    }
}

#[test]
fn test_point_set() {
    use rand::thread_rng;

    let mut rng = thread_rng();
    let mut ps = PointSet::new();
    for _ in 0 .. 100 {
        ps.insert(rng.gen())
    }

    assert!(ps.nearest(Point2D::new(0.5, 0.5)).is_some());
    assert!(ps.range_search(RectHV::new(0.4, 0.4, 0.6, 0.6)).count() > 0);
}
<|endoftext|>


<commit_before>type Cell = u8;

pub struct VM {
    cells: [Cell; 30_000],
    data_pointer: usize,
}

impl VM {
    pub fn new() -> VM {
        VM { cells: [0; 30_000], data_pointer: 0 }
    }

    pub fn output(&self) -> Cell {
        self.cells[self.data_pointer]
    }

    pub fn increment(&mut self) {
        let value = self.cells[self.data_pointer];
        self.cells[self.data_pointer] = value + 1;
    }

    pub fn decrement(&mut self) {
        self.cells[self.data_pointer] = self.cells[self.data_pointer] - 1;
    }

    pub fn right_shift(&mut self) {
        self.data_pointer = self.data_pointer + 1;
    }

    pub fn left_shift(&mut self) {
        self.data_pointer = self.data_pointer - 1;
    }
}
<commit_msg>Simplify definition of increment function<commit_after>type Cell = u8;

pub struct VM {
    cells: [Cell; 30_000],
    data_pointer: usize,
}

impl VM {
    pub fn new() -> VM {
        VM { cells: [0; 30_000], data_pointer: 0 }
    }

    pub fn output(&self) -> Cell {
        self.cells[self.data_pointer]
    }

    pub fn increment(&mut self) {
        self.cells[self.data_pointer] = self.cells[self.data_pointer] + 1;
    }

    pub fn decrement(&mut self) {
        self.cells[self.data_pointer] = self.cells[self.data_pointer] - 1;
    }

    pub fn right_shift(&mut self) {
        self.data_pointer = self.data_pointer + 1;
    }

    pub fn left_shift(&mut self) {
        self.data_pointer = self.data_pointer - 1;
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with the chunked format.
//!
//! The chunked file format is extremely simple serialization procedure where
//! each chunk of raw bytes is prepended with the size of the chunk (as 64-bit
//! unsigned big-endian integer).
//!
//! Its primary application is streaming encoding and decoding of blobs of data
//! in formats that do not support or are inefficient for these purposes (such
//! as serialized Protocol Buffer messages).

use byteorder::BigEndian;

/// Encodes a given iterator over protobuf messages into the chunked format.
///
/// This is a streaming encoder and performs the encoding in a lazy way. It
/// should compose well with other streaming encoders (e.g. these offered by
/// the [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let data = vec! {
///     String::from("foo"),
///     String::from("bar"),
///     String::from("baz")
/// };
///
/// let mut stream = rrg::chunked::encode(data.into_iter());
/// let mut file = File::create("output.chunked").unwrap();
/// std::io::copy(&mut stream, &mut file).unwrap();
/// ```
pub fn encode<I>(iter: I) -> Encode<I>
where
    I: Iterator,
    I::Item: prost::Message,
{
    Encode {
        iter: iter,
        cur: std::io::Cursor::new(vec!()),
    }
}

/// Decodes a buffer in the chunked format into a stream of protobuf messages.
///
/// This is a streaming decoder and performs the decoding in a lazy way. It
/// should compose well with other streaming decoders (e.g. these offered by the
/// [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let file = File::open("input.chunked").unwrap();
/// for (idx, msg) in rrg::chunked::decode(file).enumerate() {
///     let msg: String = msg.unwrap();
///     println!("message #{}: {:?}", idx, msg);
/// }
/// ```
pub fn decode<R, M>(buf: R) -> Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message,
{
    Decode {
        reader: buf,
        buf: vec!(),
        marker: std::marker::PhantomData,
    }
}

/// Streaming encoder for the chunked format.
///
/// It implements the `Read` trait, lazily polling the underlying iterator over
/// Protocol Buffers messages as needed.
///
/// Instances of this type can be constructed using the [`encode`] function.
///
/// [`encode`]: fn.encode.html
pub struct Encode<I> {
    iter: I,
    cur: std::io::Cursor<Vec<u8>>,
}

impl<I, M> Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    /// Checks whether all the data from the underlying cursor has been read.
    fn is_empty(&self) -> bool {
        self.cur.position() == self.cur.get_ref().len() as u64
    }

    /// Pulls another message from the underlying iterator.
    fn pull(&mut self) -> std::io::Result<()> {
        use byteorder::WriteBytesExt as _;

        let msg = match self.iter.next() {
            Some(msg) => msg,
            None => return Ok(()),
        };

        self.cur.get_mut().clear();
        self.cur.set_position(0);

        self.cur.write_u64::<BigEndian>(msg.encoded_len() as u64)?;
        msg.encode(&mut self.cur.get_mut())?;
        self.cur.set_position(0);

        Ok(())
    }
}

impl<I, M> std::io::Read for Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        if self.is_empty() {
            self.pull()?;
        }

        self.cur.read(buf)
    }
}

/// Streaming decoder for the chunked format.
///
/// It implements the `Iterator` trait yielding Protocol Buffers messages,
/// lazily decoding data from the underlying buffer.
///
/// Instances of this type can be constructed using the [`decode`] function.
///
/// [`decode`]: fn.decode.html
pub struct Decode<R, M> {
    reader: R,
    buf: Vec<u8>,
    marker: std::marker::PhantomData<M>,
}

impl<R: std::io::Read, M> Decode<R, M> {

    /// Reads a size tag from the underlying buffer.
    ///
    /// It will return `None` if the is no more data in the buffer.
    fn read_len(&mut self) -> std::io::Result<Option<usize>> {
        use byteorder::ReadBytesExt as _;

        // `read` might not always read all 8 bytes. On the other hand, we also
        // cannot use just `read_exact` because the stream might have ended
        // already. Hence, we combine the two. First we attempt to read some
        // bytes with `read`: it should either return 0 (indicating end of the
        // stream), 8 (indicating that we have filled the whole buffer fully)
        // or something in between. In the last case, we use `read_exact to get
        // the remaining bytes (which should be non-zero now).
        let mut buf = [0; 8];
        match self.reader.read(&mut buf[..])? {
            8 => (),
            0 => return Ok(None),
            len => self.reader.read_exact(&mut buf[len..])?,
        }

        let len = (&buf[..]).read_u64::<BigEndian>()? as usize;
        Ok(Some(len))
    }
}

impl<R, M> Iterator for Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message + Default,
{
    type Item = std::io::Result<M>;

    fn next(&mut self) -> Option<std::io::Result<M>> {
        let len = match self.read_len() {
            Ok(Some(len)) => len,
            Ok(None) => return None,
            Err(error) => return Some(Err(error)),
        };

        self.buf.resize(len, u8::default());
        match self.reader.read_exact(&mut self.buf[..]) {
            Ok(()) => (),
            Err(error) => return Some(Err(error)),
        }

        let msg = match M::decode(&self.buf[..]) {
            Ok(msg) => msg,
            Err(error) => return Some(Err(error.into())),
        };

        Some(Ok(msg))
    }
}

#[cfg(test)]
pub mod tests {

    use super::*;

    #[test]
    pub fn test_encode_empty_iter() {
        use std::io::Read as _;

        let mut stream = encode(std::iter::empty::<()>());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert!(output.is_empty());
    }

    #[test]
    pub fn test_decode_empty_buf() {
        let buf: &[u8] = b"";
        let mut iter = decode::<_, ()>(buf);

        assert!(iter.next().is_none());
    }

    #[test]
    pub fn test_decode_incorrect_size_tag() {
        let buf: &[u8] = b"\x12\x34\x56";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    pub fn test_decode_zero_size_tag() {
        let buf: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x00";

        let mut iter = decode(buf).map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_decode_partial_size_tag() {

        // A simple reader that yields a 0-valued size tag byte by byte.
        struct Reader(u8);

        impl std::io::Read for Reader {

            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
                if self.0 == 8 {
                    Ok(0)
                } else {
                    buf[0] = 0;
                    self.0 += 1;
                    Ok(1)
                }
            }
        }

        let mut iter = decode(Reader(0)).map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_decode_short_input() {
        let buf: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x42foo";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    pub fn test_encode_and_decode_single_message() {
        let mut iter = decode(encode(std::iter::once(String::from("foo"))))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(String::from("foo")));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_single_unit_message() {
        let mut iter = decode(encode(std::iter::once(())))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_messages() {
        let msgs = vec! {
            b"foo".to_vec(),
            b"bar".to_vec(),
            b"baz".to_vec(),
        };

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), Some(b"bar".to_vec()));
        assert_eq!(iter.next(), Some(b"baz".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_unit_messages() {
        let msgs = vec!((), (), ());

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }
}
<commit_msg>Remove spurious visibility declaration on tests.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with the chunked format.
//!
//! The chunked file format is extremely simple serialization procedure where
//! each chunk of raw bytes is prepended with the size of the chunk (as 64-bit
//! unsigned big-endian integer).
//!
//! Its primary application is streaming encoding and decoding of blobs of data
//! in formats that do not support or are inefficient for these purposes (such
//! as serialized Protocol Buffer messages).

use byteorder::BigEndian;

/// Encodes a given iterator over protobuf messages into the chunked format.
///
/// This is a streaming encoder and performs the encoding in a lazy way. It
/// should compose well with other streaming encoders (e.g. these offered by
/// the [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let data = vec! {
///     String::from("foo"),
///     String::from("bar"),
///     String::from("baz")
/// };
///
/// let mut stream = rrg::chunked::encode(data.into_iter());
/// let mut file = File::create("output.chunked").unwrap();
/// std::io::copy(&mut stream, &mut file).unwrap();
/// ```
pub fn encode<I>(iter: I) -> Encode<I>
where
    I: Iterator,
    I::Item: prost::Message,
{
    Encode {
        iter: iter,
        cur: std::io::Cursor::new(vec!()),
    }
}

/// Decodes a buffer in the chunked format into a stream of protobuf messages.
///
/// This is a streaming decoder and performs the decoding in a lazy way. It
/// should compose well with other streaming decoders (e.g. these offered by the
/// [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let file = File::open("input.chunked").unwrap();
/// for (idx, msg) in rrg::chunked::decode(file).enumerate() {
///     let msg: String = msg.unwrap();
///     println!("message #{}: {:?}", idx, msg);
/// }
/// ```
pub fn decode<R, M>(buf: R) -> Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message,
{
    Decode {
        reader: buf,
        buf: vec!(),
        marker: std::marker::PhantomData,
    }
}

/// Streaming encoder for the chunked format.
///
/// It implements the `Read` trait, lazily polling the underlying iterator over
/// Protocol Buffers messages as needed.
///
/// Instances of this type can be constructed using the [`encode`] function.
///
/// [`encode`]: fn.encode.html
pub struct Encode<I> {
    iter: I,
    cur: std::io::Cursor<Vec<u8>>,
}

impl<I, M> Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    /// Checks whether all the data from the underlying cursor has been read.
    fn is_empty(&self) -> bool {
        self.cur.position() == self.cur.get_ref().len() as u64
    }

    /// Pulls another message from the underlying iterator.
    fn pull(&mut self) -> std::io::Result<()> {
        use byteorder::WriteBytesExt as _;

        let msg = match self.iter.next() {
            Some(msg) => msg,
            None => return Ok(()),
        };

        self.cur.get_mut().clear();
        self.cur.set_position(0);

        self.cur.write_u64::<BigEndian>(msg.encoded_len() as u64)?;
        msg.encode(&mut self.cur.get_mut())?;
        self.cur.set_position(0);

        Ok(())
    }
}

impl<I, M> std::io::Read for Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        if self.is_empty() {
            self.pull()?;
        }

        self.cur.read(buf)
    }
}

/// Streaming decoder for the chunked format.
///
/// It implements the `Iterator` trait yielding Protocol Buffers messages,
/// lazily decoding data from the underlying buffer.
///
/// Instances of this type can be constructed using the [`decode`] function.
///
/// [`decode`]: fn.decode.html
pub struct Decode<R, M> {
    reader: R,
    buf: Vec<u8>,
    marker: std::marker::PhantomData<M>,
}

impl<R: std::io::Read, M> Decode<R, M> {

    /// Reads a size tag from the underlying buffer.
    ///
    /// It will return `None` if the is no more data in the buffer.
    fn read_len(&mut self) -> std::io::Result<Option<usize>> {
        use byteorder::ReadBytesExt as _;

        // `read` might not always read all 8 bytes. On the other hand, we also
        // cannot use just `read_exact` because the stream might have ended
        // already. Hence, we combine the two. First we attempt to read some
        // bytes with `read`: it should either return 0 (indicating end of the
        // stream), 8 (indicating that we have filled the whole buffer fully)
        // or something in between. In the last case, we use `read_exact to get
        // the remaining bytes (which should be non-zero now).
        let mut buf = [0; 8];
        match self.reader.read(&mut buf[..])? {
            8 => (),
            0 => return Ok(None),
            len => self.reader.read_exact(&mut buf[len..])?,
        }

        let len = (&buf[..]).read_u64::<BigEndian>()? as usize;
        Ok(Some(len))
    }
}

impl<R, M> Iterator for Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message + Default,
{
    type Item = std::io::Result<M>;

    fn next(&mut self) -> Option<std::io::Result<M>> {
        let len = match self.read_len() {
            Ok(Some(len)) => len,
            Ok(None) => return None,
            Err(error) => return Some(Err(error)),
        };

        self.buf.resize(len, u8::default());
        match self.reader.read_exact(&mut self.buf[..]) {
            Ok(()) => (),
            Err(error) => return Some(Err(error)),
        }

        let msg = match M::decode(&self.buf[..]) {
            Ok(msg) => msg,
            Err(error) => return Some(Err(error.into())),
        };

        Some(Ok(msg))
    }
}

#[cfg(test)]
pub mod tests {

    use super::*;

    #[test]
    fn test_encode_empty_iter() {
        use std::io::Read as _;

        let mut stream = encode(std::iter::empty::<()>());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert!(output.is_empty());
    }

    #[test]
    fn test_decode_empty_buf() {
        let buf: &[u8] = b"";
        let mut iter = decode::<_, ()>(buf);

        assert!(iter.next().is_none());
    }

    #[test]
    fn test_decode_incorrect_size_tag() {
        let buf: &[u8] = b"\x12\x34\x56";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    fn test_decode_zero_size_tag() {
        let buf: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x00";

        let mut iter = decode(buf).map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn test_decode_partial_size_tag() {

        // A simple reader that yields a 0-valued size tag byte by byte.
        struct Reader(u8);

        impl std::io::Read for Reader {

            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
                if self.0 == 8 {
                    Ok(0)
                } else {
                    buf[0] = 0;
                    self.0 += 1;
                    Ok(1)
                }
            }
        }

        let mut iter = decode(Reader(0)).map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn test_decode_short_input() {
        let buf: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x42foo";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    fn test_encode_and_decode_single_message() {
        let mut iter = decode(encode(std::iter::once(String::from("foo"))))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(String::from("foo")));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn test_encode_and_decode_single_unit_message() {
        let mut iter = decode(encode(std::iter::once(())))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn test_encode_and_decode_multiple_messages() {
        let msgs = vec! {
            b"foo".to_vec(),
            b"bar".to_vec(),
            b"baz".to_vec(),
        };

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), Some(b"bar".to_vec()));
        assert_eq!(iter.next(), Some(b"baz".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn test_encode_and_decode_multiple_unit_messages() {
        let msgs = vec!((), (), ());

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }
}
<|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/. */

//! Rust helpers for Gecko's `nsStyleCoord`.

use gecko_bindings::bindings::{Gecko_ResetStyleCoord, Gecko_SetStyleCoordCalcValue, Gecko_AddRefCalcArbitraryThread};
use gecko_bindings::structs::{nsStyleCoord_Calc, nsStyleUnit, nsStyleUnion, nsStyleCoord, nsStyleSides, nsStyleCorners};
use gecko_bindings::structs::{nsStyleCoord_CalcValue, nscoord};
use std::mem;

impl nsStyleCoord {
    #[inline]
    /// Get a `null` nsStyleCoord.
    pub fn null() -> Self {
        // Can't construct directly because it has private fields
        let mut coord: Self = unsafe { mem::zeroed() };
        coord.leaky_set_null();
        coord
    }
}

impl CoordData for nsStyleCoord {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            *self.get_mUnit()
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            *self.get_mValue()
        }
    }
}

impl CoordDataMut for nsStyleCoord {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = self.get_mUnit_mut() as *mut _;
        let value = self.get_mValue_mut() as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCoord_CalcValue {
    /// Create an "empty" CalcValue (whose value is `0`).
    pub fn new() -> Self {
        nsStyleCoord_CalcValue {
            mLength: 0,
            mPercent: 0.0,
            mHasPercent: false,
        }
    }
}

impl nsStyleSides {
    /// Immutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at(&self, index: usize) -> SidesData {
        SidesData {
            sides: self,
            index: index,
        }
    }

    /// Mutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> SidesDataMut {
        SidesDataMut {
            sides: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like object on top of an immutable reference to
/// `nsStyleSides`.
pub struct SidesData<'a> {
    sides: &'a nsStyleSides,
    index: usize,
}

/// A `nsStyleCoord`-like object on top of an mutable reference to
/// `nsStyleSides`.
pub struct SidesDataMut<'a> {
    sides: &'a mut nsStyleSides,
    index: usize,
}

impl<'a> CoordData for SidesData<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordData for SidesDataMut<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordDataMut for SidesDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.sides.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.sides.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCorners {
    /// Get a `nsStyleCoord` like object representing the given index's value
    /// and unit.
    #[inline]
    pub fn data_at(&self, index: usize) -> CornersData {
        CornersData {
            corners: self,
            index: index,
        }
    }

    /// Get a `nsStyleCoord` like object representing the mutable given index's
    /// value and unit.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> CornersDataMut {
        CornersDataMut {
            corners: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like struct on top of `nsStyleCorners`.
pub struct CornersData<'a> {
    corners: &'a nsStyleCorners,
    index: usize,
}

/// A `nsStyleCoord`-like struct on top of a mutable `nsStyleCorners` reference.
pub struct CornersDataMut<'a> {
    corners: &'a mut nsStyleCorners,
    index: usize,
}

impl<'a> CoordData for CornersData<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordData for CornersDataMut<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordDataMut for CornersDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.corners.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.corners.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

#[derive(Copy, Clone)]
/// Enum representing the tagged union that is CoordData.
///
/// In release mode this should never actually exist in the code, and will be
/// optimized out by threading matches and inlining.
pub enum CoordDataValue {
    /// eStyleUnit_Null
    Null,
    /// eStyleUnit_Normal
    Normal,
    /// eStyleUnit_Auto
    Auto,
    /// eStyleUnit_None
    None,
    /// eStyleUnit_Percent
    Percent(f32),
    /// eStyleUnit_Factor
    Factor(f32),
    /// eStyleUnit_Degree
    Degree(f32),
    /// eStyleUnit_Grad
    Grad(f32),
    /// eStyleUnit_Radian
    Radian(f32),
    /// eStyleUnit_Turn
    Turn(f32),
    /// eStyleUnit_FlexFraction
    FlexFraction(f32),
    /// eStyleUnit_Coord
    Coord(nscoord),
    /// eStyleUnit_Integer
    Integer(i32),
    /// eStyleUnit_Enumerated
    Enumerated(u32),
    /// eStyleUnit_Calc
    Calc(nsStyleCoord_CalcValue),
}

/// A trait to abstract on top of a mutable `nsStyleCoord`-like object.
pub trait CoordDataMut : CoordData {
    /// Get mutably the unit and the union.
    ///
    /// This is unsafe since it's possible to modify the unit without changing
    /// the union.
    ///
    /// NB: This can't be two methods since we can't mutably borrow twice
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion);

    /// Clean up any resources used by the union.
    ///
    /// Currently, this only happens if the nsStyleUnit is a Calc.
    #[inline]
    fn reset(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                let (unit, union) = self.values_mut();
                Gecko_ResetStyleCoord(unit, union);
            }
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type.
    fn copy_from<T: CoordData>(&mut self, other: &T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(other);
            self.addref_if_calc();
        }
    }

    #[inline]
    /// Moves the unit and value from another `CoordData` type.
    fn move_from<T: CoordData>(&mut self, other: T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(&other);
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type without checking
    /// the type of the value (so refcounted values like calc may leak).
    unsafe fn copy_from_unchecked<T: CoordData>(&mut self, other: &T) {
        let (unit, union) = self.values_mut();
        *unit = other.unit();
        *union = other.union();
    }

    /// Useful for initializing uninits, given that `set_value` may segfault on
    /// uninits.
    fn leaky_set_null(&mut self) {
        use gecko_bindings::structs::nsStyleUnit::*;
        unsafe {
            let (unit, union) = self.values_mut();
            *unit = eStyleUnit_Null;
            *union.mInt.as_mut() = 0;
        }
    }

    #[inline(always)]
    /// Sets the inner value.
    fn set_value(&mut self, value: CoordDataValue) {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        self.reset();
        unsafe {
            let (unit, union) = self.values_mut();
            match value {
                Null => {
                    *unit = eStyleUnit_Null;
                    *union.mInt.as_mut() = 0;
                }
                Normal => {
                    *unit = eStyleUnit_Normal;
                    *union.mInt.as_mut() = 0;
                }
                Auto => {
                    *unit = eStyleUnit_Auto;
                    *union.mInt.as_mut() = 0;
                }
                None => {
                    *unit = eStyleUnit_None;
                    *union.mInt.as_mut() = 0;
                }
                Percent(f) => {
                    *unit = eStyleUnit_Percent;
                    *union.mFloat.as_mut() = f;
                }
                Factor(f) => {
                    *unit = eStyleUnit_Factor;
                    *union.mFloat.as_mut() = f;
                }
                Degree(f) => {
                    *unit = eStyleUnit_Degree;
                    *union.mFloat.as_mut() = f;
                }
                Grad(f) => {
                    *unit = eStyleUnit_Grad;
                    *union.mFloat.as_mut() = f;
                }
                Radian(f) => {
                    *unit = eStyleUnit_Radian;
                    *union.mFloat.as_mut() = f;
                }
                Turn(f) => {
                    *unit = eStyleUnit_Turn;
                    *union.mFloat.as_mut() = f;
                }
                FlexFraction(f) => {
                    *unit = eStyleUnit_FlexFraction;
                    *union.mFloat.as_mut() = f;
                }
                Coord(coord) => {
                    *unit = eStyleUnit_Coord;
                    *union.mInt.as_mut() = coord;
                }
                Integer(i) => {
                    *unit = eStyleUnit_Integer;
                    *union.mInt.as_mut() = i;
                }
                Enumerated(i) => {
                    *unit = eStyleUnit_Enumerated;
                    *union.mInt.as_mut() = i as i32;
                }
                Calc(calc) => {
                    // Gecko_SetStyleCoordCalcValue changes the unit internally
                    Gecko_SetStyleCoordCalcValue(unit, union, calc);
                }
            }
        }
    }

    #[inline]
    /// Gets the `Calc` value mutably, asserts in debug builds if the unit is
    /// not `Calc`.
    unsafe fn as_calc_mut(&mut self) -> &mut nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &mut *(*self.union().mPointer.as_mut() as *mut nsStyleCoord_Calc)
    }

    #[inline]
    /// Does what it promises, if the unit is `calc`, it bumps the reference
    /// count _of the calc expression_.
    fn addref_if_calc(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                Gecko_AddRefCalcArbitraryThread(self.as_calc_mut());
            }
        }
    }
}
/// A trait to abstract on top of a `nsStyleCoord`-like object.
pub trait CoordData {
    /// Get the unit of this object.
    fn unit(&self) -> nsStyleUnit;
    /// Get the `nsStyleUnion` for this object.
    fn union(&self) -> nsStyleUnion;

    #[inline(always)]
    /// Get the appropriate value for this object.
    fn as_value(&self) -> CoordDataValue {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        unsafe {
            match self.unit() {
                eStyleUnit_Null => Null,
                eStyleUnit_Normal => Normal,
                eStyleUnit_Auto => Auto,
                eStyleUnit_None => None,
                eStyleUnit_Percent => Percent(self.get_float()),
                eStyleUnit_Factor => Factor(self.get_float()),
                eStyleUnit_Degree => Degree(self.get_float()),
                eStyleUnit_Grad => Grad(self.get_float()),
                eStyleUnit_Radian => Radian(self.get_float()),
                eStyleUnit_Turn => Turn(self.get_float()),
                eStyleUnit_FlexFraction => FlexFraction(self.get_float()),
                eStyleUnit_Coord => Coord(self.get_integer()),
                eStyleUnit_Integer => Integer(self.get_integer()),
                eStyleUnit_Enumerated => Enumerated(self.get_integer() as u32),
                eStyleUnit_Calc => Calc(self.get_calc_value()),
            }
        }
    }

    #[inline]
    /// Pretend inner value is a float; obtain it.
    unsafe fn get_float(&self) -> f32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Percent || self.unit() == eStyleUnit_Factor
                      || self.unit() == eStyleUnit_Degree || self.unit() == eStyleUnit_Grad
                      || self.unit() == eStyleUnit_Radian || self.unit() == eStyleUnit_Turn
                      || self.unit() == eStyleUnit_FlexFraction);
        *self.union().mFloat.as_ref()
    }

    #[inline]
    /// Pretend inner value is an int; obtain it.
    unsafe fn get_integer(&self) -> i32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Coord || self.unit() == eStyleUnit_Integer
                      || self.unit() == eStyleUnit_Enumerated);
        *self.union().mInt.as_ref()
    }

    #[inline]
    /// Pretend inner value is a calc; obtain it.
    /// Ensure that the unit is Calc before calling this.
    unsafe fn get_calc_value(&self) -> nsStyleCoord_CalcValue {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        (*self.as_calc())._base
    }

    #[inline]
    /// Pretend the inner value is a calc expression, and obtain it.
    unsafe fn as_calc(&self) -> &nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &*(*self.union().mPointer.as_ref() as *const nsStyleCoord_Calc)
    }
}
<commit_msg>Have CoordDataValue derive Debug and PartialEq.<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/. */

//! Rust helpers for Gecko's `nsStyleCoord`.

use gecko_bindings::bindings::{Gecko_ResetStyleCoord, Gecko_SetStyleCoordCalcValue, Gecko_AddRefCalcArbitraryThread};
use gecko_bindings::structs::{nsStyleCoord_Calc, nsStyleUnit, nsStyleUnion, nsStyleCoord, nsStyleSides, nsStyleCorners};
use gecko_bindings::structs::{nsStyleCoord_CalcValue, nscoord};
use std::mem;

impl nsStyleCoord {
    #[inline]
    /// Get a `null` nsStyleCoord.
    pub fn null() -> Self {
        // Can't construct directly because it has private fields
        let mut coord: Self = unsafe { mem::zeroed() };
        coord.leaky_set_null();
        coord
    }
}

impl CoordData for nsStyleCoord {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            *self.get_mUnit()
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            *self.get_mValue()
        }
    }
}

impl CoordDataMut for nsStyleCoord {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = self.get_mUnit_mut() as *mut _;
        let value = self.get_mValue_mut() as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCoord_CalcValue {
    /// Create an "empty" CalcValue (whose value is `0`).
    pub fn new() -> Self {
        nsStyleCoord_CalcValue {
            mLength: 0,
            mPercent: 0.0,
            mHasPercent: false,
        }
    }
}

impl PartialEq for nsStyleCoord_CalcValue {
    fn eq(&self, other: &Self) -> bool {
        self.mLength == other.mLength &&
            self.mPercent == other.mPercent &&
            self.mHasPercent == other.mHasPercent
    }
}

impl nsStyleSides {
    /// Immutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at(&self, index: usize) -> SidesData {
        SidesData {
            sides: self,
            index: index,
        }
    }

    /// Mutably get the `nsStyleCoord`-like object representing the side at
    /// index `index`.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> SidesDataMut {
        SidesDataMut {
            sides: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like object on top of an immutable reference to
/// `nsStyleSides`.
pub struct SidesData<'a> {
    sides: &'a nsStyleSides,
    index: usize,
}

/// A `nsStyleCoord`-like object on top of an mutable reference to
/// `nsStyleSides`.
pub struct SidesDataMut<'a> {
    sides: &'a mut nsStyleSides,
    index: usize,
}

impl<'a> CoordData for SidesData<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordData for SidesDataMut<'a> {
    #[inline]
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.sides.get_mUnits()[self.index]
        }
    }
    #[inline]
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.sides.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordDataMut for SidesDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.sides.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.sides.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

impl nsStyleCorners {
    /// Get a `nsStyleCoord` like object representing the given index's value
    /// and unit.
    #[inline]
    pub fn data_at(&self, index: usize) -> CornersData {
        CornersData {
            corners: self,
            index: index,
        }
    }

    /// Get a `nsStyleCoord` like object representing the mutable given index's
    /// value and unit.
    #[inline]
    pub fn data_at_mut(&mut self, index: usize) -> CornersDataMut {
        CornersDataMut {
            corners: self,
            index: index,
        }
    }
}

/// A `nsStyleCoord`-like struct on top of `nsStyleCorners`.
pub struct CornersData<'a> {
    corners: &'a nsStyleCorners,
    index: usize,
}

/// A `nsStyleCoord`-like struct on top of a mutable `nsStyleCorners` reference.
pub struct CornersDataMut<'a> {
    corners: &'a mut nsStyleCorners,
    index: usize,
}

impl<'a> CoordData for CornersData<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordData for CornersDataMut<'a> {
    fn unit(&self) -> nsStyleUnit {
        unsafe {
            self.corners.get_mUnits()[self.index]
        }
    }
    fn union(&self) -> nsStyleUnion {
        unsafe {
            self.corners.get_mValues()[self.index]
        }
    }
}
impl<'a> CoordDataMut for CornersDataMut<'a> {
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
        let unit = &mut self.corners.get_mUnits_mut()[self.index] as *mut _;
        let value = &mut self.corners.get_mValues_mut()[self.index] as *mut _;
        (&mut *unit, &mut *value)
    }
}

/// Enum representing the tagged union that is CoordData.
///
/// In release mode this should never actually exist in the code, and will be
/// optimized out by threading matches and inlining.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum CoordDataValue {
    /// eStyleUnit_Null
    Null,
    /// eStyleUnit_Normal
    Normal,
    /// eStyleUnit_Auto
    Auto,
    /// eStyleUnit_None
    None,
    /// eStyleUnit_Percent
    Percent(f32),
    /// eStyleUnit_Factor
    Factor(f32),
    /// eStyleUnit_Degree
    Degree(f32),
    /// eStyleUnit_Grad
    Grad(f32),
    /// eStyleUnit_Radian
    Radian(f32),
    /// eStyleUnit_Turn
    Turn(f32),
    /// eStyleUnit_FlexFraction
    FlexFraction(f32),
    /// eStyleUnit_Coord
    Coord(nscoord),
    /// eStyleUnit_Integer
    Integer(i32),
    /// eStyleUnit_Enumerated
    Enumerated(u32),
    /// eStyleUnit_Calc
    Calc(nsStyleCoord_CalcValue),
}

/// A trait to abstract on top of a mutable `nsStyleCoord`-like object.
pub trait CoordDataMut : CoordData {
    /// Get mutably the unit and the union.
    ///
    /// This is unsafe since it's possible to modify the unit without changing
    /// the union.
    ///
    /// NB: This can't be two methods since we can't mutably borrow twice
    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion);

    /// Clean up any resources used by the union.
    ///
    /// Currently, this only happens if the nsStyleUnit is a Calc.
    #[inline]
    fn reset(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                let (unit, union) = self.values_mut();
                Gecko_ResetStyleCoord(unit, union);
            }
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type.
    fn copy_from<T: CoordData>(&mut self, other: &T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(other);
            self.addref_if_calc();
        }
    }

    #[inline]
    /// Moves the unit and value from another `CoordData` type.
    fn move_from<T: CoordData>(&mut self, other: T) {
        unsafe {
            self.reset();
            self.copy_from_unchecked(&other);
        }
    }

    #[inline]
    /// Copies the unit and value from another `CoordData` type without checking
    /// the type of the value (so refcounted values like calc may leak).
    unsafe fn copy_from_unchecked<T: CoordData>(&mut self, other: &T) {
        let (unit, union) = self.values_mut();
        *unit = other.unit();
        *union = other.union();
    }

    /// Useful for initializing uninits, given that `set_value` may segfault on
    /// uninits.
    fn leaky_set_null(&mut self) {
        use gecko_bindings::structs::nsStyleUnit::*;
        unsafe {
            let (unit, union) = self.values_mut();
            *unit = eStyleUnit_Null;
            *union.mInt.as_mut() = 0;
        }
    }

    #[inline(always)]
    /// Sets the inner value.
    fn set_value(&mut self, value: CoordDataValue) {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        self.reset();
        unsafe {
            let (unit, union) = self.values_mut();
            match value {
                Null => {
                    *unit = eStyleUnit_Null;
                    *union.mInt.as_mut() = 0;
                }
                Normal => {
                    *unit = eStyleUnit_Normal;
                    *union.mInt.as_mut() = 0;
                }
                Auto => {
                    *unit = eStyleUnit_Auto;
                    *union.mInt.as_mut() = 0;
                }
                None => {
                    *unit = eStyleUnit_None;
                    *union.mInt.as_mut() = 0;
                }
                Percent(f) => {
                    *unit = eStyleUnit_Percent;
                    *union.mFloat.as_mut() = f;
                }
                Factor(f) => {
                    *unit = eStyleUnit_Factor;
                    *union.mFloat.as_mut() = f;
                }
                Degree(f) => {
                    *unit = eStyleUnit_Degree;
                    *union.mFloat.as_mut() = f;
                }
                Grad(f) => {
                    *unit = eStyleUnit_Grad;
                    *union.mFloat.as_mut() = f;
                }
                Radian(f) => {
                    *unit = eStyleUnit_Radian;
                    *union.mFloat.as_mut() = f;
                }
                Turn(f) => {
                    *unit = eStyleUnit_Turn;
                    *union.mFloat.as_mut() = f;
                }
                FlexFraction(f) => {
                    *unit = eStyleUnit_FlexFraction;
                    *union.mFloat.as_mut() = f;
                }
                Coord(coord) => {
                    *unit = eStyleUnit_Coord;
                    *union.mInt.as_mut() = coord;
                }
                Integer(i) => {
                    *unit = eStyleUnit_Integer;
                    *union.mInt.as_mut() = i;
                }
                Enumerated(i) => {
                    *unit = eStyleUnit_Enumerated;
                    *union.mInt.as_mut() = i as i32;
                }
                Calc(calc) => {
                    // Gecko_SetStyleCoordCalcValue changes the unit internally
                    Gecko_SetStyleCoordCalcValue(unit, union, calc);
                }
            }
        }
    }

    #[inline]
    /// Gets the `Calc` value mutably, asserts in debug builds if the unit is
    /// not `Calc`.
    unsafe fn as_calc_mut(&mut self) -> &mut nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &mut *(*self.union().mPointer.as_mut() as *mut nsStyleCoord_Calc)
    }

    #[inline]
    /// Does what it promises, if the unit is `calc`, it bumps the reference
    /// count _of the calc expression_.
    fn addref_if_calc(&mut self) {
        unsafe {
            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
                Gecko_AddRefCalcArbitraryThread(self.as_calc_mut());
            }
        }
    }
}
/// A trait to abstract on top of a `nsStyleCoord`-like object.
pub trait CoordData {
    /// Get the unit of this object.
    fn unit(&self) -> nsStyleUnit;
    /// Get the `nsStyleUnion` for this object.
    fn union(&self) -> nsStyleUnion;

    #[inline(always)]
    /// Get the appropriate value for this object.
    fn as_value(&self) -> CoordDataValue {
        use gecko_bindings::structs::nsStyleUnit::*;
        use self::CoordDataValue::*;
        unsafe {
            match self.unit() {
                eStyleUnit_Null => Null,
                eStyleUnit_Normal => Normal,
                eStyleUnit_Auto => Auto,
                eStyleUnit_None => None,
                eStyleUnit_Percent => Percent(self.get_float()),
                eStyleUnit_Factor => Factor(self.get_float()),
                eStyleUnit_Degree => Degree(self.get_float()),
                eStyleUnit_Grad => Grad(self.get_float()),
                eStyleUnit_Radian => Radian(self.get_float()),
                eStyleUnit_Turn => Turn(self.get_float()),
                eStyleUnit_FlexFraction => FlexFraction(self.get_float()),
                eStyleUnit_Coord => Coord(self.get_integer()),
                eStyleUnit_Integer => Integer(self.get_integer()),
                eStyleUnit_Enumerated => Enumerated(self.get_integer() as u32),
                eStyleUnit_Calc => Calc(self.get_calc_value()),
            }
        }
    }

    #[inline]
    /// Pretend inner value is a float; obtain it.
    unsafe fn get_float(&self) -> f32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Percent || self.unit() == eStyleUnit_Factor
                      || self.unit() == eStyleUnit_Degree || self.unit() == eStyleUnit_Grad
                      || self.unit() == eStyleUnit_Radian || self.unit() == eStyleUnit_Turn
                      || self.unit() == eStyleUnit_FlexFraction);
        *self.union().mFloat.as_ref()
    }

    #[inline]
    /// Pretend inner value is an int; obtain it.
    unsafe fn get_integer(&self) -> i32 {
        use gecko_bindings::structs::nsStyleUnit::*;
        debug_assert!(self.unit() == eStyleUnit_Coord || self.unit() == eStyleUnit_Integer
                      || self.unit() == eStyleUnit_Enumerated);
        *self.union().mInt.as_ref()
    }

    #[inline]
    /// Pretend inner value is a calc; obtain it.
    /// Ensure that the unit is Calc before calling this.
    unsafe fn get_calc_value(&self) -> nsStyleCoord_CalcValue {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        (*self.as_calc())._base
    }

    #[inline]
    /// Pretend the inner value is a calc expression, and obtain it.
    unsafe fn as_calc(&self) -> &nsStyleCoord_Calc {
        debug_assert!(self.unit() == nsStyleUnit::eStyleUnit_Calc);
        &*(*self.union().mPointer.as_ref() as *const nsStyleCoord_Calc)
    }
}
<|endoftext|>