<commit_before>use std::path::Path;
use super::{Driver, Dataset, Feature, FeatureIterator, FieldValue, Geometry, OGRFieldType};

mod convert_geo;

macro_rules! fixture {
    ($name:expr) => (
        Path::new(file!())
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .join("fixtures").as_path()
            .join($name).as_path()
    )
}

fn assert_almost_eq(a: f64, b: f64) {
    let f: f64 = a / b;
    assert!(f < 1.00001);
    assert!(f > 0.99999);
}

#[test]
fn test_layer_count() {
    let ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    assert_eq!(ds.count(), 1);
}

fn with_features<F>(name: &str, f: F) where F: Fn(FeatureIterator) {
    let mut ds = Dataset::open(fixture!(name)).unwrap();
    let layer = ds.layer(0).unwrap();
    f(layer.features());
}

fn with_first_feature<F>(name: &str, f: F) where F: Fn(Feature) {
    with_features(name, |mut features| f(features.next().unwrap()));
}

#[test]
fn test_iterate_features() {
    with_features("roads.geojson", |features| {
        let feature_vec: Vec<Feature> = features.collect();
        assert_eq!(feature_vec.len(), 21);
    });
}

#[test]
fn test_string_field() {
    with_features("roads.geojson", |mut features| {
        let feature = features.next().unwrap();
        assert_eq!(feature.field("highway")
                          .unwrap()
                          .to_string(),
                   Some("footway".to_string()));
        assert_eq!(
            features.filter(|field| {
                let highway = field.field("highway")
                                   .unwrap()
                                   .to_string();
                highway == Some("residential".to_string()) })
                .count(),
            2);
    });
}

#[test]
fn test_float_field() {
    with_first_feature("roads.geojson", |feature| {
        assert_almost_eq(
            feature.field("sort_key")
                   .unwrap()
                   .to_real()
                   .unwrap(),
            -9.0
        );
    });
}

#[test]
fn test_missing_field() {
    with_first_feature("roads.geojson", |feature| {
        assert!(feature.field("no such field").is_err());
    });
}

#[test]
fn test_wkt() {
    with_first_feature("roads.geojson", |feature| {
        let wkt = feature.geometry().wkt().unwrap();
        let wkt_ok = format!("{}{}",
            "LINESTRING (26.1019276 44.4302748,",
            "26.1019382 44.4303191,26.1020002 44.4304202)"
            ).to_string();
        assert_eq!(wkt, wkt_ok);
    });
}

#[test]
fn test_json() {
    with_first_feature("roads.geojson", |feature| {
        let json = feature.geometry().json();
        let json_ok = format!("{}{}{}{}",
            "{ \"type\": \"LineString\", \"coordinates\": [ ",
            "[ 26.1019276, 44.4302748 ], ",
            "[ 26.1019382, 44.4303191 ], ",
            "[ 26.1020002, 44.4304202 ] ] }"
            ).to_string();
        assert_eq!(json.unwrap(), json_ok);
    });
}

#[test]
fn test_schema() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let name_list: Vec<(String, OGRFieldType)> = layer
        .defn().fields()
        .map(|f| (f.name(), f.field_type()))
        .collect();
    let ok_names_types: Vec<(String, OGRFieldType)> = vec!(
        ("kind", OGRFieldType::OFTString),
        ("sort_key",  OGRFieldType::OFTReal),
        ("is_link", OGRFieldType::OFTString),
        ("is_tunnel", OGRFieldType::OFTString),
        ("is_bridge", OGRFieldType::OFTString),
        ("railway", OGRFieldType::OFTString),
        ("highway", OGRFieldType::OFTString))
        .iter().map(|s| (s.0.to_string(), s.1)).collect();
    assert_eq!(name_list, ok_names_types);
}

#[test]
fn test_create_bbox() {
    let bbox = Geometry::bbox(-27., 33., 52., 85.).unwrap();
    assert_eq!(bbox.json().unwrap(), "{ \"type\": \"Polygon\", \"coordinates\": [ [ [ -27.0, 85.0 ], [ 52.0, 85.0 ], [ 52.0, 33.0 ], [ -27.0, 33.0 ], [ -27.0, 85.0 ] ] ] }");
}

#[test]
fn test_spatial_filter() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();

    let all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(all_features.len(), 21);

    let bbox = Geometry::bbox(26.1017, 44.4297, 26.1025, 44.4303).unwrap();
    layer.set_spatial_filter(&bbox);

    let some_features: Vec<Feature> = layer.features().collect();
    assert_eq!(some_features.len(), 7);

    layer.clear_spatial_filter();

    let again_all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(again_all_features.len(), 21);
}

#[test]
fn test_convex_hull() {
    let star = "POLYGON ((0 1,3 1,1 3,1.5 0.0,2 3,0 1))";
    let hull = "POLYGON ((1.5 0.0,0 1,1 3,2 3,3 1,1.5 0.0))";
    assert_eq!(Geometry::from_wkt(star).unwrap().convex_hull().unwrap().wkt().unwrap(), hull);
}

#[test]
fn test_write_features() {
    use std::fs;

    {
        let driver = Driver::get("GeoJSON").unwrap();
        let mut ds = driver.create(fixture!("output.geojson")).unwrap();
        let mut layer = ds.create_layer().unwrap();
        layer.create_defn_fields(&[("Name",  OGRFieldType::OFTString), ("Value",  OGRFieldType::OFTReal), ("Int_value", OGRFieldType::OFTInteger)]);
        layer.create_feature_fields(
            Geometry::from_wkt("POINT (1 2)").unwrap(), &["Name", "Value", "Int_value"],
            &[FieldValue::StringValue("Feature 1".to_string()), FieldValue::RealValue(45.78), FieldValue::IntegerValue(1)]
            ).unwrap();
        // dataset is closed here
    }

    let mut ds = Dataset::open(fixture!("output.geojson")).unwrap();
    fs::remove_file(fixture!("output.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let ft = layer.features().next().unwrap();
    assert_eq!(ft.geometry().wkt().unwrap(), "POINT (1 2)");
    assert_eq!(ft.field("Name").unwrap().to_string(), Some("Feature 1".to_string()));
    assert_eq!(ft.field("Value").unwrap().to_real(), Some(45.78));
    assert_eq!(ft.field("Int_value").unwrap().to_int(), Some(1));
}
<commit_msg>adapt "test_schema()" to GDAL 2.x<commit_after>use std::path::Path;
use super::{Driver, Dataset, Feature, FeatureIterator, FieldValue, Geometry, OGRFieldType};

mod convert_geo;

macro_rules! fixture {
    ($name:expr) => (
        Path::new(file!())
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .join("fixtures").as_path()
            .join($name).as_path()
    )
}

fn assert_almost_eq(a: f64, b: f64) {
    let f: f64 = a / b;
    assert!(f < 1.00001);
    assert!(f > 0.99999);
}

#[test]
fn test_layer_count() {
    let ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    assert_eq!(ds.count(), 1);
}

fn with_features<F>(name: &str, f: F) where F: Fn(FeatureIterator) {
    let mut ds = Dataset::open(fixture!(name)).unwrap();
    let layer = ds.layer(0).unwrap();
    f(layer.features());
}

fn with_first_feature<F>(name: &str, f: F) where F: Fn(Feature) {
    with_features(name, |mut features| f(features.next().unwrap()));
}

#[test]
fn test_iterate_features() {
    with_features("roads.geojson", |features| {
        let feature_vec: Vec<Feature> = features.collect();
        assert_eq!(feature_vec.len(), 21);
    });
}

#[test]
fn test_string_field() {
    with_features("roads.geojson", |mut features| {
        let feature = features.next().unwrap();
        assert_eq!(feature.field("highway")
                          .unwrap()
                          .to_string(),
                   Some("footway".to_string()));
        assert_eq!(
            features.filter(|field| {
                let highway = field.field("highway")
                                   .unwrap()
                                   .to_string();
                highway == Some("residential".to_string()) })
                .count(),
            2);
    });
}

#[test]
fn test_float_field() {
    with_first_feature("roads.geojson", |feature| {
        assert_almost_eq(
            feature.field("sort_key")
                   .unwrap()
                   .to_real()
                   .unwrap(),
            -9.0
        );
    });
}

#[test]
fn test_missing_field() {
    with_first_feature("roads.geojson", |feature| {
        assert!(feature.field("no such field").is_err());
    });
}

#[test]
fn test_wkt() {
    with_first_feature("roads.geojson", |feature| {
        let wkt = feature.geometry().wkt().unwrap();
        let wkt_ok = format!("{}{}",
            "LINESTRING (26.1019276 44.4302748,",
            "26.1019382 44.4303191,26.1020002 44.4304202)"
            ).to_string();
        assert_eq!(wkt, wkt_ok);
    });
}

#[test]
fn test_json() {
    with_first_feature("roads.geojson", |feature| {
        let json = feature.geometry().json();
        let json_ok = format!("{}{}{}{}",
            "{ \"type\": \"LineString\", \"coordinates\": [ ",
            "[ 26.1019276, 44.4302748 ], ",
            "[ 26.1019382, 44.4303191 ], ",
            "[ 26.1020002, 44.4304202 ] ] }"
            ).to_string();
        assert_eq!(json.unwrap(), json_ok);
    });
}

#[test]
fn test_schema() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let name_list: Vec<(String, OGRFieldType)> = layer
        .defn().fields()
        .map(|f| (f.name(), f.field_type()))
        .collect();
    let ok_names_types: Vec<(String, OGRFieldType)> = vec!(
        ("id", OGRFieldType::OFTString),
        ("kind", OGRFieldType::OFTString),
        ("sort_key",  OGRFieldType::OFTReal),
        ("is_link", OGRFieldType::OFTString),
        ("is_tunnel", OGRFieldType::OFTString),
        ("is_bridge", OGRFieldType::OFTString),
        ("railway", OGRFieldType::OFTString),
        ("highway", OGRFieldType::OFTString))
        .iter().map(|s| (s.0.to_string(), s.1)).collect();
    assert_eq!(name_list, ok_names_types);
}

#[test]
fn test_create_bbox() {
    let bbox = Geometry::bbox(-27., 33., 52., 85.).unwrap();
    assert_eq!(bbox.json().unwrap(), "{ \"type\": \"Polygon\", \"coordinates\": [ [ [ -27.0, 85.0 ], [ 52.0, 85.0 ], [ 52.0, 33.0 ], [ -27.0, 33.0 ], [ -27.0, 85.0 ] ] ] }");
}

#[test]
fn test_spatial_filter() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();

    let all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(all_features.len(), 21);

    let bbox = Geometry::bbox(26.1017, 44.4297, 26.1025, 44.4303).unwrap();
    layer.set_spatial_filter(&bbox);

    let some_features: Vec<Feature> = layer.features().collect();
    assert_eq!(some_features.len(), 7);

    layer.clear_spatial_filter();

    let again_all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(again_all_features.len(), 21);
}

#[test]
fn test_convex_hull() {
    let star = "POLYGON ((0 1,3 1,1 3,1.5 0.0,2 3,0 1))";
    let hull = "POLYGON ((1.5 0.0,0 1,1 3,2 3,3 1,1.5 0.0))";
    assert_eq!(Geometry::from_wkt(star).unwrap().convex_hull().unwrap().wkt().unwrap(), hull);
}

#[test]
fn test_write_features() {
    use std::fs;

    {
        let driver = Driver::get("GeoJSON").unwrap();
        let mut ds = driver.create(fixture!("output.geojson")).unwrap();
        let mut layer = ds.create_layer().unwrap();
        layer.create_defn_fields(&[("Name",  OGRFieldType::OFTString), ("Value",  OGRFieldType::OFTReal), ("Int_value", OGRFieldType::OFTInteger)]);
        layer.create_feature_fields(
            Geometry::from_wkt("POINT (1 2)").unwrap(), &["Name", "Value", "Int_value"],
            &[FieldValue::StringValue("Feature 1".to_string()), FieldValue::RealValue(45.78), FieldValue::IntegerValue(1)]
            ).unwrap();
        // dataset is closed here
    }

    let mut ds = Dataset::open(fixture!("output.geojson")).unwrap();
    fs::remove_file(fixture!("output.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let ft = layer.features().next().unwrap();
    assert_eq!(ft.geometry().wkt().unwrap(), "POINT (1 2)");
    assert_eq!(ft.field("Name").unwrap().to_string(), Some("Feature 1".to_string()));
    assert_eq!(ft.field("Value").unwrap().to_real(), Some(45.78));
    assert_eq!(ft.field("Int_value").unwrap().to_int(), Some(1));
}
<|endoftext|>


<commit_before>use crate::common::util::*;

#[test]
fn test_with_tab() {
    new_ucmd!()
        .arg("with-tab.txt")
        .succeeds()
        .stdout_contains("        ")
        .stdout_does_not_contain("\t");
}

#[test]
fn test_with_trailing_tab() {
    new_ucmd!()
        .arg("with-trailing-tab.txt")
        .succeeds()
        .stdout_contains("with tabs=>  ")
        .stdout_does_not_contain("\t");
}

#[test]
fn test_with_trailing_tab_i() {
    new_ucmd!()
        .arg("with-trailing-tab.txt")
        .arg("-i")
        .succeeds()
        .stdout_contains("        // with tabs=>\t");
}

#[test]
fn test_with_tab_size() {
    new_ucmd!()
        .arg("with-tab.txt")
        .arg("--tabs=10")
        .succeeds()
        .stdout_contains("          ");
}

#[test]
fn test_with_space() {
    new_ucmd!()
        .arg("with-spaces.txt")
        .succeeds()
        .stdout_contains("    return");
}

#[test]
fn test_with_multiple_files() {
    new_ucmd!()
        .arg("with-spaces.txt")
        .arg("with-tab.txt")
        .succeeds()
        .stdout_contains("    return")
        .stdout_contains("        ");
}

#[test]
fn test_tabs_space_separated_list() {
    new_ucmd!()
        .args(&["--tabs", "3 6 9"])
        .pipe_in("a\tb\tc\td\te")
        .succeeds()
        .stdout_is("a  b  c  d e");
}

#[test]
fn test_tabs_mixed_style_list() {
    new_ucmd!()
        .args(&["--tabs", ", 3,6 9"])
        .pipe_in("a\tb\tc\td\te")
        .succeeds()
        .stdout_is("a  b  c  d e");
}

#[test]
fn test_tabs_empty_string() {
    new_ucmd!()
        .args(&["--tabs", ""])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_comma_only() {
    new_ucmd!()
        .args(&["--tabs", ","])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_space_only() {
    new_ucmd!()
        .args(&["--tabs", " "])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_slash() {
    new_ucmd!()
        .args(&["--tabs", "/"])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_plus() {
    new_ucmd!()
        .args(&["--tabs", "+"])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_trailing_slash() {
    new_ucmd!()
        .arg("--tabs=1,/5")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        //          0         1
        //          01234567890
        .stdout_is(" a   b    c");
}

#[test]
fn test_tabs_trailing_slash_long_columns() {
    new_ucmd!()
        .arg("--tabs=1,/3")
        .pipe_in("\taaaa\tbbbb\tcccc")
        .succeeds()
        //          0         1
        //          01234567890123456
        .stdout_is(" aaaa bbbb  cccc");
}

#[test]
fn test_tabs_trailing_plus() {
    new_ucmd!()
        .arg("--tabs=1,+5")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        //          0         1
        //          012345678901
        .stdout_is(" a    b    c");
}

#[test]
fn test_tabs_trailing_plus_long_columns() {
    new_ucmd!()
        .arg("--tabs=1,+3")
        .pipe_in("\taaaa\tbbbb\tcccc")
        .succeeds()
        //          0         1
        //          012345678901234567
        .stdout_is(" aaaa  bbbb  cccc");
}

#[test]
fn test_tabs_must_be_ascending() {
    new_ucmd!()
        .arg("--tabs=1,1")
        .fails()
        .stderr_contains("tab sizes must be ascending");
}

#[test]
fn test_tabs_keep_last_trailing_specifier() {
    // If there are multiple trailing specifiers, use only the last one
    // before the number.
    new_ucmd!()
        .arg("--tabs=1,+/+/5")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        //          0         1
        //          01234567890
        .stdout_is(" a   b    c");
}

#[test]
fn test_tabs_comma_separated_no_numbers() {
    new_ucmd!()
        .arg("--tabs=+,/,+,/")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        .stdout_is("        a       b       c");
}
<commit_msg>Silent the spell checker on a test examples<commit_after>use crate::common::util::*;
// spell-checker:ignore (ToDO) taaaa tbbbb tcccc

#[test]
fn test_with_tab() {
    new_ucmd!()
        .arg("with-tab.txt")
        .succeeds()
        .stdout_contains("        ")
        .stdout_does_not_contain("\t");
}

#[test]
fn test_with_trailing_tab() {
    new_ucmd!()
        .arg("with-trailing-tab.txt")
        .succeeds()
        .stdout_contains("with tabs=>  ")
        .stdout_does_not_contain("\t");
}

#[test]
fn test_with_trailing_tab_i() {
    new_ucmd!()
        .arg("with-trailing-tab.txt")
        .arg("-i")
        .succeeds()
        .stdout_contains("        // with tabs=>\t");
}

#[test]
fn test_with_tab_size() {
    new_ucmd!()
        .arg("with-tab.txt")
        .arg("--tabs=10")
        .succeeds()
        .stdout_contains("          ");
}

#[test]
fn test_with_space() {
    new_ucmd!()
        .arg("with-spaces.txt")
        .succeeds()
        .stdout_contains("    return");
}

#[test]
fn test_with_multiple_files() {
    new_ucmd!()
        .arg("with-spaces.txt")
        .arg("with-tab.txt")
        .succeeds()
        .stdout_contains("    return")
        .stdout_contains("        ");
}

#[test]
fn test_tabs_space_separated_list() {
    new_ucmd!()
        .args(&["--tabs", "3 6 9"])
        .pipe_in("a\tb\tc\td\te")
        .succeeds()
        .stdout_is("a  b  c  d e");
}

#[test]
fn test_tabs_mixed_style_list() {
    new_ucmd!()
        .args(&["--tabs", ", 3,6 9"])
        .pipe_in("a\tb\tc\td\te")
        .succeeds()
        .stdout_is("a  b  c  d e");
}

#[test]
fn test_tabs_empty_string() {
    new_ucmd!()
        .args(&["--tabs", ""])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_comma_only() {
    new_ucmd!()
        .args(&["--tabs", ","])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_space_only() {
    new_ucmd!()
        .args(&["--tabs", " "])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_slash() {
    new_ucmd!()
        .args(&["--tabs", "/"])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_plus() {
    new_ucmd!()
        .args(&["--tabs", "+"])
        .pipe_in("a\tb\tc")
        .succeeds()
        .stdout_is("a       b       c");
}

#[test]
fn test_tabs_trailing_slash() {
    new_ucmd!()
        .arg("--tabs=1,/5")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        //          0         1
        //          01234567890
        .stdout_is(" a   b    c");
}

#[test]
fn test_tabs_trailing_slash_long_columns() {
    new_ucmd!()
        .arg("--tabs=1,/3")
        .pipe_in("\taaaa\tbbbb\tcccc")
        .succeeds()
        //          0         1
        //          01234567890123456
        .stdout_is(" aaaa bbbb  cccc");
}

#[test]
fn test_tabs_trailing_plus() {
    new_ucmd!()
        .arg("--tabs=1,+5")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        //          0         1
        //          012345678901
        .stdout_is(" a    b    c");
}

#[test]
fn test_tabs_trailing_plus_long_columns() {
    new_ucmd!()
        .arg("--tabs=1,+3")
        .pipe_in("\taaaa\tbbbb\tcccc")
        .succeeds()
        //          0         1
        //          012345678901234567
        .stdout_is(" aaaa  bbbb  cccc");
}

#[test]
fn test_tabs_must_be_ascending() {
    new_ucmd!()
        .arg("--tabs=1,1")
        .fails()
        .stderr_contains("tab sizes must be ascending");
}

#[test]
fn test_tabs_keep_last_trailing_specifier() {
    // If there are multiple trailing specifiers, use only the last one
    // before the number.
    new_ucmd!()
        .arg("--tabs=1,+/+/5")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        //          0         1
        //          01234567890
        .stdout_is(" a   b    c");
}

#[test]
fn test_tabs_comma_separated_no_numbers() {
    new_ucmd!()
        .arg("--tabs=+,/,+,/")
        .pipe_in("\ta\tb\tc")
        .succeeds()
        .stdout_is("        a       b       c");
}
<|endoftext|>


<commit_before>/*******************************************************************************
 *
 * kit/kernel/memory.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

//! Physical memory management and kernel heap.

use core::mem;
use core::cmp::{min, Ordering};
use collections::{Vec, BTreeMap, BinaryHeap};

use paging::{self, kernel_pageset, Pageset};
use paging::{GenericPageset, PagesetExt, PageType};

use multiboot::MmapEntry;
use process::Id as ProcessId;

/// The first "safe" physical address. Memory below this is not likely to be
/// safe for general use, and may include parts of the kernel image among other
/// things.
const SAFE_BOUNDARY: usize = 0x400000;

const INITIAL_HEAP_LENGTH: usize = 131072;

extern {
    static mut MEMORY_INITIAL_HEAP: [u8; INITIAL_HEAP_LENGTH];
}

static mut KERNEL_HEAP: KernelHeap = KernelHeap::InitialHeap(0);

#[derive(Debug)]
enum KernelHeap {
    InitialHeap(usize),
    LargeHeap(HeapState)
}

#[derive(Debug)]
struct HeapState {
    start: *mut u8,
    end: *mut u8,
    length: usize,
    regions: Vec<(PhysicalAddress, PageCount)>,
    can_grow: bool
}

static mut ALLOC_REGIONS: Option<BTreeMap<PhysicalAddress, AllocRegionState>> = None;
static mut FREE_REGIONS: Option<BinaryHeap<FreeRegion>> = None;

static mut TOTAL_FREE: PageCount = 0;

pub type PhysicalAddress = usize;
pub type PageCount = usize;

#[derive(Debug)]
struct AllocRegionState {
    length: PageCount,
    users: Vec<RegionUser>
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RegionUser {
    Kernel,
    Process(ProcessId)
}

#[derive(Debug, PartialEq, Eq)]
struct FreeRegion {
    start: PhysicalAddress,
    length: PageCount
}

impl PartialOrd for FreeRegion {
    fn partial_cmp(&self, other: &FreeRegion) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for FreeRegion {
    fn cmp(&self, other: &FreeRegion) -> Ordering {
        if self.length == other.length {
            self.start.cmp(&other.start)
        } else {
            other.length.cmp(&self.length)
        }
    }
}

/// Loads the memory map information into the region tree in order to know where
/// in physical memory it's safe to allocate fresh pages.
pub unsafe fn initialize(mmap_buffer: *const u8, mmap_length: u32) {
    let mut current_mmap = mmap_buffer;
    let mmap_end = mmap_buffer.offset(mmap_length as isize);

    let page_size = Pageset::page_size();

    let mut free_regions = BinaryHeap::with_capacity(16);

    while current_mmap < mmap_end {
        let entry_ptr: *const MmapEntry = mem::transmute(current_mmap);
        let entry = entry_ptr.as_ref().unwrap();

        current_mmap = current_mmap.offset(entry.size as isize + 4);

        let addr = entry.addr as usize;
        let len  = entry.len  as usize;

        // Align physical base address to page size.
        let mut physical_base =
            if addr % page_size != 0 {
                (addr / page_size + 1) * page_size
            } else {
                addr
            };

        // Remove remainder from length and count pages.
        let mut pages = (len - (addr % page_size)) / page_size;

        // If the base starts before SAFE_BOUNDARY, remove the pages before that
        // (and make sure we still have pages left).
        if physical_base < SAFE_BOUNDARY {
            let diff = (SAFE_BOUNDARY - physical_base) / page_size;

            if diff < pages {
                physical_base += diff * page_size;
                pages         -= diff;
            } else {
                continue; // skip this entry
            }
        }

        // If the entry is marked as available and has at least one page, add it
        // to the free regions.
        if entry.is_available() && pages > 0 {
            free_regions.push(FreeRegion {
                start: physical_base,
                length: pages
            });
        }
    }

    TOTAL_FREE = free_regions.iter().fold(0, |s, r| s + r.length);

    FREE_REGIONS = Some(free_regions);
    ALLOC_REGIONS = Some(BTreeMap::new());
}

const LARGE_HEAP_START: usize = 0xffff_ffff_8100_0000;
const BUFZONE_SIZE: usize = 0x4000;

pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
    match KERNEL_HEAP {
        KernelHeap::InitialHeap(ref mut counter) =>
            initial_heap_allocate(counter, size, align),
        KernelHeap::LargeHeap(ref mut state) =>
            large_heap_allocate(state, size, align)
    }
}

pub unsafe fn deallocate(_ptr: *mut u8, _size: usize, _align: usize) {
    // TODO
}

fn align_addr(mut addr: usize, align: usize) -> usize {
    if addr % align != 0 {
        addr += align - (addr % align);
    }
    addr
}

unsafe fn initial_heap_allocate(counter: &mut usize, size: usize, align: usize)
                                -> *mut u8 {

    let new_counter = align_addr(*counter, align);

    if new_counter + size >= INITIAL_HEAP_LENGTH {
        panic!("out of memory in initial heap!");
    }

    let ptr = (&mut MEMORY_INITIAL_HEAP[new_counter]) as *mut u8;

    *counter = new_counter + size;

    ptr
}

unsafe fn large_heap_allocate(state: &mut HeapState, size: usize, align: usize)
                              -> *mut u8 {

    let start = state.start as usize;

    if state.can_grow {
        // Avoid recursively growing the heap
        state.can_grow = false;

        // The maximum amount of room we will need to align is (align - 1)
        // bytes, so reserve that just in case.
        //
        // We can't predict what the alignment will be after the
        // kernel_acquire_and_map() call, since mapping pages sometimes requires
        // memory allocation, but it should always fit within the BUFZONE_SIZE.
        let min_end = start + state.length +
            (align - 1) + size + BUFZONE_SIZE;

        if min_end >= state.end as usize {
            let page_size = Pageset::page_size();
            let needed_bytes = min_end - state.end as usize;
            let mut needed_pages = needed_bytes / page_size;

            if needed_bytes % page_size != 0 {
                needed_pages += 1;
            }

            kernel_acquire_and_map(state.end, needed_pages, &mut state.regions);

            asm!("nop" :::: "volatile");

            state.end =
                (state.end as usize + (needed_pages * page_size)) as *mut u8;
        }

        state.can_grow = true;
    } else {
        let new_addr = align_addr(start + state.length, align);

        if new_addr + size >= state.end as usize {
            panic!("ran out of bufzone memory while trying to get more memory");
        }
    }

    state.length = align_addr(start + state.length, align) - start;

    let alloc_addr = (start + state.length) as *mut u8;

    state.length += size;

    alloc_addr
}

pub unsafe fn enable_large_heap() {
    assert!(paging::initialized());

    if let KernelHeap::LargeHeap(_) = KERNEL_HEAP {
        // Already enabled, don't need to do anything.
        return;
    }

    let bufzone_pages = BUFZONE_SIZE/Pageset::page_size();

    let mut regions = vec![];

    kernel_acquire_and_map(
        LARGE_HEAP_START as *mut u8, bufzone_pages, &mut regions);

    KERNEL_HEAP = KernelHeap::LargeHeap(HeapState {
        start:    LARGE_HEAP_START as *mut u8,
        end:      (LARGE_HEAP_START + BUFZONE_SIZE) as *mut u8,
        length:   0,
        regions:  regions,
        can_grow: true
    });
}

pub fn acquire_region(owner: RegionUser, pages: PageCount)
                      -> Option<(PhysicalAddress, PageCount)> {
    let page_size = Pageset::page_size();

    unsafe {
        let free_regions = FREE_REGIONS.as_mut()
            .expect("memory::initialize() not called");

        let alloc_regions = ALLOC_REGIONS.as_mut().unwrap();

        if TOTAL_FREE < pages {
            return None;
        }

        if let Some(free_region) = free_regions.pop() {
            if free_region.length > pages {
                free_regions.push(FreeRegion {
                    start: free_region.start + (pages * page_size),
                    length: free_region.length - pages
                });
            }

            let captured_length = min(free_region.length, pages);

            alloc_regions.insert(free_region.start, AllocRegionState {
                length: captured_length,
                users: vec![owner]
            });

            TOTAL_FREE -= captured_length;

            Some((free_region.start, captured_length))
        } else {
            None
        }
    }
}

pub fn release_region(user: RegionUser, paddr: PhysicalAddress) {
    unimplemented!()
}

fn kernel_acquire_and_map(vaddr: *mut u8,
                          pages: PageCount,
                          regions: &mut Vec<(PhysicalAddress, PageCount)>) {
    let page_size = Pageset::page_size();

    let mut cur_vaddr = vaddr as usize;
    let mut cur_pages = pages;

    while cur_pages > 0 {
        let (got_paddr, got_pages) =
            acquire_region(RegionUser::Kernel, cur_pages)
                .expect("not enough memory for kernel");

        unsafe {
            kernel_pageset().map_pages_with_type(
                cur_vaddr,
                (got_paddr..).step_by(page_size).take(got_pages),
                PageType::default().writable()
            ).expect("unable to map acquired pages into kernel space")
        }

        cur_vaddr += got_pages * page_size;
        cur_pages -= got_pages;

        regions.push((got_paddr, got_pages));
    }
}

/// C foreign interface.
pub mod ffi {
    use super::*;

    #[no_mangle]
    pub unsafe extern fn memory_alloc_aligned(size: u64, align: u64)
                                              -> *mut u8 {
        allocate(size as usize, align as usize)
    }

    #[no_mangle]
    pub unsafe extern fn memory_alloc(size: u64) -> *mut u8 {
        memory_alloc_aligned(size, 1)
    }

    #[no_mangle]
    pub unsafe extern fn memory_free(ptr: *mut u8) {
        deallocate(ptr, 0, 0)
    }

    #[no_mangle]
    pub extern fn memory_get_total_free() -> u64 {
        unimplemented!()
    }
}
<commit_msg>change SAFE_BOUNDARY to 0x800000, to reflect the fact that identity mapping changed to that quite a while ago<commit_after>/*******************************************************************************
 *
 * kit/kernel/memory.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

//! Physical memory management and kernel heap.

use core::mem;
use core::cmp::{min, Ordering};
use collections::{Vec, BTreeMap, BinaryHeap};

use paging::{self, kernel_pageset, Pageset};
use paging::{GenericPageset, PagesetExt, PageType};

use multiboot::MmapEntry;
use process::Id as ProcessId;

/// The first "safe" physical address. Memory below this is not likely to be
/// safe for general use, and may include parts of the kernel image among other
/// things.
///
/// 0x0 to SAFE_BOUNDARY are identity-mapped starting at 0xffff_ffff_8000_0000,
/// so we avoid that region
const SAFE_BOUNDARY: usize = 0x800000;

const INITIAL_HEAP_LENGTH: usize = 131072;

extern {
    static mut MEMORY_INITIAL_HEAP: [u8; INITIAL_HEAP_LENGTH];
}

static mut KERNEL_HEAP: KernelHeap = KernelHeap::InitialHeap(0);

#[derive(Debug)]
enum KernelHeap {
    InitialHeap(usize),
    LargeHeap(HeapState)
}

#[derive(Debug)]
struct HeapState {
    start: *mut u8,
    end: *mut u8,
    length: usize,
    regions: Vec<(PhysicalAddress, PageCount)>,
    can_grow: bool
}

static mut ALLOC_REGIONS: Option<BTreeMap<PhysicalAddress, AllocRegionState>> = None;
static mut FREE_REGIONS: Option<BinaryHeap<FreeRegion>> = None;

static mut TOTAL_FREE: PageCount = 0;

pub type PhysicalAddress = usize;
pub type PageCount = usize;

#[derive(Debug)]
struct AllocRegionState {
    length: PageCount,
    users: Vec<RegionUser>
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RegionUser {
    Kernel,
    Process(ProcessId)
}

#[derive(Debug, PartialEq, Eq)]
struct FreeRegion {
    start: PhysicalAddress,
    length: PageCount
}

impl PartialOrd for FreeRegion {
    fn partial_cmp(&self, other: &FreeRegion) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for FreeRegion {
    fn cmp(&self, other: &FreeRegion) -> Ordering {
        if self.length == other.length {
            self.start.cmp(&other.start)
        } else {
            other.length.cmp(&self.length)
        }
    }
}

/// Loads the memory map information into the region tree in order to know where
/// in physical memory it's safe to allocate fresh pages.
pub unsafe fn initialize(mmap_buffer: *const u8, mmap_length: u32) {
    let mut current_mmap = mmap_buffer;
    let mmap_end = mmap_buffer.offset(mmap_length as isize);

    let page_size = Pageset::page_size();

    let mut free_regions = BinaryHeap::with_capacity(16);

    while current_mmap < mmap_end {
        let entry_ptr: *const MmapEntry = mem::transmute(current_mmap);
        let entry = entry_ptr.as_ref().unwrap();

        current_mmap = current_mmap.offset(entry.size as isize + 4);

        let addr = entry.addr as usize;
        let len  = entry.len  as usize;

        // Align physical base address to page size.
        let mut physical_base =
            if addr % page_size != 0 {
                (addr / page_size + 1) * page_size
            } else {
                addr
            };

        // Remove remainder from length and count pages.
        let mut pages = (len - (addr % page_size)) / page_size;

        // If the base starts before SAFE_BOUNDARY, remove the pages before that
        // (and make sure we still have pages left).
        if physical_base < SAFE_BOUNDARY {
            let diff = (SAFE_BOUNDARY - physical_base) / page_size;

            if diff < pages {
                physical_base += diff * page_size;
                pages         -= diff;
            } else {
                continue; // skip this entry
            }
        }

        // If the entry is marked as available and has at least one page, add it
        // to the free regions.
        if entry.is_available() && pages > 0 {
            free_regions.push(FreeRegion {
                start: physical_base,
                length: pages
            });
        }
    }

    TOTAL_FREE = free_regions.iter().fold(0, |s, r| s + r.length);

    FREE_REGIONS = Some(free_regions);
    ALLOC_REGIONS = Some(BTreeMap::new());
}

const LARGE_HEAP_START: usize = 0xffff_ffff_8100_0000;
const BUFZONE_SIZE: usize = 0x4000;

pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
    match KERNEL_HEAP {
        KernelHeap::InitialHeap(ref mut counter) =>
            initial_heap_allocate(counter, size, align),
        KernelHeap::LargeHeap(ref mut state) =>
            large_heap_allocate(state, size, align)
    }
}

pub unsafe fn deallocate(_ptr: *mut u8, _size: usize, _align: usize) {
    // TODO
}

fn align_addr(mut addr: usize, align: usize) -> usize {
    if addr % align != 0 {
        addr += align - (addr % align);
    }
    addr
}

unsafe fn initial_heap_allocate(counter: &mut usize, size: usize, align: usize)
                                -> *mut u8 {

    let new_counter = align_addr(*counter, align);

    if new_counter + size >= INITIAL_HEAP_LENGTH {
        panic!("out of memory in initial heap!");
    }

    let ptr = (&mut MEMORY_INITIAL_HEAP[new_counter]) as *mut u8;

    *counter = new_counter + size;

    ptr
}

unsafe fn large_heap_allocate(state: &mut HeapState, size: usize, align: usize)
                              -> *mut u8 {

    let start = state.start as usize;

    if state.can_grow {
        // Avoid recursively growing the heap
        state.can_grow = false;

        // The maximum amount of room we will need to align is (align - 1)
        // bytes, so reserve that just in case.
        //
        // We can't predict what the alignment will be after the
        // kernel_acquire_and_map() call, since mapping pages sometimes requires
        // memory allocation, but it should always fit within the BUFZONE_SIZE.
        let min_end = start + state.length +
            (align - 1) + size + BUFZONE_SIZE;

        if min_end >= state.end as usize {
            let page_size = Pageset::page_size();
            let needed_bytes = min_end - state.end as usize;
            let mut needed_pages = needed_bytes / page_size;

            if needed_bytes % page_size != 0 {
                needed_pages += 1;
            }

            kernel_acquire_and_map(state.end, needed_pages, &mut state.regions);

            asm!("nop" :::: "volatile");

            state.end =
                (state.end as usize + (needed_pages * page_size)) as *mut u8;
        }

        state.can_grow = true;
    } else {
        let new_addr = align_addr(start + state.length, align);

        if new_addr + size >= state.end as usize {
            panic!("ran out of bufzone memory while trying to get more memory");
        }
    }

    state.length = align_addr(start + state.length, align) - start;

    let alloc_addr = (start + state.length) as *mut u8;

    state.length += size;

    alloc_addr
}

pub unsafe fn enable_large_heap() {
    assert!(paging::initialized());

    if let KernelHeap::LargeHeap(_) = KERNEL_HEAP {
        // Already enabled, don't need to do anything.
        return;
    }

    let bufzone_pages = BUFZONE_SIZE/Pageset::page_size();

    let mut regions = vec![];

    kernel_acquire_and_map(
        LARGE_HEAP_START as *mut u8, bufzone_pages, &mut regions);

    KERNEL_HEAP = KernelHeap::LargeHeap(HeapState {
        start:    LARGE_HEAP_START as *mut u8,
        end:      (LARGE_HEAP_START + BUFZONE_SIZE) as *mut u8,
        length:   0,
        regions:  regions,
        can_grow: true
    });
}

pub fn acquire_region(owner: RegionUser, pages: PageCount)
                      -> Option<(PhysicalAddress, PageCount)> {
    let page_size = Pageset::page_size();

    unsafe {
        let free_regions = FREE_REGIONS.as_mut()
            .expect("memory::initialize() not called");

        let alloc_regions = ALLOC_REGIONS.as_mut().unwrap();

        if TOTAL_FREE < pages {
            return None;
        }

        if let Some(free_region) = free_regions.pop() {
            if free_region.length > pages {
                free_regions.push(FreeRegion {
                    start: free_region.start + (pages * page_size),
                    length: free_region.length - pages
                });
            }

            let captured_length = min(free_region.length, pages);

            alloc_regions.insert(free_region.start, AllocRegionState {
                length: captured_length,
                users: vec![owner]
            });

            TOTAL_FREE -= captured_length;

            Some((free_region.start, captured_length))
        } else {
            None
        }
    }
}

pub fn release_region(user: RegionUser, paddr: PhysicalAddress) {
    unimplemented!()
}

fn kernel_acquire_and_map(vaddr: *mut u8,
                          pages: PageCount,
                          regions: &mut Vec<(PhysicalAddress, PageCount)>) {
    let page_size = Pageset::page_size();

    let mut cur_vaddr = vaddr as usize;
    let mut cur_pages = pages;

    while cur_pages > 0 {
        let (got_paddr, got_pages) =
            acquire_region(RegionUser::Kernel, cur_pages)
                .expect("not enough memory for kernel");

        unsafe {
            kernel_pageset().map_pages_with_type(
                cur_vaddr,
                (got_paddr..).step_by(page_size).take(got_pages),
                PageType::default().writable()
            ).expect("unable to map acquired pages into kernel space")
        }

        cur_vaddr += got_pages * page_size;
        cur_pages -= got_pages;

        regions.push((got_paddr, got_pages));
    }
}

/// C foreign interface.
pub mod ffi {
    use super::*;

    #[no_mangle]
    pub unsafe extern fn memory_alloc_aligned(size: u64, align: u64)
                                              -> *mut u8 {
        allocate(size as usize, align as usize)
    }

    #[no_mangle]
    pub unsafe extern fn memory_alloc(size: u64) -> *mut u8 {
        memory_alloc_aligned(size, 1)
    }

    #[no_mangle]
    pub unsafe extern fn memory_free(ptr: *mut u8) {
        deallocate(ptr, 0, 0)
    }

    #[no_mangle]
    pub extern fn memory_get_total_free() -> u64 {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clap::Parser;

#[test]
fn basic() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Bar
        },
        Opt::try_parse_from(&["", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}

#[test]
fn default_value() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    impl Default for ArgChoice {
        fn default() -> Self {
            Self::Bar
        }
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, default_value_t)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Bar
        },
        Opt::try_parse_from(&["", "bar"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Bar
        },
        Opt::try_parse_from(&[""]).unwrap()
    );
}

#[test]
fn multi_word_is_renamed_kebab() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[allow(non_camel_case_types)]
    enum ArgChoice {
        FooBar,
        BAR_BAZ,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "foo-bar"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::BAR_BAZ
        },
        Opt::try_parse_from(&["", "bar-baz"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}

#[test]
fn variant_with_defined_casing() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        #[clap(rename_all = "screaming_snake")]
        FooBar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "FOO_BAR"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}

#[test]
fn casing_is_propagated_from_parent() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[clap(rename_all = "screaming_snake")]
    enum ArgChoice {
        FooBar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "FOO_BAR"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}

#[test]
fn casing_propagation_is_overridden() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[clap(rename_all = "screaming_snake")]
    enum ArgChoice {
        #[clap(rename_all = "camel")]
        FooBar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "fooBar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
    assert!(Opt::try_parse_from(&["", "FOO_BAR"]).is_err());
}

#[test]
fn ignore_case() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(true))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "fOo"]).unwrap()
    );
}

#[test]
fn ignore_case_set_to_false() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(false))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}

#[test]
fn alias() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        #[clap(alias = "TOTP")]
        Totp,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(false))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "totp"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "TOTP"]).unwrap()
    );
}

#[test]
fn multiple_alias() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        #[clap(alias = "TOTP", alias = "t")]
        Totp,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(false))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "totp"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "TOTP"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "t"]).unwrap()
    );
}

#[test]
fn skip_variant() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[allow(dead_code)] // silence warning about `Baz` being unused
    enum ArgChoice {
        Foo,
        Bar,
        #[clap(skip)]
        Baz,
    }

    assert_eq!(
        <ArgChoice as clap::ArgEnum>::value_variants()
            .iter()
            .map(clap::ArgEnum::to_possible_value)
            .map(Option::unwrap)
            .collect::<Vec<_>>(),
        vec![
            clap::PossibleValue::new("foo"),
            clap::PossibleValue::new("bar")
        ]
    );

    {
        use clap::ArgEnum;
        assert!(ArgChoice::from_str("foo", true).is_ok());
        assert!(ArgChoice::from_str("bar", true).is_ok());
        assert!(ArgChoice::from_str("baz", true).is_err());
    }
}

#[test]
fn from_str_invalid() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
    }

    {
        use clap::ArgEnum;
        assert!(ArgChoice::from_str("bar", true).is_err());
    }
}

#[test]
fn option_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: Option<ArgChoice>,
    }

    assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt {
            arg: Some(ArgChoice::Foo)
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(ArgChoice::Bar)
        },
        Opt::try_parse_from(&["", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}

#[test]
fn option_option_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, long)]
        arg: Option<Option<ArgChoice>>,
    }

    assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt { arg: Some(None) },
        Opt::try_parse_from(&["", "--arg"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(Some(ArgChoice::Foo))
        },
        Opt::try_parse_from(&["", "--arg", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(Some(ArgChoice::Bar))
        },
        Opt::try_parse_from(&["", "--arg", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "--arg", "fOo"]).is_err());
}

#[test]
fn vec_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, short, long)]
        arg: Vec<ArgChoice>,
    }

    assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt {
            arg: vec![ArgChoice::Foo]
        },
        Opt::try_parse_from(&["", "-a", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: vec![ArgChoice::Foo, ArgChoice::Bar]
        },
        Opt::try_parse_from(&["", "-a", "foo", "-a", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
}

#[test]
fn option_vec_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, short, long)]
        arg: Option<Vec<ArgChoice>>,
    }

    assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt {
            arg: Some(vec![ArgChoice::Foo])
        },
        Opt::try_parse_from(&["", "-a", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(vec![ArgChoice::Foo, ArgChoice::Bar])
        },
        Opt::try_parse_from(&["", "-a", "foo", "-a", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
}
<commit_msg>test: Add test to demonstrate regression<commit_after>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clap::Parser;

#[test]
fn basic() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Bar
        },
        Opt::try_parse_from(&["", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}

#[test]
fn default_value() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    impl Default for ArgChoice {
        fn default() -> Self {
            Self::Bar
        }
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, default_value_t)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Bar
        },
        Opt::try_parse_from(&["", "bar"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Bar
        },
        Opt::try_parse_from(&[""]).unwrap()
    );
}

#[test]
fn multi_word_is_renamed_kebab() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[allow(non_camel_case_types)]
    enum ArgChoice {
        FooBar,
        BAR_BAZ,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "foo-bar"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::BAR_BAZ
        },
        Opt::try_parse_from(&["", "bar-baz"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}

#[test]
fn variant_with_defined_casing() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        #[clap(rename_all = "screaming_snake")]
        FooBar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "FOO_BAR"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}

#[test]
fn casing_is_propagated_from_parent() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[clap(rename_all = "screaming_snake")]
    enum ArgChoice {
        FooBar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "FOO_BAR"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}

#[test]
fn casing_propagation_is_overridden() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[clap(rename_all = "screaming_snake")]
    enum ArgChoice {
        #[clap(rename_all = "camel")]
        FooBar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::FooBar
        },
        Opt::try_parse_from(&["", "fooBar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
    assert!(Opt::try_parse_from(&["", "FOO_BAR"]).is_err());
}

#[test]
fn ignore_case() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(true))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "fOo"]).unwrap()
    );
}

#[test]
fn ignore_case_set_to_false() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(false))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Foo
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}

#[test]
fn alias() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        #[clap(alias = "TOTP")]
        Totp,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(false))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "totp"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "TOTP"]).unwrap()
    );
}

#[test]
fn multiple_alias() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        #[clap(alias = "TOTP", alias = "t")]
        Totp,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, ignore_case(false))]
        arg: ArgChoice,
    }

    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "totp"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "TOTP"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: ArgChoice::Totp
        },
        Opt::try_parse_from(&["", "t"]).unwrap()
    );
}

#[test]
fn skip_variant() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    #[allow(dead_code)] // silence warning about `Baz` being unused
    enum ArgChoice {
        Foo,
        Bar,
        #[clap(skip)]
        Baz,
    }

    assert_eq!(
        <ArgChoice as clap::ArgEnum>::value_variants()
            .iter()
            .map(clap::ArgEnum::to_possible_value)
            .map(Option::unwrap)
            .collect::<Vec<_>>(),
        vec![
            clap::PossibleValue::new("foo"),
            clap::PossibleValue::new("bar")
        ]
    );

    {
        use clap::ArgEnum;
        assert!(ArgChoice::from_str("foo", true).is_ok());
        assert!(ArgChoice::from_str("bar", true).is_ok());
        assert!(ArgChoice::from_str("baz", true).is_err());
    }
}

#[test]
fn from_str_invalid() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
    }

    {
        use clap::ArgEnum;
        assert!(ArgChoice::from_str("bar", true).is_err());
    }
}

#[test]
fn option_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum)]
        arg: Option<ArgChoice>,
    }

    assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt {
            arg: Some(ArgChoice::Foo)
        },
        Opt::try_parse_from(&["", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(ArgChoice::Bar)
        },
        Opt::try_parse_from(&["", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}

#[test]
fn option_option_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, long)]
        arg: Option<Option<ArgChoice>>,
    }

    assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt { arg: Some(None) },
        Opt::try_parse_from(&["", "--arg"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(Some(ArgChoice::Foo))
        },
        Opt::try_parse_from(&["", "--arg", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(Some(ArgChoice::Bar))
        },
        Opt::try_parse_from(&["", "--arg", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "--arg", "fOo"]).is_err());
}

#[test]
fn vec_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, short, long)]
        arg: Vec<ArgChoice>,
    }

    assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt {
            arg: vec![ArgChoice::Foo]
        },
        Opt::try_parse_from(&["", "-a", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: vec![ArgChoice::Foo, ArgChoice::Bar]
        },
        Opt::try_parse_from(&["", "-a", "foo", "-a", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
}

#[test]
fn option_vec_type() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(arg_enum, short, long)]
        arg: Option<Vec<ArgChoice>>,
    }

    assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
    assert_eq!(
        Opt {
            arg: Some(vec![ArgChoice::Foo])
        },
        Opt::try_parse_from(&["", "-a", "foo"]).unwrap()
    );
    assert_eq!(
        Opt {
            arg: Some(vec![ArgChoice::Foo, ArgChoice::Bar])
        },
        Opt::try_parse_from(&["", "-a", "foo", "-a", "bar"]).unwrap()
    );
    assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
}

#[test]
fn vec_type_default_value() {
    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
    enum ArgChoice {
        Foo,
        Bar,
        Baz,
    }

    #[derive(Parser, PartialEq, Debug)]
    struct Opt {
        #[clap(
            arg_enum,
            short,
            long,
            default_value = "foo,bar",
            value_delimiter = ','
        )]
        arg: Vec<ArgChoice>,
    }

    assert_eq!(
        Opt {
            arg: vec![ArgChoice::Foo, ArgChoice::Bar]
        },
        Opt::try_parse_from(&[""]).unwrap()
    );

    assert_eq!(
        Opt {
            arg: vec![ArgChoice::Foo, ArgChoice::Baz]
        },
        Opt::try_parse_from(&["", "-a", "foo,baz"]).unwrap()
    );
}
<|endoftext|>


<commit_before>extern crate half;
extern crate openexr;
extern crate openexr_sys;

use half::f16;
use openexr::{FrameBuffer, FrameBufferMut, Header, InputFile, ScanlineOutputFile};
use std::fs::File;

// OpenEXR file data.
const DATA: &[u8] = include_bytes!("data/negative_window.exr");

#[test]
fn negative_window_read_multiple_channels() {
    let mut exr_file = InputFile::from_slice(DATA).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    println!("Reading pixels from {},{},{}", "R", "G", "B");
    let mut pixel_data = vec![(zero, zero, zero); (width * height) as usize];

    // let's try a mismatched origin 0,0
    {
        let mut fb = FrameBufferMut::new(width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // let's try a mismatched origin somewhere else
    {
        let mut fb = FrameBufferMut::new_with_origin(10, 11, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // and then the real thing
    let origin_offset = {
        let mut fb = FrameBufferMut::new_with_origin(x, y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
        exr_file.read_pixels(&mut fb).unwrap();
        exr_file.read_pixels(&mut fb).unwrap();
        fb.origin_offset() as usize
    };
    // check the pixel value at (0,0). "==" is
    assert!(f32::abs(pixel_data[origin_offset].0.to_f32() - 0.5f32) < 0.0001f32);

    // we write the file back out with a different offset
    {
        let mut fb = FrameBuffer::new_with_origin(-8, -8, width, height);
        println!("Loading buffer as {}x{}", width, height);
        let mut file = File::create("target/negative_window_with_offset.exr")
            .expect("Could not create output file");
        let mut exr_file = ScanlineOutputFile::new(
            &mut file,
            &Header::new()
                .set_data_window(Header::box2i(-8, -8, width, height))
                .set_display_window(Header::box2i(0, 0, width - 16, height - 16))
                .add_channel("R", openexr::PixelType::HALF)
                .add_channel("G", openexr::PixelType::HALF)
                .add_channel("B", openexr::PixelType::HALF),
        )
        .unwrap();

        fb.insert_channels(&["R", "G", "B"], &pixel_data);
        exr_file.write_pixels(&fb).unwrap();
    }
}

// with one channel only as well
#[test]
fn negative_window_read_single_channel() {
    let mut exr_file = InputFile::from_slice(DATA).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    println!("Reading pixels from {},{},{}", "R", "G", "B");
    let mut pixel_data = vec![zero; (width * height) as usize];

    // let's try a mismatched origin in 0,0 (default)
    {
        let mut fb = FrameBufferMut::new(width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channel("R", 0.0, &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // let's try a mismatched origin in 0,0 (default)
    {
        let mut fb = FrameBufferMut::new_with_origin(-1, -10, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channel("R", 0.0, &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // now the real deal
    {
        let mut fb = FrameBufferMut::new_with_origin(x, y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channel("R", 0.0, &mut pixel_data);
        exr_file.read_pixels(&mut fb).unwrap();
    }
}
<commit_msg>Slightly improved example comment<commit_after>extern crate half;
extern crate openexr;
extern crate openexr_sys;

use half::f16;
use openexr::{FrameBuffer, FrameBufferMut, Header, InputFile, ScanlineOutputFile};
use std::fs::File;

// OpenEXR file data.
const DATA: &[u8] = include_bytes!("data/negative_window.exr");

#[test]
fn negative_window_read_multiple_channels() {
    let mut exr_file = InputFile::from_slice(DATA).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    println!("Reading pixels from {},{},{}", "R", "G", "B");
    let mut pixel_data = vec![(zero, zero, zero); (width * height) as usize];

    // let's try a mismatched origin 0,0
    {
        let mut fb = FrameBufferMut::new(width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // let's try a mismatched origin somewhere else
    {
        let mut fb = FrameBufferMut::new_with_origin(10, 11, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // and then the real thing
    let origin_offset = {
        let mut fb = FrameBufferMut::new_with_origin(x, y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
        exr_file.read_pixels(&mut fb).unwrap();
        exr_file.read_pixels(&mut fb).unwrap();
        fb.origin_offset() as usize
    };
    // check the pixel value at coordinates (0,0) of the data window
    assert!(f32::abs(pixel_data[origin_offset].0.to_f32() - 0.5f32) < 0.0001f32);

    // we write the file back out with a different offset
    {
        let mut fb = FrameBuffer::new_with_origin(-8, -8, width, height);
        println!("Loading buffer as {}x{}", width, height);
        let mut file = File::create("target/negative_window_with_offset.exr")
            .expect("Could not create output file");
        let mut exr_file = ScanlineOutputFile::new(
            &mut file,
            &Header::new()
                .set_data_window(Header::box2i(-8, -8, width, height))
                .set_display_window(Header::box2i(0, 0, width - 16, height - 16))
                .add_channel("R", openexr::PixelType::HALF)
                .add_channel("G", openexr::PixelType::HALF)
                .add_channel("B", openexr::PixelType::HALF),
        )
        .unwrap();

        fb.insert_channels(&["R", "G", "B"], &pixel_data);
        exr_file.write_pixels(&fb).unwrap();
    }
}

// with one channel only as well
#[test]
fn negative_window_read_single_channel() {
    let mut exr_file = InputFile::from_slice(DATA).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    println!("Reading pixels from {},{},{}", "R", "G", "B");
    let mut pixel_data = vec![zero; (width * height) as usize];

    // let's try a mismatched origin in 0,0 (default)
    {
        let mut fb = FrameBufferMut::new(width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channel("R", 0.0, &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // let's try a mismatched origin in 0,0 (default)
    {
        let mut fb = FrameBufferMut::new_with_origin(-1, -10, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channel("R", 0.0, &mut pixel_data);
        assert!(exr_file.read_pixels(&mut fb).is_err());
    }

    // now the real deal
    {
        let mut fb = FrameBufferMut::new_with_origin(x, y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        fb.insert_channel("R", 0.0, &mut pixel_data);
        exr_file.read_pixels(&mut fb).unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::utils::{get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, SpanlessEq};
use crate::utils::{higher, sugg};
use rustc::hir;
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc::lint::*;
use rustc::{declare_lint, lint_array};
use if_chain::if_chain;
use syntax::ast;

/// **What it does:** Checks for compound assignment operations (`+=` and
/// similar).
///
/// **Why is this bad?** Projects with many developers from languages without
/// those operations may find them unreadable and not worth their weight.
///
/// **Known problems:** Types implementing `OpAssign` don't necessarily
/// implement `Op`.
///
/// **Example:**
/// ```rust
/// a += 1;
/// ```
declare_clippy_lint! {
    pub ASSIGN_OPS,
    restriction,
    "any compound assignment operation"
}

/// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
/// patterns.
///
/// **Why is this bad?** These can be written as the shorter `a op= b`.
///
/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have
/// implementations that differ from the regular `Op` impl.
///
/// **Example:**
/// ```rust
/// let mut a = 5;
/// ...
/// a = a + b;
/// ```
declare_clippy_lint! {
    pub ASSIGN_OP_PATTERN,
    style,
    "assigning the result of an operation on a variable to that same variable"
}

/// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
///
/// **Why is this bad?** Most likely these are bugs where one meant to write `a
/// op= b`.
///
/// **Known problems:** Someone might actually mean `a op= a op b`, but that
/// should rather be written as `a = (2 * a) op b` where applicable.
///
/// **Example:**
/// ```rust
/// let mut a = 5;
/// ...
/// a += a + b;
/// ```
declare_clippy_lint! {
    pub MISREFACTORED_ASSIGN_OP,
    complexity,
    "having a variable on both sides of an assign op"
}

#[derive(Copy, Clone, Default)]
pub struct AssignOps;

impl LintPass for AssignOps {
    fn get_lints(&self) -> LintArray {
        lint_array!(ASSIGN_OPS, ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        match expr.node {
            hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
                span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
                    let lhs = &sugg::Sugg::hir(cx, lhs, "..");
                    let rhs = &sugg::Sugg::hir(cx, rhs, "..");

                    db.span_suggestion(
                        expr.span,
                        "replace it with",
                        format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)),
                    );
                });
                if let hir::ExprKind::Binary(binop, ref l, ref r) = rhs.node {
                    if op.node == binop.node {
                        let lint = |assignee: &hir::Expr, rhs_other: &hir::Expr| {
                            span_lint_and_then(
                                cx,
                                MISREFACTORED_ASSIGN_OP,
                                expr.span,
                                "variable appears on both sides of an assignment operation",
                                |db| {
                                    if let (Some(snip_a), Some(snip_r)) =
                                        (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span))
                                    {
                                        let a = &sugg::Sugg::hir(cx, assignee, "..");
                                        let r = &sugg::Sugg::hir(cx, rhs, "..");
                                        let long =
                                            format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
                                        db.span_suggestion(
                                            expr.span,
                                            &format!(
                                                "Did you mean {} = {} {} {} or {}? Consider replacing it with",
                                                snip_a,
                                                snip_a,
                                                op.node.as_str(),
                                                snip_r,
                                                long
                                            ),
                                            format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
                                        );
                                        db.span_suggestion(expr.span, "or", long);
                                    }
                                },
                            );
                        };
                        // lhs op= l op r
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
                            lint(lhs, r);
                        }
                        // lhs op= l commutative_op r
                        if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
                            lint(lhs, l);
                        }
                    }
                }
            },
            hir::ExprKind::Assign(ref assignee, ref e) => {
                if let hir::ExprKind::Binary(op, ref l, ref r) = e.node {
                    #[allow(cyclomatic_complexity)]
                    let lint = |assignee: &hir::Expr, rhs: &hir::Expr| {
                        let ty = cx.tables.expr_ty(assignee);
                        let rty = cx.tables.expr_ty(rhs);
                        macro_rules! ops {
                            ($op:expr,
                             $cx:expr,
                             $ty:expr,
                             $rty:expr,
                             $($trait_name:ident),+) => {
                                match $op {
                                    $(hir::BinOpKind::$trait_name => {
                                        let [krate, module] = crate::utils::paths::OPS_MODULE;
                                        let path = [krate, module, concat!(stringify!($trait_name), "Assign")];
                                        let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) {
                                            trait_id
                                        } else {
                                            return; // useless if the trait doesn't exist
                                        };
                                        // check that we are not inside an `impl AssignOp` of this exact operation
                                        let parent_fn = cx.tcx.hir.get_parent(e.id);
                                        let parent_impl = cx.tcx.hir.get_parent(parent_fn);
                                        // the crate node is the only one that is not in the map
                                        if_chain! {
                                            if parent_impl != ast::CRATE_NODE_ID;
                                            if let hir::map::Node::NodeItem(item) = cx.tcx.hir.get(parent_impl);
                                            if let hir::ItemKind::Impl(_, _, _, _, Some(ref trait_ref), _, _) =
                                                item.node;
                                            if trait_ref.path.def.def_id() == trait_id;
                                            then { return; }
                                        }
                                        implements_trait($cx, $ty, trait_id, &[$rty])
                                    },)*
                                    _ => false,
                                }
                            }
                        }
                        if ops!(
                            op.node,
                            cx,
                            ty,
                            rty.into(),
                            Add,
                            Sub,
                            Mul,
                            Div,
                            Rem,
                            And,
                            Or,
                            BitAnd,
                            BitOr,
                            BitXor,
                            Shr,
                            Shl
                        ) {
                            span_lint_and_then(
                                cx,
                                ASSIGN_OP_PATTERN,
                                expr.span,
                                "manual implementation of an assign operation",
                                |db| {
                                    if let (Some(snip_a), Some(snip_r)) =
                                        (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
                                    {
                                        db.span_suggestion(
                                            expr.span,
                                            "replace it with",
                                            format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
                                        );
                                    }
                                },
                            );
                        }
                    };

                    let mut visitor = ExprVisitor {
                        assignee,
                        counter: 0,
                        cx,
                    };

                    walk_expr(&mut visitor, e);

                    if visitor.counter == 1 {
                        // a = a op b
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
                            lint(assignee, r);
                        }
                        // a = b commutative_op a
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) {
                            match op.node {
                                hir::BinOpKind::Add
                                | hir::BinOpKind::Mul
                                | hir::BinOpKind::And
                                | hir::BinOpKind::Or
                                | hir::BinOpKind::BitXor
                                | hir::BinOpKind::BitAnd
                                | hir::BinOpKind::BitOr => {
                                    lint(assignee, l);
                                },
                                _ => {},
                            }
                        }
                    }
                }
            },
            _ => {},
        }
    }
}

fn is_commutative(op: hir::BinOpKind) -> bool {
    use rustc::hir::BinOpKind::*;
    match op {
        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
    }
}

struct ExprVisitor<'a, 'tcx: 'a> {
    assignee: &'a hir::Expr,
    counter: u8,
    cx: &'a LateContext<'a, 'tcx>,
}

impl<'a, 'tcx: 'a> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) {
            self.counter += 1;
        }

        walk_expr(self, expr);
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}
<commit_msg>Expand on misrefactored_assign_op known problems<commit_after>use crate::utils::{get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, SpanlessEq};
use crate::utils::{higher, sugg};
use rustc::hir;
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc::lint::*;
use rustc::{declare_lint, lint_array};
use if_chain::if_chain;
use syntax::ast;

/// **What it does:** Checks for compound assignment operations (`+=` and
/// similar).
///
/// **Why is this bad?** Projects with many developers from languages without
/// those operations may find them unreadable and not worth their weight.
///
/// **Known problems:** Types implementing `OpAssign` don't necessarily
/// implement `Op`.
///
/// **Example:**
/// ```rust
/// a += 1;
/// ```
declare_clippy_lint! {
    pub ASSIGN_OPS,
    restriction,
    "any compound assignment operation"
}

/// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
/// patterns.
///
/// **Why is this bad?** These can be written as the shorter `a op= b`.
///
/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have
/// implementations that differ from the regular `Op` impl.
///
/// **Example:**
/// ```rust
/// let mut a = 5;
/// ...
/// a = a + b;
/// ```
declare_clippy_lint! {
    pub ASSIGN_OP_PATTERN,
    style,
    "assigning the result of an operation on a variable to that same variable"
}

/// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
///
/// **Why is this bad?** Most likely these are bugs where one meant to write `a
/// op= b`.
///
/// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have
/// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore it suggests both.
/// If `a op= a op b` is really the correct behaviour then
/// rewrite it as `a = (2 * a) op b` as it's less confusing.
///
/// **Example:**
/// ```rust
/// let mut a = 5;
/// ...
/// a += a + b;
/// ```
declare_clippy_lint! {
    pub MISREFACTORED_ASSIGN_OP,
    complexity,
    "having a variable on both sides of an assign op"
}

#[derive(Copy, Clone, Default)]
pub struct AssignOps;

impl LintPass for AssignOps {
    fn get_lints(&self) -> LintArray {
        lint_array!(ASSIGN_OPS, ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        match expr.node {
            hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
                span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
                    let lhs = &sugg::Sugg::hir(cx, lhs, "..");
                    let rhs = &sugg::Sugg::hir(cx, rhs, "..");

                    db.span_suggestion(
                        expr.span,
                        "replace it with",
                        format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)),
                    );
                });
                if let hir::ExprKind::Binary(binop, ref l, ref r) = rhs.node {
                    if op.node == binop.node {
                        let lint = |assignee: &hir::Expr, rhs_other: &hir::Expr| {
                            span_lint_and_then(
                                cx,
                                MISREFACTORED_ASSIGN_OP,
                                expr.span,
                                "variable appears on both sides of an assignment operation",
                                |db| {
                                    if let (Some(snip_a), Some(snip_r)) =
                                        (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span))
                                    {
                                        let a = &sugg::Sugg::hir(cx, assignee, "..");
                                        let r = &sugg::Sugg::hir(cx, rhs, "..");
                                        let long =
                                            format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
                                        db.span_suggestion(
                                            expr.span,
                                            &format!(
                                                "Did you mean {} = {} {} {} or {}? Consider replacing it with",
                                                snip_a,
                                                snip_a,
                                                op.node.as_str(),
                                                snip_r,
                                                long
                                            ),
                                            format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
                                        );
                                        db.span_suggestion(expr.span, "or", long);
                                    }
                                },
                            );
                        };
                        // lhs op= l op r
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
                            lint(lhs, r);
                        }
                        // lhs op= l commutative_op r
                        if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
                            lint(lhs, l);
                        }
                    }
                }
            },
            hir::ExprKind::Assign(ref assignee, ref e) => {
                if let hir::ExprKind::Binary(op, ref l, ref r) = e.node {
                    #[allow(cyclomatic_complexity)]
                    let lint = |assignee: &hir::Expr, rhs: &hir::Expr| {
                        let ty = cx.tables.expr_ty(assignee);
                        let rty = cx.tables.expr_ty(rhs);
                        macro_rules! ops {
                            ($op:expr,
                             $cx:expr,
                             $ty:expr,
                             $rty:expr,
                             $($trait_name:ident),+) => {
                                match $op {
                                    $(hir::BinOpKind::$trait_name => {
                                        let [krate, module] = crate::utils::paths::OPS_MODULE;
                                        let path = [krate, module, concat!(stringify!($trait_name), "Assign")];
                                        let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) {
                                            trait_id
                                        } else {
                                            return; // useless if the trait doesn't exist
                                        };
                                        // check that we are not inside an `impl AssignOp` of this exact operation
                                        let parent_fn = cx.tcx.hir.get_parent(e.id);
                                        let parent_impl = cx.tcx.hir.get_parent(parent_fn);
                                        // the crate node is the only one that is not in the map
                                        if_chain! {
                                            if parent_impl != ast::CRATE_NODE_ID;
                                            if let hir::map::Node::NodeItem(item) = cx.tcx.hir.get(parent_impl);
                                            if let hir::ItemKind::Impl(_, _, _, _, Some(ref trait_ref), _, _) =
                                                item.node;
                                            if trait_ref.path.def.def_id() == trait_id;
                                            then { return; }
                                        }
                                        implements_trait($cx, $ty, trait_id, &[$rty])
                                    },)*
                                    _ => false,
                                }
                            }
                        }
                        if ops!(
                            op.node,
                            cx,
                            ty,
                            rty.into(),
                            Add,
                            Sub,
                            Mul,
                            Div,
                            Rem,
                            And,
                            Or,
                            BitAnd,
                            BitOr,
                            BitXor,
                            Shr,
                            Shl
                        ) {
                            span_lint_and_then(
                                cx,
                                ASSIGN_OP_PATTERN,
                                expr.span,
                                "manual implementation of an assign operation",
                                |db| {
                                    if let (Some(snip_a), Some(snip_r)) =
                                        (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
                                    {
                                        db.span_suggestion(
                                            expr.span,
                                            "replace it with",
                                            format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
                                        );
                                    }
                                },
                            );
                        }
                    };

                    let mut visitor = ExprVisitor {
                        assignee,
                        counter: 0,
                        cx,
                    };

                    walk_expr(&mut visitor, e);

                    if visitor.counter == 1 {
                        // a = a op b
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
                            lint(assignee, r);
                        }
                        // a = b commutative_op a
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) {
                            match op.node {
                                hir::BinOpKind::Add
                                | hir::BinOpKind::Mul
                                | hir::BinOpKind::And
                                | hir::BinOpKind::Or
                                | hir::BinOpKind::BitXor
                                | hir::BinOpKind::BitAnd
                                | hir::BinOpKind::BitOr => {
                                    lint(assignee, l);
                                },
                                _ => {},
                            }
                        }
                    }
                }
            },
            _ => {},
        }
    }
}

fn is_commutative(op: hir::BinOpKind) -> bool {
    use rustc::hir::BinOpKind::*;
    match op {
        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
    }
}

struct ExprVisitor<'a, 'tcx: 'a> {
    assignee: &'a hir::Expr,
    counter: u8,
    cx: &'a LateContext<'a, 'tcx>,
}

impl<'a, 'tcx: 'a> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) {
            self.counter += 1;
        }

        walk_expr(self, expr);
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}
<|endoftext|>