<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Fangxu Hu <framlog@gmail.com>
// (c) Sylvestre Ledru <sylvestre@debian.org>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
// spell-checker:ignore itotal iused iavail ipcent pcent
mod table;

#[cfg(unix)]
use uucore::fsext::statfs_fn;
use uucore::fsext::{read_fs_list, FsUsage, MountInfo};
use uucore::{error::UResult, format_usage};

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};

use std::collections::HashSet;
#[cfg(unix)]
use std::ffi::CString;
use std::iter::FromIterator;
#[cfg(unix)]
use std::mem;

#[cfg(windows)]
use std::path::Path;

use crate::table::{DisplayRow, Header, Row};

static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
                      or all file systems by default.";
const USAGE: &str = "{} [OPTION]... [FILE]...";

static OPT_ALL: &str = "all";
static OPT_BLOCKSIZE: &str = "blocksize";
static OPT_DIRECT: &str = "direct";
static OPT_TOTAL: &str = "total";
static OPT_HUMAN_READABLE: &str = "human-readable";
static OPT_HUMAN_READABLE_2: &str = "human-readable-2";
static OPT_INODES: &str = "inodes";
static OPT_KILO: &str = "kilo";
static OPT_LOCAL: &str = "local";
static OPT_NO_SYNC: &str = "no-sync";
static OPT_OUTPUT: &str = "output";
static OPT_PATHS: &str = "paths";
static OPT_PORTABILITY: &str = "portability";
static OPT_SYNC: &str = "sync";
static OPT_TYPE: &str = "type";
static OPT_PRINT_TYPE: &str = "print-type";
static OPT_EXCLUDE_TYPE: &str = "exclude-type";
static OUTPUT_FIELD_LIST: [&str; 12] = [
    "source", "fstype", "itotal", "iused", "iavail", "ipcent", "size", "used", "avail", "pcent",
    "file", "target",
];

/// Store names of file systems as a selector.
/// Note: `exclude` takes priority over `include`.
#[derive(Default)]
struct FsSelector {
    include: HashSet<String>,
    exclude: HashSet<String>,
}

/// A block size to use in condensing the display of a large number of bytes.
///
/// The [`Bytes`] variant represents a static block size. The
/// [`HumanReadableDecimal`] and [`HumanReadableBinary`] variants
/// represent dynamic block sizes: as the number of bytes increases,
/// the divisor increases as well (for example, from 1 to 1,000 to
/// 1,000,000 and so on in the case of [`HumanReadableDecimal`]).
///
/// The default variant is `Bytes(1024)`.
enum BlockSize {
    /// A fixed number of bytes.
    ///
    /// The number must be positive.
    Bytes(u64),

    /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
    ///
    /// This variant represents powers of 1,000. Contrast with
    /// [`HumanReadableBinary`], which represents powers of 1,024.
    HumanReadableDecimal,

    /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
    ///
    /// This variant represents powers of 1,000. Contrast with
    /// [`HumanReadableBinary`], which represents powers of 1,024.
    HumanReadableBinary,
}

impl Default for BlockSize {
    fn default() -> Self {
        Self::Bytes(1024)
    }
}

impl From<&ArgMatches> for BlockSize {
    fn from(matches: &ArgMatches) -> Self {
        if matches.is_present(OPT_HUMAN_READABLE) {
            Self::HumanReadableBinary
        } else if matches.is_present(OPT_HUMAN_READABLE_2) {
            Self::HumanReadableDecimal
        } else {
            Self::default()
        }
    }
}

#[derive(Default)]
struct Options {
    show_local_fs: bool,
    show_all_fs: bool,
    show_listed_fs: bool,
    show_fs_type: bool,
    show_inode_instead: bool,
    block_size: BlockSize,
    fs_selector: FsSelector,
}

impl Options {
    /// Convert command-line arguments into [`Options`].
    fn from(matches: &ArgMatches) -> Self {
        Self {
            show_local_fs: matches.is_present(OPT_LOCAL),
            show_all_fs: matches.is_present(OPT_ALL),
            show_listed_fs: false,
            show_fs_type: matches.is_present(OPT_PRINT_TYPE),
            show_inode_instead: matches.is_present(OPT_INODES),
            block_size: BlockSize::from(matches),
            fs_selector: FsSelector::from(matches),
        }
    }
}

#[derive(Debug, Clone)]
struct Filesystem {
    mount_info: MountInfo,
    usage: FsUsage,
}

impl FsSelector {
    /// Convert command-line arguments into a [`FsSelector`].
    ///
    /// This function reads the include and exclude sets from
    /// [`ArgMatches`] and returns the corresponding [`FsSelector`]
    /// instance.
    fn from(matches: &ArgMatches) -> Self {
        let include = HashSet::from_iter(matches.values_of_lossy(OPT_TYPE).unwrap_or_default());
        let exclude = HashSet::from_iter(
            matches
                .values_of_lossy(OPT_EXCLUDE_TYPE)
                .unwrap_or_default(),
        );
        Self { include, exclude }
    }

    fn should_select(&self, fs_type: &str) -> bool {
        if self.exclude.contains(fs_type) {
            return false;
        }
        self.include.is_empty() || self.include.contains(fs_type)
    }
}

impl Filesystem {
    // TODO: resolve uuid in `mount_info.dev_name` if exists
    fn new(mount_info: MountInfo) -> Option<Self> {
        let _stat_path = if !mount_info.mount_dir.is_empty() {
            mount_info.mount_dir.clone()
        } else {
            #[cfg(unix)]
            {
                mount_info.dev_name.clone()
            }
            #[cfg(windows)]
            {
                // On windows, we expect the volume id
                mount_info.dev_id.clone()
            }
        };
        #[cfg(unix)]
        unsafe {
            let path = CString::new(_stat_path).unwrap();
            let mut statvfs = mem::zeroed();
            if statfs_fn(path.as_ptr(), &mut statvfs) < 0 {
                None
            } else {
                Some(Self {
                    mount_info,
                    usage: FsUsage::new(statvfs),
                })
            }
        }
        #[cfg(windows)]
        Some(Self {
            mount_info,
            usage: FsUsage::new(Path::new(&_stat_path)),
        })
    }
}

/// Whether to display the mount info given the inclusion settings.
fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool {
    // Don't show remote filesystems if `--local` has been given.
    if mi.remote && opt.show_local_fs {
        return false;
    }

    // Don't show pseudo filesystems unless `--all` has been given.
    if mi.dummy && !opt.show_all_fs && !opt.show_listed_fs {
        return false;
    }

    // Don't show filesystems if they have been explicitly excluded.
    if !opt.fs_selector.should_select(&mi.fs_type) {
        return false;
    }

    // Don't show filesystems other than the ones specified on the
    // command line, if any.
    if !paths.is_empty() && !paths.contains(&mi.mount_dir) {
        return false;
    }

    true
}

/// Whether the mount info in `m2` should be prioritized over `m1`.
///
/// The "lt" in the function name is in analogy to the
/// [`std::cmp::PartialOrd::lt`].
fn mount_info_lt(m1: &MountInfo, m2: &MountInfo) -> bool {
    // let "real" devices with '/' in the name win.
    if m1.dev_name.starts_with('/') && !m2.dev_name.starts_with('/') {
        return false;
    }

    let m1_nearer_root = m1.mount_dir.len() < m2.mount_dir.len();
    // With bind mounts, prefer items nearer the root of the source
    let m2_below_root = !m1.mount_root.is_empty()
        && !m2.mount_root.is_empty()
        && m1.mount_root.len() > m2.mount_root.len();
    // let points towards the root of the device win.
    if m1_nearer_root && !m2_below_root {
        return false;
    }

    // let an entry over-mounted on a new device win, but only when
    // matching an existing mnt point, to avoid problematic
    // replacement when given inaccurate mount lists, seen with some
    // chroot environments for example.
    if m1.dev_name != m2.dev_name && m1.mount_dir == m2.mount_dir {
        return false;
    }

    true
}

/// Whether to prioritize given mount info over all others on the same device.
///
/// This function decides whether the mount info `mi` is better than
/// all others in `previous` that mount the same device as `mi`.
fn is_best(previous: &[MountInfo], mi: &MountInfo) -> bool {
    for seen in previous {
        if seen.dev_id == mi.dev_id && mount_info_lt(mi, seen) {
            return false;
        }
    }
    true
}

/// Keep only the specified subset of [`MountInfo`] instances.
///
/// If `paths` is non-empty, this function excludes any [`MountInfo`]
/// that is not mounted at the specified path.
///
/// The `opt` argument specifies a variety of ways of excluding
/// [`MountInfo`] instances; see [`Options`] for more information.
///
/// Finally, if there are duplicate entries, the one with the shorter
/// path is kept.
fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Vec<MountInfo> {
    let mut result = vec![];
    for mi in vmi {
        // TODO The running time of the `is_best()` function is linear
        // in the length of `result`. That makes the running time of
        // this loop quadratic in the length of `vmi`. This could be
        // improved by a more efficient implementation of `is_best()`,
        // but `vmi` is probably not very long in practice.
        if is_included(&mi, paths, opt) && is_best(&result, &mi) {
            result.push(mi);
        }
    }
    result
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let matches = uu_app().get_matches_from(args);

    let paths: Vec<String> = matches
        .values_of(OPT_PATHS)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    #[cfg(windows)]
    {
        if matches.is_present(OPT_INODES) {
            println!("{}: doesn't support -i option", uucore::util_name());
            return Ok(());
        }
    }

    let opt = Options::from(&matches);

    let mounts = read_fs_list();
    let data: Vec<Row> = filter_mount_list(mounts, &paths, &opt)
        .into_iter()
        .filter_map(Filesystem::new)
        .filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)
        .map(Into::into)
        .collect();
    println!("{}", Header::new(&opt));
    for row in data {
        println!("{}", DisplayRow::new(row, &opt));
    }

    Ok(())
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(format_usage(USAGE))
        .setting(AppSettings::InferLongArgs)
        .arg(
            Arg::new(OPT_ALL)
                .short('a')
                .long("all")
                .help("include dummy file systems"),
        )
        .arg(
            Arg::new(OPT_BLOCKSIZE)
                .short('B')
                .long("block-size")
                .takes_value(true)
                .help(
                    "scale sizes by SIZE before printing them; e.g.\
                     '-BM' prints sizes in units of 1,048,576 bytes",
                ),
        )
        .arg(
            Arg::new(OPT_DIRECT)
                .long("direct")
                .help("show statistics for a file instead of mount point"),
        )
        .arg(
            Arg::new(OPT_TOTAL)
                .long("total")
                .help("produce a grand total"),
        )
        .arg(
            Arg::new(OPT_HUMAN_READABLE)
                .short('h')
                .long("human-readable")
                .conflicts_with(OPT_HUMAN_READABLE_2)
                .help("print sizes in human readable format (e.g., 1K 234M 2G)"),
        )
        .arg(
            Arg::new(OPT_HUMAN_READABLE_2)
                .short('H')
                .long("si")
                .conflicts_with(OPT_HUMAN_READABLE)
                .help("likewise, but use powers of 1000 not 1024"),
        )
        .arg(
            Arg::new(OPT_INODES)
                .short('i')
                .long("inodes")
                .help("list inode information instead of block usage"),
        )
        .arg(Arg::new(OPT_KILO).short('k').help("like --block-size=1K"))
        .arg(
            Arg::new(OPT_LOCAL)
                .short('l')
                .long("local")
                .help("limit listing to local file systems"),
        )
        .arg(
            Arg::new(OPT_NO_SYNC)
                .long("no-sync")
                .conflicts_with(OPT_SYNC)
                .help("do not invoke sync before getting usage info (default)"),
        )
        .arg(
            Arg::new(OPT_OUTPUT)
                .long("output")
                .takes_value(true)
                .use_delimiter(true)
                .possible_values(OUTPUT_FIELD_LIST)
                .default_missing_values(&OUTPUT_FIELD_LIST)
                .default_values(&["source", "size", "used", "avail", "pcent", "target"])
                .conflicts_with_all(&[OPT_INODES, OPT_PORTABILITY, OPT_PRINT_TYPE])
                .help(
                    "use the output format defined by FIELD_LIST,\
                     or print all fields if FIELD_LIST is omitted.",
                ),
        )
        .arg(
            Arg::new(OPT_PORTABILITY)
                .short('P')
                .long("portability")
                .help("use the POSIX output format"),
        )
        .arg(
            Arg::new(OPT_SYNC)
                .long("sync")
                .conflicts_with(OPT_NO_SYNC)
                .help("invoke sync before getting usage info"),
        )
        .arg(
            Arg::new(OPT_TYPE)
                .short('t')
                .long("type")
                .allow_invalid_utf8(true)
                .takes_value(true)
                .multiple_occurrences(true)
                .help("limit listing to file systems of type TYPE"),
        )
        .arg(
            Arg::new(OPT_PRINT_TYPE)
                .short('T')
                .long("print-type")
                .help("print file system type"),
        )
        .arg(
            Arg::new(OPT_EXCLUDE_TYPE)
                .short('x')
                .long("exclude-type")
                .takes_value(true)
                .use_delimiter(true)
                .help("limit listing to file systems not of type TYPE"),
        )
        .arg(Arg::new(OPT_PATHS).multiple_occurrences(true))
}
<commit_msg>df: correct links in BlockSize documentation<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Fangxu Hu <framlog@gmail.com>
// (c) Sylvestre Ledru <sylvestre@debian.org>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
// spell-checker:ignore itotal iused iavail ipcent pcent
mod table;

#[cfg(unix)]
use uucore::fsext::statfs_fn;
use uucore::fsext::{read_fs_list, FsUsage, MountInfo};
use uucore::{error::UResult, format_usage};

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};

use std::collections::HashSet;
#[cfg(unix)]
use std::ffi::CString;
use std::iter::FromIterator;
#[cfg(unix)]
use std::mem;

#[cfg(windows)]
use std::path::Path;

use crate::table::{DisplayRow, Header, Row};

static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
                      or all file systems by default.";
const USAGE: &str = "{} [OPTION]... [FILE]...";

static OPT_ALL: &str = "all";
static OPT_BLOCKSIZE: &str = "blocksize";
static OPT_DIRECT: &str = "direct";
static OPT_TOTAL: &str = "total";
static OPT_HUMAN_READABLE: &str = "human-readable";
static OPT_HUMAN_READABLE_2: &str = "human-readable-2";
static OPT_INODES: &str = "inodes";
static OPT_KILO: &str = "kilo";
static OPT_LOCAL: &str = "local";
static OPT_NO_SYNC: &str = "no-sync";
static OPT_OUTPUT: &str = "output";
static OPT_PATHS: &str = "paths";
static OPT_PORTABILITY: &str = "portability";
static OPT_SYNC: &str = "sync";
static OPT_TYPE: &str = "type";
static OPT_PRINT_TYPE: &str = "print-type";
static OPT_EXCLUDE_TYPE: &str = "exclude-type";
static OUTPUT_FIELD_LIST: [&str; 12] = [
    "source", "fstype", "itotal", "iused", "iavail", "ipcent", "size", "used", "avail", "pcent",
    "file", "target",
];

/// Store names of file systems as a selector.
/// Note: `exclude` takes priority over `include`.
#[derive(Default)]
struct FsSelector {
    include: HashSet<String>,
    exclude: HashSet<String>,
}

/// A block size to use in condensing the display of a large number of bytes.
///
/// The [`BlockSize::Bytes`] variant represents a static block
/// size. The [`BlockSize::HumanReadableDecimal`] and
/// [`BlockSize::HumanReadableBinary`] variants represent dynamic
/// block sizes: as the number of bytes increases, the divisor
/// increases as well (for example, from 1 to 1,000 to 1,000,000 and
/// so on in the case of [`BlockSize::HumanReadableDecimal`]).
///
/// The default variant is `Bytes(1024)`.
enum BlockSize {
    /// A fixed number of bytes.
    ///
    /// The number must be positive.
    Bytes(u64),

    /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
    ///
    /// This variant represents powers of 1,000. Contrast with
    /// [`BlockSize::HumanReadableBinary`], which represents powers of
    /// 1,024.
    HumanReadableDecimal,

    /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
    ///
    /// This variant represents powers of 1,024. Contrast with
    /// [`BlockSize::HumanReadableDecimal`], which represents powers
    /// of 1,000.
    HumanReadableBinary,
}

impl Default for BlockSize {
    fn default() -> Self {
        Self::Bytes(1024)
    }
}

impl From<&ArgMatches> for BlockSize {
    fn from(matches: &ArgMatches) -> Self {
        if matches.is_present(OPT_HUMAN_READABLE) {
            Self::HumanReadableBinary
        } else if matches.is_present(OPT_HUMAN_READABLE_2) {
            Self::HumanReadableDecimal
        } else {
            Self::default()
        }
    }
}

#[derive(Default)]
struct Options {
    show_local_fs: bool,
    show_all_fs: bool,
    show_listed_fs: bool,
    show_fs_type: bool,
    show_inode_instead: bool,
    block_size: BlockSize,
    fs_selector: FsSelector,
}

impl Options {
    /// Convert command-line arguments into [`Options`].
    fn from(matches: &ArgMatches) -> Self {
        Self {
            show_local_fs: matches.is_present(OPT_LOCAL),
            show_all_fs: matches.is_present(OPT_ALL),
            show_listed_fs: false,
            show_fs_type: matches.is_present(OPT_PRINT_TYPE),
            show_inode_instead: matches.is_present(OPT_INODES),
            block_size: BlockSize::from(matches),
            fs_selector: FsSelector::from(matches),
        }
    }
}

#[derive(Debug, Clone)]
struct Filesystem {
    mount_info: MountInfo,
    usage: FsUsage,
}

impl FsSelector {
    /// Convert command-line arguments into a [`FsSelector`].
    ///
    /// This function reads the include and exclude sets from
    /// [`ArgMatches`] and returns the corresponding [`FsSelector`]
    /// instance.
    fn from(matches: &ArgMatches) -> Self {
        let include = HashSet::from_iter(matches.values_of_lossy(OPT_TYPE).unwrap_or_default());
        let exclude = HashSet::from_iter(
            matches
                .values_of_lossy(OPT_EXCLUDE_TYPE)
                .unwrap_or_default(),
        );
        Self { include, exclude }
    }

    fn should_select(&self, fs_type: &str) -> bool {
        if self.exclude.contains(fs_type) {
            return false;
        }
        self.include.is_empty() || self.include.contains(fs_type)
    }
}

impl Filesystem {
    // TODO: resolve uuid in `mount_info.dev_name` if exists
    fn new(mount_info: MountInfo) -> Option<Self> {
        let _stat_path = if !mount_info.mount_dir.is_empty() {
            mount_info.mount_dir.clone()
        } else {
            #[cfg(unix)]
            {
                mount_info.dev_name.clone()
            }
            #[cfg(windows)]
            {
                // On windows, we expect the volume id
                mount_info.dev_id.clone()
            }
        };
        #[cfg(unix)]
        unsafe {
            let path = CString::new(_stat_path).unwrap();
            let mut statvfs = mem::zeroed();
            if statfs_fn(path.as_ptr(), &mut statvfs) < 0 {
                None
            } else {
                Some(Self {
                    mount_info,
                    usage: FsUsage::new(statvfs),
                })
            }
        }
        #[cfg(windows)]
        Some(Self {
            mount_info,
            usage: FsUsage::new(Path::new(&_stat_path)),
        })
    }
}

/// Whether to display the mount info given the inclusion settings.
fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool {
    // Don't show remote filesystems if `--local` has been given.
    if mi.remote && opt.show_local_fs {
        return false;
    }

    // Don't show pseudo filesystems unless `--all` has been given.
    if mi.dummy && !opt.show_all_fs && !opt.show_listed_fs {
        return false;
    }

    // Don't show filesystems if they have been explicitly excluded.
    if !opt.fs_selector.should_select(&mi.fs_type) {
        return false;
    }

    // Don't show filesystems other than the ones specified on the
    // command line, if any.
    if !paths.is_empty() && !paths.contains(&mi.mount_dir) {
        return false;
    }

    true
}

/// Whether the mount info in `m2` should be prioritized over `m1`.
///
/// The "lt" in the function name is in analogy to the
/// [`std::cmp::PartialOrd::lt`].
fn mount_info_lt(m1: &MountInfo, m2: &MountInfo) -> bool {
    // let "real" devices with '/' in the name win.
    if m1.dev_name.starts_with('/') && !m2.dev_name.starts_with('/') {
        return false;
    }

    let m1_nearer_root = m1.mount_dir.len() < m2.mount_dir.len();
    // With bind mounts, prefer items nearer the root of the source
    let m2_below_root = !m1.mount_root.is_empty()
        && !m2.mount_root.is_empty()
        && m1.mount_root.len() > m2.mount_root.len();
    // let points towards the root of the device win.
    if m1_nearer_root && !m2_below_root {
        return false;
    }

    // let an entry over-mounted on a new device win, but only when
    // matching an existing mnt point, to avoid problematic
    // replacement when given inaccurate mount lists, seen with some
    // chroot environments for example.
    if m1.dev_name != m2.dev_name && m1.mount_dir == m2.mount_dir {
        return false;
    }

    true
}

/// Whether to prioritize given mount info over all others on the same device.
///
/// This function decides whether the mount info `mi` is better than
/// all others in `previous` that mount the same device as `mi`.
fn is_best(previous: &[MountInfo], mi: &MountInfo) -> bool {
    for seen in previous {
        if seen.dev_id == mi.dev_id && mount_info_lt(mi, seen) {
            return false;
        }
    }
    true
}

/// Keep only the specified subset of [`MountInfo`] instances.
///
/// If `paths` is non-empty, this function excludes any [`MountInfo`]
/// that is not mounted at the specified path.
///
/// The `opt` argument specifies a variety of ways of excluding
/// [`MountInfo`] instances; see [`Options`] for more information.
///
/// Finally, if there are duplicate entries, the one with the shorter
/// path is kept.
fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Vec<MountInfo> {
    let mut result = vec![];
    for mi in vmi {
        // TODO The running time of the `is_best()` function is linear
        // in the length of `result`. That makes the running time of
        // this loop quadratic in the length of `vmi`. This could be
        // improved by a more efficient implementation of `is_best()`,
        // but `vmi` is probably not very long in practice.
        if is_included(&mi, paths, opt) && is_best(&result, &mi) {
            result.push(mi);
        }
    }
    result
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let matches = uu_app().get_matches_from(args);

    let paths: Vec<String> = matches
        .values_of(OPT_PATHS)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    #[cfg(windows)]
    {
        if matches.is_present(OPT_INODES) {
            println!("{}: doesn't support -i option", uucore::util_name());
            return Ok(());
        }
    }

    let opt = Options::from(&matches);

    let mounts = read_fs_list();
    let data: Vec<Row> = filter_mount_list(mounts, &paths, &opt)
        .into_iter()
        .filter_map(Filesystem::new)
        .filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)
        .map(Into::into)
        .collect();
    println!("{}", Header::new(&opt));
    for row in data {
        println!("{}", DisplayRow::new(row, &opt));
    }

    Ok(())
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(format_usage(USAGE))
        .setting(AppSettings::InferLongArgs)
        .arg(
            Arg::new(OPT_ALL)
                .short('a')
                .long("all")
                .help("include dummy file systems"),
        )
        .arg(
            Arg::new(OPT_BLOCKSIZE)
                .short('B')
                .long("block-size")
                .takes_value(true)
                .help(
                    "scale sizes by SIZE before printing them; e.g.\
                     '-BM' prints sizes in units of 1,048,576 bytes",
                ),
        )
        .arg(
            Arg::new(OPT_DIRECT)
                .long("direct")
                .help("show statistics for a file instead of mount point"),
        )
        .arg(
            Arg::new(OPT_TOTAL)
                .long("total")
                .help("produce a grand total"),
        )
        .arg(
            Arg::new(OPT_HUMAN_READABLE)
                .short('h')
                .long("human-readable")
                .conflicts_with(OPT_HUMAN_READABLE_2)
                .help("print sizes in human readable format (e.g., 1K 234M 2G)"),
        )
        .arg(
            Arg::new(OPT_HUMAN_READABLE_2)
                .short('H')
                .long("si")
                .conflicts_with(OPT_HUMAN_READABLE)
                .help("likewise, but use powers of 1000 not 1024"),
        )
        .arg(
            Arg::new(OPT_INODES)
                .short('i')
                .long("inodes")
                .help("list inode information instead of block usage"),
        )
        .arg(Arg::new(OPT_KILO).short('k').help("like --block-size=1K"))
        .arg(
            Arg::new(OPT_LOCAL)
                .short('l')
                .long("local")
                .help("limit listing to local file systems"),
        )
        .arg(
            Arg::new(OPT_NO_SYNC)
                .long("no-sync")
                .conflicts_with(OPT_SYNC)
                .help("do not invoke sync before getting usage info (default)"),
        )
        .arg(
            Arg::new(OPT_OUTPUT)
                .long("output")
                .takes_value(true)
                .use_delimiter(true)
                .possible_values(OUTPUT_FIELD_LIST)
                .default_missing_values(&OUTPUT_FIELD_LIST)
                .default_values(&["source", "size", "used", "avail", "pcent", "target"])
                .conflicts_with_all(&[OPT_INODES, OPT_PORTABILITY, OPT_PRINT_TYPE])
                .help(
                    "use the output format defined by FIELD_LIST,\
                     or print all fields if FIELD_LIST is omitted.",
                ),
        )
        .arg(
            Arg::new(OPT_PORTABILITY)
                .short('P')
                .long("portability")
                .help("use the POSIX output format"),
        )
        .arg(
            Arg::new(OPT_SYNC)
                .long("sync")
                .conflicts_with(OPT_NO_SYNC)
                .help("invoke sync before getting usage info"),
        )
        .arg(
            Arg::new(OPT_TYPE)
                .short('t')
                .long("type")
                .allow_invalid_utf8(true)
                .takes_value(true)
                .multiple_occurrences(true)
                .help("limit listing to file systems of type TYPE"),
        )
        .arg(
            Arg::new(OPT_PRINT_TYPE)
                .short('T')
                .long("print-type")
                .help("print file system type"),
        )
        .arg(
            Arg::new(OPT_EXCLUDE_TYPE)
                .short('x')
                .long("exclude-type")
                .takes_value(true)
                .use_delimiter(true)
                .help("limit listing to file systems not of type TYPE"),
        )
        .arg(Arg::new(OPT_PATHS).multiple_occurrences(true))
}
<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use glib_ffi;
use translate::*;
use std::borrow::{Borrow, Cow, ToOwned};
use std::cmp::{PartialEq, Eq};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Deref;
use std::slice;

/// Describes `Variant` types.
///
/// The `Variant` type system (based on the D-Bus one) describes types with
/// "type strings". `VariantType` is an owned immutable type string (you can
/// think of it as a `Box<str>` statically guaranteed to be a valid type
/// string), `&VariantTy` is a borrowed one (like `&str`).
pub struct VariantType {
    // GVariantType* essentially is a char*, that always is valid UTF-8 but
    // isn't NUL-terminated.
    ptr: *mut glib_ffi::GVariantType,
    // We query the length on creation assuming it's cheap (because type strings
    // are short) and likely to happen anyway.
    len: usize,
}

impl VariantType {
    /// Tries to create a `VariantType` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<VariantType, ()> {
        VariantTy::new(type_string).map(ToOwned::to_owned)
    }
}

unsafe impl Send for VariantType { }
unsafe impl Sync for VariantType { }

impl Drop for VariantType {
    fn drop(&mut self) {
        unsafe { glib_ffi::g_variant_type_free(self.ptr) }
    }
}

impl Borrow<VariantTy> for VariantType {
    fn borrow(&self) -> &VariantTy {
        &self
    }
}

impl Clone for VariantType {
    fn clone(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.ptr),
                len: self.len,
            }
        }
    }
}

impl Deref for VariantType {
    type Target = VariantTy;
    fn deref(&self) -> &VariantTy {
        unsafe {
            mem::transmute(slice::from_raw_parts(self.ptr as *const u8, self.len))
        }
    }
}

impl fmt::Debug for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <VariantTy as fmt::Debug>::fmt(self, f)
    }
}

impl fmt::Display for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl Hash for VariantType {
    fn hash<H: Hasher>(&self, state: &mut H) {
        <VariantTy as Hash>::hash(self, state)
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for VariantType {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Owned(self)
    }
}

#[doc(hidden)]
impl<'a> ToGlibPtrMut<'a, *mut glib_ffi::GVariantType> for VariantType {
    type Storage = &'a mut Self;

    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut glib_ffi::GVariantType, Self> {
        StashMut(self.ptr, self)
    }
}

/// Describes `Variant` types.
///
/// This is a borrowed counterpart of [`VariantType`](struct.VariantType.html).
/// Essentially it's a `str` statically guaranteed to be a valid type string.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct VariantTy {
    inner: str,
}

impl VariantTy {
    /// Tries to create a `&VariantTy` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<&VariantTy, ()> {
        let ptr = type_string.as_ptr();
        let limit = ptr as usize + type_string.len();
        let mut end = 0_usize;
        unsafe {
            let ok = from_glib(glib_ffi::g_variant_type_string_scan(ptr as *const _,
                limit as *const _, &mut end as *mut usize as *mut _));
            if ok && end == limit {
                Ok(mem::transmute(type_string))
            } else {
                Err(())
            }
        }
    }

    /// Converts a type string into `&VariantTy` without any checks.
    pub unsafe fn from_str_unchecked(type_string: &str) -> &VariantTy {
        mem::transmute(type_string)
    }

    /// Creates `&VariantTy` with a wildcard lifetime from a `GVariantType`
    /// pointer.
    #[doc(hidden)]
    pub unsafe fn from_ptr<'a>(ptr: *const glib_ffi::GVariantType) -> &'a VariantTy {
        let len = glib_ffi::g_variant_type_get_string_length(ptr) as usize;
        mem::transmute(slice::from_raw_parts(ptr as *const u8, len))
    }

    /// Returns a `GVariantType` pointer.
    #[doc(hidden)]
    pub fn as_ptr(&self) -> *const glib_ffi::GVariantType {
        self.inner.as_ptr() as *const _
    }

    /// Converts to a string slice.
    pub fn to_str(&self) -> &str {
        &self.inner
    }
}

unsafe impl Sync for VariantTy { }

#[doc(hidden)]
impl<'a> ToGlibPtr<'a, *const glib_ffi::GVariantType> for VariantTy {
    type Storage = &'a Self;

    fn to_glib_none(&'a self) -> Stash<'a, *const glib_ffi::GVariantType, Self> {
        Stash(self.as_ptr(), self)
    }
}

impl fmt::Display for VariantTy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for &'a VariantTy {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Borrowed(self)
    }
}

impl ToOwned for VariantTy {
    type Owned = VariantType;

    fn to_owned(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.as_ptr()),
                len: self.inner.len(),
            }
        }
    }
}

impl PartialEq for VariantType {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        <VariantTy as PartialEq>::eq(self, other)
    }
}

macro_rules! impl_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }
    }
}

impl_eq!(VariantType, VariantTy);
impl_eq!(VariantType, &'a VariantTy);
impl_eq!(VariantType, Cow<'a, VariantTy>);
impl_eq!(&'a VariantTy, Cow<'b, VariantTy>);

macro_rules! impl_str_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                self.to_str().eq(&other[..])
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                self[..].eq(other.to_str())
            }
        }
    }
}

impl_str_eq!(VariantTy, str);
impl_str_eq!(VariantTy, &'a str);
impl_str_eq!(&'a VariantTy, str);
impl_str_eq!(VariantTy, String);
impl_str_eq!(&'a VariantTy, String);
impl_str_eq!(VariantType, str);
impl_str_eq!(VariantType, &'a str);
impl_str_eq!(VariantType, String);

impl Eq for VariantType { }

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

    unsafe fn equal<T, U>(ptr1: *const T, ptr2: *const U) -> bool {
        from_glib(glib_ffi::g_variant_type_equal(ptr1 as *const _, ptr2 as *const _))
    }

    #[test]
    fn new() {
        let ty = VariantTy::new("((iii)s)").unwrap();
        unsafe {
            assert!(equal(ty.as_ptr(), b"((iii)s)\0" as *const u8));
        }
    }

    #[test]
    fn new_empty() {
        assert!(VariantTy::new("").is_err());
    }

    #[test]
    fn new_with_nul() {
        assert!(VariantTy::new("((iii\0)s)").is_err());
    }

    #[test]
    fn new_too_short() {
        assert!(VariantTy::new("((iii").is_err());
    }

    #[test]
    fn new_too_long() {
        assert!(VariantTy::new("(iii)s").is_err());
    }

    #[test]
    fn eq() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)s)").unwrap();
        assert_eq!(ty1, ty2);
        assert_eq!(ty1, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn ne() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)o)").unwrap();
        assert!(ty1 != ty2);
        assert!(ty1 != "((iii)o)");
        unsafe {
            assert!(!equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn from_bytes() {
        unsafe {
            let ty = VariantTy::from_ptr(b"((iii)s)" as *const u8 as *const _);
            assert_eq!(ty, "((iii)s)");
            assert!(equal(ty.as_ptr(), "((iii)s)".as_ptr()));
        }
    }

    #[test]
    fn to_owned() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = ty1.to_owned();
        assert_eq!(ty1, ty2);
        assert_eq!(ty2, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }
}
<commit_msg>Implement FromGlibPtr for VariantType<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use glib_ffi;
use translate::*;
use std::borrow::{Borrow, Cow, ToOwned};
use std::cmp::{PartialEq, Eq};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Deref;
use std::slice;

/// Describes `Variant` types.
///
/// The `Variant` type system (based on the D-Bus one) describes types with
/// "type strings". `VariantType` is an owned immutable type string (you can
/// think of it as a `Box<str>` statically guaranteed to be a valid type
/// string), `&VariantTy` is a borrowed one (like `&str`).
pub struct VariantType {
    // GVariantType* essentially is a char*, that always is valid UTF-8 but
    // isn't NUL-terminated.
    ptr: *mut glib_ffi::GVariantType,
    // We query the length on creation assuming it's cheap (because type strings
    // are short) and likely to happen anyway.
    len: usize,
}

impl VariantType {
    /// Tries to create a `VariantType` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<VariantType, ()> {
        VariantTy::new(type_string).map(ToOwned::to_owned)
    }
}

unsafe impl Send for VariantType { }
unsafe impl Sync for VariantType { }

impl Drop for VariantType {
    fn drop(&mut self) {
        unsafe { glib_ffi::g_variant_type_free(self.ptr) }
    }
}

impl Borrow<VariantTy> for VariantType {
    fn borrow(&self) -> &VariantTy {
        &self
    }
}

impl Clone for VariantType {
    fn clone(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.ptr),
                len: self.len,
            }
        }
    }
}

impl Deref for VariantType {
    type Target = VariantTy;
    fn deref(&self) -> &VariantTy {
        unsafe {
            mem::transmute(slice::from_raw_parts(self.ptr as *const u8, self.len))
        }
    }
}

impl fmt::Debug for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <VariantTy as fmt::Debug>::fmt(self, f)
    }
}

impl fmt::Display for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl Hash for VariantType {
    fn hash<H: Hasher>(&self, state: &mut H) {
        <VariantTy as Hash>::hash(self, state)
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for VariantType {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Owned(self)
    }
}

#[doc(hidden)]
impl<'a> ToGlibPtrMut<'a, *mut glib_ffi::GVariantType> for VariantType {
    type Storage = &'a mut Self;

    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut glib_ffi::GVariantType, Self> {
        StashMut(self.ptr, self)
    }
}

#[doc(hidden)]
impl FromGlibPtr<*const glib_ffi::GVariantType> for VariantType {
    unsafe fn from_glib_none(ptr: *const glib_ffi::GVariantType) -> VariantType {
        VariantTy::from_ptr(ptr).to_owned()
    }

    unsafe fn from_glib_full(ptr: *const glib_ffi::GVariantType) -> VariantType {
        // Don't assume ownership of a const pointer.
        // A transfer: full annotation on a `const GVariantType*` is likely a bug.
        VariantTy::from_ptr(ptr).to_owned()
    }
}

/// Describes `Variant` types.
///
/// This is a borrowed counterpart of [`VariantType`](struct.VariantType.html).
/// Essentially it's a `str` statically guaranteed to be a valid type string.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct VariantTy {
    inner: str,
}

impl VariantTy {
    /// Tries to create a `&VariantTy` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<&VariantTy, ()> {
        let ptr = type_string.as_ptr();
        let limit = ptr as usize + type_string.len();
        let mut end = 0_usize;
        unsafe {
            let ok = from_glib(glib_ffi::g_variant_type_string_scan(ptr as *const _,
                limit as *const _, &mut end as *mut usize as *mut _));
            if ok && end == limit {
                Ok(mem::transmute(type_string))
            } else {
                Err(())
            }
        }
    }

    /// Converts a type string into `&VariantTy` without any checks.
    pub unsafe fn from_str_unchecked(type_string: &str) -> &VariantTy {
        mem::transmute(type_string)
    }

    /// Creates `&VariantTy` with a wildcard lifetime from a `GVariantType`
    /// pointer.
    #[doc(hidden)]
    pub unsafe fn from_ptr<'a>(ptr: *const glib_ffi::GVariantType) -> &'a VariantTy {
        let len = glib_ffi::g_variant_type_get_string_length(ptr) as usize;
        mem::transmute(slice::from_raw_parts(ptr as *const u8, len))
    }

    /// Returns a `GVariantType` pointer.
    #[doc(hidden)]
    pub fn as_ptr(&self) -> *const glib_ffi::GVariantType {
        self.inner.as_ptr() as *const _
    }

    /// Converts to a string slice.
    pub fn to_str(&self) -> &str {
        &self.inner
    }
}

unsafe impl Sync for VariantTy { }

#[doc(hidden)]
impl<'a> ToGlibPtr<'a, *const glib_ffi::GVariantType> for VariantTy {
    type Storage = &'a Self;

    fn to_glib_none(&'a self) -> Stash<'a, *const glib_ffi::GVariantType, Self> {
        Stash(self.as_ptr(), self)
    }
}

impl fmt::Display for VariantTy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for &'a VariantTy {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Borrowed(self)
    }
}

impl ToOwned for VariantTy {
    type Owned = VariantType;

    fn to_owned(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.as_ptr()),
                len: self.inner.len(),
            }
        }
    }
}

impl PartialEq for VariantType {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        <VariantTy as PartialEq>::eq(self, other)
    }
}

macro_rules! impl_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }
    }
}

impl_eq!(VariantType, VariantTy);
impl_eq!(VariantType, &'a VariantTy);
impl_eq!(VariantType, Cow<'a, VariantTy>);
impl_eq!(&'a VariantTy, Cow<'b, VariantTy>);

macro_rules! impl_str_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                self.to_str().eq(&other[..])
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                self[..].eq(other.to_str())
            }
        }
    }
}

impl_str_eq!(VariantTy, str);
impl_str_eq!(VariantTy, &'a str);
impl_str_eq!(&'a VariantTy, str);
impl_str_eq!(VariantTy, String);
impl_str_eq!(&'a VariantTy, String);
impl_str_eq!(VariantType, str);
impl_str_eq!(VariantType, &'a str);
impl_str_eq!(VariantType, String);

impl Eq for VariantType { }

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

    unsafe fn equal<T, U>(ptr1: *const T, ptr2: *const U) -> bool {
        from_glib(glib_ffi::g_variant_type_equal(ptr1 as *const _, ptr2 as *const _))
    }

    #[test]
    fn new() {
        let ty = VariantTy::new("((iii)s)").unwrap();
        unsafe {
            assert!(equal(ty.as_ptr(), b"((iii)s)\0" as *const u8));
        }
    }

    #[test]
    fn new_empty() {
        assert!(VariantTy::new("").is_err());
    }

    #[test]
    fn new_with_nul() {
        assert!(VariantTy::new("((iii\0)s)").is_err());
    }

    #[test]
    fn new_too_short() {
        assert!(VariantTy::new("((iii").is_err());
    }

    #[test]
    fn new_too_long() {
        assert!(VariantTy::new("(iii)s").is_err());
    }

    #[test]
    fn eq() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)s)").unwrap();
        assert_eq!(ty1, ty2);
        assert_eq!(ty1, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn ne() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)o)").unwrap();
        assert!(ty1 != ty2);
        assert!(ty1 != "((iii)o)");
        unsafe {
            assert!(!equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn from_bytes() {
        unsafe {
            let ty = VariantTy::from_ptr(b"((iii)s)" as *const u8 as *const _);
            assert_eq!(ty, "((iii)s)");
            assert!(equal(ty.as_ptr(), "((iii)s)".as_ptr()));
        }
    }

    #[test]
    fn to_owned() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = ty1.to_owned();
        assert_eq!(ty1, ty2);
        assert_eq!(ty2, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Write;
use std::collections::HashMap;
use std::collections::hash_map::Entry::Occupied;
use std::collections::hash_map::Entry::Vacant;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::rc::Rc;

extern crate tools;
use tools::find_source_file;
use tools::file_format::analysis::{read_analysis, read_target, AnalysisKind};
use tools::config;

extern crate rustc_serialize;
use rustc_serialize::json::{Json, ToJson};

#[derive(Debug)]
struct SearchResult {
    lineno: u32,
    bounds: (u32, u32),
    line: Rc<String>,
    context: Rc<String>,
    contextsym: Rc<String>,
}

impl ToJson for SearchResult {
    fn to_json(&self) -> Json {
        let (st, en) = self.bounds;
        let bounds = vec![st, en];

        let mut obj = BTreeMap::new();
        obj.insert("lno".to_string(), self.lineno.to_json());
        obj.insert("bounds".to_string(), bounds.to_json());
        obj.insert("line".to_string(), self.line.to_json());
        obj.insert("context".to_string(), self.context.to_json());
        obj.insert("contextsym".to_string(), self.contextsym.to_json());
        Json::Object(obj)
    }
}

fn split_scopes(id: &str) -> Vec<String> {
    let mut result = Vec::new();
    let mut start = 0;
    let mut argument_nesting = 0;
    for (index, m) in id.match_indices(|c| c == ':' || c == '<' || c == '>') {
        if m == ":" && argument_nesting == 0 {
            if start != index {
                result.push(id[start .. index].to_owned());
                start = index + 1;
            } else {
                start = index + 1;
            }
        } else if m == "<" {
            argument_nesting += 1;
        } else if m == ">" {
            argument_nesting -= 1;
        }
    }
    result.push(id[start ..].to_owned());
    return result;
}

struct StringIntern {
    set: HashMap<Rc<String>, ()>
}

impl StringIntern {
    fn new() -> StringIntern {
        StringIntern {set: HashMap::new()}
    }

    fn add(&mut self, s: String) -> Rc<String> {
        let new_rc = Rc::new(s);
        match self.set.entry(new_rc) {
            Occupied(o) => Rc::clone(&o.key()),
            Vacant(v) => {
                let rval = Rc::clone(&v.key());
                v.insert(());
                rval
            }
        }
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();

    let cfg = config::load(&args[1], false);

    let tree_name = &args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let filenames_file = &args[3];

    let file_paths: Vec<String> = BufReader::new(File::open(filenames_file).unwrap())
        .lines().map(|x| x.unwrap()).collect();
    let output_file = format!("{}/crossref", tree_config.paths.index_path);
    let jump_file = format!("{}/jumps", tree_config.paths.index_path);
    let id_file = format!("{}/identifiers", tree_config.paths.index_path);

    let mut strings = StringIntern::new();
    let empty_string = strings.add("".to_string());

    let mut table = BTreeMap::new();
    let mut pretty_table = HashMap::new();
    let mut id_table = BTreeMap::new();
    let mut jumps = Vec::new();

    for path in &file_paths {
        print!("File {}\n", path);

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &read_target);

        let source_fname = find_source_file(path, &tree_config.paths.files_path, &tree_config.paths.objdir_path);
        let source_file = match File::open(source_fname) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open source file");
                continue;
            },
        };
        let reader = BufReader::new(&source_file);
        let lines: Vec<_> = reader.lines().map(
            |l| match l {
                Ok(line) => {
                    let line_cut = line.trim_right();
                    let len = line_cut.len();
                    let line_cut = line_cut.trim_left();
                    let offset = (len - line_cut.len()) as u32;
                    let mut buf = String::new();
                    let mut i = 0;
                    for c in line_cut.chars() {
                        buf.push(c);
                        i += 1;
                        if i > 100 {
                            break;
                        }
                    };
                    (strings.add(buf), offset)
                },
                Err(_) => (Rc::clone(&empty_string), 0)
            }).collect();

        for datum in analysis {
            for piece in datum.data {
                let sym = strings.add(piece.sym.to_owned());
                let t1 = table.entry(Rc::clone(&sym)).or_insert(BTreeMap::new());
                let t2 = t1.entry(piece.kind).or_insert(BTreeMap::new());
                let p: &str = &path;
                let t3 = t2.entry(p).or_insert(Vec::new());
                let lineno = (datum.loc.lineno - 1) as usize;
                if lineno >= lines.len() {
                    print!("Bad line number in file {} (line {})\n", path, lineno);
                    continue;
                }
                let (line, offset) = lines[lineno].clone();
                t3.push(SearchResult {
                    lineno: datum.loc.lineno,
                    bounds: (datum.loc.col_start - offset, datum.loc.col_end - offset),
                    line: line,
                    context: strings.add(piece.context),
                    contextsym: strings.add(piece.contextsym),
                });

                pretty_table.insert(Rc::clone(&sym), piece.pretty.to_owned());

                let ch = piece.sym.chars().nth(0).unwrap();
                if !(ch >= '0' && ch <= '9') && !piece.sym.contains(' ') {
                    let t1 = id_table.entry(piece.pretty.to_owned()).or_insert(BTreeSet::new());
                    t1.insert(sym);
                }
            }
        }
    }

    let mut outputf = File::create(output_file).unwrap();

    for (id, id_data) in table {
        let mut kindmap = BTreeMap::new();
        for (kind, kind_data) in &id_data {
            let mut result = Vec::new();
            for (path, results) in kind_data {
                let mut obj = BTreeMap::new();
                obj.insert("path".to_string(), path.to_json());
                obj.insert("lines".to_string(), results.to_json());
                result.push(Json::Object(obj));
            }
            let kindstr = match *kind {
                AnalysisKind::Use => "Uses",
                AnalysisKind::Def => "Definitions",
                AnalysisKind::Assign => "Assignments",
                AnalysisKind::Decl => "Declarations",
                AnalysisKind::Idl => "IDL",
            };
            kindmap.insert(kindstr.to_string(), Json::Array(result));
        }
        let kindmap = Json::Object(kindmap);

        let _ = outputf.write_all(format!("{}\n{}\n", id, kindmap.to_string()).as_bytes());

        if id_data.contains_key(&AnalysisKind::Def) {
            let defs = id_data.get(&AnalysisKind::Def).unwrap();
            if defs.len() == 1 {
                for (path, results) in defs {
                    if results.len() == 1 {
                        let mut v = Vec::new();
                        v.push(id.to_json());
                        v.push(path.to_json());
                        v.push(results[0].lineno.to_json());
                        let pretty = pretty_table.get(&id).unwrap();
                        v.push(pretty.to_json());
                        jumps.push(Json::Array(v));
                    }
                }
            }
        }
    }

    let mut jumpf = File::create(jump_file).unwrap();
    for jump in jumps {
        let _ = jumpf.write_all((jump.to_string() + "\n").as_bytes());
    }

    let mut idf = File::create(id_file).unwrap();
    for (id, syms) in id_table {
        for sym in syms {
            let components = split_scopes(&id);
            for i in 0..components.len() {
                let sub = &components[i..components.len()];
                let sub = sub.join("::");

                if !sub.is_empty() {
                    let line = format!("{} {}\n", sub, sym);
                    let _ = idf.write_all(line.as_bytes());
                }
            }
        }
    }
}
<commit_msg>Bug 1415700, part 6 - Intern piece.pretty.<commit_after>use std::fs::File;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Write;
use std::collections::HashMap;
use std::collections::hash_map::Entry::Occupied;
use std::collections::hash_map::Entry::Vacant;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::rc::Rc;

extern crate tools;
use tools::find_source_file;
use tools::file_format::analysis::{read_analysis, read_target, AnalysisKind};
use tools::config;

extern crate rustc_serialize;
use rustc_serialize::json::{Json, ToJson};

#[derive(Debug)]
struct SearchResult {
    lineno: u32,
    bounds: (u32, u32),
    line: Rc<String>,
    context: Rc<String>,
    contextsym: Rc<String>,
}

impl ToJson for SearchResult {
    fn to_json(&self) -> Json {
        let (st, en) = self.bounds;
        let bounds = vec![st, en];

        let mut obj = BTreeMap::new();
        obj.insert("lno".to_string(), self.lineno.to_json());
        obj.insert("bounds".to_string(), bounds.to_json());
        obj.insert("line".to_string(), self.line.to_json());
        obj.insert("context".to_string(), self.context.to_json());
        obj.insert("contextsym".to_string(), self.contextsym.to_json());
        Json::Object(obj)
    }
}

fn split_scopes(id: &str) -> Vec<String> {
    let mut result = Vec::new();
    let mut start = 0;
    let mut argument_nesting = 0;
    for (index, m) in id.match_indices(|c| c == ':' || c == '<' || c == '>') {
        if m == ":" && argument_nesting == 0 {
            if start != index {
                result.push(id[start .. index].to_owned());
                start = index + 1;
            } else {
                start = index + 1;
            }
        } else if m == "<" {
            argument_nesting += 1;
        } else if m == ">" {
            argument_nesting -= 1;
        }
    }
    result.push(id[start ..].to_owned());
    return result;
}

struct StringIntern {
    set: HashMap<Rc<String>, ()>
}

impl StringIntern {
    fn new() -> StringIntern {
        StringIntern {set: HashMap::new()}
    }

    fn add(&mut self, s: String) -> Rc<String> {
        let new_rc = Rc::new(s);
        match self.set.entry(new_rc) {
            Occupied(o) => Rc::clone(&o.key()),
            Vacant(v) => {
                let rval = Rc::clone(&v.key());
                v.insert(());
                rval
            }
        }
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();

    let cfg = config::load(&args[1], false);

    let tree_name = &args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let filenames_file = &args[3];

    let file_paths: Vec<String> = BufReader::new(File::open(filenames_file).unwrap())
        .lines().map(|x| x.unwrap()).collect();
    let output_file = format!("{}/crossref", tree_config.paths.index_path);
    let jump_file = format!("{}/jumps", tree_config.paths.index_path);
    let id_file = format!("{}/identifiers", tree_config.paths.index_path);

    let mut strings = StringIntern::new();
    let empty_string = strings.add("".to_string());

    let mut table = BTreeMap::new();
    let mut pretty_table = HashMap::new();
    let mut id_table = BTreeMap::new();
    let mut jumps = Vec::new();

    for path in &file_paths {
        print!("File {}\n", path);

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &read_target);

        let source_fname = find_source_file(path, &tree_config.paths.files_path, &tree_config.paths.objdir_path);
        let source_file = match File::open(source_fname) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open source file");
                continue;
            },
        };
        let reader = BufReader::new(&source_file);
        let lines: Vec<_> = reader.lines().map(
            |l| match l {
                Ok(line) => {
                    let line_cut = line.trim_right();
                    let len = line_cut.len();
                    let line_cut = line_cut.trim_left();
                    let offset = (len - line_cut.len()) as u32;
                    let mut buf = String::new();
                    let mut i = 0;
                    for c in line_cut.chars() {
                        buf.push(c);
                        i += 1;
                        if i > 100 {
                            break;
                        }
                    };
                    (strings.add(buf), offset)
                },
                Err(_) => (Rc::clone(&empty_string), 0)
            }).collect();

        for datum in analysis {
            for piece in datum.data {
                let sym = strings.add(piece.sym.to_owned());
                let t1 = table.entry(Rc::clone(&sym)).or_insert(BTreeMap::new());
                let t2 = t1.entry(piece.kind).or_insert(BTreeMap::new());
                let p: &str = &path;
                let t3 = t2.entry(p).or_insert(Vec::new());
                let lineno = (datum.loc.lineno - 1) as usize;
                if lineno >= lines.len() {
                    print!("Bad line number in file {} (line {})\n", path, lineno);
                    continue;
                }
                let (line, offset) = lines[lineno].clone();
                t3.push(SearchResult {
                    lineno: datum.loc.lineno,
                    bounds: (datum.loc.col_start - offset, datum.loc.col_end - offset),
                    line: line,
                    context: strings.add(piece.context),
                    contextsym: strings.add(piece.contextsym),
                });

                let pretty = strings.add(piece.pretty.to_owned());
                pretty_table.insert(Rc::clone(&sym), Rc::clone(&pretty));

                let ch = piece.sym.chars().nth(0).unwrap();
                if !(ch >= '0' && ch <= '9') && !piece.sym.contains(' ') {
                    let t1 = id_table.entry(pretty).or_insert(BTreeSet::new());
                    t1.insert(sym);
                }
            }
        }
    }

    let mut outputf = File::create(output_file).unwrap();

    for (id, id_data) in table {
        let mut kindmap = BTreeMap::new();
        for (kind, kind_data) in &id_data {
            let mut result = Vec::new();
            for (path, results) in kind_data {
                let mut obj = BTreeMap::new();
                obj.insert("path".to_string(), path.to_json());
                obj.insert("lines".to_string(), results.to_json());
                result.push(Json::Object(obj));
            }
            let kindstr = match *kind {
                AnalysisKind::Use => "Uses",
                AnalysisKind::Def => "Definitions",
                AnalysisKind::Assign => "Assignments",
                AnalysisKind::Decl => "Declarations",
                AnalysisKind::Idl => "IDL",
            };
            kindmap.insert(kindstr.to_string(), Json::Array(result));
        }
        let kindmap = Json::Object(kindmap);

        let _ = outputf.write_all(format!("{}\n{}\n", id, kindmap.to_string()).as_bytes());

        if id_data.contains_key(&AnalysisKind::Def) {
            let defs = id_data.get(&AnalysisKind::Def).unwrap();
            if defs.len() == 1 {
                for (path, results) in defs {
                    if results.len() == 1 {
                        let mut v = Vec::new();
                        v.push(id.to_json());
                        v.push(path.to_json());
                        v.push(results[0].lineno.to_json());
                        let pretty = pretty_table.get(&id).unwrap();
                        v.push(pretty.to_json());
                        jumps.push(Json::Array(v));
                    }
                }
            }
        }
    }

    let mut jumpf = File::create(jump_file).unwrap();
    for jump in jumps {
        let _ = jumpf.write_all((jump.to_string() + "\n").as_bytes());
    }

    let mut idf = File::create(id_file).unwrap();
    for (id, syms) in id_table {
        for sym in syms {
            let components = split_scopes(&id);
            for i in 0..components.len() {
                let sub = &components[i..components.len()];
                let sub = sub.join("::");

                if !sub.is_empty() {
                    let line = format!("{} {}\n", sub, sym);
                    let _ = idf.write_all(line.as_bytes());
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! A generic circular ring buffer.
//!
//! A ring buffer can continue appending data to itself indefinitely.  However,
//! it has a limited capacity; when that capacity is reached, it will overwrite
//! the oldest data to make space.

use std::iter::Zip;
use std::ops::{Index, IndexMut};
use std::vec::Vec;

use types::Time;

/// A generic ring buffer.
#[derive(Clone, Debug)]
pub struct RingBuffer<T> {
    buf: Vec<T>,
    capacity: usize,
    start_i: usize,
    start_t: Time,
    end_t: Time,
}

impl<T> RingBuffer<T> {
    /// Return an empty ring buffer that can hold at most `capacity` elements.
    pub fn new(capacity: usize) -> Self {
        RingBuffer {
            buf: Vec::with_capacity(capacity),
            start_i: 0,
            capacity: capacity,
            start_t: 0,
            end_t: 0
        }
    }

    /// Returns the number of elements the ringbuffer currently contains.
    pub fn len(&self) -> usize {
        (self.end_t - self.start_t) as usize
    }

    /// Returns the number of elements the ringbuffer can hold at one time.
    pub fn capacity(&self) -> usize {
        self.capacity
    }

    /// Returns the range of timestamps stored in the ringbuffer, as a tuple
    /// (first, last). First is inclusive, while last is exlusive.
    pub fn times(&self) -> (Time, Time) {
        (self.start_t, self.end_t)
    }

    /// Attempt to return a ref to the data stored at time `t`. If the requested
    /// time is not in the buffer, instead returns `None`.
    pub fn get(&self, t: Time) -> Option<&T> {
        if self.start_t <= t && t < self.end_t {
            Some(&self[t])
        } else {
            None
        }
    }

    /// Attempt to return a mutable ref to the data stored at time `t`. If the
    /// requested time is not in the buffer, instead returns `None`.
    pub fn get_mut(&mut self, t: Time) -> Option<&mut T> {
        if self.start_t <= t && t < self.end_t {
            Some(&mut self[t])
        } else {
            None
        }
    }

    /// Push the supplied data onto the end of the buffer. If the buffer is
    /// full, this will overwrite the oldest data.
    pub fn push(&mut self, data: T) {
        if self.buf.len() < self.capacity {
            self.buf.push(data);
            self.end_t += 1;
        } else {
            let i = self.start_i + (self.end_t - self.start_t) as usize;
            self.buf[i % self.capacity] = data;
            self.start_i = (self.start_i+1) % self.capacity;
            self.start_t += 1;
            self.end_t += 1;
        }
    }

    /// Clear all the elements from the buffer.
    pub fn clear(&mut self) {
        self.start_t = self.end_t;
    }

    /// Returns an iterator over the buffer.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let values: Vec<_> = rb.iter().collect();
    /// assert_eq!(values, [&"second", &"third"]);
    /// ```
    pub fn iter(&self) -> Iter<T> {
        Iter { buffer: self, t: self.start_t }
    }

    /// Returns an iterator over the buffer's timestamps.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let times: Vec<_> = rb.timestamp_iter().collect();
    /// assert_eq!(times, [1, 2]);
    /// ```
    pub fn timestamp_iter(&self) -> IterTimes {
        IterTimes { t: self.start_t, end: self.end_t }
    }

    /// Returns an iterator that pairs values with their timestamp.
    ///
    /// Equivalent to `self.timestamp_iter().zip(self.iter())`.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let zipped: Vec<_> = rb.zipped_iter().collect();
    /// assert_eq!(zipped, [(1, &"second"), (2, &"third")]);
    /// ```
    pub fn zipped_iter(&self) -> Zip<IterTimes, Iter<T>> {
        self.timestamp_iter().zip(self.iter())
    }
}

impl<T: Clone> RingBuffer<T> {
    /// Remove the oldest element from the ringbuffer and return it. If there
    /// are no elements, returns `None` insteads.
    pub fn pop(&mut self) -> Option<T> {
        if self.start_t == self.end_t {
            None
        } else {
            let result = self[self.start_t].clone();
            self.start_i = (self.start_i+1) % self.capacity;
            self.start_t += 1;
            Some(result)
        }
    }

    /// Resizes the ringbuffer to hold up to `capacity` elements. If the new
    /// capacity is smaller than the old one, then the oldest elements will be
    /// removed from the buffer.
    pub fn resize(&mut self, capacity: usize) {
        let mut elems = (self.end_t - self.start_t) as usize;
        if capacity < elems {
            let drops = elems - capacity;
            self.start_i += drops;
            self.start_t += drops as Time;
            elems = capacity;
        }

        let mut new_buf = Vec::with_capacity(capacity);
        for i in self.start_i..(self.start_i+elems) {
            new_buf.push(self.buf[i % self.capacity].clone());
        }
        self.buf = new_buf;

        self.start_i = 0;
        self.capacity = capacity;
    }
}

impl<T> Index<Time> for RingBuffer<T> {
    type Output = T;
    fn index(&self, t: Time) -> &T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        let i = self.start_i + (t - self.start_t) as usize;
        &self.buf[i % self.capacity]
    }
}

impl<T> IndexMut<Time> for RingBuffer<T> {
    fn index_mut(&mut self, t: Time) -> &mut T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        let i = self.start_i + (t - self.start_t) as usize;
        &mut self.buf[i % self.capacity]
    }
}

/// An iterator over RingBuffer values.
pub struct Iter<'a, T: 'a> {
    buffer: &'a RingBuffer<T>,
    t: Time
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.buffer.end_t {
            None
        } else {
            let result = &self.buffer[self.t];
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.buffer.end_t - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

/// An iterator over RingBuffer timestamps.
pub struct IterTimes {
    t: Time,
    end: Time
}

impl Iterator for IterTimes {
    type Item = Time;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.end {
            None
        } else {
            let result = self.t;
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.end - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

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

    fn get_test_rb() -> RingBuffer<i32> {
        RingBuffer {
            buf: vec![7,13],
            capacity: 2,
            start_i: 1,
            start_t: 7,
            end_t: 9
        }
    }

    #[test]
    fn test_push() {
        let mut rb = RingBuffer::<i32>::new(2);

        rb.push(13);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 1);
        assert_eq!(rb.buf[0], 13);

        rb.push(7);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 2);
        assert_eq!(rb.buf[0], 13);
        assert_eq!(rb.buf[1], 7);

        rb.push(3);
        assert_eq!(rb.start_t, 1);
        assert_eq!(rb.end_t, 3);
        assert_eq!(rb.buf[0], 3);
        assert_eq!(rb.buf[1], 7);
    }

    #[test]
    fn test_pop() {
        let mut rb = get_test_rb();
        assert_eq!(rb.pop(), Some(13));
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [7]);
        assert_eq!(rb.pop(), Some(7));
        assert_eq!(rb.pop(), None);
    }

    #[test]
    fn test_get() {
        // get is just a wrapper around Index, so just do some simple black box
        // testing; glass box is handled in test_index.
        let rb = get_test_rb();
        assert_eq!(rb.get(6), None);
        assert_eq!(rb.get(7), Some(&13));
        assert_eq!(rb.get(8), Some(&7));
        assert_eq!(rb.get(9), None);
    }

    #[test]
    fn test_get_mut() {
        // get_mut is just a wrapper around IndexMut, so just do some simple
        // black box testing; glass box is handled in test_index_mut.
        let mut rb = get_test_rb();
        assert_eq!(rb.get_mut(6), None);
        assert_eq!(rb.get_mut(7), Some(&mut 13));
        assert_eq!(rb.get_mut(8), Some(&mut 7));
        assert_eq!(rb.get_mut(9), None);
    }

    #[test]
    fn test_index() {
        let mut rb = get_test_rb();

        // Test with odd start
        assert_eq!(rb[7], 13);
        assert_eq!(rb[8], 7);

        // Test with even start
        rb.start_i = 0; rb.start_t = 6; rb.end_t = 8;
        assert_eq!(rb[6], 7);
        assert_eq!(rb[7], 13);
    }

    #[test]
    #[should_panic]
    fn test_index_under() {
        let rb = get_test_rb();
        rb[6];
    }

    #[test]
    #[should_panic]
    fn test_index_over() {
        let rb = get_test_rb();
        rb[9];
    }

    #[test]
    fn test_index_mut() {
        let mut rb = get_test_rb();

        rb[7] = 22;
        rb[8] = 23;
        assert_eq!(rb.buf[1], 22);
        assert_eq!(rb.buf[0], 23);
    }

    #[test]
    #[should_panic]
    fn test_index_mut_under() {
        let mut rb = get_test_rb();
        rb[6] = 3;
    }

    #[test]
    #[should_panic]
    fn test_index_mut_over() {
        let mut rb = get_test_rb();
        rb[9] = 3;
    }

    #[test]
    fn test_resize() {
        // Test expanding
        let mut rb = get_test_rb();
        rb.resize(4);
        rb.push(19);
        rb.push(21);
        rb.push(23);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [7,19,21,23]);

        // Test contracting
        rb.resize(2);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [21,23]);

        // Test expanding a partially full buffer
        let mut rb = RingBuffer::new(2);
        rb.push(1);
        rb.resize(4);
        rb.push(2);
        rb.push(3);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [1,2,3]);

        // Test contracting a partially full buffer
        let mut rb = RingBuffer::new(4);
        rb.push(1);
        rb.resize(2);
        rb.push(2);
        rb.push(3);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [2,3]);
    }
}
<commit_msg>Improve ringbuffer documentation<commit_after>//! A generic, fixed-size ring buffer.

use std::iter::Zip;
use std::ops::{Index, IndexMut};
use std::vec::Vec;

use types::Time;

/// A generic ring buffer.
///
/// A ring buffer can continue appending data to itself indefinitely. However,
/// it has a limited capacity; when that capacity is reached, it will overwrite
/// the oldest data.
///
/// The buffer is indexed with `Time` values. As data is overwritten, the
/// times stored in the buffer continue to slide forward.
///
/// # Example
///
/// ```
/// # use oxcable::utils::ringbuffer::RingBuffer;
/// // Create a new buffer that holds 4 elements...
/// let mut rb = RingBuffer::new(4);
///
/// // Insert 3 elements...
/// rb.push(1);
/// rb.push(2);
/// rb.push(3);
/// assert_eq!(rb.len(), 3);
/// assert_eq!(rb.get(0), Some(&1));
/// assert_eq!(rb.get(1), Some(&2));
/// assert_eq!(rb.get(2), Some(&3));
///
/// // Insert two more elements. This will push the oldest element out...
/// rb.push(4);
/// rb.push(5);
/// assert_eq!(rb.len(), 4);
/// assert_eq!(rb.get(0), None);
/// assert_eq!(rb.get(4), Some(&5));
///
/// // Pop the oldest element off the buffer...
/// assert_eq!(rb.get(1), Some(&2));
/// assert_eq!(rb.pop(), Some(2));
/// assert_eq!(rb.get(1), None);
/// assert_eq!(rb.len(), 3);
/// ```
#[derive(Clone, Debug)]
pub struct RingBuffer<T> {
    buf: Vec<T>,
    capacity: usize,
    start_i: usize,
    start_t: Time,
    end_t: Time,
}

impl<T> RingBuffer<T> {
    /// Returns an empty ring buffer that can hold at most `capacity` elements.
    pub fn new(capacity: usize) -> Self {
        RingBuffer {
            buf: Vec::with_capacity(capacity),
            start_i: 0,
            capacity: capacity,
            start_t: 0,
            end_t: 0
        }
    }

    /// Returns the number of elements the ringbuffer currently contains.
    pub fn len(&self) -> usize {
        (self.end_t - self.start_t) as usize
    }

    /// Returns the number of elements the ringbuffer can hold at one time.
    pub fn capacity(&self) -> usize {
        self.capacity
    }

    /// Returns the range of timestamps stored in the ringbuffer, as a tuple
    /// (first, last). First is inclusive, while last is exlusive.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");  // t=0
    /// rb.push("second"); // t=1
    /// rb.push("third");  // t=2, first is overwritten
    /// assert_eq!(rb.times(), (1,3));
    /// ```
    pub fn times(&self) -> (Time, Time) {
        (self.start_t, self.end_t)
    }

    /// Attempts to return a ref to the data stored at time `t`. If the requested
    /// time is not in the buffer, instead returns `None`.
    pub fn get(&self, t: Time) -> Option<&T> {
        if self.start_t <= t && t < self.end_t {
            Some(&self[t])
        } else {
            None
        }
    }

    /// Attempts to return a mutable ref to the data stored at time `t`. If the
    /// requested time is not in the buffer, instead returns `None`.
    pub fn get_mut(&mut self, t: Time) -> Option<&mut T> {
        if self.start_t <= t && t < self.end_t {
            Some(&mut self[t])
        } else {
            None
        }
    }

    /// Pushes the supplied data onto the end of the buffer. If the buffer is
    /// full, this will overwrite the oldest data.
    pub fn push(&mut self, data: T) {
        if self.buf.len() < self.capacity {
            self.buf.push(data);
            self.end_t += 1;
        } else {
            let i = self.start_i + (self.end_t - self.start_t) as usize;
            self.buf[i % self.capacity] = data;
            self.start_i = (self.start_i+1) % self.capacity;
            self.start_t += 1;
            self.end_t += 1;
        }
    }

    /// Clears all the elements from the buffer.
    pub fn clear(&mut self) {
        self.start_t = self.end_t;
    }

    /// Returns an iterator over the buffer.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let values: Vec<_> = rb.iter().collect();
    /// assert_eq!(values, [&"second", &"third"]);
    /// ```
    pub fn iter(&self) -> Iter<T> {
        Iter { buffer: self, t: self.start_t }
    }

    /// Returns an iterator over the buffer's timestamps.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let times: Vec<_> = rb.timestamp_iter().collect();
    /// assert_eq!(times, [1, 2]);
    /// ```
    pub fn timestamp_iter(&self) -> IterTimes {
        IterTimes { t: self.start_t, end: self.end_t }
    }

    /// Returns an iterator that pairs values with their timestamp.
    ///
    /// Equivalent to `self.timestamp_iter().zip(self.iter())`.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let zipped: Vec<_> = rb.zipped_iter().collect();
    /// assert_eq!(zipped, [(1, &"second"), (2, &"third")]);
    /// ```
    pub fn zipped_iter(&self) -> Zip<IterTimes, Iter<T>> {
        self.timestamp_iter().zip(self.iter())
    }
}

impl<T: Clone> RingBuffer<T> {
    /// Removes the oldest element from the ringbuffer and return it. If there
    /// are no elements, returns `None` insteads.
    pub fn pop(&mut self) -> Option<T> {
        if self.start_t == self.end_t {
            None
        } else {
            let result = self[self.start_t].clone();
            self.start_i = (self.start_i+1) % self.capacity;
            self.start_t += 1;
            Some(result)
        }
    }

    /// Resizes the ringbuffer to hold up to `capacity` elements. If the new
    /// capacity is smaller than the old one, then the oldest elements will be
    /// removed from the buffer.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(4);
    /// rb.push(1); rb.push(2); rb.push(3); rb.push(4);
    /// rb.resize(2);
    /// assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [3,4]);
    /// ```
    pub fn resize(&mut self, capacity: usize) {
        let mut elems = (self.end_t - self.start_t) as usize;
        if capacity < elems {
            let drops = elems - capacity;
            self.start_i += drops;
            self.start_t += drops as Time;
            elems = capacity;
        }

        let mut new_buf = Vec::with_capacity(capacity);
        for i in self.start_i..(self.start_i+elems) {
            new_buf.push(self.buf[i % self.capacity].clone());
        }
        self.buf = new_buf;

        self.start_i = 0;
        self.capacity = capacity;
    }
}

impl<T> Index<Time> for RingBuffer<T> {
    type Output = T;
    fn index(&self, t: Time) -> &T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        let i = self.start_i + (t - self.start_t) as usize;
        &self.buf[i % self.capacity]
    }
}

impl<T> IndexMut<Time> for RingBuffer<T> {
    fn index_mut(&mut self, t: Time) -> &mut T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        let i = self.start_i + (t - self.start_t) as usize;
        &mut self.buf[i % self.capacity]
    }
}

/// An iterator over RingBuffer values.
pub struct Iter<'a, T: 'a> {
    buffer: &'a RingBuffer<T>,
    t: Time
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.buffer.end_t {
            None
        } else {
            let result = &self.buffer[self.t];
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.buffer.end_t - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

/// An iterator over RingBuffer timestamps.
pub struct IterTimes {
    t: Time,
    end: Time
}

impl Iterator for IterTimes {
    type Item = Time;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.end {
            None
        } else {
            let result = self.t;
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.end - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

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

    fn get_test_rb() -> RingBuffer<i32> {
        RingBuffer {
            buf: vec![7,13],
            capacity: 2,
            start_i: 1,
            start_t: 7,
            end_t: 9
        }
    }

    #[test]
    fn test_push() {
        let mut rb = RingBuffer::<i32>::new(2);

        rb.push(13);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 1);
        assert_eq!(rb.buf[0], 13);

        rb.push(7);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 2);
        assert_eq!(rb.buf[0], 13);
        assert_eq!(rb.buf[1], 7);

        rb.push(3);
        assert_eq!(rb.start_t, 1);
        assert_eq!(rb.end_t, 3);
        assert_eq!(rb.buf[0], 3);
        assert_eq!(rb.buf[1], 7);
    }

    #[test]
    fn test_pop() {
        let mut rb = get_test_rb();
        assert_eq!(rb.pop(), Some(13));
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [7]);
        assert_eq!(rb.pop(), Some(7));
        assert_eq!(rb.pop(), None);
    }

    #[test]
    fn test_get() {
        // get is just a wrapper around Index, so just do some simple black box
        // testing; glass box is handled in test_index.
        let rb = get_test_rb();
        assert_eq!(rb.get(6), None);
        assert_eq!(rb.get(7), Some(&13));
        assert_eq!(rb.get(8), Some(&7));
        assert_eq!(rb.get(9), None);
    }

    #[test]
    fn test_get_mut() {
        // get_mut is just a wrapper around IndexMut, so just do some simple
        // black box testing; glass box is handled in test_index_mut.
        let mut rb = get_test_rb();
        assert_eq!(rb.get_mut(6), None);
        assert_eq!(rb.get_mut(7), Some(&mut 13));
        assert_eq!(rb.get_mut(8), Some(&mut 7));
        assert_eq!(rb.get_mut(9), None);
    }

    #[test]
    fn test_index() {
        let mut rb = get_test_rb();

        // Test with odd start
        assert_eq!(rb[7], 13);
        assert_eq!(rb[8], 7);

        // Test with even start
        rb.start_i = 0; rb.start_t = 6; rb.end_t = 8;
        assert_eq!(rb[6], 7);
        assert_eq!(rb[7], 13);
    }

    #[test]
    #[should_panic]
    fn test_index_under() {
        let rb = get_test_rb();
        rb[6];
    }

    #[test]
    #[should_panic]
    fn test_index_over() {
        let rb = get_test_rb();
        rb[9];
    }

    #[test]
    fn test_index_mut() {
        let mut rb = get_test_rb();

        rb[7] = 22;
        rb[8] = 23;
        assert_eq!(rb.buf[1], 22);
        assert_eq!(rb.buf[0], 23);
    }

    #[test]
    #[should_panic]
    fn test_index_mut_under() {
        let mut rb = get_test_rb();
        rb[6] = 3;
    }

    #[test]
    #[should_panic]
    fn test_index_mut_over() {
        let mut rb = get_test_rb();
        rb[9] = 3;
    }

    #[test]
    fn test_resize() {
        // Test expanding
        let mut rb = get_test_rb();
        rb.resize(4);
        rb.push(19);
        rb.push(21);
        rb.push(23);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [7,19,21,23]);

        // Test contracting
        rb.resize(2);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [21,23]);

        // Test expanding a partially full buffer
        let mut rb = RingBuffer::new(2);
        rb.push(1);
        rb.resize(4);
        rb.push(2);
        rb.push(3);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [1,2,3]);

        // Test contracting a partially full buffer
        let mut rb = RingBuffer::new(4);
        rb.push(1);
        rb.resize(2);
        rb.push(2);
        rb.push(3);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [2,3]);
    }
}
<|endoftext|>


<commit_before>pub fn recurse<X, Y>(x: X, f: &Fn(X, &Fn(X) -> Y) -> Y) -> Y {
    f(x, &|x: X| recurse(x, &f))
}
<commit_msg>Add test of recurse<commit_after>pub fn recurse<X, Y>(x: X, f: &Fn(X, &Fn(X) -> Y) -> Y) -> Y {
    f(x, &|x: X| recurse(x, &f))
}

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

    #[test]
    fn test_recurse_fib() {
        let fib_5 = recurse(5, &|n, fib| {
            if n <= 1 {
                1
            } else {
                fib(n - 1) + fib(n - 2)
            }
        });
        assert_eq!(fib_5, 8);
    }
}
<|endoftext|>


<commit_before>// High-level bindings to Cairo.

import cairo::{cairo_surface_t, cairo_format_t, cairo_t};
import cairo::bindgen::{cairo_create, cairo_fill, cairo_image_surface_create};
import cairo::bindgen::{cairo_image_surface_get_data, cairo_image_surface_get_format};
import cairo::bindgen::{cairo_image_surface_get_height, cairo_image_surface_get_stride};
import cairo::bindgen::{cairo_image_surface_get_width, cairo_rectangle, cairo_set_line_width};
import cairo::bindgen::{cairo_set_source_rgb, cairo_stroke, cairo_surface_destroy};
import vec::unsafe::from_buf;

// FIXME: We should have a hierarchy of surfaces, but this needs to wait on case classes.
class ImageSurface {
    let cairo_surface: *cairo_surface_t;

    new(format: cairo_format_t, width: c_int, height: c_int) {
        self.cairo_surface = cairo_image_surface_create(format, width, height);
        if self.cairo_surface.is_null() {
            fail ~"couldn't create Cairo image surface";
        };
    }

    fn width()  -> c_int    { cairo_image_surface_get_width(self.cairo_surface)  }
    fn height() -> c_int    { cairo_image_surface_get_height(self.cairo_surface) }
    fn stride() -> c_int    { cairo_image_surface_get_stride(self.cairo_surface) }
    fn format() -> c_int    { cairo_image_surface_get_format(self.cairo_surface) }

    fn data() -> ~[u8] unsafe {
        let buffer = cairo_image_surface_get_data(self.cairo_surface);
        return from_buf(buffer, (self.stride() * self.height()) as uint);
    }

    drop {
        cairo_surface_destroy(self.cairo_surface);
    }
}

class Context {
    let cairo_context: *cairo_t;

    new(&&surface: ImageSurface) {
        self.cairo_context = cairo_create(surface.cairo_surface);
    }

    fn set_line_width(width: c_double) {
        cairo_set_line_width(self.cairo_context, width);
    }

    fn set_source_rgb(r: c_double, g: c_double, b: c_double) {
        cairo_set_source_rgb(self.cairo_context, r, g, b);
    }

    fn rectangle(x: c_double, y: c_double, width: c_double, height: c_double) {
        cairo_rectangle(self.cairo_context, x, y, width, height);
    }

    fn stroke() {
        cairo_stroke(self.cairo_context);
    }

    fn fill() {
        cairo_fill(self.cairo_context);
    }
}

<commit_msg>Implement a safe API to Cairo image surfaces<commit_after>// High-level bindings to Cairo.

import cairo::{CAIRO_STATUS_SUCCESS, cairo_format_t, cairo_status_t, cairo_surface_t, cairo_t};
import cairo::bindgen::{cairo_create, cairo_fill, cairo_image_surface_create};
import cairo::bindgen::{cairo_image_surface_get_data, cairo_image_surface_get_format};
import cairo::bindgen::{cairo_image_surface_get_height, cairo_image_surface_get_stride};
import cairo::bindgen::{cairo_image_surface_get_width, cairo_rectangle, cairo_set_line_width};
import cairo::bindgen::{cairo_set_source_rgb, cairo_stroke, cairo_surface_destroy};
import cairo::bindgen::{cairo_surface_write_to_png_stream};
import io::{mem_buffer, writer};
import ptr::addr_of;
import result::{err, ok, result};
import unsafe::reinterpret_cast;
import vec::unsafe::{form_slice, from_buf};

// FIXME: We should have a hierarchy of surfaces, but this needs to wait on case classes.
class ImageSurface {
    let cairo_surface: *cairo_surface_t;

    new(format: cairo_format_t, width: c_int, height: c_int) {
        self.cairo_surface = cairo_image_surface_create(format, width, height);
        if self.cairo_surface.is_null() {
            fail ~"couldn't create Cairo image surface";
        };
    }

    fn width()  -> c_int    { cairo_image_surface_get_width(self.cairo_surface)  }
    fn height() -> c_int    { cairo_image_surface_get_height(self.cairo_surface) }
    fn stride() -> c_int    { cairo_image_surface_get_stride(self.cairo_surface) }
    fn format() -> c_int    { cairo_image_surface_get_format(self.cairo_surface) }

    fn data() -> ~[u8] unsafe {
        let buffer = cairo_image_surface_get_data(self.cairo_surface);
        return from_buf(buffer, (self.stride() * self.height()) as uint);
    }

    drop {
        cairo_surface_destroy(self.cairo_surface);
    }
}

impl ImageSurface {
    fn write_to_png_stream(buffer: &io::mem_buffer) -> result<(),cairo_status_t> unsafe {
        extern fn write_fn(closure: *c_void, data: *c_uchar, len: c_uint)
                        -> cairo_status_t unsafe {
            let writer: *mem_buffer = reinterpret_cast(closure);
            do form_slice(data, len as uint) |bytes| {
                (*writer).write(bytes);
            }
            return CAIRO_STATUS_SUCCESS;
        }

        let buffer_ptr = reinterpret_cast(buffer);
        let status = cairo_surface_write_to_png_stream(self.cairo_surface, write_fn, buffer_ptr);
        if status != CAIRO_STATUS_SUCCESS {
            return err(status);
        }

        return ok(());
    }
}

class Context {
    let cairo_context: *cairo_t;

    new(&&surface: ImageSurface) {
        self.cairo_context = cairo_create(surface.cairo_surface);
    }

    fn set_line_width(width: c_double) {
        cairo_set_line_width(self.cairo_context, width);
    }

    fn set_source_rgb(r: c_double, g: c_double, b: c_double) {
        cairo_set_source_rgb(self.cairo_context, r, g, b);
    }

    fn rectangle(x: c_double, y: c_double, width: c_double, height: c_double) {
        cairo_rectangle(self.cairo_context, x, y, width, height);
    }

    fn stroke() {
        cairo_stroke(self.cairo_context);
    }

    fn fill() {
        cairo_fill(self.cairo_context);
    }
}

<|endoftext|>