<commit_before>use super::*;

#[derive(Copy, Clone)]
pub enum PlStage {
    IC = 0,
    RF = 1,
    EX = 2,
    DC = 3,
    WB = 4
}

#[derive(Copy, Clone)]
pub struct Ic {
    pub op: Inst
}

#[derive(Copy, Clone)]
pub struct Rf {
    /* RF stage */
    pub rt: u64,
    pub rs: u64
}

#[derive(Copy, Clone)]
pub struct Ex {
    /* EX stage */
    pub ol: u64,
    pub br: bool,
    pub wlr: bool
}

#[derive(Copy, Clone)]
pub struct Dc {
    pub dc: u64
}

#[derive(Copy, Clone)]
pub struct Wb {

}

#[derive(Copy, Clone)]
pub struct Pls {
    pub st: usize,
    pub interlock: bool,
    pub ic : Ic,
    pub rf: Rf,
    pub ex: Ex,
    pub dc: Dc,
    pub wb: Wb
}

impl Pls {
    pub fn new() -> Pls {
        Pls {

            st: 0,

            interlock: false,

            /* IC stage */
            ic: Ic {
                op: Inst(0)
            },

            /* RF stage */
            rf: Rf {
                rt: 0,
                rs: 0
            },

            /* EX stage */
            ex: Ex {
                ol: 0,
                br: false,
                wlr: false
            },

            /* DC stage */
            dc: Dc {
                dc: 0
            },

            /* WB stage */
            wb: Wb {

            }
        }
    }
}

pub struct Pl {
    /* pipeline cycle */
    pcycle: usize,
    /* pipeline stages */
    st: [Pls; 5],
    /* delay slot program counter */
    ds_pc: u64
}

impl Pl {
    pub fn new() -> Pl {
        let mut pl = Pl {
            pcycle: 0,
            st: [Pls::new(); 5],
            ds_pc: 0
        };

        pl.st[0].st = 4; // wb
        pl.st[1].st = 3; // dc
        pl.st[2].st = 2; // ex
        pl.st[3].st = 1; // rf
        pl.st[4].st = 0; // ic

        pl
    }
}

/* IC - Instruction Cache Fetch */
pub fn ic(i: usize, cpu: &mut VR4300, mc: &MC) {

    /* if there is a branch waiting in the delay slot, we need to update the PC here */
    if cpu.pl.ds_pc != 0 {
        cpu.pc = cpu.pl.ds_pc;
        cpu.pl.ds_pc = 0;
    }

    let val = mc.read((cpu.pc + (4*i as u64)) as u32);
    cpu.pl.st[i].ic.op = Inst(val);

    /* go to the rf stage */
    cpu.pl.st[i].st = 1;
}

/* RF - Register Fetch */
pub fn rf(i: usize, cpu: &mut VR4300) {

    // match cpu.pl.st[i - 1].ic.op.class() {
    //     OpC::L | OpC::S => {
    //         /* interlock until the load delay slot is written back */
    //         if cpu.pl.st[i - 1].rf.rt == cpu.pl.st[i].rf.rt {
    //             cpu.pl.st[i].interlock = true;
    //             return;
    //         }
    //     }, _ => {
    //
    //     }
    // }

    cpu.pc += 4;

    match cpu.pl.st[i].ic.op.class() {
        OpC::C => {
            cpu.pl.st[i].rf.rs = cpu.cp0.rgpr(cpu.pl.st[i].ic.op._rd()) as u64
        }, _ => {
            cpu.pl.st[i].rf.rs = cpu.rgpr(cpu.pl.st[i].ic.op._rs());
        }
    }

    cpu.pl.st[i].rf.rt = cpu.rgpr(cpu.pl.st[i].ic.op._rt());

    /* go to the ex stage */
    cpu.pl.st[i].st = 2;
}

/* EX - Execution */
pub fn ex(i: usize, cpu: &mut VR4300) {

    match cpu.pl.st[i].ic.op.op() {
        Op::Syscall => {
            if cpu.pl.st[i].ic.op.sa() > 0 {
                let result = if cpu.pl.st[i].ic.op._rt() == 16 { "Pass" }  else { "Fail" };
                println!("Test Result - ISA:{:X}  Set:{:X}  Test:{:X}  Result:{:?}", cpu.pl.st[i].ic.op._rs(), cpu.pl.st[i].ic.op._rd(), cpu.pl.st[i].ic.op.sa(), result);
            }
        }, _ => {
            match cpu.pl.st[i].ic.op.class() {
                OpC::L => {

                }, OpC::C => {

                }, OpC::B => {
                    cpu.pl.st[i].ic.op.ex()(&mut cpu.pl.st[i]);

                    /* if a branch will occur, set the delay slot program counter */
                    if cpu.pl.st[i].ex.br {
                        let offset = ((cpu.pl.st[i].ic.op.offset() as i16 as i32) << 2) as i64;
                        cpu.pl.ds_pc = (cpu.pc as i64 + offset) as u64;
                    }

                }, OpC::J => {

                }, _ => {
                    cpu.pl.st[i].ic.op.ex()(&mut cpu.pl.st[i]);
                }
            }
        }
    }

    /* go to the dc stage */
    cpu.pl.st[i].st = 3;
}

/* DC - Data Cache Fetch */
pub fn dc(i: usize, cpu: &mut VR4300, mc: &MC) {

    match cpu.pl.st[i].ic.op.class() {
        OpC::L => {
            let base = cpu.pl.st[i].rf.rs as i64;
            let offset = cpu.pl.st[i].ic.op.offset() as i16 as i64;
            cpu.pl.st[i].dc.dc = mc.read((base + offset) as u32) as u64;
            /* need to call the ex function as a hack to get ol populated */
            cpu.pl.st[i].ic.op.ex()(&mut cpu.pl.st[i]);
        }, _ => {

        }
    }

    /* go to the wb stage */
    cpu.pl.st[i].st = 4;
}

/* WB - Write Back */
pub fn wb(i: usize, cpu: &mut VR4300, mc: &mut MC) {

    match cpu.pl.st[i].ic.op.class() {

        /* decode instruction types I, L, S, J, B, R, C for writeback */

        OpC::I | OpC::L => {
            /* I and L instructions write back to the rt register */
            cpu.wgpr(cpu.pl.st[i].ex.ol, cpu.pl.st[i].ic.op._rt());
        }, OpC::S => {
            /* S instructions write back to memory */
            let base = cpu.pl.st[i].rf.rs as i64;
            let offset = cpu.pl.st[i].ic.op.offset() as i16 as i64;
            mc.write((base + offset) as u64 as u32, cpu.pl.st[i].ex.ol as u32);
        }, OpC::J | OpC::B => {
            /* J and B instructions wrote to the delay slot program counter and link register */
            if cpu.pl.st[i].ex.wlr {
                cpu.wgpr(cpu.pc + 8, 31);
            }
        }, OpC::R => {
            /* write back to rd */
            cpu.wgpr(cpu.pl.st[i].ex.ol, cpu.pl.st[i].ic.op._rd());
        }, OpC::C => {
            /* write back to rt on the coprocessor */
            cpu.cp0.wgpr(cpu.pl.st[i].ex.ol as u32, cpu.pl.st[i].ic.op._rt());
        }
    }

    /* go to the ic stage */
    cpu.pl.st[i].st = 0;
}

pub fn clock(cpu: &mut VR4300, mc: &mut MC) {

    println!("{:?}", cpu.pl);

    /* run 5 pcycles per clock */
    for i in 0..5 {
        match cpu.pl.st[i].st {
            0 => {
                ic(i, cpu, mc);
            }, 1 => {
                rf(i, cpu);
            }, 2 => {
                ex(i, cpu);
            }, 3 => {
                dc(i, cpu, mc);
            }, 4 => {
                wb(i, cpu, mc);
            } _ => {
                panic!("invalid pipeline stage");
            }
        }
    }

    cpu.pl.pcycle += 5;
}

impl fmt::Debug for Pls {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        match self.st {
            0 => {
                try!(write!(f, "IC"));
            }, 1 => {
                try!(write!(f, "RF"));
            }, 2 => {
                try!(write!(f, "EX"));
            }, 3 => {
                try!(write!(f, "DC"));
            }, 4 => {
                try!(write!(f, "WB"));
            }, _ => {
                panic!("invalid stage");
            }
        }

        Ok(())

    }
}

impl fmt::Debug for Pl {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        for i in 0..5 {
            try!(writeln!(f, "{}: ({:?}) - {}", i, self.st[i], self.st[i].ic.op));
        }

        Ok(())

    }
}
<commit_msg>update pipeline<commit_after>use super::*;

#[derive(Copy, Clone)]
pub enum PlStage {
    IC = 0,
    RF = 1,
    EX = 2,
    DC = 3,
    WB = 4
}

#[derive(Copy, Clone)]
pub struct Ic {
    pub op: Inst
}

#[derive(Copy, Clone)]
pub struct Rf {
    /* RF stage */
    pub rt: u64,
    pub rs: u64
}

#[derive(Copy, Clone)]
pub struct Ex {
    /* EX stage */
    pub stalled: bool,
    pub ol: u64,
    pub br: bool,
    pub wlr: bool
}

#[derive(Copy, Clone)]
pub struct Dc {
    pub dc: u64
}

#[derive(Copy, Clone)]
pub struct Wb {

}

#[derive(Copy, Clone)]
pub struct Pls {
    pub st: usize,
    pub interlock: bool,
    pub ic : Ic,
    pub rf: Rf,
    pub ex: Ex,
    pub dc: Dc,
    pub wb: Wb
}

impl Pls {
    pub fn new() -> Pls {
        Pls {

            st: 0,

            interlock: false,

            /* IC stage */
            ic: Ic {
                op: Inst(0)
            },

            /* RF stage */
            rf: Rf {
                rt: 0,
                rs: 0
            },

            /* EX stage */
            ex: Ex {
                stalled: false,
                ol: 0,
                br: false,
                wlr: false
            },

            /* DC stage */
            dc: Dc {
                dc: 0
            },

            /* WB stage */
            wb: Wb {

            }
        }
    }
}

pub struct Pl {
    /* pipeline cycle */
    pcycle: usize,
    /* pipeline stages */
    st: [Pls; 5],
    /* delay slot program counter */
    ds_pc: u64
}

impl Pl {
    pub fn new() -> Pl {
        let mut pl = Pl {
            pcycle: 0,
            st: [Pls::new(); 5],
            ds_pc: 0
        };

        pl.st[0].st = 4; // wb
        pl.st[1].st = 3; // dc
        pl.st[2].st = 2; // ex
        pl.st[3].st = 1; // rf
        pl.st[4].st = 0; // ic

        pl
    }
}

/* IC - Instruction Cache Fetch */
pub fn ic(cpu: &mut VR4300, mc: &MC) {

    /* if there is a branch waiting in the delay slot, we need to update the PC here */
    if cpu.pl.ds_pc != 0 {
        cpu.pc = cpu.pl.ds_pc;
        cpu.pl.ds_pc = 0;
    }

    let val = mc.read((cpu.pc) as u32);
    cpu.pl.st[4].ic.op = Inst(val);

    println!("IC {}", cpu.pl.st[4].ic.op);

    cpu.pl.st[3] = cpu.pl.st[4]; // IC -> RF
}

/* RF - Register Fetch */
pub fn rf(cpu: &mut VR4300) {

    println!("RF {}", cpu.pl.st[3].ic.op);

    cpu.pc += 4;

    match cpu.pl.st[3].ic.op.class() {
        OpC::C => {
            cpu.pl.st[3].rf.rs = cpu.cp0.rgpr(cpu.pl.st[3].ic.op._rd()) as u64
        }, _ => {
            cpu.pl.st[3].rf.rs = cpu.rgpr(cpu.pl.st[3].ic.op._rs());
        }
    }

    cpu.pl.st[3].rf.rt = cpu.rgpr(cpu.pl.st[3].ic.op._rt());

    cpu.pl.st[2] = cpu.pl.st[3]; // RF -> EX
}

/* EX - Execution */
pub fn ex(cpu: &mut VR4300) {

    println!("EX {}", cpu.pl.st[2].ic.op);

    /* stall if the register is the dest of the RF instruction */
    match cpu.pl.st[1].ic.op.class() {

        OpC::I | OpC::L => {
            if cpu.pl.st[2].ic.op._rs() == cpu.pl.st[3].ic.op._rt() || cpu.pl.st[2].ic.op._rt() == cpu.pl.st[3].ic.op._rt() {
                cpu.pl.st[2].ex.stalled = true;
                cpu.pl.st[1] = Pls::new();
                return;
            }
        }, OpC::R => {
            if cpu.pl.st[2].ic.op._rs() == cpu.pl.st[3].ic.op._rd() || cpu.pl.st[2].ic.op._rt() == cpu.pl.st[3].ic.op._rd() {
                cpu.pl.st[2].ex.stalled = true;
                cpu.pl.st[1] = Pls::new();
                return;
            }
        } _=> {

        }
    }

    match cpu.pl.st[2].ic.op.op() {
        Op::Syscall => {
            if cpu.pl.st[2].ic.op.sa() > 0 {
                let result = if cpu.pl.st[2].ic.op._rt() == 16 { "Pass" }  else { "Fail" };
                println!("Test Result - ISA:{:X}  Set:{:X}  Test:{:X}  Result:{:?}", cpu.pl.st[2].ic.op._rs(), cpu.pl.st[2].ic.op._rd(), cpu.pl.st[2].ic.op.sa(), result);
            }
        }, _ => {
            match cpu.pl.st[2].ic.op.class() {
                OpC::L => {

                }, OpC::C => {

                }, OpC::B => {
                    cpu.pl.st[2].ic.op.ex()(&mut cpu.pl.st[2]);

                    /* if a branch will occur, set the delay slot program counter */
                    if cpu.pl.st[2].ex.br {
                        let offset = ((cpu.pl.st[2].ic.op.offset() as i16 as i32) << 2) as i64;
                        cpu.pl.ds_pc = (cpu.pc as i64 + offset) as u64;
                    }

                }, OpC::J => {

                }, _ => {
                    cpu.pl.st[2].ic.op.ex()(&mut cpu.pl.st[2]);
                }
            }
        }
    }

    cpu.pl.st[1] = cpu.pl.st[2];
}

/* DC - Data Cache Fetch */
pub fn dc(cpu: &mut VR4300, mc: &MC) {

    println!("DC {}", cpu.pl.st[1].ic.op);

    match cpu.pl.st[1].ic.op.class() {
        OpC::L => {
            let base = cpu.pl.st[1].rf.rs as i64;
            let offset = cpu.pl.st[1].ic.op.offset() as i16 as i64;
            cpu.pl.st[1].dc.dc = mc.read((base + offset) as u32) as u64;
            /* need to call the ex function as a hack to get ol populated */
            cpu.pl.st[1].ic.op.ex()(&mut cpu.pl.st[1]);
        }, _ => {

        }
    }

    cpu.pl.st[0] = cpu.pl.st[1];
}

/* WB - Write Back */
pub fn wb(cpu: &mut VR4300, mc: &mut MC) {

    println!("WB {}", cpu.pl.st[0].ic.op);

    match cpu.pl.st[0].ic.op.class() {

        /* decode instruction types I, L, S, J, B, R, C for writeback */

        OpC::I | OpC::L => {
            /* I and L instructions write back to the rt register */
            cpu.wgpr(cpu.pl.st[0].ex.ol, cpu.pl.st[0].ic.op._rt());
        }, OpC::S => {
            /* S instructions write back to memory */
            let base = cpu.pl.st[0].rf.rs as i64;
            let offset = cpu.pl.st[0].ic.op.offset() as i16 as i64;
            mc.write((base + offset) as u64 as u32, cpu.pl.st[0].ex.ol as u32);
        }, OpC::J | OpC::B => {
            /* J and B instructions wrote to the delay slot program counter and link register */
            if cpu.pl.st[0].ex.wlr {
                cpu.wgpr(cpu.pc + 8, 31);
            }
        }, OpC::R => {
            /* write back to rd */
            cpu.wgpr(cpu.pl.st[0].ex.ol, cpu.pl.st[0].ic.op._rd());
        }, OpC::C => {
            /* write back to rt on the coprocessor */
            cpu.cp0.wgpr(cpu.pl.st[0].ex.ol as u32, cpu.pl.st[0].ic.op._rt());
        }
    }

    if cpu.pl.st[2].ex.stalled {
        cpu.pl.st[2].ex.stalled = false;
    }

    cpu.pl.st[0] = Pls::new();
}

pub fn clock(cpu: &mut VR4300, mc: &mut MC) {

    wb(cpu, mc);
    dc(cpu, mc);
    ex(cpu);
    rf(cpu);
    ic(cpu, mc);

}

impl fmt::Debug for Pls {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        Ok(())

    }
}

impl fmt::Debug for Pl {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        let mut i = 1;

        for j in 0..5 {

            try!(write!(f, "{0: <10}", self.st[j].ic.op));

            for k in 0..5 {

                match k {
                    0 => {
                        try!(write!(f, "IC"));
                    }, 1 => {
                        try!(write!(f, "RF"));
                    }, 2 => {
                        try!(write!(f, "EX"));
                    }, 3 => {
                        try!(write!(f, "DC"));
                    }, 4 => {
                        try!(write!(f, "WB"));
                    }, _ => {
                        panic!("invalid stage");
                    }
                }

                try!(write!(f, " "));
            }

            try!(write!(f, "\n"));

            i += 1;
        }

        Ok(())

    }
}
<|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 std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;
use engine::Filesystem;
use engine::Pool;
use engine::RenameAction;
use engine::engine::Redundancy;
use engine::strat_engine::blockdev::wipe_sectors;
use consts::IEC::Mi;

use super::super::engine::{FilesystemUuid, HasName, HasUuid};
use super::super::structures::Table;

use super::serde_structs::StratSave;
use super::blockdev::{initialize, resolve_devices};
use super::blockdevmgr::BlockDevMgr;
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::metadata::MIN_MDA_SECTORS;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
pub const META_LOWATER: u64 = 512;
pub const DATA_LOWATER: DataBlocks = DataBlocks(512);

pub const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
pub const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let devices = try!(resolve_devices(paths));
        let mut block_mgr =
            BlockDevMgr::new(try!(initialize(&pool_uuid, devices, MIN_MDA_SECTORS, force)));

        if block_mgr.avail_space() < INITIAL_META_SIZE + INITIAL_DATA_SIZE {
            let short_bytes = *(INITIAL_META_SIZE + INITIAL_DATA_SIZE - block_mgr.avail_space()) *
                              SECTOR_SIZE as u64;
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Initial total usable pool capacity too \
                                                    small by {} bytes",
                                                   short_bytes)));
        }

        let meta_regions = block_mgr.alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let meta_dev = try!(LinearDev::new(&format!("stratis_{}_meta", name), dm, &meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr.alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let data_dev = try!(LinearDev::new(&format!("stratis_{}_data", name), dm, &data_regions));
        let length = try!(data_dev.size()).sectors();

        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&format!("stratis_{}_thinpool", name),
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Return the metadata from the first blockdev with up-to-date, readable
    /// metadata.
    pub fn load_state(&self) -> Option<Vec<u8>> {
        self.block_devs.load_state()
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        self.block_devs.save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn to_save(&self) -> StratSave {
        StratSave {
            name: self.name.clone(),
            id: self.pool_uuid.simple().to_string(),
            block_devs: self.block_devs.to_save(),
        }
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in self.filesystems.iter_mut() {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let dm = try!(DM::new());
            let new_filesystem = try!(StratFilesystem::new(uuid, name, &dm, &mut self.thin_pool));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}
<commit_msg>Add a teardown() method to StratPool<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::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;
use engine::Filesystem;
use engine::Pool;
use engine::RenameAction;
use engine::engine::Redundancy;
use engine::strat_engine::blockdev::wipe_sectors;
use consts::IEC::Mi;

use super::super::engine::{FilesystemUuid, HasName, HasUuid};
use super::super::structures::Table;

use super::serde_structs::StratSave;
use super::blockdev::{initialize, resolve_devices};
use super::blockdevmgr::BlockDevMgr;
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::metadata::MIN_MDA_SECTORS;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
pub const META_LOWATER: u64 = 512;
pub const DATA_LOWATER: DataBlocks = DataBlocks(512);

pub const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
pub const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let devices = try!(resolve_devices(paths));
        let mut block_mgr =
            BlockDevMgr::new(try!(initialize(&pool_uuid, devices, MIN_MDA_SECTORS, force)));

        if block_mgr.avail_space() < INITIAL_META_SIZE + INITIAL_DATA_SIZE {
            let short_bytes = *(INITIAL_META_SIZE + INITIAL_DATA_SIZE - block_mgr.avail_space()) *
                              SECTOR_SIZE as u64;
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Initial total usable pool capacity too \
                                                    small by {} bytes",
                                                   short_bytes)));
        }

        let meta_regions = block_mgr.alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let meta_dev = try!(LinearDev::new(&format!("stratis_{}_meta", name), dm, &meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr.alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let data_dev = try!(LinearDev::new(&format!("stratis_{}_data", name), dm, &data_regions));
        let length = try!(data_dev.size()).sectors();

        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&format!("stratis_{}_thinpool", name),
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Return the metadata from the first blockdev with up-to-date, readable
    /// metadata.
    pub fn load_state(&self) -> Option<Vec<u8>> {
        self.block_devs.load_state()
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        self.block_devs.save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn to_save(&self) -> StratSave {
        StratSave {
            name: self.name.clone(),
            id: self.pool_uuid.simple().to_string(),
            block_devs: self.block_devs.to_save(),
        }
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in self.filesystems.iter_mut() {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }

    /// Teardown a pool.
    /// Take down the device mapper devices belonging to the pool.
    /// This method and destroy() must keep their DM teardown operations
    /// in sync.
    pub fn teardown(self) -> EngineResult<()> {
        // TODO: any necessary clean up of filesystems
        if !self.filesystems.is_empty() {
            return Err(EngineError::Engine(ErrorEnum::Busy,
                                           format!("May be unsynced files on device.")));
        }
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let dm = try!(DM::new());
            let new_filesystem = try!(StratFilesystem::new(uuid, name, &dm, &mut self.thin_pool));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        // Ensure that DM teardown operations in this method are in sync
        // with operations in teardown().
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}
<|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 std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use super::super::super::consts::IEC::Mi;

use super::super::engine::{Filesystem, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{FilesystemUuid, RenameAction, Redundancy};

use super::serde_structs::{Isomorphism, PoolSave};
use super::blockdevmgr::BlockDevMgr;
use super::device::wipe_sectors;
use super::dmdevice::{FlexRole, ThinPoolRole, format_flex_name, format_thinpool_name};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::mdv::MetadataVol;
use super::metadata::MIN_MDA_SECTORS;

const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
const META_LOWATER: u64 = 512;
const DATA_LOWATER: DataBlocks = DataBlocks(512);

const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);
const INITIAL_MDV_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
    mdv: MetadataVol,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr =
            try!(BlockDevMgr::initialize(&pool_uuid, paths, MIN_MDA_SECTORS, force));

        if block_mgr.avail_space() < StratPool::min_initial_size() {
            let avail_size = block_mgr.avail_space().bytes();
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Space on pool must be at least {} bytes, \
                                                   available space is only {} bytes",
                                                   StratPool::min_initial_size().bytes(),
                                                   avail_size)));

        }

        let meta_regions = block_mgr
            .alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinMeta);
        let meta_dev = try!(LinearDev::new(&device_name, dm, meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr
            .alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinData);
        let data_dev = try!(LinearDev::new(&device_name, dm, data_regions));
        let length = try!(data_dev.size()).sectors();

        let device_name = format_thinpool_name(&pool_uuid, ThinPoolRole::Pool);
        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&device_name,
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mdv_regions = block_mgr
            .alloc_space(INITIAL_MDV_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::MetadataVolume);
        let mdv_dev = try!(LinearDev::new(&device_name, dm, mdv_regions));
        let mdv = try!(MetadataVol::initialize(&pool_uuid, mdv_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
            mdv: mdv,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Minimum initial size for a pool.
    pub fn min_initial_size() -> Sectors {
        INITIAL_META_SIZE + INITIAL_DATA_SIZE + INITIAL_MDV_SIZE
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        self.block_devs
            .save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        if let Err(e) = self.mdv.check() {
            error!("MDV error: {}", e);
            return;
        }

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in &mut self.filesystems {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }

    /// Teardown a pool.
    /// Take down the device mapper devices belonging to the pool.
    /// This method and destroy() must keep their DM teardown operations
    /// in sync.
    pub fn teardown(self) -> EngineResult<()> {
        // TODO: any necessary clean up of filesystems
        if !self.filesystems.is_empty() {
            return Err(EngineError::Engine(ErrorEnum::Busy,
                                           format!("May be unsynced files on device.")));
        }
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = try!(DM::new());
        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let new_filesystem = try!(StratFilesystem::initialize(&self.pool_uuid,
                                                                  uuid,
                                                                  name,
                                                                  &dm,
                                                                  &mut self.thin_pool));
            try!(self.mdv.save_fs(&new_filesystem));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        // Ensure that DM teardown operations in this method are in sync
        // with operations in teardown().
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        for fsid in fs_uuids {
            try!(self.mdv.rm_fs(fsid));
        }
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Isomorphism<PoolSave> for StratPool {
    fn to_save(&self) -> PoolSave {
        PoolSave { name: self.name.clone() }
    }
}
<commit_msg>Rearrange out-of-order import<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::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use super::super::super::consts::IEC::Mi;

use super::super::engine::{Filesystem, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{FilesystemUuid, RenameAction, Redundancy};

use super::blockdevmgr::BlockDevMgr;
use super::device::wipe_sectors;
use super::dmdevice::{FlexRole, ThinPoolRole, format_flex_name, format_thinpool_name};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::mdv::MetadataVol;
use super::metadata::MIN_MDA_SECTORS;
use super::serde_structs::{Isomorphism, PoolSave};

const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
const META_LOWATER: u64 = 512;
const DATA_LOWATER: DataBlocks = DataBlocks(512);

const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);
const INITIAL_MDV_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
    mdv: MetadataVol,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr =
            try!(BlockDevMgr::initialize(&pool_uuid, paths, MIN_MDA_SECTORS, force));

        if block_mgr.avail_space() < StratPool::min_initial_size() {
            let avail_size = block_mgr.avail_space().bytes();
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Space on pool must be at least {} bytes, \
                                                   available space is only {} bytes",
                                                   StratPool::min_initial_size().bytes(),
                                                   avail_size)));

        }

        let meta_regions = block_mgr
            .alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinMeta);
        let meta_dev = try!(LinearDev::new(&device_name, dm, meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr
            .alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::ThinData);
        let data_dev = try!(LinearDev::new(&device_name, dm, data_regions));
        let length = try!(data_dev.size()).sectors();

        let device_name = format_thinpool_name(&pool_uuid, ThinPoolRole::Pool);
        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&device_name,
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mdv_regions = block_mgr
            .alloc_space(INITIAL_MDV_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let device_name = format_flex_name(&pool_uuid, FlexRole::MetadataVolume);
        let mdv_dev = try!(LinearDev::new(&device_name, dm, mdv_regions));
        let mdv = try!(MetadataVol::initialize(&pool_uuid, mdv_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
            mdv: mdv,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Minimum initial size for a pool.
    pub fn min_initial_size() -> Sectors {
        INITIAL_META_SIZE + INITIAL_DATA_SIZE + INITIAL_MDV_SIZE
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        self.block_devs
            .save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        if let Err(e) = self.mdv.check() {
            error!("MDV error: {}", e);
            return;
        }

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in &mut self.filesystems {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }

    /// Teardown a pool.
    /// Take down the device mapper devices belonging to the pool.
    /// This method and destroy() must keep their DM teardown operations
    /// in sync.
    pub fn teardown(self) -> EngineResult<()> {
        // TODO: any necessary clean up of filesystems
        if !self.filesystems.is_empty() {
            return Err(EngineError::Engine(ErrorEnum::Busy,
                                           format!("May be unsynced files on device.")));
        }
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = try!(DM::new());
        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let new_filesystem = try!(StratFilesystem::initialize(&self.pool_uuid,
                                                                  uuid,
                                                                  name,
                                                                  &dm,
                                                                  &mut self.thin_pool));
            try!(self.mdv.save_fs(&new_filesystem));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        // Ensure that DM teardown operations in this method are in sync
        // with operations in teardown().
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.mdv.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        for fsid in fs_uuids {
            try!(self.mdv.rm_fs(fsid));
        }
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Isomorphism<PoolSave> for StratPool {
    fn to_save(&self) -> PoolSave {
        PoolSave { name: self.name.clone() }
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/lib/vec.rs
//! Dynamically growable vector type
use core::prelude::*;
use core::iter::{FromIterator};
use core::{ops,fmt};
use lib::collections::{MutableSeq};
use memory::heap::ArrayAlloc;

// TODO: Replace allocation with a boxed slice (or some other managed allocation)
// - Maybe a heap-provided "Array" type that is safe to alloc/free, but unsafe to access

/// Growable array of items
pub struct Vec<T>
{
	data: ArrayAlloc<T>,
	size: usize,
}

/// Owning iterator
pub struct MoveItems<T>
{
	data: ArrayAlloc<T>,
	count: usize,
	ofs: usize,
}

impl<T> Vec<T>
{
	//pub static EMPTY: Vec<T> = Vec { data: Unique(::memory::heap::ZERO_ALLOC), size: 0, capacity: 0 };

	/// Create a new, empty vector
	pub fn new() -> Vec<T>
	{
		Vec::with_capacity(0)
	}
	/// Create a vector with an initialised capacity
	pub fn with_capacity(size: usize) -> Vec<T>
	{
		Vec {
			data: ArrayAlloc::new(size),
			size: 0,
		}
	}
	/// Populate vector using a provided callback
	pub fn from_fn<Fcn>(length: usize, op: Fcn) -> Vec<T>
	where
		Fcn: Fn(usize) -> T
	{
		let mut ret = Vec::with_capacity(length);
		for i in (0 .. length) {
			ret.push( op(i) );
		}
		ret
	}

	/// Obtain a mutable pointer to an item within the vector
	fn get_mut_ptr(&mut self, index: usize) -> *mut T
	{
		assert!(index < self.size, "Vec<{}>::get_mut_ptr(): Index out of range, {} >= {}", type_name!(T), index, self.size);
		self.data.get_ptr_mut(index)
	}

	/// Move contents into an iterator (consuming self)
	pub fn into_iter(mut self) -> MoveItems<T>
	{
		let dataptr = ::core::mem::replace(&mut self.data, ArrayAlloc::new(0));
		let count = self.size;
		unsafe { ::core::mem::forget(self) };
		MoveItems {
			data: dataptr,
			ofs: 0,
			count: count,
		}
	}

	/// Clear the vector
	pub fn clear(&mut self)
	{
		self.truncate(0);
		unimplemented!();
	}

	fn reserve_cap(&mut self, size: usize)
	{
		let newcap = ::lib::num::round_up(size, 1 << (64-size.leading_zeros()));
		if newcap > self.data.count()
		{
			if self.data.expand(newcap)
			{
				// All good
			}
			else
			{
				let mut newdata = ArrayAlloc::new(newcap);
				unsafe {
					for i in (0 .. self.size) {
						let val = self.move_ent(i as usize);
						::core::ptr::write(newdata.get_ptr_mut(i), val);
					}
				}
				//log_debug!("Vec<{}>::reserve_cap({}): newdata={:?}", type_name!(T), size, newdata);
				self.data = newdata;
			}
		}
	}

	/// Reserve space in the vector for `extras` new elements
	pub fn reserve(&mut self, extras: usize) {
		let newcap = self.size + extras;
		self.reserve_cap(newcap);
	}

	/// Obtain a mutable slice to the content
	pub fn slice_mut<'a>(&'a mut self) -> &'a mut [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: self.data.get_base_mut(), len: self.size } ) }
	}

	/// Move out of a slot in the vector, leaving unitialise memory in its place
	unsafe fn move_ent(&mut self, pos: usize) -> T
	{
		::core::ptr::read(self.data.get_ptr(pos))
	}

	/// Insert an item at the specified index (moving subsequent items up)
	pub fn insert(&mut self, pos: usize, value: T)
	{
		// Expand by one element
		let ns = self.size + 1;
		self.reserve_cap(ns);
		self.size = ns;
		unsafe
		{
			// Move elements (pos .. len) to (pos+1 .. len+1)
			for i in (pos .. self.size).rev()
			{
				let src = self.data.get_ptr( i );
				let dst = self.data.get_ptr_mut( i+1 );
				::core::ptr::write(dst, ::core::ptr::read(src));
			}
			// Store new element
			::core::ptr::write( self.data.get_ptr_mut(pos), value );
		}
	}

	/// Truncate a vector to the given size
	pub fn truncate(&mut self, newsize: usize)
	{
		if newsize >= self.size
		{
			unsafe
			{
				for i in (newsize .. self.size) {
					::core::mem::drop( ::core::ptr::read(self.get_mut_ptr(i) as *const T) );
				}
				self.size = newsize;
			}
		}
	}

	fn as_slice(&self) -> &[T]
	{
		let rawslice = ::core::raw::Slice { data: self.data.get_base() as *const T, len: self.size };
		unsafe { ::core::mem::transmute( rawslice ) }
	}
}

#[macro_export]
/// Create a new vector in a manner similar array literals
///
/// ```
/// vec![1u32, 2, 3, 4]
/// ```
macro_rules! vec
{
	($( $v:expr ),*) => ({
		let mut v = $crate::lib::Vec::new();
		v.reserve( _count!( $($v),* ) );
		$( v.push($v); )*
		v
		});
}

impl<T: Clone> Vec<T>
{
	pub fn resize(&mut self, new_len: usize, value: T)
	{
		if self.len() > new_len {
			self.truncate(new_len);
		}
		else {
			self.reserve_cap(new_len);
			for _ in self.size .. new_len {
				self.push(value.clone());
			}
		}
	}
}

impl<T: Clone> Clone for Vec<T>
{
	fn clone(&self) -> Vec<T> {
		self.iter().cloned().collect()
	}
}

impl<T: fmt::Debug> fmt::Debug for Vec<T> {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		fmt::Debug::fmt(&**self, f)
	}
}

impl<T> ::core::default::Default for Vec<T>
{
	fn default() -> Vec<T> { Vec::new() }
}

impl<T> ops::Deref for Vec<T>
{
	type Target = [T];
	fn deref(&self) -> &[T] {
		self.as_slice()
	}
}
impl<T> ops::DerefMut for Vec<T>
{
	fn deref_mut(&mut self) -> &mut [T] {
		self.slice_mut()
	}
}

impl<T:Clone> Vec<T>
{
	pub fn from_elem(size: usize, elem: T) -> Vec<T>
	{
		let mut ret = Vec::with_capacity(size);
		for _ in 0 .. size-1 {
			ret.push(elem.clone());
		}
		ret.push(elem);
		ret
	}

	pub fn push_all(&mut self, other: &[T])
	{
		self.reserve(other.len());
		for v in other.iter() {
			self.push(v.clone());
		}
	}
}

impl<T> ops::Drop for Vec<T>
{
	fn drop(&mut self)
	{
		log_debug!("Vec::<{}>::drop() - {:?} w/ {} ents", type_name!(T), self.data, self.size);
		unsafe {
			while self.size > 0 {
				self.size -= 1;
				let idx = self.size;
				let ptr = self.data.get_ptr(idx) as *const T;
				::core::mem::drop( ::core::ptr::read(ptr) );
			}
		}
	}
}

macro_rules! vec_index {
	($T:ident -> $rv:ty : $($idx:ty)*) => { $(
		impl<$T> ops::Index<$idx> for Vec<$T>
		{
			type Output = $rv;
			fn index<'a>(&'a self, index: $idx) -> &'a $rv
			{
				&self.as_slice()[index]
			}
		}
		impl<$T> ops::IndexMut<$idx> for Vec<$T>
		{
			fn index_mut<'a>(&'a mut self, index: $idx) -> &'a mut $rv
			{
				&mut self.slice_mut()[index]
			}
		}
		)* }
	}
vec_index!{ T -> T : usize }
vec_index!{ T -> [T] : ops::Range<usize> ops::RangeTo<usize> ops::RangeFrom<usize> ops::RangeFull }

impl<T> MutableSeq<T> for Vec<T>
{
	fn push(&mut self, t: T)
	{
		let pos = self.size;
		self.reserve(1);
		self.size += 1;
		let ptr = self.get_mut_ptr(pos);
		//log_debug!("Vec.push {}", HexDump(&t));
		unsafe { ::core::ptr::write(ptr, t); }
	}
	fn pop(&mut self) -> Option<T>
	{
		if self.size == 0
		{
			None
		}
		else
		{
			self.size -= 1;
			let pos = self.size;
			Some( unsafe { self.move_ent(pos) } )
		}
	}
}

impl<T> FromIterator<T> for Vec<T>
{
	fn from_iter<IT>(src: IT) -> Vec<T>
	where
		IT: ::core::iter::IntoIterator<Item=T>
	{
		let iterator = src.into_iter();
		let mut ret = Vec::new();
		if let (_, Some(size)) = iterator.size_hint()
		{
			ret.reserve_cap(size);
		}
		for val in iterator
		{
			ret.push(val);
		}
		ret
	}
}

impl<T> ::core::iter::Extend<T> for Vec<T>
{
	fn extend<I: IntoIterator<Item=T>>(&mut self, src: I)
	{
		let iter = src.into_iter();

		if let (_, Some(size)) = iter.size_hint()
		{
			self.reserve(size);
		}
		for val in iter {
			self.push(val);
		}
	}
}

impl<T> IntoIterator for Vec<T>
{
	type IntoIter = MoveItems<T>;
	type Item = T;
	fn into_iter(self) -> MoveItems<T> {
		self.into_iter()
	}
}
impl<'a, T> IntoIterator for &'a Vec<T>
{
	type IntoIter = ::core::slice::Iter<'a,T>;
	type Item = &'a T;

	fn into_iter(self) -> ::core::slice::Iter<'a, T> {
		self.iter()
	}
}

impl<'a, T> IntoIterator for &'a mut Vec<T>
{
	type IntoIter = ::core::slice::IterMut<'a,T>;
	type Item = &'a mut T;

	fn into_iter(self) -> ::core::slice::IterMut<'a, T> {
		self.iter_mut()
	}
}

impl<T> MoveItems<T>
{
	/// Pop an item from the iterator
	fn pop_item(&mut self) -> T
	{
		//log_debug!("MoveItems.pop_item() ofs={}, count={}, data={}", self.ofs, self.count, self.data);
		assert!(self.ofs < self.count);
		let v: T = unsafe {
			let ptr = self.data.get_ptr(self.ofs);
			::core::ptr::read(ptr as *const _)
			};
		//log_debug!("MoveItems.pop_item() v = {}", HexDump(&v));
		self.ofs += 1;
		v
	}
}

impl<T> Iterator for MoveItems<T>
{
	type Item = T;
	fn next(&mut self) -> Option<T>
	{
		if self.ofs == self.count
		{
			None
		}
		else
		{
			Some( self.pop_item() )
		}
	}
}

impl<T> ops::Drop for MoveItems<T>
{
	fn drop(&mut self)
	{
		for _ in (self.ofs .. self.count) {
			self.pop_item();
		}
	}
}

// vim: ft=rust
<commit_msg>Kernel - (minor) Debug cleanup in Vec<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/lib/vec.rs
//! Dynamically growable vector type
use core::prelude::*;
use core::iter::{FromIterator};
use core::{ops,fmt};
use lib::collections::{MutableSeq};
use memory::heap::ArrayAlloc;

// TODO: Replace allocation with a boxed slice (or some other managed allocation)
// - Maybe a heap-provided "Array" type that is safe to alloc/free, but unsafe to access

/// Growable array of items
pub struct Vec<T>
{
	data: ArrayAlloc<T>,
	size: usize,
}

/// Owning iterator
pub struct MoveItems<T>
{
	data: ArrayAlloc<T>,
	count: usize,
	ofs: usize,
}

impl<T> Vec<T>
{
	//pub static EMPTY: Vec<T> = Vec { data: Unique(::memory::heap::ZERO_ALLOC), size: 0, capacity: 0 };

	/// Create a new, empty vector
	pub fn new() -> Vec<T>
	{
		Vec::with_capacity(0)
	}
	/// Create a vector with an initialised capacity
	pub fn with_capacity(size: usize) -> Vec<T>
	{
		Vec {
			data: ArrayAlloc::new(size),
			size: 0,
		}
	}
	/// Populate vector using a provided callback
	pub fn from_fn<Fcn>(length: usize, op: Fcn) -> Vec<T>
	where
		Fcn: Fn(usize) -> T
	{
		let mut ret = Vec::with_capacity(length);
		for i in (0 .. length) {
			ret.push( op(i) );
		}
		ret
	}

	/// Obtain a mutable pointer to an item within the vector
	fn get_mut_ptr(&mut self, index: usize) -> *mut T
	{
		assert!(index < self.size, "Vec<{}>::get_mut_ptr(): Index out of range, {} >= {}", type_name!(T), index, self.size);
		self.data.get_ptr_mut(index)
	}

	/// Move contents into an iterator (consuming self)
	pub fn into_iter(mut self) -> MoveItems<T>
	{
		let dataptr = ::core::mem::replace(&mut self.data, ArrayAlloc::new(0));
		let count = self.size;
		unsafe { ::core::mem::forget(self) };
		MoveItems {
			data: dataptr,
			ofs: 0,
			count: count,
		}
	}

	/// Clear the vector
	pub fn clear(&mut self)
	{
		self.truncate(0);
		unimplemented!();
	}

	fn reserve_cap(&mut self, size: usize)
	{
		let newcap = ::lib::num::round_up(size, 1 << (64-size.leading_zeros()));
		if newcap > self.data.count()
		{
			if self.data.expand(newcap)
			{
				// All good
			}
			else
			{
				let mut newdata = ArrayAlloc::new(newcap);
				unsafe {
					for i in (0 .. self.size) {
						let val = self.move_ent(i as usize);
						::core::ptr::write(newdata.get_ptr_mut(i), val);
					}
				}
				//log_debug!("Vec<{}>::reserve_cap({}): newdata={:?}", type_name!(T), size, newdata);
				self.data = newdata;
			}
		}
	}

	/// Reserve space in the vector for `extras` new elements
	pub fn reserve(&mut self, extras: usize) {
		let newcap = self.size + extras;
		self.reserve_cap(newcap);
	}

	/// Obtain a mutable slice to the content
	pub fn slice_mut<'a>(&'a mut self) -> &'a mut [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: self.data.get_base_mut(), len: self.size } ) }
	}

	/// Move out of a slot in the vector, leaving unitialise memory in its place
	unsafe fn move_ent(&mut self, pos: usize) -> T
	{
		::core::ptr::read(self.data.get_ptr(pos))
	}

	/// Insert an item at the specified index (moving subsequent items up)
	pub fn insert(&mut self, pos: usize, value: T)
	{
		// Expand by one element
		let ns = self.size + 1;
		self.reserve_cap(ns);
		self.size = ns;
		unsafe
		{
			// Move elements (pos .. len) to (pos+1 .. len+1)
			for i in (pos .. self.size).rev()
			{
				let src = self.data.get_ptr( i );
				let dst = self.data.get_ptr_mut( i+1 );
				::core::ptr::write(dst, ::core::ptr::read(src));
			}
			// Store new element
			::core::ptr::write( self.data.get_ptr_mut(pos), value );
		}
	}

	/// Truncate a vector to the given size
	pub fn truncate(&mut self, newsize: usize)
	{
		if newsize >= self.size
		{
			unsafe
			{
				for i in (newsize .. self.size) {
					::core::mem::drop( ::core::ptr::read(self.get_mut_ptr(i) as *const T) );
				}
				self.size = newsize;
			}
		}
	}

	fn as_slice(&self) -> &[T]
	{
		let rawslice = ::core::raw::Slice { data: self.data.get_base() as *const T, len: self.size };
		unsafe { ::core::mem::transmute( rawslice ) }
	}
}

#[macro_export]
/// Create a new vector in a manner similar array literals
///
/// ```
/// vec![1u32, 2, 3, 4]
/// ```
macro_rules! vec
{
	($( $v:expr ),*) => ({
		let mut v = $crate::lib::Vec::new();
		v.reserve( _count!( $($v),* ) );
		$( v.push($v); )*
		v
		});
}

impl<T: Clone> Vec<T>
{
	pub fn resize(&mut self, new_len: usize, value: T)
	{
		if self.len() > new_len {
			self.truncate(new_len);
		}
		else {
			self.reserve_cap(new_len);
			for _ in self.size .. new_len {
				self.push(value.clone());
			}
		}
	}
}

impl<T: Clone> Clone for Vec<T>
{
	fn clone(&self) -> Vec<T> {
		self.iter().cloned().collect()
	}
}

impl<T: fmt::Debug> fmt::Debug for Vec<T> {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		fmt::Debug::fmt(&**self, f)
	}
}

impl<T> ::core::default::Default for Vec<T>
{
	fn default() -> Vec<T> { Vec::new() }
}

impl<T> ops::Deref for Vec<T>
{
	type Target = [T];
	fn deref(&self) -> &[T] {
		self.as_slice()
	}
}
impl<T> ops::DerefMut for Vec<T>
{
	fn deref_mut(&mut self) -> &mut [T] {
		self.slice_mut()
	}
}

impl<T:Clone> Vec<T>
{
	pub fn from_elem(size: usize, elem: T) -> Vec<T>
	{
		let mut ret = Vec::with_capacity(size);
		for _ in 0 .. size-1 {
			ret.push(elem.clone());
		}
		ret.push(elem);
		ret
	}

	pub fn push_all(&mut self, other: &[T])
	{
		self.reserve(other.len());
		for v in other.iter() {
			self.push(v.clone());
		}
	}
}

impl<T> ops::Drop for Vec<T>
{
	fn drop(&mut self)
	{
		unsafe {
			while self.size > 0 {
				self.size -= 1;
				let idx = self.size;
				let ptr = self.data.get_ptr(idx) as *const T;
				::core::mem::drop( ::core::ptr::read(ptr) );
			}
		}
	}
}

macro_rules! vec_index {
	($T:ident -> $rv:ty : $($idx:ty)*) => { $(
		impl<$T> ops::Index<$idx> for Vec<$T>
		{
			type Output = $rv;
			fn index<'a>(&'a self, index: $idx) -> &'a $rv
			{
				&self.as_slice()[index]
			}
		}
		impl<$T> ops::IndexMut<$idx> for Vec<$T>
		{
			fn index_mut<'a>(&'a mut self, index: $idx) -> &'a mut $rv
			{
				&mut self.slice_mut()[index]
			}
		}
		)* }
	}
vec_index!{ T -> T : usize }
vec_index!{ T -> [T] : ops::Range<usize> ops::RangeTo<usize> ops::RangeFrom<usize> ops::RangeFull }

impl<T> MutableSeq<T> for Vec<T>
{
	fn push(&mut self, t: T)
	{
		let pos = self.size;
		self.reserve(1);
		self.size += 1;
		let ptr = self.get_mut_ptr(pos);
		unsafe { ::core::ptr::write(ptr, t); }
	}
	fn pop(&mut self) -> Option<T>
	{
		if self.size == 0
		{
			None
		}
		else
		{
			self.size -= 1;
			let pos = self.size;
			Some( unsafe { self.move_ent(pos) } )
		}
	}
}

impl<T> FromIterator<T> for Vec<T>
{
	fn from_iter<IT>(src: IT) -> Vec<T>
	where
		IT: ::core::iter::IntoIterator<Item=T>
	{
		let iterator = src.into_iter();
		let mut ret = Vec::new();
		if let (_, Some(size)) = iterator.size_hint()
		{
			ret.reserve_cap(size);
		}
		for val in iterator
		{
			ret.push(val);
		}
		ret
	}
}

impl<T> ::core::iter::Extend<T> for Vec<T>
{
	fn extend<I: IntoIterator<Item=T>>(&mut self, src: I)
	{
		let iter = src.into_iter();

		if let (_, Some(size)) = iter.size_hint()
		{
			self.reserve(size);
		}
		for val in iter {
			self.push(val);
		}
	}
}

impl<T> IntoIterator for Vec<T>
{
	type IntoIter = MoveItems<T>;
	type Item = T;
	fn into_iter(self) -> MoveItems<T> {
		self.into_iter()
	}
}
impl<'a, T> IntoIterator for &'a Vec<T>
{
	type IntoIter = ::core::slice::Iter<'a,T>;
	type Item = &'a T;

	fn into_iter(self) -> ::core::slice::Iter<'a, T> {
		self.iter()
	}
}

impl<'a, T> IntoIterator for &'a mut Vec<T>
{
	type IntoIter = ::core::slice::IterMut<'a,T>;
	type Item = &'a mut T;

	fn into_iter(self) -> ::core::slice::IterMut<'a, T> {
		self.iter_mut()
	}
}

impl<T> MoveItems<T>
{
	/// Pop an item from the iterator
	fn pop_item(&mut self) -> T
	{
		assert!(self.ofs < self.count);
		let v: T = unsafe {
			let ptr = self.data.get_ptr(self.ofs);
			::core::ptr::read(ptr as *const _)
			};
		self.ofs += 1;
		v
	}
}

impl<T> Iterator for MoveItems<T>
{
	type Item = T;
	fn next(&mut self) -> Option<T>
	{
		if self.ofs == self.count
		{
			None
		}
		else
		{
			Some( self.pop_item() )
		}
	}
}

impl<T> ops::Drop for MoveItems<T>
{
	fn drop(&mut self)
	{
		for _ in (self.ofs .. self.count) {
			self.pop_item();
		}
	}
}

// vim: ft=rust
<|endoftext|>


<commit_before>use liquid_compiler::Filter;
use liquid_derive::*;
use liquid_error::Result;
use liquid_interpreter::Context;
use liquid_value::Value;

/// Removes all whitespace (tabs, spaces, and newlines) from both the left and right side of a
/// string.
///
/// It does not affect spaces between words.  Note that while this works for the case of tabs,
/// spaces, and newlines, it also removes any other codepoints defined by the Unicode Derived Core
/// Property `White_Space` (per [rust
/// documentation](https://doc.rust-lang.org/std/primitive.str.html#method.trim_left).
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "strip",
    description = "Removes all whitespace (tabs, spaces, and newlines) from both the left and right side of a string.",
    parsed(StripFilter)
)]
pub struct Strip;

#[derive(Debug, Default, Display_filter)]
#[name = "strip"]
struct StripFilter;

impl Filter for StripFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(input.trim().to_owned()))
    }
}

/// Removes all whitespaces (tabs, spaces, and newlines) from the beginning of a string.
///
/// The filter does not affect spaces between words.  Note that while this works for the case of
/// tabs, spaces, and newlines, it also removes any other codepoints defined by the Unicode Derived
/// Core Property `White_Space` (per [rust
/// documentation](https://doc.rust-lang.org/std/primitive.str.html#method.trim_left).
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "lstrip",
    description = "Removes all whitespaces (tabs, spaces, and newlines) from the beginning of a string.",
    parsed(LstripFilter)
)]
pub struct Lstrip;

#[derive(Debug, Default, Display_filter)]
#[name = "lstrip"]
struct LstripFilter;

impl Filter for LstripFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(input.trim_left().to_owned()))
    }
}

/// Removes all whitespace (tabs, spaces, and newlines) from the right side of a string.
///
/// The filter does not affect spaces between words.  Note that while this works for the case of
/// tabs, spaces, and newlines, it also removes any other codepoints defined by the Unicode Derived
/// Core Property `White_Space` (per [rust
/// documentation](https://doc.rust-lang.org/std/primitive.str.html#method.trim_left).
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "rstrip",
    description = "Removes all whitespace (tabs, spaces, and newlines) from the right side of a string.",
    parsed(RstripFilter)
)]
pub struct Rstrip;

#[derive(Debug, Default, Display_filter)]
#[name = "rstrip"]
struct RstripFilter;

impl Filter for RstripFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(input.trim_right().to_owned()))
    }
}

#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "strip_newlines",
    description = "Removes any newline characters (line breaks) from a string.",
    parsed(StripNewlinesFilter)
)]
pub struct StripNewlines;

#[derive(Debug, Default, Display_filter)]
#[name = "strip_newlines"]
struct StripNewlinesFilter;

impl Filter for StripNewlinesFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(
            input
                .chars()
                .filter(|c| *c != '\n' && *c != '\r')
                .collect::<String>(),
        ))
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    macro_rules! unit {
        ($a:ident, $b:expr) => {{
            unit!($a, $b, )
        }};
        ($a:ident, $b:expr, $($c:expr),*) => {{
            let positional = Box::new(vec![$(::liquid::interpreter::Expression::Literal($c)),*].into_iter());
            let keyword = Box::new(Vec::new().into_iter());
            let args = ::liquid::compiler::FilterArguments { positional, keyword };

            let context = ::liquid::interpreter::Context::default();

            let filter = ::liquid::compiler::ParseFilter::parse(&$a, args).unwrap();
            ::liquid::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
        }};
    }

    macro_rules! failed {
        ($a:ident, $b:expr) => {{
            failed!($a, $b, )
        }};
        ($a:ident, $b:expr, $($c:expr),*) => {{
            let positional = Box::new(vec![$(::liquid::interpreter::Expression::Literal($c)),*].into_iter());
            let keyword = Box::new(Vec::new().into_iter());
            let args = ::liquid::compiler::FilterArguments { positional, keyword };

            let context = ::liquid::interpreter::Context::default();

            ::liquid::compiler::ParseFilter::parse(&$a, args)
                .and_then(|filter| ::liquid::compiler::Filter::evaluate(&*filter, &$b, &context))
                .unwrap_err()
        }};
    }

    macro_rules! tos {
        ($a:expr) => {{
            Value::scalar($a.to_owned())
        }};
    }

    #[test]
    fn unit_lstrip() {
        let input = &tos!(" 	 \n \r test");
        let desired_result = tos!("test");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_one_argument() {
        let input = &tos!(" 	 \n \r test");
        failed!(Lstrip, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_lstrip_shopify_liquid() {
        // One test from https://shopify.github.io/liquid/filters/lstrip/
        let input = &tos!("          So much room for activities!          ");
        let desired_result = tos!("So much room for activities!          ");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_trailing_sequence() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        let desired_result = tos!("test 	 \n \r ");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_trailing_sequence_only() {
        let input = &tos!("test 	 \n \r ");
        let desired_result = tos!("test 	 \n \r ");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip() {
        let input = &tos!("test 	 \n \r ");
        let desired_result = tos!("test");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_leading_sequence() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        let desired_result = tos!(" 	 \n \r test");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_leading_sequence_only() {
        let input = &tos!(" 	 \n \r test");
        let desired_result = tos!(" 	 \n \r test");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_one_argument() {
        let input = &tos!(" 	 \n \r test");
        failed!(Rstrip, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_rstrip_shopify_liquid() {
        // One test from https://shopify.github.io/liquid/filters/rstrip/
        let input = &tos!("          So much room for activities!          ");
        let desired_result = tos!("          So much room for activities!");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_strip() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        let desired_result = tos!("test");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_leading_sequence_only() {
        let input = &tos!(" 	 \n \r test");
        let desired_result = tos!("test");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_one_argument() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        failed!(Strip, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_strip_shopify_liquid() {
        // One test from https://shopify.github.io/liquid/filters/strip/
        let input = &tos!("          So much room for activities!          ");
        let desired_result = tos!("So much room for activities!");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_trailing_sequence_only() {
        let input = &tos!("test 	 \n \r ");
        let desired_result = tos!("test");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines() {
        let input = &tos!("a\nb\n");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_between_only() {
        let input = &tos!("a\nb");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_leading_only() {
        let input = &tos!("\nab");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_one_argument() {
        let input = &tos!("ab\n");
        failed!(StripNewlines, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_strip_newlines_shopify_liquid() {
        // Test from https://shopify.github.io/liquid/filters/strip_newlines/
        let input = &tos!("\nHello\nthere\n");
        let desired_result = tos!("Hellothere");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_trailing_only() {
        let input = &tos!("ab\n");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }
}
<commit_msg>chore: Don't use deprecated methods<commit_after>use liquid_compiler::Filter;
use liquid_derive::*;
use liquid_error::Result;
use liquid_interpreter::Context;
use liquid_value::Value;

/// Removes all whitespace (tabs, spaces, and newlines) from both the left and right side of a
/// string.
///
/// It does not affect spaces between words.  Note that while this works for the case of tabs,
/// spaces, and newlines, it also removes any other codepoints defined by the Unicode Derived Core
/// Property `White_Space` (per [rust
/// documentation](https://doc.rust-lang.org/std/primitive.str.html#method.trim_start).
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "strip",
    description = "Removes all whitespace (tabs, spaces, and newlines) from both the left and right side of a string.",
    parsed(StripFilter)
)]
pub struct Strip;

#[derive(Debug, Default, Display_filter)]
#[name = "strip"]
struct StripFilter;

impl Filter for StripFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(input.trim().to_owned()))
    }
}

/// Removes all whitespaces (tabs, spaces, and newlines) from the beginning of a string.
///
/// The filter does not affect spaces between words.  Note that while this works for the case of
/// tabs, spaces, and newlines, it also removes any other codepoints defined by the Unicode Derived
/// Core Property `White_Space` (per [rust
/// documentation](https://doc.rust-lang.org/std/primitive.str.html#method.trim_start).
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "lstrip",
    description = "Removes all whitespaces (tabs, spaces, and newlines) from the beginning of a string.",
    parsed(LstripFilter)
)]
pub struct Lstrip;

#[derive(Debug, Default, Display_filter)]
#[name = "lstrip"]
struct LstripFilter;

impl Filter for LstripFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(input.trim_start().to_owned()))
    }
}

/// Removes all whitespace (tabs, spaces, and newlines) from the right side of a string.
///
/// The filter does not affect spaces between words.  Note that while this works for the case of
/// tabs, spaces, and newlines, it also removes any other codepoints defined by the Unicode Derived
/// Core Property `White_Space` (per [rust
/// documentation](https://doc.rust-lang.org/std/primitive.str.html#method.trim_start).
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "rstrip",
    description = "Removes all whitespace (tabs, spaces, and newlines) from the right side of a string.",
    parsed(RstripFilter)
)]
pub struct Rstrip;

#[derive(Debug, Default, Display_filter)]
#[name = "rstrip"]
struct RstripFilter;

impl Filter for RstripFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(input.trim_end().to_owned()))
    }
}

#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "strip_newlines",
    description = "Removes any newline characters (line breaks) from a string.",
    parsed(StripNewlinesFilter)
)]
pub struct StripNewlines;

#[derive(Debug, Default, Display_filter)]
#[name = "strip_newlines"]
struct StripNewlinesFilter;

impl Filter for StripNewlinesFilter {
    fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
        let input = input.to_str();
        Ok(Value::scalar(
            input
                .chars()
                .filter(|c| *c != '\n' && *c != '\r')
                .collect::<String>(),
        ))
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    macro_rules! unit {
        ($a:ident, $b:expr) => {{
            unit!($a, $b, )
        }};
        ($a:ident, $b:expr, $($c:expr),*) => {{
            let positional = Box::new(vec![$(::liquid::interpreter::Expression::Literal($c)),*].into_iter());
            let keyword = Box::new(Vec::new().into_iter());
            let args = ::liquid::compiler::FilterArguments { positional, keyword };

            let context = ::liquid::interpreter::Context::default();

            let filter = ::liquid::compiler::ParseFilter::parse(&$a, args).unwrap();
            ::liquid::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
        }};
    }

    macro_rules! failed {
        ($a:ident, $b:expr) => {{
            failed!($a, $b, )
        }};
        ($a:ident, $b:expr, $($c:expr),*) => {{
            let positional = Box::new(vec![$(::liquid::interpreter::Expression::Literal($c)),*].into_iter());
            let keyword = Box::new(Vec::new().into_iter());
            let args = ::liquid::compiler::FilterArguments { positional, keyword };

            let context = ::liquid::interpreter::Context::default();

            ::liquid::compiler::ParseFilter::parse(&$a, args)
                .and_then(|filter| ::liquid::compiler::Filter::evaluate(&*filter, &$b, &context))
                .unwrap_err()
        }};
    }

    macro_rules! tos {
        ($a:expr) => {{
            Value::scalar($a.to_owned())
        }};
    }

    #[test]
    fn unit_lstrip() {
        let input = &tos!(" 	 \n \r test");
        let desired_result = tos!("test");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_one_argument() {
        let input = &tos!(" 	 \n \r test");
        failed!(Lstrip, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_lstrip_shopify_liquid() {
        // One test from https://shopify.github.io/liquid/filters/lstrip/
        let input = &tos!("          So much room for activities!          ");
        let desired_result = tos!("So much room for activities!          ");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_trailing_sequence() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        let desired_result = tos!("test 	 \n \r ");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_lstrip_trailing_sequence_only() {
        let input = &tos!("test 	 \n \r ");
        let desired_result = tos!("test 	 \n \r ");
        assert_eq!(unit!(Lstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip() {
        let input = &tos!("test 	 \n \r ");
        let desired_result = tos!("test");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_leading_sequence() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        let desired_result = tos!(" 	 \n \r test");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_leading_sequence_only() {
        let input = &tos!(" 	 \n \r test");
        let desired_result = tos!(" 	 \n \r test");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_rstrip_one_argument() {
        let input = &tos!(" 	 \n \r test");
        failed!(Rstrip, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_rstrip_shopify_liquid() {
        // One test from https://shopify.github.io/liquid/filters/rstrip/
        let input = &tos!("          So much room for activities!          ");
        let desired_result = tos!("          So much room for activities!");
        assert_eq!(unit!(Rstrip, input), desired_result);
    }

    #[test]
    fn unit_strip() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        let desired_result = tos!("test");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_leading_sequence_only() {
        let input = &tos!(" 	 \n \r test");
        let desired_result = tos!("test");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_one_argument() {
        let input = &tos!(" 	 \n \r test 	 \n \r ");
        failed!(Strip, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_strip_shopify_liquid() {
        // One test from https://shopify.github.io/liquid/filters/strip/
        let input = &tos!("          So much room for activities!          ");
        let desired_result = tos!("So much room for activities!");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_trailing_sequence_only() {
        let input = &tos!("test 	 \n \r ");
        let desired_result = tos!("test");
        assert_eq!(unit!(Strip, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines() {
        let input = &tos!("a\nb\n");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_between_only() {
        let input = &tos!("a\nb");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_leading_only() {
        let input = &tos!("\nab");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_non_string() {
        let input = &Value::scalar(0f64);
        let desired_result = tos!("0");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_one_argument() {
        let input = &tos!("ab\n");
        failed!(StripNewlines, input, Value::scalar(0f64));
    }

    #[test]
    fn unit_strip_newlines_shopify_liquid() {
        // Test from https://shopify.github.io/liquid/filters/strip_newlines/
        let input = &tos!("\nHello\nthere\n");
        let desired_result = tos!("Hellothere");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }

    #[test]
    fn unit_strip_newlines_trailing_only() {
        let input = &tos!("ab\n");
        let desired_result = tos!("ab");
        assert_eq!(unit!(StripNewlines, input), desired_result);
    }
}
<|endoftext|>


<commit_before>// Parts of this are based on the work on alexcrichton, here:
// https://github.com/alexcrichton/git2-rs/blob/master/libgit2-sys/build.rs
//
// This has been tested on Ubuntu and it assumes that CMake is available.
// Patches are welcome to help make it work on other operating systems!

extern crate pkg_config;
extern crate cmake;

fn main() {
    // Do nothing if this package is already provided by the system.
    if pkg_config::find_library("uchardet").is_ok() { return; }

    // Build uchardet ourselves
    let dst = cmake::build("uchardet");

    // Decide how to link our C++ runtime.  Feel free to submit patches
    // to make this work on your platform.  Other likely options are "c++"
    // and "c++abi" depending on OS and compiler.
    let cxx_abi = "stdc++";

    // Print out link instructions for Cargo.
    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-search=native={}/lib64", dst.display());
    println!("cargo:rustc-link-lib=static=uchardet");
    println!("cargo:rustc-flags=-l {}", cxx_abi);
}
<commit_msg>Make it work on windows-msvc<commit_after>// Parts of this are based on the work on alexcrichton, here:
// https://github.com/alexcrichton/git2-rs/blob/master/libgit2-sys/build.rs
//
// This has been tested on Ubuntu and it assumes that CMake is available.
// Patches are welcome to help make it work on other operating systems!

extern crate pkg_config;
extern crate cmake;

use cmake::Config;

fn main() {
    // Do nothing if this package is already provided by the system.
    if pkg_config::find_library("uchardet").is_ok() { return; }

    // Build uchardet ourselves
    // Mustn't build the binaries as they aren't compatible with Windows
    // and cause a compiler error
    let dst = Config::new("uchardet")
        .define("BUILD_BINARY", "OFF")
        .build();

    // Print out link instructions for Cargo.
    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-search=native={}/lib64", dst.display());
    println!("cargo:rustc-link-lib=static=uchardet");

    if !(cfg!(target_os = "windows") && cfg!(target_env = "msvc")) {
        // Not needed on windows-msvc

        // Decide how to link our C++ runtime.  Feel free to submit patches
        // to make this work on your platform.  Other likely options are "c++"
        // and "c++abi" depending on OS and compiler.
        let cxx_abi = "stdc++";
        println!("cargo:rustc-flags=-l {}", cxx_abi);
    }
}
<|endoftext|>