<commit_before>#![feature(io, path)]
#![deny(missing_docs)]
//! Fern is a runtime-configurable rust logging library.
//!
//! Current features:
//!
//! - Multiple loggers. You can create as many loggers as you need, and configure them separately.
//! - Configurable output format via closures.
//! - Multiple outputs per logger - output to any combination of:
//!   - log files
//!   - stdout or stderr
//!   - your own custom implementation
//! - Each output can have a Level configured, so you can output all log messages to a log file,
//!   and only have warnings and above show up in the console.
//! - You can also define your own custom logging endpoints - have messages end up where you need
//!   them.
//! - Acts as a backend to the `log` crate - use `trace!()` through `error!()` on your main logger
//!   - Note that fern can also have loggers separate from the `log` crate's global system. It's
//!     possible to just set your main logger as the global logger, then use other ones manually
//!     as well.
//!
//! Although mostly stabilized, fern is still in development. The library is subject to
//! change in non-backwards-compatible ways before the API is completely stabilized.
//!
//! This library can only be used while complying to the license terms in the `LICENSE` file.
//!
//! Examples
//! ========
//!
//!
//! Usually, the first thing you want to do is create a logger. In fern, you can do this by first
//! creating a LoggerConfig struct, and then calling `.into_logger()` to turn it into a logger.
//!
//! Here's a logger that simply logs all messages to stdout, and an output.log file, formatting
//! each message with the current date, time, and logging level.
//!
//! ```rust
//! # extern crate fern;
//! # extern crate log;
//! extern crate chrono;
//! use std::path::PathBuf;
//!
//! let logger_config = fern::DispatchConfig {
//!     format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
//!         // This is a fairly simple format, though it's possible to do more complicated ones
//!         format!("[{}][{}] {}", chrono::Local::now().format("%Y-%m-%d][%H:%M:%S"), level, msg)
//!     }),
//!     output: vec![fern::OutputConfig::Stdout, fern::OutputConfig::File(PathBuf::new("output.log"))],
//!     level: log::LogLevelFilter::Debug,
//! };
//! ```
//!
//!
//! In the above example, here's what each part does.
//!
//! `format:` is a closure which all messages will be run through. In this example, use a
//! format!() macro to format the messages using the logging level.
//!
//! Side note: `chrono::Local::now().format("%Y-%m-%d][%H:%M:%S")` is a usage of the chrono time
//! library, and just returns the date in the format described. An output of this looks something
//! like `2015-03-07][12:55:04`.
//!
//! With this formatting, the final output of the logger will look something like:
//!
//! ```text
//! [2015-03-07][12:55:04][INFO] A message logged at the Info logging level.
//! ```
//!
//! `output:` is a Vec<> of other configurations to send the messages to. In this example, we send
//! messages to stdout (the console), and the file "output.log".
//!
//! `level:` is a `log::LogLevelFilter` which describes the minimum level that should be allowed to pass
//! through this logger. Setting this to `Level::Debug` allows all messages to be logged, as Debug
//! is the lowest logging level.
//!
//! After creating your logging config, you can pass it to `init_global_logger` to set it as the
//! logger used with logging macros in the `log` crate. This function can only be called once, as
//! the log crate may only be set up once.
//!
//! Note that this function also accepts a `LogLevelFilter`. This is so that you can set a global
//! minimum log level separate from the logger configuration. If you don't have any reason for
//! anything else, just set this to `log::LogLevelFilter::Trace`.
//!
//! ```rust
//! # extern crate fern;
//! # extern crate log;
//! # let logger_config = fern::DispatchConfig {
//! #     format: Box::new(|msg: &str, _level: &log::LogLevel, _location: &log::LogLocation| {
//! #         format!("{}", msg)
//! #     }),
//! #     output: vec![],
//! #     level: log::LogLevelFilter::Debug,
//! # };
//! if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) {
//!     panic!("Failed to initialize global logger: {}", e);
//! }
//! ```
//!
//! This uses the `if let Err(e) =` syntax to catch any errors that happen when initializing
//! the logger.
//!
//!
//! Eventually, this section will contain a few more examples as well. For now though, the above
//! tutorial paired with the fern docs should be enough to get you started on configuring a logger
//! for your project.
//!
//! #### Logging
//!
//! With the `log` crate, outputting messages is fairly simple. As long as you are using the log
//! crate with `#[macro_use]`, and you have initialized the global logger as shown above, you can
//! use the log macros to log messages.
//!
//! ```rust
//! // You need to have #[macro_use] to use log macros
//! #[macro_use]
//! extern crate log;
//! extern crate fern;
//!
//! # fn main() {
//! # /*
//! fern::init_global_logger(...);
//! # */
//! # fern::init_global_logger(fern::OutputConfig::Null, log::LogLevelFilter::Trace);
//!
//! trace!("Trace message");
//! debug!("Debug message");
//! info!("Info message");
//! warn!("Warning message");
//! error!("Error message");
//! # }
//! ```

extern crate log;

pub use errors::{LogError, InitError};
pub use api::Logger;
pub use config::{DispatchConfig, OutputConfig, IntoLog, init_global_logger};
pub use loggers::NullLogger;

mod api;
mod config;
mod loggers;
mod errors;
<commit_msg>Even more documentation tweaks!<commit_after>#![feature(io, path)]
#![deny(missing_docs)]
//! Fern is a runtime-configurable rust logging library.
//!
//! Current features:
//!
//! - Multiple loggers. You can create as many loggers as you need, and configure them separately.
//! - Configurable output format via closures.
//! - Multiple outputs per logger - output to any combination of:
//!   - log files
//!   - stdout or stderr
//!   - your own custom implementation
//! - Each output can have a Level configured, so you can output all log messages to a log file,
//!   and only have warnings and above show up in the console.
//! - You can also define your own custom logging endpoints - have messages end up where you need
//!   them.
//! - Acts as a backend to the `log` crate - use `trace!()` through `error!()` to log to the global
//!   fern logger.
//!   - Note that fern can also have loggers separate from the global system. You can always set
//!     your main logger as the global logger, then use other fern loggers manually.
//!
//! Although mostly stabilized, fern is still in development. The library is subject to
//! change in non-backwards-compatible ways before the API is completely stabilized.
//!
//! This library can only be used while complying to the license terms in the `LICENSE` file.
//!
//! Examples
//! ========
//!
//!
//! Usually, the first thing you want to do is create a logger. In fern, you can do this by first
//! creating a LoggerConfig struct, and then calling `.into_logger()` to turn it into a logger.
//!
//! Here's a logger that simply logs all messages to stdout, and an output.log file, formatting
//! each message with the current date, time, and logging level.
//!
//! ```rust
//! # extern crate fern;
//! # extern crate log;
//! extern crate chrono;
//! use std::path::PathBuf;
//!
//! let logger_config = fern::DispatchConfig {
//!     format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
//!         // This is a fairly simple format, though it's possible to do more complicated ones
//!         format!("[{}][{}] {}", chrono::Local::now().format("%Y-%m-%d][%H:%M:%S"), level, msg)
//!     }),
//!     output: vec![fern::OutputConfig::Stdout, fern::OutputConfig::File(PathBuf::new("output.log"))],
//!     level: log::LogLevelFilter::Debug,
//! };
//! ```
//!
//!
//! In the above example, here's what each part does.
//!
//! `format:` is a closure which all messages will be run through. In this example, use a
//! format!() macro to format the messages using the logging level.
//!
//! Side note: `chrono::Local::now().format("%Y-%m-%d][%H:%M:%S")` is a usage of the chrono time
//! library, and just returns the date in the format described. An output of this looks something
//! like `2015-03-07][12:55:04`.
//!
//! With this formatting, the final output of the logger will look something like:
//!
//! ```text
//! [2015-03-07][12:55:04][INFO] A message logged at the Info logging level.
//! ```
//!
//! `output:` is a Vec<> of other configurations to send the messages to. In this example, we send
//! messages to stdout (the console), and the file "output.log".
//!
//! `level:` is a `log::LogLevelFilter` which describes the minimum level that should be allowed to pass
//! through this logger. Setting this to `Level::Debug` allows all messages to be logged, as Debug
//! is the lowest logging level.
//!
//! After creating your logging config, you can pass it to `init_global_logger` to set it as the
//! logger used with logging macros in the `log` crate. This function can only be called once, as
//! the log crate may only be set up once.
//!
//! Note that this function also accepts a `LogLevelFilter`. This is so that you can set a global
//! minimum log level separate from the logger configuration. If you don't have any reason for
//! anything else, just set this to `log::LogLevelFilter::Trace`.
//!
//! ```rust
//! # extern crate fern;
//! # extern crate log;
//! # let logger_config = fern::DispatchConfig {
//! #     format: Box::new(|msg: &str, _level: &log::LogLevel, _location: &log::LogLocation| {
//! #         format!("{}", msg)
//! #     }),
//! #     output: vec![],
//! #     level: log::LogLevelFilter::Debug,
//! # };
//! if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) {
//!     panic!("Failed to initialize global logger: {}", e);
//! }
//! ```
//!
//! This uses the `if let Err(e) =` syntax to catch any errors that happen when initializing
//! the logger.
//!
//!
//! Eventually, this section will contain a few more examples as well. For now though, the above
//! tutorial paired with the fern docs should be enough to get you started on configuring a logger
//! for your project.
//!
//! #### Logging
//!
//! With the `log` crate, outputting messages is fairly simple. As long as you are using the log
//! crate with `#[macro_use]`, and you have initialized the global logger as shown above, you can
//! use the log macros to log messages.
//!
//! ```rust
//! // You need to have #[macro_use] to use log macros
//! #[macro_use]
//! extern crate log;
//! extern crate fern;
//!
//! # fn main() {
//! # /*
//! fern::init_global_logger(...);
//! # */
//! # fern::init_global_logger(fern::OutputConfig::Null, log::LogLevelFilter::Trace);
//!
//! trace!("Trace message");
//! debug!("Debug message");
//! info!("Info message");
//! warn!("Warning message");
//! error!("Error message");
//! # }
//! ```

extern crate log;

pub use errors::{LogError, InitError};
pub use api::Logger;
pub use config::{DispatchConfig, OutputConfig, IntoLog, init_global_logger};
pub use loggers::NullLogger;

mod api;
mod config;
mod loggers;
mod errors;
<|endoftext|>


<commit_before>//! A pure-Rust library to work with GPT partition tables.
//!
//! It provides support for manipulating (R/W) GPT headers and partition
//! tables. Raw disk devices as well as disk images are supported.
//!
//! ```rust,no_run
//! extern crate gpt;
//!
//! fn inspect_disk() {
//!     let diskpath = std::path::Path::new("/dev/sdz");
//!     let cfg = gpt::GptConfig::new().writable(false);
//!
//!     let disk = cfg.open(diskpath).expect("failed to open disk");
//!
//!     println!("Disk header: {:#?}", disk.primary_header());
//!     println!("Partition layout: {:#?}", disk.partitions());
//! }
//!
//! fn create_partition() {
//!     let diskpath = std::path::Path::new("/tmp/chris.img");
//!     let cfg = gpt::GptConfig::new().writable(true).initialized(true);
//!     let mut disk = cfg.open(diskpath).expect("failed to open disk");
//!     let result = disk.add_partition(
//!         "rust_partition",
//!         100,
//!         gpt::partition_types::LINUX_FS,
//!         0,
//!     );
//!     disk.write().unwrap();
//! }
//! ```

#![deny(missing_docs)]

use log::*;
use std::collections::BTreeMap;
use std::io::Write;
use std::{fs, io, path};

#[macro_use]
mod macros;
pub mod disk;
pub mod header;
pub mod mbr;
pub mod partition;
pub mod partition_types;

/// Configuration options to open a GPT disk.
#[derive(Debug, Eq, PartialEq)]
pub struct GptConfig {
    /// Logical block size.
    lb_size: disk::LogicalBlockSize,
    /// Whether to open a GPT partition table in writable mode.
    writable: bool,
    /// Whether to expect and parse an initialized disk image.
    initialized: bool,
}

impl GptConfig {
    // TODO(lucab): complete support for skipping backup
    // header, etc, then expose all config knobs here.

    /// Create a new default configuration.
    pub fn new() -> Self {
        GptConfig::default()
    }

    /// Whether to open a GPT partition table in writable mode.
    pub fn writable(mut self, writable: bool) -> Self {
        self.writable = writable;
        self
    }

    /// Whether to assume an initialized GPT disk and read its
    /// partition table on open.
    pub fn initialized(mut self, initialized: bool) -> Self {
        self.initialized = initialized;
        self
    }

    /// Size of logical blocks (sectors) for this disk.
    pub fn logical_block_size(mut self, lb_size: disk::LogicalBlockSize) -> Self {
        self.lb_size = lb_size;
        self
    }

    /// Open the GPT disk at the given path and inspect it according
    /// to configuration options.
    pub fn open(self, diskpath: &path::Path) -> io::Result<GptDisk> {
        // Uninitialized disk, no headers/table to parse.
        if !self.initialized {
            let file = fs::OpenOptions::new()
                .write(self.writable)
                .read(true)
                .open(diskpath)?;
            let empty = GptDisk {
                config: self,
                file,
                guid: uuid::Uuid::new_v4(),
                path: diskpath.to_path_buf(),
                primary_header: None,
                backup_header: None,
                partitions: BTreeMap::new(),
            };
            return Ok(empty);
        }

        // Proper GPT disk, fully inspect its layout.
        let mut file = fs::OpenOptions::new()
            .write(self.writable)
            .read(true)
            .open(diskpath)?;
        let h1 = header::read_primary_header(&mut file, self.lb_size)?;
        let h2 = header::read_backup_header(&mut file, self.lb_size)?;
        let table = partition::file_read_partitions(&mut file, &h1, self.lb_size)?;
        let disk = GptDisk {
            config: self,
            file,
            guid: h1.disk_guid,
            path: diskpath.to_path_buf(),
            primary_header: Some(h1),
            backup_header: Some(h2),
            partitions: table,
        };
        debug!("disk: {:?}", disk);
        Ok(disk)
    }
}

impl Default for GptConfig {
    fn default() -> Self {
        Self {
            lb_size: disk::DEFAULT_SECTOR_SIZE,
            initialized: true,
            writable: false,
        }
    }
}

/// A file-backed GPT disk.
#[derive(Debug)]
pub struct GptDisk {
    config: GptConfig,
    file: fs::File,
    guid: uuid::Uuid,
    path: path::PathBuf,
    primary_header: Option<header::Header>,
    backup_header: Option<header::Header>,
    partitions: BTreeMap<u32, partition::Partition>,
}

impl GptDisk {
    /// Add another partition to this disk.  This tries to find
    /// the optimum partition location with the lowest block device.
    /// Returns the new partition id if there was sufficient room
    /// to add the partition. Size is specified in bytes.
    pub fn add_partition(
        &mut self,
        name: &str,
        size: u64,
        part_type: partition_types::Type,
        flags: u64,
    ) -> io::Result<u32> {
        let size_lba = match size.checked_div(self.config.lb_size.into()) {
            Some(s) => s,
            None => {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "size must be greater than {} which is the logical block size.",
                        self.config.lb_size
                    ),
                ));
            }
        };
        // Find the lowest lba that is larger than size.
        let free_sections = self.find_free_sectors();
        for (starting_lba, length) in free_sections {
            if length >= size_lba {
                // Found our free slice.
                let partition_id = self.find_next_partition_id();
                debug!(
                    "Adding partition id: {} {:?}.  first_lba: {} last_lba: {}",
                    partition_id,
                    part_type,
                    starting_lba,
                    starting_lba + size_lba as u64
                );
                let part = partition::Partition {
                    part_type_guid: part_type,
                    part_guid: uuid::Uuid::new_v4(),
                    first_lba: starting_lba,
                    last_lba: starting_lba + size_lba as u64,
                    flags,
                    name: name.to_string(),
                };
                if let Some(p) = self.partitions.insert(partition_id, part.clone()) {
                    debug!("Replacing\n{}\nwith\n{}", p, part);
                }
                return Ok(partition_id);
            }
        }

        Err(io::Error::new(
            io::ErrorKind::Other,
            "Unable to find enough space on drive",
        ))
    }
    /// remove partition from this disk. This tries to find the partition based on either a
    /// given partition number (id) or a partition guid.  Returns the partition id if the
    /// partition is removed
    pub fn remove_partition(
        &mut self,
        id: Option<u32>,
        partguid: Option<uuid::Uuid>,
    ) -> io::Result<u32> {
        if let Some(part_id) = id {
            if let Some(partition_id) = self.partitions.remove(&part_id) {
                debug!("Removing partition number {}", partition_id);
            }
            return Ok(part_id);
        }
        if let Some(part_guid) = partguid {
            for (key, partition) in &self.partitions.clone() {
                if partition.part_guid == part_guid {
                    if let Some(partition_id) = self.partitions.remove(&key) {
                        debug!("Removing partition number {}", partition_id);
                    }
                    return Ok(*key);
                }
            }
        }
        Err(io::Error::new(
            io::ErrorKind::Other,
            "Unable to find partition to remove",
        ))
    }

    /// Find free space on the disk.
    /// Returns a tuple of (starting_lba, length in lba's).
    pub fn find_free_sectors(&self) -> Vec<(u64, u64)> {
        if let Some(header) = self.primary_header().or_else(|| self.backup_header()) {
            trace!("first_usable: {}", header.first_usable);
            let mut disk_positions = vec![header.first_usable + 1];
            for part in self.partitions().iter().filter(|p| p.1.is_used()) {
                trace!("partition: ({}, {})", part.1.first_lba, part.1.last_lba);
                disk_positions.push(part.1.first_lba);
                disk_positions.push(part.1.last_lba);
            }
            disk_positions.push(header.last_usable - 1);
            trace!("last_usable: {}", header.last_usable);
            disk_positions.sort();

            return disk_positions
                // Walk through the LBA's in chunks of 2 (ending, starting).
                .chunks(2)
                // Add 1 to the ending and then subtract the starting.
                .map(|p| (p[0] + 1, p[1].saturating_sub(p[0])))
                .collect();
        }
        // No primary header. Return nothing.
        vec![]
    }

    /// Find next highest partition id.
    pub fn find_next_partition_id(&self) -> u32 {
        match self
            .partitions()
            .iter()
            // Skip unused partitions.
            .filter(|p| p.1.is_used())
            // Find the maximum id.
            .max_by_key(|x| x.0)
        {
            Some(i) => i.0 + 1,
            // Partitions start at 1.
            None => 1,
        }
    }

    /// Retrieve primary header, if any.
    pub fn primary_header(&self) -> Option<&header::Header> {
        self.primary_header.as_ref()
    }

    /// Retrieve backup header, if any.
    pub fn backup_header(&self) -> Option<&header::Header> {
        self.backup_header.as_ref()
    }

    /// Retrieve partition entries.
    pub fn partitions(&self) -> &BTreeMap<u32, partition::Partition> {
        &self.partitions
    }

    /// Retrieve disk UUID.
    pub fn guid(&self) -> &uuid::Uuid {
        &self.guid
    }

    /// Retrieve disk logical block size.
    pub fn logical_block_size(&self) -> &disk::LogicalBlockSize {
        &self.config.lb_size
    }

    /// Update disk UUID.
    ///
    /// If no UUID is specified, a new random one is generated.
    /// No changes are recorded to disk until `write()` is called.
    pub fn update_guid(&mut self, uuid: Option<uuid::Uuid>) -> io::Result<&Self> {
        let guid = match uuid {
            Some(u) => u,
            None => {
                let u = uuid::Uuid::new_v4();
                debug!("Generated random uuid: {}", u);
                u
            }
        };
        self.guid = guid;
        Ok(self)
    }

    /// Update current partition table.
    ///
    /// No changes are recorded to disk until `write()` is called.
    pub fn update_partitions(
        &mut self,
        pp: BTreeMap<u32, partition::Partition>,
    ) -> io::Result<&Self> {
        // TODO(lucab): validate partitions.
        let bak = header::find_backup_lba(&mut self.file, self.config.lb_size)?;
        let h1 = header::Header::compute_new(true, &pp, self.guid, bak, &self.primary_header)?;
        let h2 = header::Header::compute_new(false, &pp, self.guid, bak, &self.backup_header)?;
        self.primary_header = Some(h1);
        self.backup_header = Some(h2);
        self.partitions = pp;
        self.config.initialized = true;
        Ok(self)
    }

    /// Persist state to disk, consuming this disk object.
    ///
    /// This is a destructive action, as it overwrite headers and
    /// partitions entries on disk. All writes are flushed to disk
    /// before returning the underlying `File` object.
    pub fn write(mut self) -> io::Result<fs::File> {
        if !self.config.writable {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "disk not opened in writable mode",
            ));
        }
        if !self.config.initialized {
            return Err(io::Error::new(io::ErrorKind::Other, "disk not initialized"));
        }
        debug!("Computing new headers");
        trace!("old primary header: {:?}", self.primary_header);
        trace!("old backup header: {:?}", self.backup_header);
        let bak = header::find_backup_lba(&mut self.file, self.config.lb_size)?;
        trace!("old backup lba: {}", bak);
        for partition in self.partitions().iter().filter(|p| p.1.is_used()) {
            partition.1.write(
                &self.path,
                u64::from(partition.0.checked_sub(1).unwrap_or_else(|| 0)),
                self.primary_header.clone().unwrap().part_start,
                self.config.lb_size,
            )?;
        }
        let new_backup_header = header::Header::compute_new(
            false,
            &self.partitions,
            self.guid,
            bak,
            &self.primary_header,
        )?;
        let new_primary_header = header::Header::compute_new(
            true,
            &self.partitions,
            self.guid,
            bak,
            &self.backup_header,
        )?;
        debug!("Writing backup header");
        new_backup_header.write_backup(&mut self.file, self.config.lb_size)?;
        debug!("Writing primary header");
        new_primary_header.write_primary(&mut self.file, self.config.lb_size)?;
        trace!("new primary header: {:?}", new_primary_header);
        trace!("new backup header: {:?}", new_backup_header);

        self.file.flush()?;
        self.primary_header = Some(new_primary_header.clone());
        self.backup_header = Some(new_backup_header);

        Ok(self.file)
    }
}
<commit_msg>Instead of getting the highest partition id + 1 as next partition, get the partition id from 0..max_existing+1 that isn't used.  So if you have partition ids [2,3], return 1.<commit_after>//! A pure-Rust library to work with GPT partition tables.
//!
//! It provides support for manipulating (R/W) GPT headers and partition
//! tables. Raw disk devices as well as disk images are supported.
//!
//! ```rust,no_run
//! extern crate gpt;
//!
//! fn inspect_disk() {
//!     let diskpath = std::path::Path::new("/dev/sdz");
//!     let cfg = gpt::GptConfig::new().writable(false);
//!
//!     let disk = cfg.open(diskpath).expect("failed to open disk");
//!
//!     println!("Disk header: {:#?}", disk.primary_header());
//!     println!("Partition layout: {:#?}", disk.partitions());
//! }
//!
//! fn create_partition() {
//!     let diskpath = std::path::Path::new("/tmp/chris.img");
//!     let cfg = gpt::GptConfig::new().writable(true).initialized(true);
//!     let mut disk = cfg.open(diskpath).expect("failed to open disk");
//!     let result = disk.add_partition(
//!         "rust_partition",
//!         100,
//!         gpt::partition_types::LINUX_FS,
//!         0,
//!     );
//!     disk.write().unwrap();
//! }
//! ```

#![deny(missing_docs)]

use log::*;
use std::collections::BTreeMap;
use std::io::Write;
use std::{fs, io, path};

#[macro_use]
mod macros;
pub mod disk;
pub mod header;
pub mod mbr;
pub mod partition;
pub mod partition_types;

/// Configuration options to open a GPT disk.
#[derive(Debug, Eq, PartialEq)]
pub struct GptConfig {
    /// Logical block size.
    lb_size: disk::LogicalBlockSize,
    /// Whether to open a GPT partition table in writable mode.
    writable: bool,
    /// Whether to expect and parse an initialized disk image.
    initialized: bool,
}

impl GptConfig {
    // TODO(lucab): complete support for skipping backup
    // header, etc, then expose all config knobs here.

    /// Create a new default configuration.
    pub fn new() -> Self {
        GptConfig::default()
    }

    /// Whether to open a GPT partition table in writable mode.
    pub fn writable(mut self, writable: bool) -> Self {
        self.writable = writable;
        self
    }

    /// Whether to assume an initialized GPT disk and read its
    /// partition table on open.
    pub fn initialized(mut self, initialized: bool) -> Self {
        self.initialized = initialized;
        self
    }

    /// Size of logical blocks (sectors) for this disk.
    pub fn logical_block_size(mut self, lb_size: disk::LogicalBlockSize) -> Self {
        self.lb_size = lb_size;
        self
    }

    /// Open the GPT disk at the given path and inspect it according
    /// to configuration options.
    pub fn open(self, diskpath: &path::Path) -> io::Result<GptDisk> {
        // Uninitialized disk, no headers/table to parse.
        if !self.initialized {
            let file = fs::OpenOptions::new()
                .write(self.writable)
                .read(true)
                .open(diskpath)?;
            let empty = GptDisk {
                config: self,
                file,
                guid: uuid::Uuid::new_v4(),
                path: diskpath.to_path_buf(),
                primary_header: None,
                backup_header: None,
                partitions: BTreeMap::new(),
            };
            return Ok(empty);
        }

        // Proper GPT disk, fully inspect its layout.
        let mut file = fs::OpenOptions::new()
            .write(self.writable)
            .read(true)
            .open(diskpath)?;
        let h1 = header::read_primary_header(&mut file, self.lb_size)?;
        let h2 = header::read_backup_header(&mut file, self.lb_size)?;
        let table = partition::file_read_partitions(&mut file, &h1, self.lb_size)?;
        let disk = GptDisk {
            config: self,
            file,
            guid: h1.disk_guid,
            path: diskpath.to_path_buf(),
            primary_header: Some(h1),
            backup_header: Some(h2),
            partitions: table,
        };
        debug!("disk: {:?}", disk);
        Ok(disk)
    }
}

impl Default for GptConfig {
    fn default() -> Self {
        Self {
            lb_size: disk::DEFAULT_SECTOR_SIZE,
            initialized: true,
            writable: false,
        }
    }
}

/// A file-backed GPT disk.
#[derive(Debug)]
pub struct GptDisk {
    config: GptConfig,
    file: fs::File,
    guid: uuid::Uuid,
    path: path::PathBuf,
    primary_header: Option<header::Header>,
    backup_header: Option<header::Header>,
    partitions: BTreeMap<u32, partition::Partition>,
}

impl GptDisk {
    /// Add another partition to this disk.  This tries to find
    /// the optimum partition location with the lowest block device.
    /// Returns the new partition id if there was sufficient room
    /// to add the partition. Size is specified in bytes.
    pub fn add_partition(
        &mut self,
        name: &str,
        size: u64,
        part_type: partition_types::Type,
        flags: u64,
    ) -> io::Result<u32> {
        let size_lba = match size.checked_div(self.config.lb_size.into()) {
            Some(s) => s,
            None => {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "size must be greater than {} which is the logical block size.",
                        self.config.lb_size
                    ),
                ));
            }
        };
        // Find the lowest lba that is larger than size.
        let free_sections = self.find_free_sectors();
        for (starting_lba, length) in free_sections {
            debug!("starting_lba {}, length {}", starting_lba, length);
            if length >= size_lba {
                // Found our free slice.
                let partition_id = self.find_next_partition_id();
                debug!(
                    "Adding partition id: {} {:?}.  first_lba: {} last_lba: {}",
                    partition_id,
                    part_type,
                    starting_lba,
                    starting_lba + size_lba as u64
                );
                let part = partition::Partition {
                    part_type_guid: part_type,
                    part_guid: uuid::Uuid::new_v4(),
                    first_lba: starting_lba,
                    last_lba: starting_lba + size_lba as u64,
                    flags,
                    name: name.to_string(),
                };
                if let Some(p) = self.partitions.insert(partition_id, part.clone()) {
                    debug!("Replacing\n{}\nwith\n{}", p, part);
                }
                return Ok(partition_id);
            }
        }

        Err(io::Error::new(
            io::ErrorKind::Other,
            "Unable to find enough space on drive",
        ))
    }
    /// remove partition from this disk. This tries to find the partition based on either a
    /// given partition number (id) or a partition guid.  Returns the partition id if the
    /// partition is removed
    pub fn remove_partition(
        &mut self,
        id: Option<u32>,
        partguid: Option<uuid::Uuid>,
    ) -> io::Result<u32> {
        if let Some(part_id) = id {
            if let Some(partition_id) = self.partitions.remove(&part_id) {
                debug!("Removing partition number {}", partition_id);
            }
            return Ok(part_id);
        }
        if let Some(part_guid) = partguid {
            for (key, partition) in &self.partitions.clone() {
                if partition.part_guid == part_guid {
                    if let Some(partition_id) = self.partitions.remove(&key) {
                        debug!("Removing partition number {}", partition_id);
                    }
                    return Ok(*key);
                }
            }
        }
        Err(io::Error::new(
            io::ErrorKind::Other,
            "Unable to find partition to remove",
        ))
    }

    /// Find free space on the disk.
    /// Returns a tuple of (starting_lba, length in lba's).
    pub fn find_free_sectors(&self) -> Vec<(u64, u64)> {
        if let Some(header) = self.primary_header().or_else(|| self.backup_header()) {
            trace!("first_usable: {}", header.first_usable);
            let mut disk_positions = vec![header.first_usable + 1];
            for part in self.partitions().iter().filter(|p| p.1.is_used()) {
                trace!("partition: ({}, {})", part.1.first_lba, part.1.last_lba);
                disk_positions.push(part.1.first_lba);
                disk_positions.push(part.1.last_lba);
            }
            disk_positions.push(header.last_usable - 1);
            trace!("last_usable: {}", header.last_usable);
            disk_positions.sort();

            return disk_positions
                // Walk through the LBA's in chunks of 2 (ending, starting).
                .chunks(2)
                // Add 1 to the ending and then subtract the starting.
                .map(|p| (p[0] + 1, p[1].saturating_sub(p[0])))
                .collect();
        }
        // No primary header. Return nothing.
        vec![]
    }

    /// Find next highest partition id.
    pub fn find_next_partition_id(&self) -> u32 {
        let max = match self
            .partitions()
            .iter()
            // Skip unused partitions.
            .filter(|p| p.1.is_used())
            // Find the maximum id.
            .max_by_key(|x| x.0)
        {
            Some(i) => i.0 + 0,
            // Partitions start at 1.
            None => 1,
        };
        for i in 0..max {
            if self.partitions().get(&i).is_none() {
                return i;
            }
        }
        max + 1
    }

    /// Retrieve primary header, if any.
    pub fn primary_header(&self) -> Option<&header::Header> {
        self.primary_header.as_ref()
    }

    /// Retrieve backup header, if any.
    pub fn backup_header(&self) -> Option<&header::Header> {
        self.backup_header.as_ref()
    }

    /// Retrieve partition entries.
    pub fn partitions(&self) -> &BTreeMap<u32, partition::Partition> {
        &self.partitions
    }

    /// Retrieve disk UUID.
    pub fn guid(&self) -> &uuid::Uuid {
        &self.guid
    }

    /// Retrieve disk logical block size.
    pub fn logical_block_size(&self) -> &disk::LogicalBlockSize {
        &self.config.lb_size
    }

    /// Update disk UUID.
    ///
    /// If no UUID is specified, a new random one is generated.
    /// No changes are recorded to disk until `write()` is called.
    pub fn update_guid(&mut self, uuid: Option<uuid::Uuid>) -> io::Result<&Self> {
        let guid = match uuid {
            Some(u) => u,
            None => {
                let u = uuid::Uuid::new_v4();
                debug!("Generated random uuid: {}", u);
                u
            }
        };
        self.guid = guid;
        Ok(self)
    }

    /// Update current partition table.
    ///
    /// No changes are recorded to disk until `write()` is called.
    pub fn update_partitions(
        &mut self,
        pp: BTreeMap<u32, partition::Partition>,
    ) -> io::Result<&Self> {
        // TODO(lucab): validate partitions.
        let bak = header::find_backup_lba(&mut self.file, self.config.lb_size)?;
        let h1 = header::Header::compute_new(true, &pp, self.guid, bak, &self.primary_header)?;
        let h2 = header::Header::compute_new(false, &pp, self.guid, bak, &self.backup_header)?;
        self.primary_header = Some(h1);
        self.backup_header = Some(h2);
        self.partitions = pp;
        self.config.initialized = true;
        Ok(self)
    }

    /// Persist state to disk, consuming this disk object.
    ///
    /// This is a destructive action, as it overwrite headers and
    /// partitions entries on disk. All writes are flushed to disk
    /// before returning the underlying `File` object.
    pub fn write(mut self) -> io::Result<fs::File> {
        if !self.config.writable {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "disk not opened in writable mode",
            ));
        }
        if !self.config.initialized {
            return Err(io::Error::new(io::ErrorKind::Other, "disk not initialized"));
        }
        debug!("Computing new headers");
        trace!("old primary header: {:?}", self.primary_header);
        trace!("old backup header: {:?}", self.backup_header);
        let bak = header::find_backup_lba(&mut self.file, self.config.lb_size)?;
        trace!("old backup lba: {}", bak);
        for partition in self.partitions().iter().filter(|p| p.1.is_used()) {
            partition.1.write(
                &self.path,
                u64::from(partition.0.checked_sub(1).unwrap_or_else(|| 0)),
                self.primary_header.clone().unwrap().part_start,
                self.config.lb_size,
            )?;
        }
        let new_backup_header = header::Header::compute_new(
            false,
            &self.partitions,
            self.guid,
            bak,
            &self.primary_header,
        )?;
        let new_primary_header = header::Header::compute_new(
            true,
            &self.partitions,
            self.guid,
            bak,
            &self.backup_header,
        )?;
        debug!("Writing backup header");
        new_backup_header.write_backup(&mut self.file, self.config.lb_size)?;
        debug!("Writing primary header");
        new_primary_header.write_primary(&mut self.file, self.config.lb_size)?;
        trace!("new primary header: {:?}", new_primary_header);
        trace!("new backup header: {:?}", new_backup_header);

        self.file.flush()?;
        self.primary_header = Some(new_primary_header.clone());
        self.backup_header = Some(new_backup_header);

        Ok(self.file)
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::iter::FromIterator;

pub fn puzzle(input: &str) -> u32 {
    0
}

#[derive(Debug, PartialEq)]
pub struct Screen {
    display: Vec<Vec<char>>,
}

impl Screen {
    pub fn new(width: usize, height: usize) -> Screen {
        Screen {
            display: vec![vec!['.'; width]; height],
        }
    }

    pub fn rect(&mut self, rect_width: usize, rect_height: usize) {
        for h in 0..rect_height {
            for w in 0..rect_width {
                self.display[h][w] = '#';
            }
        }
    }

    pub fn rotate_column(&mut self, col_index: usize, how_many: usize) {
        let col: Vec<_> = self.display.iter()
            .map(|row| row[col_index] ).collect();

        for i in 0..self.display.len() {
            let from_index = (i + self.display.len() - how_many) % self.display.len();
            self.display[i][col_index] = col[from_index];
        }
    }

    pub fn rotate_row(&mut self, row_index: usize, how_many: usize) {
        let row = self.display[row_index].clone();

        for i in 0..row.len() {
            let from_index = (i + row.len() - how_many) % row.len();
            self.display[row_index][i] = row[from_index];
        }
    }

    pub fn num_lit_pixels(&self) -> usize {
        self.display.iter().map(|row| {
            row.iter().filter(|&&c| c == '#').count()
        }).sum()
    }
}

impl fmt::Display for Screen {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for row in self.display.iter() {
            write!(f, "{}\n", String::from_iter(row.iter().cloned()))?;
        }
        Ok(())
    }
}

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

    #[test]
    fn new_screen(){
        let s = Screen::new(1, 2);
        assert_eq!(s, Screen {
            display: vec![vec!['.'], vec!['.']],
        });
        assert_eq!(s.to_string(), ".\n.\n");
    }

    #[test]
    fn rect_drawing() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        assert_eq!(s.to_string(), "###....\n###....\n.......\n");
    }

    #[test]
    fn rotate_column() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        s.rotate_column(1, 1);
        assert_eq!(s.to_string(), "#.#....\n###....\n.#.....\n");
    }

    #[test]
    fn rotate_row() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        s.rotate_column(1, 1);
        s.rotate_row(0, 4);
        assert_eq!(s.to_string(), "....#.#\n###....\n.#.....\n");
    }

    #[test]
    fn count_pixels() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        s.rotate_column(1, 1);
        s.rotate_row(0, 4);
        assert_eq!(s.num_lit_pixels(), 6);
    }
}
<commit_msg>Hook up input instructions<commit_after>use std::fmt;
use std::iter::FromIterator;

pub fn puzzle(input: &str) -> usize {
    let mut screen = Screen::new(50, 6);
    for line in input.lines() {
        let commands: Vec<_> = line.split_whitespace().collect();

        if commands[0] == "rect" {

            let mut dims = commands[1].split('x');
            screen.rect(
                dims.next().expect("no rect width")
                    .parse().expect("can't parse rect width"),
                dims.next().expect("no rect height")
                    .parse().expect("can't parse rect height"),
            );

        } else if commands[1] == "row" {

            let row_num = commands[2].split('=')
                                     .last()
                                     .expect("no row num")
                                     .parse().expect("can't parse row num");
            let how_many = commands[4].parse()
                                      .expect("can't parse how many for row");
            screen.rotate_row(row_num, how_many);

        } else if commands[1] == "column" {

            let col_num = commands[2].split('=')
                                     .last()
                                     .expect("no col num")
                                     .parse().expect("can't parse col num");
            let how_many = commands[4].parse()
                                      .expect("can't parse how many for col");
            screen.rotate_column(col_num, how_many);

        }
    }
    screen.num_lit_pixels()
}

#[derive(Debug, PartialEq)]
pub struct Screen {
    display: Vec<Vec<char>>,
}

impl Screen {
    pub fn new(width: usize, height: usize) -> Screen {
        Screen {
            display: vec![vec!['.'; width]; height],
        }
    }

    pub fn rect(&mut self, rect_width: usize, rect_height: usize) {
        for h in 0..rect_height {
            for w in 0..rect_width {
                self.display[h][w] = '#';
            }
        }
    }

    pub fn rotate_column(&mut self, col_index: usize, how_many: usize) {
        let col: Vec<_> = self.display.iter()
            .map(|row| row[col_index] ).collect();

        for i in 0..self.display.len() {
            let from_index = (i + self.display.len() - how_many) % self.display.len();
            self.display[i][col_index] = col[from_index];
        }
    }

    pub fn rotate_row(&mut self, row_index: usize, how_many: usize) {
        let row = self.display[row_index].clone();

        for i in 0..row.len() {
            let from_index = (i + row.len() - how_many) % row.len();
            self.display[row_index][i] = row[from_index];
        }
    }

    pub fn num_lit_pixels(&self) -> usize {
        self.display.iter().map(|row| {
            row.iter().filter(|&&c| c == '#').count()
        }).sum()
    }
}

impl fmt::Display for Screen {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for row in self.display.iter() {
            write!(f, "{}\n", String::from_iter(row.iter().cloned()))?;
        }
        Ok(())
    }
}

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

    #[test]
    fn new_screen(){
        let s = Screen::new(1, 2);
        assert_eq!(s, Screen {
            display: vec![vec!['.'], vec!['.']],
        });
        assert_eq!(s.to_string(), ".\n.\n");
    }

    #[test]
    fn rect_drawing() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        assert_eq!(s.to_string(), "###....\n###....\n.......\n");
    }

    #[test]
    fn rotate_column() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        s.rotate_column(1, 1);
        assert_eq!(s.to_string(), "#.#....\n###....\n.#.....\n");
    }

    #[test]
    fn rotate_row() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        s.rotate_column(1, 1);
        s.rotate_row(0, 4);
        assert_eq!(s.to_string(), "....#.#\n###....\n.#.....\n");
    }

    #[test]
    fn count_pixels() {
        let mut s = Screen::new(7, 3);
        s.rect(3, 2);
        s.rotate_column(1, 1);
        s.rotate_row(0, 4);
        assert_eq!(s.num_lit_pixels(), 6);
    }
}
<|endoftext|>


<commit_before>//! # Russet
//!
//! __Russet__ is a simple string tokenising library for Rust.  It supports
//! POSIX shell-style separation of a line of _words_ into a vector of
//! strings.
#![experimental]

#![feature(phase)]
#[phase(plugin)]
extern crate quickcheck_macros;
extern crate quickcheck;

use std::char::is_whitespace;
use std::collections::hashmap::HashMap;

/// A tokeniser object.
///
/// A Tokeniser can be fed characters from an iterator, string, or individually.
/// It is an _immutable_ object: actions on a Tokeniser consume the Tokeniser,
/// and produce a fresh copy of the Tokeniser.
///
/// At any stage, a Tokeniser can be consumed to produce the vector of words
/// it has read, using the `into_strings` method.  This method may fail if the
/// Tokeniser ended in a bad state (in the middle of a quoted string, or in
/// the middle of an escape sequence).
#[deriving(Clone)]
pub struct Tokeniser<Q, E> {
    /// The current vector of parsed words.
    vec: Vec<String>,

    /// Whether or not we are currently in a word.
    in_word: bool,

    /// The current closing quote character and quote mode, if any.
    quote: Option<( char, QuoteMode )>,

    /// Whether the tokeniser is currently processing an escape character.
    escaping: bool,

    /// Maps from quote openers to quote closers.
    quote_pairs: Q,

    /// Map from escape characters to their replacements.
    escape_pairs: E,

    /// The character preceding escape characters.
    escape_leader: Option<char>
}

/// A quote mode.
#[deriving(Clone)]
pub enum QuoteMode {
    /// All characters except the closing character have their literal value.
    /// This is equivalent to single-quoting in POSIX shell.
    IgnoreEscapes,

    /// All characters except the closing character and escape sequences
    /// have their literal value.  This is roughly equivalent to
    /// double-quoting in POSIX shell.
    ParseEscapes
}

/// A tokeniser error.
///
/// A Tokeniser's `into_strings` method can fail with one of the following
/// errors if called while the Tokeniser is in an unfinished state.
#[deriving(Eq, PartialEq, Show)]
pub enum Error {
    /// A quotation was opened, but not closed.
    UnmatchedQuote,

    /// An escape sequence was started, but not finished.
    UnfinishedEscape
}

impl<Q: Map<char, ( char, QuoteMode )>+Collection+Clone,
     E: Map<char, char>+Clone> Tokeniser<Q, E> {
    /// Creates a new, blank Tokeniser.
    ///
    /// # Arguments
    ///
    /// * `quote_pairs`   - A map, mapping characters that serve as opening
    ///                     quotes to their closing quotes and quote modes.
    /// * `escape_pairs`  - A map, mapping escape characters to the characters
    ///                     they represent.  An empty map is treated as a
    ///                     _shell-style escape_: the
    /// * `escape_leader` - The character, if any, that starts an escape
    ///                     sequence.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, with an empty state.  Attempting to take the
    /// string vector of the Tokeniser yields the empty vector.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::hashmap::HashMap;
    /// use russet::{ Tokeniser, ParseEscapes, QuoteMode };
    ///
    /// let quote_pairs: HashMap<char, ( char, QuoteMode )> =
    ///     vec![ ( '\"', ( '\"', ParseEscapes ) ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// assert_eq!(tok.into_strings(), Ok(vec![]));
    /// ```
    pub fn new(quote_pairs: Q, escape_pairs: E, escape_leader: Option<char>)
               -> Tokeniser<Q, E> {
        Tokeniser {
            vec: vec![ String::new() ],
            in_word: false,
            quote: None,
            escaping: false,
            quote_pairs: quote_pairs,
            escape_pairs: escape_pairs,
            escape_leader: escape_leader
        }
    }

    /// Feeds a single character `chr` to a Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `chr`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use russet::whitespace_split_tokeniser;
    ///
    /// let tok = whitespace_split_tokeniser();
    /// let tok2 = tok.add_char('a').add_char('b').add_char('c');
    /// assert_eq!(tok2.into_strings(), Ok(vec![ "abc".into_string() ]));
    /// ```
    pub fn add_char(self, chr: char) -> Tokeniser<Q, E> {
        let mut new = self.clone();

        match (chr, self) {
            // ESCAPE SEQUENCES
            //   Shell-style escaping (no escapes defined)
            //   -> Echo character
            ( c, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.is_empty() => {
                new.in_word = true;
                new.escaping = false;
                new.vec.mut_last().mutate(|s| { s.push_char(c); s });
            },
            // Known escaped character, otherwise
            // -> Escape character
            ( e, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.contains_key(&e) => {
                let x = es.find(&e).unwrap();

                new.in_word = true;
                new.escaping = false;
                new.vec.mut_last().mutate(|s| { s.push_char(x.clone()); s });
            },

            // ESCAPE LEADER
            //   Escape leader, not in quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: None,
                escape_leader: Some(e),
                ..
            } ) if e == c => {
                new.escaping = true;
                new.in_word = true;
            },
            //   Escape leader, in escape-permitting quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: Some(( _, ParseEscapes )),
                escape_leader: Some(e),
                ..
            } ) if e == c => {
                new.escaping = true;
                new.in_word = true;
            },

            // QUOTE OPENING
            //   Quote opening character, not currently in quoted word
            //   -> Start quoting
            ( q, Tokeniser {
                escaping: false,
                quote: None,
                quote_pairs: ref qs,
                ..
            } ) if qs.contains_key(&q) => {
                new.quote = Some(qs.find(&q).unwrap().clone());
                new.in_word = true;
            },

            // QUOTE CLOSING
            //   Quote closing character, in quoted word, quotes ok
            //   -> Stop quoting
            ( c, Tokeniser { escaping: false, quote: Some(( cc, _ )), .. } )
                if c == cc => {
                new.quote = None;
                new.in_word = true;
            },

            // UNESCAPED WHITESPACE
            //   Unescaped whitespace, while not in a word
            //   -> Ignore
            ( a, Tokeniser { escaping: false, in_word: false, .. } )
                if is_whitespace(a) => (),
            //   Unescaped whitespace, while in a non-quoted word
            //   -> End word
            ( a, Tokeniser { escaping: false, in_word: true, quote: None, .. } )
                if is_whitespace(a) => {
                new.in_word = false;
                new.vec.push(String::new());
            },

            // DEFAULT
            //   Anything else
            //   -> Echo
            ( a, _ ) => {
                new.in_word = true;
                new.escaping = false;
                new.vec.mut_last().mutate(|s| { s.push_char(a); s });
            }
        }

        new
    }

    /// Feeds a line into the Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `line`.
    pub fn add_line(self, line: &str) -> Tokeniser<Q, E> {
        line.trim().chars().fold(self, |s, chr| s.add_char(chr))
    }

    /// Destroys the tokeniser, extracting the string vector.
    ///
    /// # Return value
    ///
    /// A Result, containing the tokenised string vector if the Tokeniser
    /// was in a valid ending state, and an Error otherwise.
    pub fn into_strings(mut self) -> Result<Vec<String>, Error> {
        if self.in_word && self.quote.is_some() {
            Err(UnmatchedQuote)
        } else if self.escaping {
            Err(UnfinishedEscape)
        } else {
            if self.vec.last().map(|s| s.len() == 0).unwrap_or(false) {
                self.vec.pop();
            }

            Ok(self.vec)
        }
    }
}

/// A type for tokenisers returned by Russet.
pub type StockTokeniser = Tokeniser<HashMap<char, ( char, QuoteMode )>,
                                    HashMap<char, char>>;

/// Creates a Tokeniser that doesn't support quoting or escaping.
///
/// This is effectively equivalent to the Words iterator on string slices.
///
/// # Return value
///
/// A Tokeniser with no quoting or escaping.
///
/// # Example
///
/// ```rust
/// use russet::whitespace_split_tokeniser;
///
/// let tok = whitespace_split_tokeniser();
/// let tok2 = tok.add_line("this \"ignores quotes\"  and\n  \\slashes");
/// assert_eq!(tok2.into_strings(), Ok(vec!("this".into_string(),
///                                         "\"ignores".into_string(),
///                                         "quotes\"".into_string(),
///                                         "and".into_string(),
///                                         "\\slashes".into_string())));
/// ```
#[experimental]
pub fn whitespace_split_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> = HashMap::new();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, None)
}

/// Creates a Tokeniser that provides shell-style quoting.
///
/// This recognises pairs of " and ' as delineating words, and parses
/// anything following a \ as its literal value.  Anything in single quotes
/// is returned verbatim.
///
/// # Return value
///
/// A Tokeniser with shell-style quoting.
///
/// # Example
///
/// ```rust
/// use russet::shell_style_tokeniser;
///
/// let tok = shell_style_tokeniser();
/// let tok2 = tok.add_line("word1 word\\ 2 \"word\\ 3\" 'word\\ \"4\"'");
/// assert_eq!(tok2.into_strings(), Ok(vec!("word1".into_string(),
///                                         "word 2".into_string(),
///                                         "word 3".into_string(),
///                                         "word\\ \"4\"".into_string())));
/// ```
#[experimental]
pub fn shell_style_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', IgnoreEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, Some('\\'))
}

/// Unpacks a line into its constituent words.
#[experimental]
pub fn unpack(line: &str) -> Result<Vec<String>, Error> {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', ParseEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> =
        vec![ ( 'n', '\n' ) ].move_iter().collect();

    line.trim().chars().fold(
        Tokeniser::new(quote_pairs, escape_pairs, Some('\\')),
        |s, chr| s.add_char(chr)
    ).into_strings()
}

#[cfg(test)]
mod test {
    use super::{
        unpack,
        whitespace_split_tokeniser,
        Error,
        UnmatchedQuote,
        UnfinishedEscape
    };

    #[test]
    fn unpack_unmatched_quote() {
        assert_eq!(unpack("\"abcde"), Err(UnmatchedQuote));
    }

    #[test]
    fn unpack_unfinished_escape() {
        assert_eq!(unpack("zxcvbn m\\"), Err(UnfinishedEscape));
    }

    #[test]
    fn unpack_empty_string() {
        assert_eq!(unpack(""), Ok(vec![]));
    }

    #[test]
    fn unpack_leading_whitespace() {
        let rhs = vec![ "abc".into_string(), "def".into_string() ];
        assert_eq!(unpack("     abc def"), Ok(rhs));
    }

    #[test]
    fn unpack_trailing_whitespace() {
        let rhs = vec![ "ghi".into_string(), "jkl".into_string() ];
        assert_eq!(unpack("ghi jkl     \n"), Ok(rhs));
    }

    #[test]
    fn unpack_enqueue_command() {
        let lhs =
            "enqueue file \"C:\\\\Users\\\\Test\\\\Artist - Title.mp3\" 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(unpack(lhs), Ok(rhs));
    }

    #[test]
    fn unpack_escaped_newline() {
        assert_eq!(unpack("abc\\nde"),     Ok(vec![ "abc\nde".into_string() ]));
        assert_eq!(unpack("\"abc\\nde\""), Ok(vec![ "abc\nde".into_string() ]));
    }

    /// The whitespace_split_tokeniser should provide the same strings as
    /// the Words iterator for an arbitrary string.
    #[quickcheck]
    fn whitespace_split_tokeniser_words_equivalence(line : String) -> bool {
        let line_slice = line.as_slice();

        let lhs: Result<Vec<String>, Error> =
            whitespace_split_tokeniser().add_line(line_slice).into_strings();
        let rhs: Result<Vec<String>, Error> =
            Ok(line_slice.words().map(|x| x.into_string()).collect());

        lhs == rhs
    }
}
<commit_msg>Use private methods to DRY things up.<commit_after>//! # Russet
//!
//! __Russet__ is a simple string tokenising library for Rust.  It supports
//! POSIX shell-style separation of a line of _words_ into a vector of
//! strings.
#![experimental]

#![feature(phase)]
#[phase(plugin)]
extern crate quickcheck_macros;
extern crate quickcheck;

use std::char::is_whitespace;
use std::collections::hashmap::HashMap;

/// A tokeniser object.
///
/// A Tokeniser can be fed characters from an iterator, string, or individually.
/// It is an _immutable_ object: actions on a Tokeniser consume the Tokeniser,
/// and produce a fresh copy of the Tokeniser.
///
/// At any stage, a Tokeniser can be consumed to produce the vector of words
/// it has read, using the `into_strings` method.  This method may fail if the
/// Tokeniser ended in a bad state (in the middle of a quoted string, or in
/// the middle of an escape sequence).
#[deriving(Clone)]
pub struct Tokeniser<Q, E> {
    /// The current vector of parsed words.
    vec: Vec<String>,

    /// Whether or not we are currently in a word.
    in_word: bool,

    /// The current closing quote character and quote mode, if any.
    quote: Option<( char, QuoteMode )>,

    /// Whether the tokeniser is currently processing an escape character.
    escaping: bool,

    /// Maps from quote openers to quote closers.
    quote_pairs: Q,

    /// Map from escape characters to their replacements.
    escape_pairs: E,

    /// The character preceding escape characters.
    escape_leader: Option<char>
}

/// A quote mode.
#[deriving(Clone)]
pub enum QuoteMode {
    /// All characters except the closing character have their literal value.
    /// This is equivalent to single-quoting in POSIX shell.
    IgnoreEscapes,

    /// All characters except the closing character and escape sequences
    /// have their literal value.  This is roughly equivalent to
    /// double-quoting in POSIX shell.
    ParseEscapes
}

/// A tokeniser error.
///
/// A Tokeniser's `into_strings` method can fail with one of the following
/// errors if called while the Tokeniser is in an unfinished state.
#[deriving(Eq, PartialEq, Show)]
pub enum Error {
    /// A quotation was opened, but not closed.
    UnmatchedQuote,

    /// An escape sequence was started, but not finished.
    UnfinishedEscape
}

impl<Q: Map<char, ( char, QuoteMode )>+Collection+Clone,
     E: Map<char, char>+Clone> Tokeniser<Q, E> {
    /// Creates a new, blank Tokeniser.
    ///
    /// # Arguments
    ///
    /// * `quote_pairs`   - A map, mapping characters that serve as opening
    ///                     quotes to their closing quotes and quote modes.
    /// * `escape_pairs`  - A map, mapping escape characters to the characters
    ///                     they represent.  An empty map is treated as a
    ///                     _shell-style escape_: the
    /// * `escape_leader` - The character, if any, that starts an escape
    ///                     sequence.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, with an empty state.  Attempting to take the
    /// string vector of the Tokeniser yields the empty vector.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::hashmap::HashMap;
    /// use russet::{ Tokeniser, ParseEscapes, QuoteMode };
    ///
    /// let quote_pairs: HashMap<char, ( char, QuoteMode )> =
    ///     vec![ ( '\"', ( '\"', ParseEscapes ) ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// assert_eq!(tok.into_strings(), Ok(vec![]));
    /// ```
    pub fn new(quote_pairs: Q, escape_pairs: E, escape_leader: Option<char>)
               -> Tokeniser<Q, E> {
        Tokeniser {
            vec: vec![ String::new() ],
            in_word: false,
            quote: None,
            escaping: false,
            quote_pairs: quote_pairs,
            escape_pairs: escape_pairs,
            escape_leader: escape_leader
        }
    }

    /// Feeds a single character `chr` to a Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `chr`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use russet::whitespace_split_tokeniser;
    ///
    /// let tok = whitespace_split_tokeniser();
    /// let tok2 = tok.add_char('a').add_char('b').add_char('c');
    /// assert_eq!(tok2.into_strings(), Ok(vec![ "abc".into_string() ]));
    /// ```
    pub fn add_char(self, chr: char) -> Tokeniser<Q, E> {
        let mut new = self.clone();

        match (chr, self) {
            // ESCAPE SEQUENCES
            //   Shell-style escaping (no escapes defined)
            //   -> Echo character
            ( c, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.is_empty() => new.emit(c),
            // Known escaped character, otherwise
            // -> Escape character
            ( e, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.contains_key(&e) => {
                let x = es.find(&e).unwrap();

                new.emit(x.clone());
            },

            // ESCAPE LEADER
            //   Escape leader, not in quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: None,
                escape_leader: Some(e),
                ..
            } ) if e == c => new.start_escaping(),
            //   Escape leader, in escape-permitting quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: Some(( _, ParseEscapes )),
                escape_leader: Some(e),
                ..
            } ) if e == c => new.start_escaping(),

            // QUOTE OPENING
            //   Quote opening character, not currently in quoted word
            //   -> Start quoting
            ( q, Tokeniser {
                escaping: false,
                quote: None,
                quote_pairs: ref qs,
                ..
            } ) if qs.contains_key(&q) => {
                new.quote = Some(qs.find(&q).unwrap().clone());
                new.in_word = true;
            },

            // QUOTE CLOSING
            //   Quote closing character, in quoted word, quotes ok
            //   -> Stop quoting
            ( c, Tokeniser { escaping: false, quote: Some(( cc, _ )), .. } )
                if c == cc => {
                new.quote = None;
                new.in_word = true;
            },

            // UNESCAPED WHITESPACE
            //   Unescaped whitespace, while not in a word
            //   -> Ignore
            ( a, Tokeniser { escaping: false, in_word: false, .. } )
                if is_whitespace(a) => (),
            //   Unescaped whitespace, while in a non-quoted word
            //   -> End word
            ( a, Tokeniser { escaping: false, in_word: true, quote: None, .. } )
                if is_whitespace(a) => {
                new.in_word = false;
                new.vec.push(String::new());
            },

            // DEFAULT
            //   Anything else
            //   -> Echo
            ( a, _ ) => new.emit(a)
        }

        new
    }

    /// Feeds a line into the Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `line`.
    pub fn add_line(self, line: &str) -> Tokeniser<Q, E> {
        line.trim().chars().fold(self, |s, chr| s.add_char(chr))
    }

    /// Destroys the tokeniser, extracting the string vector.
    ///
    /// # Return value
    ///
    /// A Result, containing the tokenised string vector if the Tokeniser
    /// was in a valid ending state, and an Error otherwise.
    pub fn into_strings(mut self) -> Result<Vec<String>, Error> {
        if self.in_word && self.quote.is_some() {
            Err(UnmatchedQuote)
        } else if self.escaping {
            Err(UnfinishedEscape)
        } else {
            self.drop_empty_current_string();
            Ok(self.vec)
        }
    }

    /// Adds a character into a Tokeniser's current string.
    /// This automatically sets the Tokeniser's state to be in a word,
    /// and clears any escape sequence flag.
    fn emit(&mut self, c: char) {
        self.in_word = true;
        self.escaping = false;
        self.vec.mut_last().mutate(|s| { s.push_char(c); s });
    }

    /// Switches on escape mode.
    /// This automatically sets the Tokeniser to be in a word, if it isn't
    /// already.
    fn start_escaping(&mut self) {
        self.escaping = true;
        self.in_word = true;
    }

    /// Drops the current working string, if it is empty.
    fn drop_empty_current_string(&mut self) {
        if self.vec.last().map(|s| s.is_empty()).unwrap_or(false) {
            self.vec.pop();
        }
    }
}

/// A type for tokenisers returned by Russet.
pub type StockTokeniser = Tokeniser<HashMap<char, ( char, QuoteMode )>,
                                    HashMap<char, char>>;

/// Creates a Tokeniser that doesn't support quoting or escaping.
///
/// This is effectively equivalent to the Words iterator on string slices.
///
/// # Return value
///
/// A Tokeniser with no quoting or escaping.
///
/// # Example
///
/// ```rust
/// use russet::whitespace_split_tokeniser;
///
/// let tok = whitespace_split_tokeniser();
/// let tok2 = tok.add_line("this \"ignores quotes\"  and\n  \\slashes");
/// assert_eq!(tok2.into_strings(), Ok(vec!("this".into_string(),
///                                         "\"ignores".into_string(),
///                                         "quotes\"".into_string(),
///                                         "and".into_string(),
///                                         "\\slashes".into_string())));
/// ```
#[experimental]
pub fn whitespace_split_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> = HashMap::new();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, None)
}

/// Creates a Tokeniser that provides shell-style quoting.
///
/// This recognises pairs of " and ' as delineating words, and parses
/// anything following a \ as its literal value.  Anything in single quotes
/// is returned verbatim.
///
/// # Return value
///
/// A Tokeniser with shell-style quoting.
///
/// # Example
///
/// ```rust
/// use russet::shell_style_tokeniser;
///
/// let tok = shell_style_tokeniser();
/// let tok2 = tok.add_line("word1 word\\ 2 \"word\\ 3\" 'word\\ \"4\"'");
/// assert_eq!(tok2.into_strings(), Ok(vec!("word1".into_string(),
///                                         "word 2".into_string(),
///                                         "word 3".into_string(),
///                                         "word\\ \"4\"".into_string())));
/// ```
#[experimental]
pub fn shell_style_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', IgnoreEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, Some('\\'))
}

/// Unpacks a line into its constituent words.
#[experimental]
pub fn unpack(line: &str) -> Result<Vec<String>, Error> {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', ParseEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> =
        vec![ ( 'n', '\n' ) ].move_iter().collect();

    line.trim().chars().fold(
        Tokeniser::new(quote_pairs, escape_pairs, Some('\\')),
        |s, chr| s.add_char(chr)
    ).into_strings()
}

#[cfg(test)]
mod test {
    use super::{
        unpack,
        whitespace_split_tokeniser,
        Error,
        UnmatchedQuote,
        UnfinishedEscape
    };

    #[test]
    fn unpack_unmatched_quote() {
        assert_eq!(unpack("\"abcde"), Err(UnmatchedQuote));
    }

    #[test]
    fn unpack_unfinished_escape() {
        assert_eq!(unpack("zxcvbn m\\"), Err(UnfinishedEscape));
    }

    #[test]
    fn unpack_empty_string() {
        assert_eq!(unpack(""), Ok(vec![]));
    }

    #[test]
    fn unpack_leading_whitespace() {
        let rhs = vec![ "abc".into_string(), "def".into_string() ];
        assert_eq!(unpack("     abc def"), Ok(rhs));
    }

    #[test]
    fn unpack_trailing_whitespace() {
        let rhs = vec![ "ghi".into_string(), "jkl".into_string() ];
        assert_eq!(unpack("ghi jkl     \n"), Ok(rhs));
    }

    #[test]
    fn unpack_enqueue_command() {
        let lhs =
            "enqueue file \"C:\\\\Users\\\\Test\\\\Artist - Title.mp3\" 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(unpack(lhs), Ok(rhs));
    }

    #[test]
    fn unpack_escaped_newline() {
        assert_eq!(unpack("abc\\nde"),     Ok(vec![ "abc\nde".into_string() ]));
        assert_eq!(unpack("\"abc\\nde\""), Ok(vec![ "abc\nde".into_string() ]));
    }

    /// The whitespace_split_tokeniser should provide the same strings as
    /// the Words iterator for an arbitrary string.
    #[quickcheck]
    fn whitespace_split_tokeniser_words_equivalence(line : String) -> bool {
        let line_slice = line.as_slice();

        let lhs: Result<Vec<String>, Error> =
            whitespace_split_tokeniser().add_line(line_slice).into_strings();
        let rhs: Result<Vec<String>, Error> =
            Ok(line_slice.words().map(|x| x.into_string()).collect());

        lhs == rhs
    }
}
<|endoftext|>


<commit_before>//! A queue-backed exclusive data lock.
//!
//
// * It is unclear how many of the unsafe methods within need actually remain
//   unsafe.

use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::cell::UnsafeCell;
use futures::{Future, Poll, Canceled};
use futures::sync::oneshot;
use crossbeam::sync::SegQueue;

 // Allows access to the data contained within a lock just like a mutex guard.
pub struct Guard<T> {
    qutex: Qutex<T>,
}

impl<T> Guard<T> {
    /// Releases the lock held by this `Guard` and returns the original `Qutex`.
    //
    // NOTE: This increments the `Qutex` reference count before immediately
    // decrementing it. Is it worth avoiding this by wrapping it in an Option
    // or using unsafe hackery? It would make uglier code for the measly
    // savings of two atomic stores...
    pub fn unlock(self) -> Qutex<T> {
        self.qutex.clone()
    }
}

impl<T> Deref for Guard<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.qutex.inner.cell.get() }
    }
}

impl<T> DerefMut for Guard<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.qutex.inner.cell.get() }
    }
}

impl<T> Drop for Guard<T> {
    fn drop(&mut self) {
        unsafe { self.qutex.direct_unlock().expect("Error dropping Guard") };
    }
}

/// A future which resolves to a `Guard`.
pub struct FutureGuard<T> {
    qutex: Option<Qutex<T>>,
    rx: oneshot::Receiver<()>,
}

impl<T> FutureGuard<T> {
    /// Returns a new `FutureGuard`.
    fn new(qutex: Qutex<T>, rx: oneshot::Receiver<()>) -> FutureGuard<T> {
        FutureGuard {
            qutex: Some(qutex),
            rx: rx,
        }
    }

    /// Blocks the current thread until this future resolves.
    #[inline]
    pub fn wait(self) -> Result<Guard<T>, Canceled> {
        <Self as Future>::wait(self)
    }
}

impl<T> Future for FutureGuard<T> {
    type Item = Guard<T>;
    type Error = Canceled;

    #[inline]
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if self.qutex.is_some() {
            unsafe { self.qutex.as_ref().unwrap().process_queue()
                .expect("Error polling FutureGuard"); }

            match self.rx.poll() {
                Ok(status) => Ok(status.map(|_| {
                    Guard { qutex: self.qutex.take().unwrap() }
                })),
                Err(e) => Err(e.into()),
            }
        } else {
            ///// [KEEPME]:
            // Err("FutureGuard::poll: Task already completed.".into())
            panic!("FutureGuard::poll: Task already completed.");
        }
    }
}

/// A request to lock the qutex for exclusive access.
pub struct Request {
    tx: oneshot::Sender<()>,
    // wait_event: Option<Event>,
}

impl Request {
    /// Returns a new `Request`.
    pub fn new(tx: oneshot::Sender<()>) -> Request {
        Request { tx: tx }
    }
}

struct Inner<T> {
    // TODO: Convert to `AtomicBool` if no additional states are needed:
    state: AtomicUsize,
    cell: UnsafeCell<T>,
    queue: SegQueue<Request>,
}

impl<T> From<T> for Inner<T> {
    #[inline]
    fn from(val: T) -> Inner<T> {
        Inner {
            state: AtomicUsize::new(0),
            cell: UnsafeCell::new(val),
            queue: SegQueue::new(),
        }
    }
}

unsafe impl<T: Send> Send for Inner<T> {}
unsafe impl<T: Send> Sync for Inner<T> {}

/// A lock-free-queue-backed exclusive data lock.
pub struct Qutex<T> {
    inner: Arc<Inner<T>>,
}

impl<T> Qutex<T> {
    /// Creates and returns a new `Qutex`.
    #[inline]
    pub fn new(val: T) -> Qutex<T> {
        Qutex {
            inner: Arc::new(Inner::from(val)),
        }
    }

    /// Returns a new `FutureGuard` which can be used as a future and will
    /// resolve into a `Guard`.
    pub fn request_lock(self) -> FutureGuard<T> {
        let (tx, rx) = oneshot::channel();
        unsafe { self.push_request(Request::new(tx)); }
        FutureGuard::new(self, rx)
    }

    /// Pushes a lock request onto the queue.
    ///
    //
    // TODO: Evaluate unsafe-ness.
    //
    #[inline]
    pub unsafe fn push_request(&self, req: Request) {
        self.inner.queue.push(req);
    }

    /// Returns a mutable reference to the inner `Vec` if there are currently
    /// no other copies of this `Qutex`.
    ///
    /// Since this call borrows the inner lock mutably, no actual locking needs to
    /// take place---the mutable borrow statically guarantees no locks exist.
    ///
    #[inline]
    pub fn get_mut(&mut self) -> Option<&mut T> {
        Arc::get_mut(&mut self.inner).map(|inn| unsafe { &mut *inn.cell.get() })
    }

    /// Returns a reference to the inner value.
    ///
    /// This is frought with potential peril.
    ///
    #[inline]
    pub fn as_ptr(&self) -> *const T {
        self.inner.cell.get()
    }

    /// Returns a mutable reference to the inner value.
    ///
    /// Drinking water from the tap in 1850's London would be safer.
    ///
    #[inline]
    pub fn as_mut_ptr(&self) -> *mut T {
        self.inner.cell.get()
    }

    /// Pops the next lock request in the queue if this lock is unlocked.
    //
    // TODO:
    // * This is currently public due to 'derivers' (aka. sub-types). Evaluate.
    // * Consider removing unsafe qualifier.
    // * Return proper error type.
    //
    pub unsafe fn process_queue(&self) -> Result<(), &'static str> {
        match self.inner.state.compare_and_swap(0, 1, SeqCst) {
            // Unlocked:
            0 => {
                if let Some(req) = self.inner.queue.try_pop() {
                    req.tx.send(()).map_err(|_| "Qutex queue has been dropped")
                } else {
                    self.inner.state.store(0, SeqCst);
                    Ok(())
                }
            },
            // Already locked, leave it alone:
            1 => Ok(()),
            // Something else:
            n => panic!("Qutex::process_queue: inner.state: {}.", n),
        }
    }

    /// Unlocks this lock and wakes up the next task in the queue.
    //
    // TODO:
    // * Evaluate unsafe-ness.
    // * Return proper error type
    pub unsafe fn direct_unlock(&self) -> Result<(), &'static str> {
        // TODO: Consider using `Ordering::Release`.
        self.inner.state.store(0, SeqCst);
        self.process_queue()
    }
}

impl<T> From<T> for Qutex<T> {
    #[inline]
    fn from(val: T) -> Qutex<T> {
        Qutex::new(val)
    }
}

// Avoids needing `T: Clone`.
impl<T> Clone for Qutex<T> {
    #[inline]
    fn clone(&self) -> Qutex<T> {
        Qutex {
            inner: self.inner.clone(),
        }
    }
}

#[cfg(test)]
// Woefully incomplete:
mod tests {
    #![allow(unused_variables, unused_imports, dead_code)]
    use super::*;

    #[test]
    fn simple() {
        let val = Qutex::from(999i32);

        println!("Reading val...");
        {
            let future_guard = val.clone().request_lock();
            let guard = future_guard.wait().unwrap();
            println!("val: {}", *guard);
        }

        println!("Storing new val...");
        {
            let future_guard = val.clone().request_lock();
            let mut guard = future_guard.wait().unwrap();
            *guard = 5;
        }

        println!("Reading val...");
        {
            let future_guard = val.clone().request_lock();
            let guard = future_guard.wait().unwrap();
            println!("val: {}", *guard);
        }
    }

    #[test]
    fn concurrent() {
        use std::thread;

        let start_val = 10000i32;
        let thread_count = 50;
        let qutex = Qutex::new(start_val);
        let mut threads = Vec::with_capacity(thread_count);

        for i in 0..thread_count {
            let future_guard = qutex.clone().request_lock();

            threads.push(thread::spawn(|| {
                let mut guard = future_guard.wait().unwrap();
                *guard += 1
            }))
        }

        for thread in threads {
            thread.join().unwrap();
        }

        let guard = qutex.clone().request_lock().wait().unwrap();
        assert!(*guard == start_val + thread_count as i32);
    }
}<commit_msg>Add a few debug lines.<commit_after>//! A queue-backed exclusive data lock.
//!
//
// * It is unclear how many of the unsafe methods within need actually remain
//   unsafe.

use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::cell::UnsafeCell;
use futures::{Future, Poll, Canceled};
use futures::sync::oneshot;
use crossbeam::sync::SegQueue;

 // Allows access to the data contained within a lock just like a mutex guard.
pub struct Guard<T> {
    qutex: Qutex<T>,
}

impl<T> Guard<T> {
    /// Releases the lock held by this `Guard` and returns the original `Qutex`.
    //
    // NOTE: This increments the `Qutex` reference count before immediately
    // decrementing it. Is it worth avoiding this by wrapping it in an Option
    // or using unsafe hackery? It would make uglier code for the measly
    // savings of two atomic stores...
    pub fn unlock(self) -> Qutex<T> {
        self.qutex.clone()
    }
}

impl<T> Deref for Guard<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.qutex.inner.cell.get() }
    }
}

impl<T> DerefMut for Guard<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.qutex.inner.cell.get() }
    }
}

impl<T> Drop for Guard<T> {
    fn drop(&mut self) {
        unsafe { self.qutex.direct_unlock().expect("Error dropping Guard") };
    }
}

/// A future which resolves to a `Guard`.
pub struct FutureGuard<T> {
    qutex: Option<Qutex<T>>,
    rx: oneshot::Receiver<()>,
}

impl<T> FutureGuard<T> {
    /// Returns a new `FutureGuard`.
    fn new(qutex: Qutex<T>, rx: oneshot::Receiver<()>) -> FutureGuard<T> {
        FutureGuard {
            qutex: Some(qutex),
            rx: rx,
        }
    }

    /// Blocks the current thread until this future resolves.
    #[inline]
    pub fn wait(self) -> Result<Guard<T>, Canceled> {
        <Self as Future>::wait(self)
    }
}

impl<T> Future for FutureGuard<T> {
    type Item = Guard<T>;
    type Error = Canceled;

    #[inline]
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if self.qutex.is_some() {
            unsafe { self.qutex.as_ref().unwrap().process_queue()
                .expect("Error polling FutureGuard"); }

            match self.rx.poll() {
                Ok(status) => Ok(status.map(|_| {
                    Guard { qutex: self.qutex.take().unwrap() }
                })),
                Err(e) => Err(e.into()),
            }
        } else {
            ///// [KEEPME]:
            // Err("FutureGuard::poll: Task already completed.".into())
            panic!("FutureGuard::poll: Task already completed.");
        }
    }
}

/// A request to lock the qutex for exclusive access.
#[derive(Debug)]
pub struct Request {
    tx: oneshot::Sender<()>,
    // wait_event: Option<Event>,
}

impl Request {
    /// Returns a new `Request`.
    pub fn new(tx: oneshot::Sender<()>) -> Request {
        Request { tx: tx }
    }
}

struct Inner<T> {
    // TODO: Convert to `AtomicBool` if no additional states are needed:
    state: AtomicUsize,
    cell: UnsafeCell<T>,
    queue: SegQueue<Request>,
}

impl<T> From<T> for Inner<T> {
    #[inline]
    fn from(val: T) -> Inner<T> {
        Inner {
            state: AtomicUsize::new(0),
            cell: UnsafeCell::new(val),
            queue: SegQueue::new(),
        }
    }
}

unsafe impl<T: Send> Send for Inner<T> {}
unsafe impl<T: Send> Sync for Inner<T> {}

/// A lock-free-queue-backed exclusive data lock.
pub struct Qutex<T> {
    inner: Arc<Inner<T>>,
}

impl<T> Qutex<T> {
    /// Creates and returns a new `Qutex`.
    #[inline]
    pub fn new(val: T) -> Qutex<T> {
        Qutex {
            inner: Arc::new(Inner::from(val)),
        }
    }

    /// Returns a new `FutureGuard` which can be used as a future and will
    /// resolve into a `Guard`.
    pub fn request_lock(self) -> FutureGuard<T> {
        let (tx, rx) = oneshot::channel();
        unsafe { self.push_request(Request::new(tx)); }
        FutureGuard::new(self, rx)
    }

    /// Pushes a lock request onto the queue.
    ///
    //
    // TODO: Evaluate unsafe-ness.
    //
    #[inline]
    pub unsafe fn push_request(&self, req: Request) {
        self.inner.queue.push(req);
    }

    /// Returns a mutable reference to the inner `Vec` if there are currently
    /// no other copies of this `Qutex`.
    ///
    /// Since this call borrows the inner lock mutably, no actual locking needs to
    /// take place---the mutable borrow statically guarantees no locks exist.
    ///
    #[inline]
    pub fn get_mut(&mut self) -> Option<&mut T> {
        Arc::get_mut(&mut self.inner).map(|inn| unsafe { &mut *inn.cell.get() })
    }

    /// Returns a reference to the inner value.
    ///
    /// This is frought with potential peril.
    ///
    #[inline]
    pub fn as_ptr(&self) -> *const T {
        self.inner.cell.get()
    }

    /// Returns a mutable reference to the inner value.
    ///
    /// Drinking water from the tap in 1850's London would be safer.
    ///
    #[inline]
    pub fn as_mut_ptr(&self) -> *mut T {
        self.inner.cell.get()
    }

    /// Pops the next lock request in the queue if this lock is unlocked.
    //
    // TODO:
    // * This is currently public due to 'derivers' (aka. sub-types). Evaluate.
    // * Consider removing unsafe qualifier.
    // * Return proper error type.
    //
    pub unsafe fn process_queue(&self) -> Result<(), &'static str> {
        match self.inner.state.compare_and_swap(0, 1, SeqCst) {
            // Unlocked:
            0 => {
                if let Some(req) = self.inner.queue.try_pop() {
                    // println!("Qutex::process_queue: Fulfilling lock request.");
                    req.tx.send(()).map_err(|_| "Qutex queue has been dropped")
                } else {
                    // println!("Qutex::process_queue: Queue is empty.");
                    self.inner.state.store(0, SeqCst);
                    Ok(())
                }
            },
            // Already locked, leave it alone:
            1 => Ok(()),
            // Something else:
            n => panic!("Qutex::process_queue: inner.state: {}.", n),
        }
    }

    /// Unlocks this lock and wakes up the next task in the queue.
    //
    // TODO:
    // * Evaluate unsafe-ness.
    // * Return proper error type
    pub unsafe fn direct_unlock(&self) -> Result<(), &'static str> {
        // TODO: Consider using `Ordering::Release`.
        self.inner.state.store(0, SeqCst);
        self.process_queue()
    }
}

impl<T> From<T> for Qutex<T> {
    #[inline]
    fn from(val: T) -> Qutex<T> {
        Qutex::new(val)
    }
}

// Avoids needing `T: Clone`.
impl<T> Clone for Qutex<T> {
    #[inline]
    fn clone(&self) -> Qutex<T> {
        Qutex {
            inner: self.inner.clone(),
        }
    }
}

#[cfg(test)]
// Woefully incomplete:
mod tests {
    #![allow(unused_variables, unused_imports, dead_code)]
    use super::*;

    #[test]
    fn simple() {
        let val = Qutex::from(999i32);

        println!("Reading val...");
        {
            let future_guard = val.clone().request_lock();
            let guard = future_guard.wait().unwrap();
            println!("val: {}", *guard);
        }

        println!("Storing new val...");
        {
            let future_guard = val.clone().request_lock();
            let mut guard = future_guard.wait().unwrap();
            *guard = 5;
        }

        println!("Reading val...");
        {
            let future_guard = val.clone().request_lock();
            let guard = future_guard.wait().unwrap();
            println!("val: {}", *guard);
        }
    }

    #[test]
    fn concurrent() {
        use std::thread;

        let start_val = 10000i32;
        let thread_count = 50;
        let qutex = Qutex::new(start_val);
        let mut threads = Vec::with_capacity(thread_count);

        for i in 0..thread_count {
            let future_guard = qutex.clone().request_lock();

            threads.push(thread::spawn(|| {
                let mut guard = future_guard.wait().unwrap();
                *guard += 1
            }))
        }

        for thread in threads {
            thread.join().unwrap();
        }

        let guard = qutex.clone().request_lock().wait().unwrap();
        assert!(*guard == start_val + thread_count as i32);
    }
}<|endoftext|>


<commit_before>extern crate movecell;
extern crate typed_arena;

use std::cell::Cell;
use std::ops::Deref;

use movecell::MoveCell;
use typed_arena::Arena;

pub struct Graph<'a, T: 'a> {
    arena: Arena<Node<'a, T>>,
    root: Cell<Option<&'a Node<'a, T>>>
}

impl <'a, T: 'a> Graph<'a, T> {
    pub fn new() -> Self {
        Graph {
            arena: Arena::new(),
            root: Cell::new(None),
        }
    }

    #[inline]
    pub fn new_node(&'a self, node: Node<'a, T>) -> &'a Node<'a, T> {
        self.arena.alloc(node)
    }

    /// # Panics
    /// If root is None
    #[inline]
    pub fn root(&self) -> &Node<'a, T> {
        self.root.get().unwrap()
    }

    #[inline]
    pub fn set_root(&'a self, root: Node<'a, T>) -> &'a Node<'a, T> {
        let root = self.new_node(root);
        self.root.set(Some(root));
        root
    }
}

impl<'a, T> Deref for Graph<'a, T> {
    type Target = Node<'a, T>;

    /// # Panics
    /// If root is not set
    #[inline]
    fn deref(&self) -> &Self::Target {
        self.root()
    }
}

pub struct Node<'a, T: 'a> {
    pub datum: T,
    edges: MoveCell<Option<Vec<&'a Node<'a, T>>>>
}

impl<'a, T: 'a> Node<'a, T> {
    pub fn new(datum: T) -> Node<'a, T> {
        Node {
            datum: datum,
            edges: MoveCell::new(None),
        }
    }

    pub fn add_edge(&self, edge: &'a Node<'a, T>) -> &'a Node<'a, T> {
        let mut edges = self.edges.take().unwrap_or(Vec::new());
        edges.push(edge);
        self.edges.replace(Some(edges));
        edge
    }

    pub fn dfs(&'a self) -> DfsIter<T> {
        DfsIter {
            branch_points: vec![(None, self)]
        }
    }
}

pub struct DfsIter<'a, T: 'a> {
    branch_points: Vec<(Option<usize>, &'a Node<'a, T>)>,
}

impl<'a, T> Iterator for DfsIter<'a, T> {
    type Item = &'a Node<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(branch_point) = self.branch_points.pop() {
            let (index, node) = branch_point;

            // TODO: None panicing version that returns none if no edges
            if let Some(edges) = node.edges.take() {
                let found_node = match index {
                    None => {
                        self.branch_points.push((Some(0), node));
                        Some(node)
                    },
                    Some(index) => {
                        if index < edges.len() {
                            self.branch_points.push((Some(index + 1), node));
                            self.branch_points.push((None, edges[index]));
                        }
                        None
                    }
                };

                node.edges.replace(Some(edges));
                if found_node.is_some() {
                    return found_node;
                }
            } else {
                return Some(node);
            }
        }
        None
    }
}

#[test]
fn it_works() {
    let graph = Graph::new();
    let node1 = graph.set_root(Node::new(1));
    let node2 = node1.add_edge(graph.new_node(Node::new(2)));
    let _node3 = node1.add_edge(graph.new_node(Node::new(3)));
    let _node4 = node2.add_edge(graph.new_node(Node::new(4)));
    let _node5 = node2.add_edge(graph.new_node(Node::new(5)));

    for node in graph.dfs() {
        println!("{}", node.datum);
    }
}
<commit_msg>Update documentation to get better help on the reddit thread<commit_after>extern crate movecell;
extern crate typed_arena;

use std::cell::Cell;
use std::ops::Deref;
use std::iter::FromIterator;

use movecell::MoveCell;
use typed_arena::Arena;

/// An immovable graph type
pub struct Graph<'a, T: 'a> {
    arena: Arena<Node<'a, T>>,
    root: Cell<Option<&'a Node<'a, T>>>
}

impl <'a, T: 'a> Graph<'a, T> {
    pub fn new() -> Self {
        Graph {
            arena: Arena::new(),
            root: Cell::new(None),
        }
    }

    /// Moves a constructed node into the graph. This allows for construction
    /// of nodes in a more convient fashion.
    #[inline]
    pub fn own_node(&'a self, node: Node<'a, T>) -> &'a Node<'a, T> {
        self.arena.alloc(node)
    }

    /// Get the root node of the graph
    /// # Panics
    /// If root is None
    #[inline]
    pub fn root(&self) -> &Node<'a, T> {
        self.root.get().unwrap()
    }

    /// Set the root node to some `&Node` owned by the graph.
    #[inline]
    pub fn set_root(&'a self, root: &'a Node<'a, T>) -> &'a Node<'a, T> {
        self.root.set(Some(root));
        root
    }
}

/// Most actions applied to the graph are really just applied to the root node
/// recursively (or through an iterator). The graph type is just a facade to
/// own the data for each node.
impl<'a, T> Deref for Graph<'a, T> {
    type Target = Node<'a, T>;

    /// # Panics
    /// If root is not set
    #[inline]
    fn deref(&self) -> &Self::Target {
        self.root()
    }
}

/// Graph node type
pub struct Node<'a, T: 'a> {
    pub datum: T,
    // Allows interior mutability so that the graph can be constructed top down rather than bottom
    // up
    edges: MoveCell<Option<Vec<&'a Node<'a, T>>>>
}

impl<'a, T: 'a> Node<'a, T> {
    pub fn new(datum: T) -> Node<'a, T> {
        Node {
            datum: datum,
            edges: MoveCell::new(None),
        }
    }

    /// Adds a `&Node<T>` to the list of edges
    pub fn add_edge(&self, edge: &'a Node<'a, T>) -> &'a Node<'a, T> {
        let mut edges = self.edges.take().unwrap_or(Vec::new());
        edges.push(edge);
        self.edges.replace(Some(edges));
        edge
    }

    /// Iterator adapter for Depth-first traversals of the graph
    pub fn dfs(&'a self) -> DfsIter<T> {
        DfsIter {
            branch_points: vec![(None, self)]
        }
    }
}

impl<'a, T> FromIterator<&'a Node<'a, T>> for &'a Node<'a, T> {
    /// Makes the assumption that the first node to come off an IntoIterator of `&Node` is the root
    /// node. This happens to be correct for BFS and DFS iteration. Any users of `.map()` must be
    /// careful to ensure that the root node is the first produced node.
    ///
    /// NOTE: When running the evaluations this will stop at the first `None` seen. If the
    /// iteration performs operations on any nodes after a single iteration returns a `None`,
    /// those subsequent executions will be skipped.
    ///
    /// # Panics
    /// If the iteration doesn't produce any `&Node`s
    fn from_iter<I: IntoIterator<Item=&'a Node<'a, T>>>(iterable: I) -> Self {
        let mut iter = iterable.into_iter();
        let root = iter.next().unwrap();
        while iter.next().is_some() {  };
        root
    }
}

/// Depth-first iterator adapter for Nodes
pub struct DfsIter<'a, T: 'a> {
    branch_points: Vec<(Option<usize>, &'a Node<'a, T>)>,
}

impl<'a, T> Iterator for DfsIter<'a, T> {
    type Item = &'a Node<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(branch_point) = self.branch_points.pop() {
            let (index, node) = branch_point;

            if let Some(edges) = node.edges.take() {
                let found_node = match index {
                    None => {
                        self.branch_points.push((Some(0), node));
                        Some(node)
                    },
                    Some(index) => {
                        if index < edges.len() {
                            self.branch_points.push((Some(index + 1), node));
                            self.branch_points.push((None, edges[index]));
                        }
                        None
                    }
                };

                node.edges.replace(Some(edges));
                if found_node.is_some() {
                    return found_node;
                }
            } else {
                return Some(node);
            }
        }
        None
    }
}

#[test]
fn it_works() {
    let graph = Graph::new();
    let node1 = graph.set_root(graph.own_node(Node::new(1)));
    let node2 = node1.add_edge(graph.own_node(Node::new(2)));
    let _node3 = node1.add_edge(graph.own_node(Node::new(3)));
    let _node4 = node2.add_edge(graph.own_node(Node::new(4)));
    let _node5 = node2.add_edge(graph.own_node(Node::new(5)));

    println!("Graph 1 (original):");
    for node in graph.dfs() {
        println!("{}", node.datum);
    }

    println!("Graph 1 (mapped):");
    let graph2 = Graph::new();
    let root = graph.dfs().map(|e| graph2.own_node(Node::new(e.datum as f32 * 2.1))).collect();
    graph2.set_root(root);
    for node in graph2.dfs() {
        println!("{}", node.datum);
    }
}
<|endoftext|>