<commit_before>//! Compares the decoding results with reference renderings.

extern crate crc32fast;
extern crate glob;
extern crate image;

use std::fs;
use std::io;
use std::path::PathBuf;
use std::u32;

use crc32fast::Hasher as Crc32;

const BASE_PATH: [&str; 2] = [".", "tests"];
const IMAGE_DIR: &str = "images";
const OUTPUT_DIR: &str = "output";
const REFERENCE_DIR: &str = "reference";

fn process_images<F>(dir: &str, input_decoder: Option<&str>, func: F)
where
    F: Fn(&PathBuf, PathBuf, &str),
{
    let base: PathBuf = BASE_PATH.iter().collect();
    let decoders = &["tga", "tiff", "png", "gif", "bmp", "ico", "jpg", "hdr", "pbm", "webp"];
    for decoder in decoders {
        let mut path = base.clone();
        path.push(dir);
        path.push(decoder);
        path.push("**");
        path.push(
            "*.".to_string() + match input_decoder {
                Some(val) => val,
                None => decoder,
            },
        );
        let pattern = &*format!("{}", path.display());
        for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
            func(&base, path, decoder)
        }
    }
}

#[cfg(feature = "png")]
#[test]
fn render_images() {
    process_images(IMAGE_DIR, None, |base, path, decoder| {
        println!("render_images {}", path.display());
        let img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(e)) => {
                println!("UNSUPPORTED {}: {}", path.display(), e);
                return;
            }
            Err(err) => panic!(format!("decoding of {:?} failed with: {}", path, err)),
        };
        let mut crc = Crc32::new();
        crc.update(&*img);

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };
        let mut out_path = base.clone();

        out_path.push(OUTPUT_DIR);
        out_path.push(decoder);
        out_path.push(testsuite.as_os_str());
        fs::create_dir_all(&out_path).unwrap();
        out_path.push(format!(
            "{}.{}.{}",
            filename.as_os_str().to_str().unwrap(),
            format!("{:x}", crc.finalize()),
            "png"
        ));
        img.save(out_path).unwrap();
    })
}

/// Describes a single test case of `check_references`.
struct ReferenceTestCase {
    orig_filename: String,
    crc: u32,
    ty: ReferenceTestCaseType,
}

enum ReferenceTestCaseType {
    /// The test image is loaded using `image::open`, and the result is compared
    /// against the reference image.
    SingleImage,

    /// From the test image file, a single frame is extracted using
    /// `image::gif::Decoder`, and the result is compared against the reference
    /// image.
    AnimatedGifFrame {
        /// A zero-based frame number.
        frame: usize,
    },
}

impl std::str::FromStr for ReferenceTestCase {
    type Err = &'static str;

    /// Construct `ReferenceTestCase` from the file name of a reference
    /// image.
    fn from_str(filename: &str) -> Result<Self, Self::Err> {
        let mut filename_parts = filename.rsplitn(3, '.');

        // Ignore the file extension
        filename_parts.next().unwrap();

        // The penultimate part of `filename_parts` represents the metadata,
        // describing the test type and other details.
        let meta_str = filename_parts.next().ok_or("missing metadata part")?;
        let meta = meta_str.split('_').collect::<Vec<_>>();
        let (crc, ty);

        if meta.len() == 1 {
            // `CRC`
            crc = parse_crc(&meta[0]).ok_or("malformed CRC")?;
            ty = ReferenceTestCaseType::SingleImage;
        } else if meta.len() == 3 && meta[0] == "anim" {
            // `anim_FRAME_CRC`
            crc = parse_crc(&meta[2]).ok_or("malformed CRC")?;
            let frame: usize = meta[1].parse().map_err(|_| "malformed frame number")?;
            ty = ReferenceTestCaseType::AnimatedGifFrame {
                frame: frame.checked_sub(1).ok_or("frame number must be 1-based")?,
            };
        } else {
            return Err("unrecognized reference image metadata format");
        }

        // The remaining part represents the original file name
        let orig_filename = filename_parts
            .next()
            .ok_or("missing original file name")?
            .to_owned();

        Ok(Self {
            orig_filename,
            crc,
            ty,
        })
    }
}

/// Parse the given string as a hexadecimal CRC hash, used by `check_references`.
fn parse_crc(src: &str) -> Option<u32> {
    u32::from_str_radix(src, 16).ok()
}

#[test]
fn check_references() {
    process_images(REFERENCE_DIR, Some("png"), |base, path, decoder| {
        println!("check_references {}", path.display());

        let ref_img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(_)) => return,
            Err(err) => panic!(format!("{}", err)),
        };

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };

        // Parse the file name to obtain the test case information
        let filename_str = filename.as_os_str().to_str().unwrap();
        let case: ReferenceTestCase = filename_str.parse().unwrap();

        let mut img_path = base.clone();
        img_path.push(IMAGE_DIR);
        img_path.push(decoder);
        img_path.push(testsuite.as_os_str());
        img_path.push(case.orig_filename);

        // Load the test image
        let test_img;

        match case.ty {
            ReferenceTestCaseType::AnimatedGifFrame { frame: frame_num } => {
                #[cfg(feature = "gif_codec")]
                {
                    // Interpret the input file as an animation file
                    use image::AnimationDecoder;
                    let stream = io::BufReader::new(fs::File::open(&img_path).unwrap());
                    let decoder = match image::gif::Decoder::new(stream) {
                        Ok(decoder) => decoder,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => {
                            panic!(format!("decoding of {:?} failed with: {}", img_path, err))
                        }
                    };

                    let mut frames = match decoder.into_frames().collect_frames() {
                        Ok(frames) => frames,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => panic!(format!(
                            "collecting frames of {:?} failed with: {}",
                            img_path, err
                        )),
                    };

                    // Select a single frame
                    let frame = frames.drain(frame_num..).nth(0).unwrap();

                    // Convert the frame to a`RgbaImage`
                    test_img = frame.into_buffer();
                }

                #[cfg(not(feature = "gif_codec"))]
                {
                    println!("Skipping - GIF codec is not enabled");
                    return;
                }
            }

            ReferenceTestCaseType::SingleImage => {
                // Read the input file as a single image
                match image::open(&img_path) {
                    Ok(img) => test_img = img.to_rgba(),
                    // Do not fail on unsupported error
                    // This might happen because the testsuite contains unsupported images
                    // or because a specific decoder included via a feature.
                    Err(image::ImageError::UnsupportedError(_)) => return,
                    Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
                };
            }
        }

        let test_crc_actual = {
            let mut hasher = Crc32::new();
            hasher.update(&*test_img);
            hasher.finalize()
        };

        if test_crc_actual != case.crc {
            panic!(
                "The decoded image's hash does not match (expected = {:08x}, actual = {:08x}).",
                case.crc, test_crc_actual
            );
        }

        if *ref_img != *test_img {
            panic!("Reference rendering does not match.");
        }
    })
}

#[cfg(feature = "hdr")]
#[test]
fn check_hdr_references() {
    let mut ref_path: PathBuf = BASE_PATH.iter().collect();
    ref_path.push(REFERENCE_DIR);
    ref_path.push("hdr");
    let mut path: PathBuf = BASE_PATH.iter().collect();
    path.push(IMAGE_DIR);
    path.push("hdr");
    path.push("*");
    path.push("*.hdr");
    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        use std::path::Component::Normal;
        let mut ref_path = ref_path.clone();
        // append 2 last components of image path to reference path
        for c in path.components()
            .rev()
            .take(2)
            .collect::<Vec<_>>()
            .iter()
            .rev()
        {
            match *c {
                Normal(name) => ref_path.push(name),
                _ => panic!(),
            }
        }
        ref_path.set_extension("raw");
        println!("{}", ref_path.display());
        println!("{}", path.display());
        let decoder = image::hdr::HDRDecoder::new(io::BufReader::new(
            fs::File::open(&path).unwrap(),
        )).unwrap();
        let decoded = decoder.read_image_hdr().unwrap();
        let reference = image::hdr::read_raw_file(&ref_path).unwrap();
        assert_eq!(decoded, reference);
    }
}

/// Check that BMP files with large values could cause OOM issues are rejected.
///
/// The images are postfixed with `bad_bmp` to not be loaded by the other test.
#[test]
fn bad_bmps() {
    let path: PathBuf = BASE_PATH
        .iter()
        .collect::<PathBuf>()
        .join(IMAGE_DIR)
        .join("bmp/images")
        .join("*.bad_bmp");

    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        let im = image::open(path);
        assert!(im.is_err());
    }
}
<commit_msg>Rename `ReferenceTestCaseType` → `ReferenceTestKind`<commit_after>//! Compares the decoding results with reference renderings.

extern crate crc32fast;
extern crate glob;
extern crate image;

use std::fs;
use std::io;
use std::path::PathBuf;
use std::u32;

use crc32fast::Hasher as Crc32;

const BASE_PATH: [&str; 2] = [".", "tests"];
const IMAGE_DIR: &str = "images";
const OUTPUT_DIR: &str = "output";
const REFERENCE_DIR: &str = "reference";

fn process_images<F>(dir: &str, input_decoder: Option<&str>, func: F)
where
    F: Fn(&PathBuf, PathBuf, &str),
{
    let base: PathBuf = BASE_PATH.iter().collect();
    let decoders = &["tga", "tiff", "png", "gif", "bmp", "ico", "jpg", "hdr", "pbm", "webp"];
    for decoder in decoders {
        let mut path = base.clone();
        path.push(dir);
        path.push(decoder);
        path.push("**");
        path.push(
            "*.".to_string() + match input_decoder {
                Some(val) => val,
                None => decoder,
            },
        );
        let pattern = &*format!("{}", path.display());
        for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
            func(&base, path, decoder)
        }
    }
}

#[cfg(feature = "png")]
#[test]
fn render_images() {
    process_images(IMAGE_DIR, None, |base, path, decoder| {
        println!("render_images {}", path.display());
        let img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(e)) => {
                println!("UNSUPPORTED {}: {}", path.display(), e);
                return;
            }
            Err(err) => panic!(format!("decoding of {:?} failed with: {}", path, err)),
        };
        let mut crc = Crc32::new();
        crc.update(&*img);

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };
        let mut out_path = base.clone();

        out_path.push(OUTPUT_DIR);
        out_path.push(decoder);
        out_path.push(testsuite.as_os_str());
        fs::create_dir_all(&out_path).unwrap();
        out_path.push(format!(
            "{}.{}.{}",
            filename.as_os_str().to_str().unwrap(),
            format!("{:x}", crc.finalize()),
            "png"
        ));
        img.save(out_path).unwrap();
    })
}

/// Describes a single test case of `check_references`.
struct ReferenceTestCase {
    orig_filename: String,
    crc: u32,
    kind: ReferenceTestKind,
}

enum ReferenceTestKind {
    /// The test image is loaded using `image::open`, and the result is compared
    /// against the reference image.
    SingleImage,

    /// From the test image file, a single frame is extracted using
    /// `image::gif::Decoder`, and the result is compared against the reference
    /// image.
    AnimatedGifFrame {
        /// A zero-based frame number.
        frame: usize,
    },
}

impl std::str::FromStr for ReferenceTestCase {
    type Err = &'static str;

    /// Construct `ReferenceTestCase` from the file name of a reference
    /// image.
    fn from_str(filename: &str) -> Result<Self, Self::Err> {
        let mut filename_parts = filename.rsplitn(3, '.');

        // Ignore the file extension
        filename_parts.next().unwrap();

        // The penultimate part of `filename_parts` represents the metadata,
        // describing the test type and other details.
        let meta_str = filename_parts.next().ok_or("missing metadata part")?;
        let meta = meta_str.split('_').collect::<Vec<_>>();
        let (crc, kind);

        if meta.len() == 1 {
            // `CRC`
            crc = parse_crc(&meta[0]).ok_or("malformed CRC")?;
            kind = ReferenceTestKind::SingleImage;
        } else if meta.len() == 3 && meta[0] == "anim" {
            // `anim_FRAME_CRC`
            crc = parse_crc(&meta[2]).ok_or("malformed CRC")?;
            let frame: usize = meta[1].parse().map_err(|_| "malformed frame number")?;
            kind = ReferenceTestKind::AnimatedGifFrame {
                frame: frame.checked_sub(1).ok_or("frame number must be 1-based")?,
            };
        } else {
            return Err("unrecognized reference image metadata format");
        }

        // The remaining part represents the original file name
        let orig_filename = filename_parts
            .next()
            .ok_or("missing original file name")?
            .to_owned();

        Ok(Self {
            orig_filename,
            crc,
            kind,
        })
    }
}

/// Parse the given string as a hexadecimal CRC hash, used by `check_references`.
fn parse_crc(src: &str) -> Option<u32> {
    u32::from_str_radix(src, 16).ok()
}

#[test]
fn check_references() {
    process_images(REFERENCE_DIR, Some("png"), |base, path, decoder| {
        println!("check_references {}", path.display());

        let ref_img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(_)) => return,
            Err(err) => panic!(format!("{}", err)),
        };

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };

        // Parse the file name to obtain the test case information
        let filename_str = filename.as_os_str().to_str().unwrap();
        let case: ReferenceTestCase = filename_str.parse().unwrap();

        let mut img_path = base.clone();
        img_path.push(IMAGE_DIR);
        img_path.push(decoder);
        img_path.push(testsuite.as_os_str());
        img_path.push(case.orig_filename);

        // Load the test image
        let test_img;

        match case.kind {
            ReferenceTestKind::AnimatedGifFrame { frame: frame_num } => {
                #[cfg(feature = "gif_codec")]
                {
                    // Interpret the input file as an animation file
                    use image::AnimationDecoder;
                    let stream = io::BufReader::new(fs::File::open(&img_path).unwrap());
                    let decoder = match image::gif::Decoder::new(stream) {
                        Ok(decoder) => decoder,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => {
                            panic!(format!("decoding of {:?} failed with: {}", img_path, err))
                        }
                    };

                    let mut frames = match decoder.into_frames().collect_frames() {
                        Ok(frames) => frames,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => panic!(format!(
                            "collecting frames of {:?} failed with: {}",
                            img_path, err
                        )),
                    };

                    // Select a single frame
                    let frame = frames.drain(frame_num..).nth(0).unwrap();

                    // Convert the frame to a`RgbaImage`
                    test_img = frame.into_buffer();
                }

                #[cfg(not(feature = "gif_codec"))]
                {
                    println!("Skipping - GIF codec is not enabled");
                    return;
                }
            }

            ReferenceTestKind::SingleImage => {
                // Read the input file as a single image
                match image::open(&img_path) {
                    Ok(img) => test_img = img.to_rgba(),
                    // Do not fail on unsupported error
                    // This might happen because the testsuite contains unsupported images
                    // or because a specific decoder included via a feature.
                    Err(image::ImageError::UnsupportedError(_)) => return,
                    Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
                };
            }
        }

        let test_crc_actual = {
            let mut hasher = Crc32::new();
            hasher.update(&*test_img);
            hasher.finalize()
        };

        if test_crc_actual != case.crc {
            panic!(
                "The decoded image's hash does not match (expected = {:08x}, actual = {:08x}).",
                case.crc, test_crc_actual
            );
        }

        if *ref_img != *test_img {
            panic!("Reference rendering does not match.");
        }
    })
}

#[cfg(feature = "hdr")]
#[test]
fn check_hdr_references() {
    let mut ref_path: PathBuf = BASE_PATH.iter().collect();
    ref_path.push(REFERENCE_DIR);
    ref_path.push("hdr");
    let mut path: PathBuf = BASE_PATH.iter().collect();
    path.push(IMAGE_DIR);
    path.push("hdr");
    path.push("*");
    path.push("*.hdr");
    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        use std::path::Component::Normal;
        let mut ref_path = ref_path.clone();
        // append 2 last components of image path to reference path
        for c in path.components()
            .rev()
            .take(2)
            .collect::<Vec<_>>()
            .iter()
            .rev()
        {
            match *c {
                Normal(name) => ref_path.push(name),
                _ => panic!(),
            }
        }
        ref_path.set_extension("raw");
        println!("{}", ref_path.display());
        println!("{}", path.display());
        let decoder = image::hdr::HDRDecoder::new(io::BufReader::new(
            fs::File::open(&path).unwrap(),
        )).unwrap();
        let decoded = decoder.read_image_hdr().unwrap();
        let reference = image::hdr::read_raw_file(&ref_path).unwrap();
        assert_eq!(decoded, reference);
    }
}

/// Check that BMP files with large values could cause OOM issues are rejected.
///
/// The images are postfixed with `bad_bmp` to not be loaded by the other test.
#[test]
fn bad_bmps() {
    let path: PathBuf = BASE_PATH
        .iter()
        .collect::<PathBuf>()
        .join(IMAGE_DIR)
        .join("bmp/images")
        .join("*.bad_bmp");

    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        let im = image::open(path);
        assert!(im.is_err());
    }
}
<|endoftext|>


<commit_before>//! Compares the decoding results with reference renderings.

extern crate crc32fast;
extern crate glob;
extern crate image;

use std::fs;
use std::io;
use std::path::PathBuf;
use std::u32;

use crc32fast::Hasher as Crc32;

const BASE_PATH: [&str; 2] = [".", "tests"];
const IMAGE_DIR: &str = "images";
const OUTPUT_DIR: &str = "output";
const REFERENCE_DIR: &str = "reference";

fn process_images<F>(dir: &str, input_decoder: Option<&str>, func: F)
where
    F: Fn(&PathBuf, PathBuf, &str),
{
    let base: PathBuf = BASE_PATH.iter().collect();
    let decoders = &["tga", "tiff", "png", "gif", "bmp", "ico", "jpg", "hdr", "pbm", "webp"];
    for decoder in decoders {
        let mut path = base.clone();
        path.push(dir);
        path.push(decoder);
        path.push("**");
        path.push(
            "*.".to_string() + match input_decoder {
                Some(val) => val,
                None => decoder,
            },
        );
        let pattern = &*format!("{}", path.display());
        for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
            func(&base, path, decoder)
        }
    }
}

#[cfg(feature = "png")]
#[test]
fn render_images() {
    process_images(IMAGE_DIR, None, |base, path, decoder| {
        println!("render_images {}", path.display());
        let img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(e)) => {
                println!("UNSUPPORTED {}: {}", path.display(), e);
                return;
            }
            Err(err) => panic!(format!("decoding of {:?} failed with: {}", path, err)),
        };
        let mut crc = Crc32::new();
        crc.update(&*img);

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };
        let mut out_path = base.clone();

        out_path.push(OUTPUT_DIR);
        out_path.push(decoder);
        out_path.push(testsuite.as_os_str());
        fs::create_dir_all(&out_path).unwrap();
        out_path.push(format!(
            "{}.{}.{}",
            filename.as_os_str().to_str().unwrap(),
            format!("{:x}", crc.finalize()),
            "png"
        ));
        img.save(out_path).unwrap();
    })
}

#[test]
fn check_references() {
    process_images(REFERENCE_DIR, Some("png"), |base, path, decoder| {
        println!("check_references {}", path.display());

        let ref_img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(_)) => return,
            Err(err) => panic!(format!("{}", err)),
        };

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };

        let mut filename_parts = filename
            .as_os_str()
            .to_str()
            .unwrap()
            .split('.')
            .collect::<Vec<_>>();

        // Ignore the file extension
        filename_parts.pop().unwrap();

        // The penultimate part of `filename_parts` represents the metadata.
        let meta_str = filename_parts.pop().unwrap();
        let mut meta = meta_str.split('_').collect::<Vec<_>>();
        let ref_crc;
        let anim_frame: Option<usize>;

        if meta.len() == 1 {
            // `CRC`
            ref_crc = meta.pop().unwrap();
            anim_frame = None;
        } else if meta.len() == 3 && meta[0] == "anim" {
            // `anim_FRAME_CRC`
            ref_crc = meta.pop().unwrap();
            let frame: usize = meta.pop().unwrap().parse().unwrap();
            anim_frame = Some(frame.checked_sub(1).expect("frame number cannot be 0"));
        } else {
            panic!(
                "unrecognized reference image metadata format: {:?}",
                meta_str
            );
        }

        // The remaining part represents the original file name
        let orig_filename = filename_parts.join(".");

        // Check the reference image's CRC
        let ref_crc = u32::from_str_radix(ref_crc, 16).unwrap();

        let ref_crc_actual = {
            let mut hasher = Crc32::new();
            hasher.update(&*ref_img);
            hasher.finalize()
        };

        if ref_crc_actual != ref_crc {
            panic!(
                "Reference rendering hash check failed (expected = {:08x}, actual = {:08x}).",
                ref_crc, ref_crc_actual
            );
        }

        let mut img_path = base.clone();
        img_path.push(IMAGE_DIR);
        img_path.push(decoder);
        img_path.push(testsuite.as_os_str());
        img_path.push(orig_filename);

        // Load the test image
        let test_img;

        if let Some(anim_frame) = anim_frame {
            // Interpret the input file as an animation file and extract a
            // single frame
            use image::AnimationDecoder;
            let stream = io::BufReader::new(fs::File::open(&img_path).unwrap());
            let decoder = match image::gif::Decoder::new(stream) {
                Ok(decoder) => decoder,
                Err(image::ImageError::UnsupportedError(_)) => return,
                Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
            };

            let mut frames = match decoder.into_frames().collect_frames() {
                Ok(frames) => frames,
                Err(image::ImageError::UnsupportedError(_)) => return,
                Err(err) => panic!(format!(
                    "collecting frames of {:?} failed with: {}",
                    img_path, err
                )),
            };

            let frame = frames.drain(anim_frame..).nth(0).unwrap();

            // Convert the frame to a`RgbaImage`
            test_img = frame.into_buffer();
        } else {
            // Read the input file as a single image
            match image::open(&img_path) {
                Ok(img) => test_img = img.to_rgba(),
                // Do not fail on unsupported error
                // This might happen because the testsuite contains unsupported images
                // or because a specific decoder included via a feature.
                Err(image::ImageError::UnsupportedError(_)) => return,
                Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
            };
        }

        let ref_img = coalesce_transparent_pixels(ref_img);
        let test_img = coalesce_transparent_pixels(test_img);

        //ref_img.save("/tmp/test-ref.png").unwrap();
        //test_img.save("/tmp/test-in.png").unwrap();

        if *ref_img != *test_img {
            panic!("Reference rendering does not match.");
        }
    })
}

/// Replace transparent pixel values with a single value. This is useful for
/// disregarding differences in the RGB values of completely transparent pixels.
fn coalesce_transparent_pixels(mut img: image::RgbaImage) -> image::RgbaImage {
    for p in img.pixels_mut() {
        if p[3] == 0 {
            *p = image::Rgba([0, 0, 0, 0]);
        }
    }
    img
}

#[cfg(feature = "hdr")]
#[test]
fn check_hdr_references() {
    let mut ref_path: PathBuf = BASE_PATH.iter().collect();
    ref_path.push(REFERENCE_DIR);
    ref_path.push("hdr");
    let mut path: PathBuf = BASE_PATH.iter().collect();
    path.push(IMAGE_DIR);
    path.push("hdr");
    path.push("*");
    path.push("*.hdr");
    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        use std::path::Component::Normal;
        let mut ref_path = ref_path.clone();
        // append 2 last components of image path to reference path
        for c in path.components()
            .rev()
            .take(2)
            .collect::<Vec<_>>()
            .iter()
            .rev()
        {
            match *c {
                Normal(name) => ref_path.push(name),
                _ => panic!(),
            }
        }
        ref_path.set_extension("raw");
        println!("{}", ref_path.display());
        println!("{}", path.display());
        let decoder = image::hdr::HDRDecoder::new(io::BufReader::new(
            fs::File::open(&path).unwrap(),
        )).unwrap();
        let decoded = decoder.read_image_hdr().unwrap();
        let reference = image::hdr::read_raw_file(&ref_path).unwrap();
        assert_eq!(decoded, reference);
    }
}

/// Check that BMP files with large values could cause OOM issues are rejected.
///
/// The images are postfixed with `bad_bmp` to not be loaded by the other test.
#[test]
fn bad_bmps() {
    let path: PathBuf = BASE_PATH
        .iter()
        .collect::<PathBuf>()
        .join(IMAGE_DIR)
        .join("bmp/images")
        .join("*.bad_bmp");

    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        let im = image::open(path);
        assert!(im.is_err());
    }
}
<commit_msg>Skip animated GIF tests when `gif_codec` is disabled<commit_after>//! Compares the decoding results with reference renderings.

extern crate crc32fast;
extern crate glob;
extern crate image;

use std::fs;
use std::io;
use std::path::PathBuf;
use std::u32;

use crc32fast::Hasher as Crc32;

const BASE_PATH: [&str; 2] = [".", "tests"];
const IMAGE_DIR: &str = "images";
const OUTPUT_DIR: &str = "output";
const REFERENCE_DIR: &str = "reference";

fn process_images<F>(dir: &str, input_decoder: Option<&str>, func: F)
where
    F: Fn(&PathBuf, PathBuf, &str),
{
    let base: PathBuf = BASE_PATH.iter().collect();
    let decoders = &["tga", "tiff", "png", "gif", "bmp", "ico", "jpg", "hdr", "pbm", "webp"];
    for decoder in decoders {
        let mut path = base.clone();
        path.push(dir);
        path.push(decoder);
        path.push("**");
        path.push(
            "*.".to_string() + match input_decoder {
                Some(val) => val,
                None => decoder,
            },
        );
        let pattern = &*format!("{}", path.display());
        for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
            func(&base, path, decoder)
        }
    }
}

#[cfg(feature = "png")]
#[test]
fn render_images() {
    process_images(IMAGE_DIR, None, |base, path, decoder| {
        println!("render_images {}", path.display());
        let img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(e)) => {
                println!("UNSUPPORTED {}: {}", path.display(), e);
                return;
            }
            Err(err) => panic!(format!("decoding of {:?} failed with: {}", path, err)),
        };
        let mut crc = Crc32::new();
        crc.update(&*img);

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };
        let mut out_path = base.clone();

        out_path.push(OUTPUT_DIR);
        out_path.push(decoder);
        out_path.push(testsuite.as_os_str());
        fs::create_dir_all(&out_path).unwrap();
        out_path.push(format!(
            "{}.{}.{}",
            filename.as_os_str().to_str().unwrap(),
            format!("{:x}", crc.finalize()),
            "png"
        ));
        img.save(out_path).unwrap();
    })
}

#[test]
fn check_references() {
    process_images(REFERENCE_DIR, Some("png"), |base, path, decoder| {
        println!("check_references {}", path.display());

        let ref_img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(_)) => return,
            Err(err) => panic!(format!("{}", err)),
        };

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };

        let mut filename_parts = filename
            .as_os_str()
            .to_str()
            .unwrap()
            .split('.')
            .collect::<Vec<_>>();

        // Ignore the file extension
        filename_parts.pop().unwrap();

        // The penultimate part of `filename_parts` represents the metadata.
        let meta_str = filename_parts.pop().unwrap();
        let mut meta = meta_str.split('_').collect::<Vec<_>>();
        let ref_crc;
        let anim_frame: Option<usize>;

        if meta.len() == 1 {
            // `CRC`
            ref_crc = meta.pop().unwrap();
            anim_frame = None;
        } else if meta.len() == 3 && meta[0] == "anim" {
            // `anim_FRAME_CRC`
            ref_crc = meta.pop().unwrap();
            let frame: usize = meta.pop().unwrap().parse().unwrap();
            anim_frame = Some(frame.checked_sub(1).expect("frame number cannot be 0"));
        } else {
            panic!(
                "unrecognized reference image metadata format: {:?}",
                meta_str
            );
        }

        // The remaining part represents the original file name
        let orig_filename = filename_parts.join(".");

        // Check the reference image's CRC
        let ref_crc = u32::from_str_radix(ref_crc, 16).unwrap();

        let ref_crc_actual = {
            let mut hasher = Crc32::new();
            hasher.update(&*ref_img);
            hasher.finalize()
        };

        if ref_crc_actual != ref_crc {
            panic!(
                "Reference rendering hash check failed (expected = {:08x}, actual = {:08x}).",
                ref_crc, ref_crc_actual
            );
        }

        let mut img_path = base.clone();
        img_path.push(IMAGE_DIR);
        img_path.push(decoder);
        img_path.push(testsuite.as_os_str());
        img_path.push(orig_filename);

        // Load the test image
        let test_img;

        if let Some(anim_frame) = anim_frame {
            #[cfg(feature = "gif_codec")]
            {
                // Interpret the input file as an animation file and extract a
                // single frame
                use image::AnimationDecoder;
                let stream = io::BufReader::new(fs::File::open(&img_path).unwrap());
                let decoder = match image::gif::Decoder::new(stream) {
                    Ok(decoder) => decoder,
                    Err(image::ImageError::UnsupportedError(_)) => return,
                    Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
                };

                let mut frames = match decoder.into_frames().collect_frames() {
                    Ok(frames) => frames,
                    Err(image::ImageError::UnsupportedError(_)) => return,
                    Err(err) => panic!(format!(
                        "collecting frames of {:?} failed with: {}",
                        img_path, err
                    )),
                };

                let frame = frames.drain(anim_frame..).nth(0).unwrap();

                // Convert the frame to a`RgbaImage`
                test_img = frame.into_buffer();
            }

            #[cfg(not(feature = "gif_codec"))]
            {
                println!("Skipping - GIF codec is not enabled");
                return;
            }
        } else {
            // Read the input file as a single image
            match image::open(&img_path) {
                Ok(img) => test_img = img.to_rgba(),
                // Do not fail on unsupported error
                // This might happen because the testsuite contains unsupported images
                // or because a specific decoder included via a feature.
                Err(image::ImageError::UnsupportedError(_)) => return,
                Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
            };
        }

        let ref_img = coalesce_transparent_pixels(ref_img);
        let test_img = coalesce_transparent_pixels(test_img);

        //ref_img.save("/tmp/test-ref.png").unwrap();
        //test_img.save("/tmp/test-in.png").unwrap();

        if *ref_img != *test_img {
            panic!("Reference rendering does not match.");
        }
    })
}

/// Replace transparent pixel values with a single value. This is useful for
/// disregarding differences in the RGB values of completely transparent pixels.
fn coalesce_transparent_pixels(mut img: image::RgbaImage) -> image::RgbaImage {
    for p in img.pixels_mut() {
        if p[3] == 0 {
            *p = image::Rgba([0, 0, 0, 0]);
        }
    }
    img
}

#[cfg(feature = "hdr")]
#[test]
fn check_hdr_references() {
    let mut ref_path: PathBuf = BASE_PATH.iter().collect();
    ref_path.push(REFERENCE_DIR);
    ref_path.push("hdr");
    let mut path: PathBuf = BASE_PATH.iter().collect();
    path.push(IMAGE_DIR);
    path.push("hdr");
    path.push("*");
    path.push("*.hdr");
    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        use std::path::Component::Normal;
        let mut ref_path = ref_path.clone();
        // append 2 last components of image path to reference path
        for c in path.components()
            .rev()
            .take(2)
            .collect::<Vec<_>>()
            .iter()
            .rev()
        {
            match *c {
                Normal(name) => ref_path.push(name),
                _ => panic!(),
            }
        }
        ref_path.set_extension("raw");
        println!("{}", ref_path.display());
        println!("{}", path.display());
        let decoder = image::hdr::HDRDecoder::new(io::BufReader::new(
            fs::File::open(&path).unwrap(),
        )).unwrap();
        let decoded = decoder.read_image_hdr().unwrap();
        let reference = image::hdr::read_raw_file(&ref_path).unwrap();
        assert_eq!(decoded, reference);
    }
}

/// Check that BMP files with large values could cause OOM issues are rejected.
///
/// The images are postfixed with `bad_bmp` to not be loaded by the other test.
#[test]
fn bad_bmps() {
    let path: PathBuf = BASE_PATH
        .iter()
        .collect::<PathBuf>()
        .join(IMAGE_DIR)
        .join("bmp/images")
        .join("*.bad_bmp");

    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        let im = image::open(path);
        assert!(im.is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 check::{FnCtxt, structurally_resolved_type};
use middle::subst::{FnSpace};
use middle::traits;
use middle::traits::{Obligation, ObligationCause};
use middle::traits::report_fulfillment_errors;
use middle::ty::{mod, Ty};
use middle::infer;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::Span;
use util::ppaux::{Repr, ty_to_string};

pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   cast_expr: &ast::Expr,
                                   source_expr: &ast::Expr,
                                   target_object_ty: Ty<'tcx>)
{
    debug!("check_object_cast(cast_expr={}, target_object_ty={})",
           cast_expr.repr(fcx.tcx()),
           target_object_ty.repr(fcx.tcx()));

    // Look up vtables for the type we're casting to,
    // passing in the source and target type.  The source
    // must be a pointer type suitable to the object sigil,
    // e.g.: `&x as &Trait` or `box x as Box<Trait>`
    let source_ty = fcx.expr_ty(source_expr);
    let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty);
    debug!("source_ty={}", source_ty.repr(fcx.tcx()));
    match (&source_ty.sty, &target_object_ty.sty) {
        (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => {
            let object_trait = object_trait(&object_trait_ty);

            // Ensure that if ~T is cast to ~Trait, then T : Trait
            push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
            check_object_safety(fcx.tcx(), object_trait, source_expr.span);
        }

        (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
                                                mutbl: referent_mutbl }),
         &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty,
                                              mutbl: target_mutbl })) =>
        {
            let object_trait = object_trait(&object_trait_ty);
            if !mutability_allowed(referent_mutbl, target_mutbl) {
                fcx.tcx().sess.span_err(source_expr.span,
                                        "types differ in mutability");
            } else {
                // Ensure that if &'a T is cast to &'b Trait, then T : Trait
                push_cast_obligation(fcx, cast_expr,
                                     object_trait,
                                     referent_ty);

                // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a
                infer::mk_subr(fcx.infcx(),
                               infer::RelateObjectBound(source_expr.span),
                               *target_region,
                               *referent_region);

                check_object_safety(fcx.tcx(), object_trait, source_expr.span);
            }
        }

        (_, &ty::ty_uniq(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast an boxed pointer \
                         to a boxed object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        (_, &ty::ty_rptr(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast a &-pointer \
                         to an &-object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        _ => {
            fcx.tcx().sess.span_bug(
                source_expr.span,
                "expected object type");
        }
    }

    fn object_trait<'a, 'tcx>(t: &'a Ty<'tcx>) -> &'a ty::TyTrait<'tcx> {
        match t.sty {
            ty::ty_trait(ref ty_trait) => &**ty_trait,
            _ => panic!("expected ty_trait")
        }
    }

    fn mutability_allowed(a_mutbl: ast::Mutability,
                          b_mutbl: ast::Mutability)
                          -> bool {
        a_mutbl == b_mutbl ||
            (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable)
    }

    fn push_cast_obligation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      cast_expr: &ast::Expr,
                                      object_trait: &ty::TyTrait<'tcx>,
                                      referent_ty: Ty<'tcx>) {
        let object_trait_ref =
            register_object_cast_obligations(fcx,
                                             cast_expr.span,
                                             object_trait,
                                             referent_ty);

        // Finally record the object_trait_ref for use during trans
        // (it would prob be better not to do this, but it's just kind
        // of a pain to have to reconstruct it).
        fcx.write_object_cast(cast_expr.id, object_trait_ref);
    }
}

// Check that a trait is 'object-safe'. This should be checked whenever a trait object
// is created (by casting or coercion, etc.). A trait is object-safe if all its
// methods are object-safe. A trait method is object-safe if it does not take
// self by value, has no type parameters and does not use the `Self` type, except
// in self position.
pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
                                 object_trait: &ty::TyTrait<'tcx>,
                                 span: Span)
{
    let object_trait_ref = object_trait.principal_trait_ref_with_self_ty(tcx, ty::mk_err());
    for tr in traits::supertraits(tcx, object_trait_ref) {
        check_object_safety_inner(tcx, &*tr, span);
    }
}

fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
                                 object_trait: &ty::PolyTraitRef<'tcx>,
                                 span: Span) {
    let trait_items = ty::trait_items(tcx, object_trait.def_id());

    let mut errors = Vec::new();
    for item in trait_items.iter() {
        match *item {
            ty::MethodTraitItem(ref m) => {
                errors.push(check_object_safety_of_method(tcx, &**m))
            }
            ty::TypeTraitItem(_) => {}
        }
    }

    let mut errors = errors.iter().flat_map(|x| x.iter()).peekable();
    if errors.peek().is_some() {
        let trait_name = ty::item_path_str(tcx, object_trait.def_id());
        span_err!(tcx.sess, span, E0038,
            "cannot convert to a trait object because trait `{}` is not object-safe",
            trait_name);

        for msg in errors {
            tcx.sess.note(msg[]);
        }
    }

    /// Returns a vec of error messages. If hte vec is empty - no errors!
    ///
    /// There are some limitations to calling functions through an object, because (a) the self
    /// type is not known (that's the whole point of a trait instance, after all, to obscure the
    /// self type) and (b) the call must go through a vtable and hence cannot be monomorphized.
    fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                                           method: &ty::Method<'tcx>)
                                           -> Vec<String> {
        let mut msgs = Vec::new();

        let method_name = method.name.repr(tcx);

        match method.explicit_self {
            ty::ByValueExplicitSelfCategory => { // reason (a) above
                msgs.push(format!("cannot call a method (`{}`) with a by-value \
                                   receiver through a trait object", method_name))
            }

            ty::StaticExplicitSelfCategory => {
                // Static methods are always object-safe since they
                // can't be called through a trait object
                return msgs
            }
            ty::ByReferenceExplicitSelfCategory(..) |
            ty::ByBoxExplicitSelfCategory => {}
        }

        // reason (a) above
        let check_for_self_ty = |ty| {
            if ty::type_has_self(ty) {
                Some(format!(
                    "cannot call a method (`{}`) whose type contains \
                     a self-type (`{}`) through a trait object",
                    method_name, ty_to_string(tcx, ty)))
            } else {
                None
            }
        };
        let ref sig = method.fty.sig;
        for &input_ty in sig.0.inputs[1..].iter() {
            if let Some(msg) = check_for_self_ty(input_ty) {
                msgs.push(msg);
            }
        }
        if let ty::FnConverging(result_type) = sig.0.output {
            if let Some(msg) = check_for_self_ty(result_type) {
                msgs.push(msg);
            }
        }

        if method.generics.has_type_params(FnSpace) {
            // reason (b) above
            msgs.push(format!("cannot call a generic method (`{}`) through a trait object",
                              method_name));
        }

        msgs
    }
}

pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                  span: Span,
                                                  object_trait: &ty::TyTrait<'tcx>,
                                                  referent_ty: Ty<'tcx>)
                                                  -> Rc<ty::PolyTraitRef<'tcx>>
{
    // We can only make objects from sized types.
    fcx.register_builtin_bound(
        referent_ty,
        ty::BoundSized,
        traits::ObligationCause::new(span, fcx.body_id, traits::ObjectSized));

    // This is just for better error reporting. Kinda goofy. The object type stuff
    // needs some refactoring so there is a more convenient type to pass around.
    let object_trait_ty =
        ty::mk_trait(fcx.tcx(),
                     object_trait.principal.clone(),
                     object_trait.bounds);

    debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
           referent_ty.repr(fcx.tcx()),
           object_trait_ty.repr(fcx.tcx()));

    // Create the obligation for casting from T to Trait.
    let object_trait_ref =
        object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
    let object_obligation =
        Obligation::new(
            ObligationCause::new(span,
                                 fcx.body_id,
                                 traits::ObjectCastObligation(object_trait_ty)),
            ty::Predicate::Trait(object_trait_ref.clone()));
    fcx.register_predicate(object_obligation);

    // Create additional obligations for all the various builtin
    // bounds attached to the object cast. (In other words, if the
    // object type is Foo+Send, this would create an obligation
    // for the Send check.)
    for builtin_bound in object_trait.bounds.builtin_bounds.iter() {
        fcx.register_builtin_bound(
            referent_ty,
            builtin_bound,
            ObligationCause::new(span, fcx.body_id, traits::ObjectCastObligation(object_trait_ty)));
    }

    object_trait_ref
}

pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
    debug!("select_all_fcx_obligations_or_error");

    let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
    let r = fulfillment_cx.select_all_or_error(fcx.infcx(),
                                               &fcx.inh.param_env,
                                               fcx);
    match r {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Select as many obligations as we can at present.
pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt)
{
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Try to select any fcx obligation that we haven't tried yet, in an effort to improve inference.
/// You could just call `select_fcx_obligations_where_possible` except that it leads to repeated
/// work.
pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_new_obligations(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}
<commit_msg>Forbid static methods in object safe traits<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 check::{FnCtxt, structurally_resolved_type};
use middle::subst::{FnSpace};
use middle::traits;
use middle::traits::{Obligation, ObligationCause};
use middle::traits::report_fulfillment_errors;
use middle::ty::{mod, Ty};
use middle::infer;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::Span;
use util::ppaux::{Repr, ty_to_string};

pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   cast_expr: &ast::Expr,
                                   source_expr: &ast::Expr,
                                   target_object_ty: Ty<'tcx>)
{
    debug!("check_object_cast(cast_expr={}, target_object_ty={})",
           cast_expr.repr(fcx.tcx()),
           target_object_ty.repr(fcx.tcx()));

    // Look up vtables for the type we're casting to,
    // passing in the source and target type.  The source
    // must be a pointer type suitable to the object sigil,
    // e.g.: `&x as &Trait` or `box x as Box<Trait>`
    let source_ty = fcx.expr_ty(source_expr);
    let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty);
    debug!("source_ty={}", source_ty.repr(fcx.tcx()));
    match (&source_ty.sty, &target_object_ty.sty) {
        (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => {
            let object_trait = object_trait(&object_trait_ty);

            // Ensure that if ~T is cast to ~Trait, then T : Trait
            push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
            check_object_safety(fcx.tcx(), object_trait, source_expr.span);
        }

        (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
                                                mutbl: referent_mutbl }),
         &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty,
                                              mutbl: target_mutbl })) =>
        {
            let object_trait = object_trait(&object_trait_ty);
            if !mutability_allowed(referent_mutbl, target_mutbl) {
                fcx.tcx().sess.span_err(source_expr.span,
                                        "types differ in mutability");
            } else {
                // Ensure that if &'a T is cast to &'b Trait, then T : Trait
                push_cast_obligation(fcx, cast_expr,
                                     object_trait,
                                     referent_ty);

                // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a
                infer::mk_subr(fcx.infcx(),
                               infer::RelateObjectBound(source_expr.span),
                               *target_region,
                               *referent_region);

                check_object_safety(fcx.tcx(), object_trait, source_expr.span);
            }
        }

        (_, &ty::ty_uniq(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast an boxed pointer \
                         to a boxed object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        (_, &ty::ty_rptr(..)) => {
            fcx.ccx.tcx.sess.span_err(
                source_expr.span,
                format!("can only cast a &-pointer \
                         to an &-object, not a {}",
                        ty::ty_sort_string(fcx.tcx(), source_ty))[]);
        }

        _ => {
            fcx.tcx().sess.span_bug(
                source_expr.span,
                "expected object type");
        }
    }

    fn object_trait<'a, 'tcx>(t: &'a Ty<'tcx>) -> &'a ty::TyTrait<'tcx> {
        match t.sty {
            ty::ty_trait(ref ty_trait) => &**ty_trait,
            _ => panic!("expected ty_trait")
        }
    }

    fn mutability_allowed(a_mutbl: ast::Mutability,
                          b_mutbl: ast::Mutability)
                          -> bool {
        a_mutbl == b_mutbl ||
            (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable)
    }

    fn push_cast_obligation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      cast_expr: &ast::Expr,
                                      object_trait: &ty::TyTrait<'tcx>,
                                      referent_ty: Ty<'tcx>) {
        let object_trait_ref =
            register_object_cast_obligations(fcx,
                                             cast_expr.span,
                                             object_trait,
                                             referent_ty);

        // Finally record the object_trait_ref for use during trans
        // (it would prob be better not to do this, but it's just kind
        // of a pain to have to reconstruct it).
        fcx.write_object_cast(cast_expr.id, object_trait_ref);
    }
}

// Check that a trait is 'object-safe'. This should be checked whenever a trait object
// is created (by casting or coercion, etc.). A trait is object-safe if all its
// methods are object-safe. A trait method is object-safe if it does not take
// self by value, has no type parameters and does not use the `Self` type, except
// in self position.
pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
                                 object_trait: &ty::TyTrait<'tcx>,
                                 span: Span)
{
    let object_trait_ref = object_trait.principal_trait_ref_with_self_ty(tcx, ty::mk_err());
    for tr in traits::supertraits(tcx, object_trait_ref) {
        check_object_safety_inner(tcx, &*tr, span);
    }
}

fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
                                 object_trait: &ty::PolyTraitRef<'tcx>,
                                 span: Span) {
    let trait_items = ty::trait_items(tcx, object_trait.def_id());

    let mut errors = Vec::new();
    for item in trait_items.iter() {
        match *item {
            ty::MethodTraitItem(ref m) => {
                errors.push(check_object_safety_of_method(tcx, &**m))
            }
            ty::TypeTraitItem(_) => {}
        }
    }

    let mut errors = errors.iter().flat_map(|x| x.iter()).peekable();
    if errors.peek().is_some() {
        let trait_name = ty::item_path_str(tcx, object_trait.def_id());
        span_err!(tcx.sess, span, E0038,
            "cannot convert to a trait object because trait `{}` is not object-safe",
            trait_name);

        for msg in errors {
            tcx.sess.note(msg[]);
        }
    }

    /// Returns a vec of error messages. If the vec is empty - no errors!
    ///
    /// There are some limitations to calling functions through an object, because (a) the self
    /// type is not known (that's the whole point of a trait instance, after all, to obscure the
    /// self type), (b) the call must go through a vtable and hence cannot be monomorphized and
    /// (c) the trait contains static methods which can't be called because we don't know the
    /// concrete type.
    fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                                           method: &ty::Method<'tcx>)
                                           -> Vec<String> {
        let mut msgs = Vec::new();

        let method_name = method.name.repr(tcx);

        match method.explicit_self {
            ty::ByValueExplicitSelfCategory => { // reason (a) above
                msgs.push(format!("cannot call a method (`{}`) with a by-value \
                                   receiver through a trait object", method_name))
            }

            ty::StaticExplicitSelfCategory => {
                // Static methods are never object safe (reason (c)).
                msgs.push(format!("cannot call a static method (`{}`) \
                                   through a trait object",
                                  method_name));
                return msgs;
            }
            ty::ByReferenceExplicitSelfCategory(..) |
            ty::ByBoxExplicitSelfCategory => {}
        }

        // reason (a) above
        let check_for_self_ty = |ty| {
            if ty::type_has_self(ty) {
                Some(format!(
                    "cannot call a method (`{}`) whose type contains \
                     a self-type (`{}`) through a trait object",
                    method_name, ty_to_string(tcx, ty)))
            } else {
                None
            }
        };
        let ref sig = method.fty.sig;
        for &input_ty in sig.0.inputs[1..].iter() {
            if let Some(msg) = check_for_self_ty(input_ty) {
                msgs.push(msg);
            }
        }
        if let ty::FnConverging(result_type) = sig.0.output {
            if let Some(msg) = check_for_self_ty(result_type) {
                msgs.push(msg);
            }
        }

        if method.generics.has_type_params(FnSpace) {
            // reason (b) above
            msgs.push(format!("cannot call a generic method (`{}`) through a trait object",
                              method_name));
        }

        msgs
    }
}

pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                  span: Span,
                                                  object_trait: &ty::TyTrait<'tcx>,
                                                  referent_ty: Ty<'tcx>)
                                                  -> Rc<ty::PolyTraitRef<'tcx>>
{
    // We can only make objects from sized types.
    fcx.register_builtin_bound(
        referent_ty,
        ty::BoundSized,
        traits::ObligationCause::new(span, fcx.body_id, traits::ObjectSized));

    // This is just for better error reporting. Kinda goofy. The object type stuff
    // needs some refactoring so there is a more convenient type to pass around.
    let object_trait_ty =
        ty::mk_trait(fcx.tcx(),
                     object_trait.principal.clone(),
                     object_trait.bounds);

    debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
           referent_ty.repr(fcx.tcx()),
           object_trait_ty.repr(fcx.tcx()));

    // Create the obligation for casting from T to Trait.
    let object_trait_ref =
        object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
    let object_obligation =
        Obligation::new(
            ObligationCause::new(span,
                                 fcx.body_id,
                                 traits::ObjectCastObligation(object_trait_ty)),
            ty::Predicate::Trait(object_trait_ref.clone()));
    fcx.register_predicate(object_obligation);

    // Create additional obligations for all the various builtin
    // bounds attached to the object cast. (In other words, if the
    // object type is Foo+Send, this would create an obligation
    // for the Send check.)
    for builtin_bound in object_trait.bounds.builtin_bounds.iter() {
        fcx.register_builtin_bound(
            referent_ty,
            builtin_bound,
            ObligationCause::new(span, fcx.body_id, traits::ObjectCastObligation(object_trait_ty)));
    }

    object_trait_ref
}

pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
    debug!("select_all_fcx_obligations_or_error");

    let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
    let r = fulfillment_cx.select_all_or_error(fcx.infcx(),
                                               &fcx.inh.param_env,
                                               fcx);
    match r {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Select as many obligations as we can at present.
pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt)
{
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}

/// Try to select any fcx obligation that we haven't tried yet, in an effort to improve inference.
/// You could just call `select_fcx_obligations_where_possible` except that it leads to repeated
/// work.
pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
    match
        fcx.inh.fulfillment_cx
        .borrow_mut()
        .select_new_obligations(fcx.infcx(), &fcx.inh.param_env, fcx)
    {
        Ok(()) => { }
        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
    }
}
<|endoftext|>


<commit_before>use std::slice;

use crate::stash::Stash;
use crate::batched_vec::BatchedVecRef;

macro_rules! per_cache_line {
    ($t:ty) => {{ ::std::cmp::max(64 / ::std::mem::size_of::<$t>(), 4) }}
}

#[repr(align(64))]
pub struct CacheLine([u8; 64]);

pub struct SWCBuffer<T> {
    counts: [u8; 256],
    staged: Vec<CacheLine>,
    phantom: std::marker::PhantomData<T>,
}

impl<T> SWCBuffer<T> {

    pub fn new() -> Self {
        let nlines = (256 * per_cache_line!(T) * std::mem::size_of::<T>()) / 64;
        let staged = Vec::<CacheLine>::with_capacity(nlines);

        // verify cache alignment
        let addr = staged.as_ptr() as usize;
        assert_eq!(addr % 64, 0);

        SWCBuffer {
            staged: staged,
            counts: [0u8; 256],
            phantom: std::marker::PhantomData,
        }
    }

    fn staged_ptr(&self) -> *const T {
        self.staged.as_ptr() as _
    }

    fn staged_mut_ptr(&mut self) -> *mut T {
        self.staged.as_mut_ptr() as _
    }

    #[inline]
    pub fn slice(&self, byte: usize) -> &[T] {
        unsafe {
            slice::from_raw_parts(
                self.staged_ptr().offset(per_cache_line!(T) as isize * byte as isize),
                *self.counts.get_unchecked(byte) as usize
            )
        }
    }

    #[inline]
    pub fn full(&self, byte: usize) -> bool {
        unsafe { (*self.counts.get_unchecked(byte) as usize) == per_cache_line!(T) }
    }

    #[inline]
    pub fn count(&self, byte: usize) -> usize {
        unsafe { *self.counts.get_unchecked(byte) as usize }
    }

    #[inline]
    pub fn push(&mut self, element: T, byte: usize) {
        unsafe {
            let offset = per_cache_line!(T) as isize * byte as isize + *self.counts.get_unchecked(byte as usize) as isize;
            ::std::ptr::write(self.staged_mut_ptr().offset(offset), element);
            *self.counts.get_unchecked_mut(byte) += 1;
        }
    }

    #[inline]
    pub fn drain_into<'a>(&mut self, byte: usize, batch: &mut BatchedVecRef<'a, T>, stash: &mut Stash<T>) where T: 'a {
        unsafe {
            if *self.counts.get_unchecked(byte) > 0 {
                batch.push_all(self.slice(byte), stash);
                *self.counts.get_unchecked_mut(byte) = 0;
            }
        }
    }

    #[inline]
    pub fn drain_into_vec(&mut self, byte: usize, batch: &mut Vec<T>, _stash: &mut Stash<T>) {
        unsafe {
            for i in 0 .. *self.counts.get_unchecked(byte) {
                batch.push(::std::ptr::read(self.staged_mut_ptr().offset(per_cache_line!(T) as isize * byte as isize + i as isize)));
            }
            *self.counts.get_unchecked_mut(byte) = 0;
        }
    }
}
<commit_msg>sort: modernize formatting<commit_after>use std::mem::size_of;
use std::slice;

use crate::stash::Stash;
use crate::batched_vec::BatchedVecRef;

fn per_cache_line<T>() -> usize {
    std::cmp::max(64 / size_of::<T>(), 4)
}

#[repr(align(64))]
pub struct CacheLine([u8; 64]);

pub struct SWCBuffer<T> {
    counts: [u8; 256],
    staged: Vec<CacheLine>,
    phantom: std::marker::PhantomData<T>,
}

impl<T> SWCBuffer<T> {
    pub fn new() -> Self {
        let nlines = (256 * per_cache_line::<T>() * size_of::<T>()) / 64;
        let staged = Vec::<CacheLine>::with_capacity(nlines);

        // verify cache alignment
        let addr = staged.as_ptr() as usize;
        assert_eq!(addr % 64, 0);

        SWCBuffer {
            staged: staged,
            counts: [0u8; 256],
            phantom: std::marker::PhantomData,
        }
    }

    fn staged_ptr(&self) -> *const T {
        self.staged.as_ptr() as _
    }

    fn staged_mut_ptr(&mut self) -> *mut T {
        self.staged.as_mut_ptr() as _
    }

    #[inline]
    pub fn slice(&self, byte: usize) -> &[T] {
        unsafe {
            slice::from_raw_parts(
                self.staged_ptr().offset(per_cache_line::<T>() as isize * byte as isize),
                *self.counts.get_unchecked(byte) as usize
            )
        }
    }

    #[inline]
    pub fn full(&self, byte: usize) -> bool {
        unsafe { (*self.counts.get_unchecked(byte) as usize) == per_cache_line::<T>() }
    }

    #[inline]
    pub fn count(&self, byte: usize) -> usize {
        unsafe { *self.counts.get_unchecked(byte) as usize }
    }

    #[inline]
    pub fn push(&mut self, element: T, byte: usize) {
        unsafe {
            let offset = per_cache_line::<T>() as isize * byte as isize + *self.counts.get_unchecked(byte as usize) as isize;
            std::ptr::write(self.staged_mut_ptr().offset(offset), element);
            *self.counts.get_unchecked_mut(byte) += 1;
        }
    }

    #[inline]
    pub fn drain_into<'a>(&mut self, byte: usize, batch: &mut BatchedVecRef<'a, T>, stash: &mut Stash<T>) where T: 'a {
        unsafe {
            if *self.counts.get_unchecked(byte) > 0 {
                batch.push_all(self.slice(byte), stash);
                *self.counts.get_unchecked_mut(byte) = 0;
            }
        }
    }

    #[inline]
    pub fn drain_into_vec(&mut self, byte: usize, batch: &mut Vec<T>, _stash: &mut Stash<T>) {
        unsafe {
            for i in 0 .. *self.counts.get_unchecked(byte) {
                batch.push(std::ptr::read(self.staged_mut_ptr().offset(per_cache_line::<T>() as isize * byte as isize + i as isize)));
            }
            *self.counts.get_unchecked_mut(byte) = 0;
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![cfg_attr(
  feature = "cargo-clippy",
  deny(
    clippy, default_trait_access, expl_impl_clone_on_copy, if_not_else, needless_continue,
    single_match_else, unseparated_literal_suffix, used_underscore_binding
  )
)]
// It is often more clear to show that nothing is being moved.
#![cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
// Subjective style.
#![cfg_attr(feature = "cargo-clippy", allow(len_without_is_empty, redundant_field_names))]
// Default isn't as big a deal as people seem to think it is.
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![cfg_attr(feature = "cargo-clippy", allow(mutex_atomic))]

extern crate digest;
extern crate hex;
extern crate serde;
extern crate sha2;

use digest::{Digest as DigestTrait, FixedOutput};
use serde::ser::{Serialize, SerializeStruct, Serializer};
use sha2::Sha256;

use std::fmt;
use std::io::{self, Write};

const FINGERPRINT_SIZE: usize = 32;

#[derive(Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Fingerprint(pub [u8; FINGERPRINT_SIZE]);

impl Fingerprint {
  pub fn from_bytes_unsafe(bytes: &[u8]) -> Fingerprint {
    if bytes.len() != FINGERPRINT_SIZE {
      panic!(
        "Input value was not a fingerprint; had length: {}",
        bytes.len()
      );
    }

    let mut fingerprint = [0; FINGERPRINT_SIZE];
    fingerprint.clone_from_slice(&bytes[0..FINGERPRINT_SIZE]);
    Fingerprint(fingerprint)
  }

  pub fn from_hex_string(hex_string: &str) -> Result<Fingerprint, String> {
    <[u8; FINGERPRINT_SIZE] as hex::FromHex>::from_hex(hex_string)
      .map(Fingerprint)
      .map_err(|e| format!("{:?}", e))
  }

  pub fn as_bytes(&self) -> &[u8; FINGERPRINT_SIZE] {
    &self.0
  }

  pub fn to_hex(&self) -> String {
    let mut s = String::new();
    for &byte in &self.0 {
      fmt::Write::write_fmt(&mut s, format_args!("{:02x}", byte)).unwrap();
    }
    s
  }
}

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

impl fmt::Debug for Fingerprint {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Fingerprint<{}>", self.to_hex())
  }
}

impl AsRef<[u8]> for Fingerprint {
  fn as_ref(&self) -> &[u8] {
    &self.0[..]
  }
}

impl Serialize for Fingerprint {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  where
    S: Serializer,
  {
    serializer.serialize_str(self.to_hex().as_str())
  }
}

///
/// A Digest is a fingerprint, as well as the size in bytes of the plaintext for which that is the
/// fingerprint.
///
/// It is equivalent to a Bazel Remote Execution Digest, but without the overhead (and awkward API)
/// of needing to create an entire protobuf to pass around the two fields.
///
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Digest(pub Fingerprint, pub usize);

impl Serialize for Digest {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  where
    S: Serializer,
  {
    let mut obj = serializer.serialize_struct("digest", 2)?;
    obj.serialize_field("fingerprint", &self.0)?;
    obj.serialize_field("size", &self.1)?;
    obj.end()
  }
}

///
/// A Write instance that fingerprints all data that passes through it.
///
pub struct WriterHasher<W: Write> {
  hasher: Sha256,
  inner: W,
}

impl<W: Write> WriterHasher<W> {
  pub fn new(inner: W) -> WriterHasher<W> {
    WriterHasher {
      hasher: Sha256::default(),
      inner: inner,
    }
  }

  ///
  /// Returns the result of fingerprinting this stream, and Drops the stream.
  ///
  pub fn finish(self) -> Fingerprint {
    Fingerprint::from_bytes_unsafe(&self.hasher.fixed_result())
  }
}

impl<W: Write> Write for WriterHasher<W> {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    let written = self.inner.write(buf)?;
    // Hash the bytes that were successfully written.
    self.hasher.input(&buf[0..written]);
    Ok(written)
  }

  fn flush(&mut self) -> io::Result<()> {
    self.inner.flush()
  }
}

#[cfg(test)]
mod fingerprint_tests {
  use super::Fingerprint;
  extern crate serde_test;
  use self::serde_test::{assert_ser_tokens, Token};

  #[test]
  fn from_bytes_unsafe() {
    assert_eq!(
      Fingerprint::from_bytes_unsafe(&[
        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
        0xab, 0xab,
      ],),
      Fingerprint([0xab; 32])
    );
  }

  #[test]
  fn from_hex_string() {
    assert_eq!(
      Fingerprint::from_hex_string(
        "0123456789abcdefFEDCBA98765432100000000000000000ffFFfFfFFfFfFFff",
      ).unwrap(),
      Fingerprint([
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff,
      ],)
    )
  }

  #[test]
  fn from_hex_string_not_long_enough() {
    Fingerprint::from_hex_string("abcd").expect_err("Want err");
  }

  #[test]
  fn from_hex_string_too_long() {
    Fingerprint::from_hex_string(
      "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0",
    ).expect_err("Want err");
  }

  #[test]
  fn from_hex_string_invalid_chars() {
    Fingerprint::from_hex_string(
      "Q123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
    ).expect_err("Want err");
  }

  #[test]
  fn to_hex() {
    assert_eq!(
      Fingerprint([
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff,
      ],)
        .to_hex(),
      "0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff".to_lowercase()
    )
  }

  #[test]
  fn display() {
    let hex = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
    assert_eq!(
      Fingerprint::from_hex_string(hex).unwrap().to_hex(),
      hex.to_lowercase()
    )
  }

  #[test]
  fn serialize_to_str() {
    let fingerprint = Fingerprint([
      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
      0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff,
    ]);
    assert_ser_tokens(
      &fingerprint,
      &[Token::Str(
        "0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff",
      )],
    );
  }

}

#[cfg(test)]
mod digest_tests {
  use super::Digest;
  use super::Fingerprint;
  extern crate serde_test;
  use self::serde_test::{assert_ser_tokens, Token};

  #[test]
  fn serialize_to_str() {
    let digest = Digest(
      Fingerprint::from_hex_string(
        "0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff",
      ).unwrap(),
      1,
    );
    assert_ser_tokens(
      &digest,
      &[
        Token::Struct {
          name: "digest",
          len: 2,
        },
        Token::Str("fingerprint"),
        Token::Str("0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff"),
        Token::Str("size"),
        Token::U64(1),
        Token::StructEnd,
      ],
    );
  }

}
<commit_msg>s/size/size_bytes/ for consistency (#6410)<commit_after>// Copyright 2017 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![cfg_attr(
  feature = "cargo-clippy",
  deny(
    clippy, default_trait_access, expl_impl_clone_on_copy, if_not_else, needless_continue,
    single_match_else, unseparated_literal_suffix, used_underscore_binding
  )
)]
// It is often more clear to show that nothing is being moved.
#![cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
// Subjective style.
#![cfg_attr(feature = "cargo-clippy", allow(len_without_is_empty, redundant_field_names))]
// Default isn't as big a deal as people seem to think it is.
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![cfg_attr(feature = "cargo-clippy", allow(mutex_atomic))]

extern crate digest;
extern crate hex;
extern crate serde;
extern crate sha2;

use digest::{Digest as DigestTrait, FixedOutput};
use serde::ser::{Serialize, SerializeStruct, Serializer};
use sha2::Sha256;

use std::fmt;
use std::io::{self, Write};

const FINGERPRINT_SIZE: usize = 32;

#[derive(Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Fingerprint(pub [u8; FINGERPRINT_SIZE]);

impl Fingerprint {
  pub fn from_bytes_unsafe(bytes: &[u8]) -> Fingerprint {
    if bytes.len() != FINGERPRINT_SIZE {
      panic!(
        "Input value was not a fingerprint; had length: {}",
        bytes.len()
      );
    }

    let mut fingerprint = [0; FINGERPRINT_SIZE];
    fingerprint.clone_from_slice(&bytes[0..FINGERPRINT_SIZE]);
    Fingerprint(fingerprint)
  }

  pub fn from_hex_string(hex_string: &str) -> Result<Fingerprint, String> {
    <[u8; FINGERPRINT_SIZE] as hex::FromHex>::from_hex(hex_string)
      .map(Fingerprint)
      .map_err(|e| format!("{:?}", e))
  }

  pub fn as_bytes(&self) -> &[u8; FINGERPRINT_SIZE] {
    &self.0
  }

  pub fn to_hex(&self) -> String {
    let mut s = String::new();
    for &byte in &self.0 {
      fmt::Write::write_fmt(&mut s, format_args!("{:02x}", byte)).unwrap();
    }
    s
  }
}

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

impl fmt::Debug for Fingerprint {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Fingerprint<{}>", self.to_hex())
  }
}

impl AsRef<[u8]> for Fingerprint {
  fn as_ref(&self) -> &[u8] {
    &self.0[..]
  }
}

impl Serialize for Fingerprint {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  where
    S: Serializer,
  {
    serializer.serialize_str(self.to_hex().as_str())
  }
}

///
/// A Digest is a fingerprint, as well as the size in bytes of the plaintext for which that is the
/// fingerprint.
///
/// It is equivalent to a Bazel Remote Execution Digest, but without the overhead (and awkward API)
/// of needing to create an entire protobuf to pass around the two fields.
///
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Digest(pub Fingerprint, pub usize);

impl Serialize for Digest {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  where
    S: Serializer,
  {
    let mut obj = serializer.serialize_struct("digest", 2)?;
    obj.serialize_field("fingerprint", &self.0)?;
    obj.serialize_field("size_bytes", &self.1)?;
    obj.end()
  }
}

///
/// A Write instance that fingerprints all data that passes through it.
///
pub struct WriterHasher<W: Write> {
  hasher: Sha256,
  inner: W,
}

impl<W: Write> WriterHasher<W> {
  pub fn new(inner: W) -> WriterHasher<W> {
    WriterHasher {
      hasher: Sha256::default(),
      inner: inner,
    }
  }

  ///
  /// Returns the result of fingerprinting this stream, and Drops the stream.
  ///
  pub fn finish(self) -> Fingerprint {
    Fingerprint::from_bytes_unsafe(&self.hasher.fixed_result())
  }
}

impl<W: Write> Write for WriterHasher<W> {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    let written = self.inner.write(buf)?;
    // Hash the bytes that were successfully written.
    self.hasher.input(&buf[0..written]);
    Ok(written)
  }

  fn flush(&mut self) -> io::Result<()> {
    self.inner.flush()
  }
}

#[cfg(test)]
mod fingerprint_tests {
  use super::Fingerprint;
  extern crate serde_test;
  use self::serde_test::{assert_ser_tokens, Token};

  #[test]
  fn from_bytes_unsafe() {
    assert_eq!(
      Fingerprint::from_bytes_unsafe(&[
        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
        0xab, 0xab,
      ],),
      Fingerprint([0xab; 32])
    );
  }

  #[test]
  fn from_hex_string() {
    assert_eq!(
      Fingerprint::from_hex_string(
        "0123456789abcdefFEDCBA98765432100000000000000000ffFFfFfFFfFfFFff",
      ).unwrap(),
      Fingerprint([
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff,
      ],)
    )
  }

  #[test]
  fn from_hex_string_not_long_enough() {
    Fingerprint::from_hex_string("abcd").expect_err("Want err");
  }

  #[test]
  fn from_hex_string_too_long() {
    Fingerprint::from_hex_string(
      "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0",
    ).expect_err("Want err");
  }

  #[test]
  fn from_hex_string_invalid_chars() {
    Fingerprint::from_hex_string(
      "Q123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
    ).expect_err("Want err");
  }

  #[test]
  fn to_hex() {
    assert_eq!(
      Fingerprint([
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff,
      ],)
        .to_hex(),
      "0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff".to_lowercase()
    )
  }

  #[test]
  fn display() {
    let hex = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
    assert_eq!(
      Fingerprint::from_hex_string(hex).unwrap().to_hex(),
      hex.to_lowercase()
    )
  }

  #[test]
  fn serialize_to_str() {
    let fingerprint = Fingerprint([
      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
      0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff,
    ]);
    assert_ser_tokens(
      &fingerprint,
      &[Token::Str(
        "0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff",
      )],
    );
  }

}

#[cfg(test)]
mod digest_tests {
  use super::Digest;
  use super::Fingerprint;
  extern crate serde_test;
  use self::serde_test::{assert_ser_tokens, Token};

  #[test]
  fn serialize_to_str() {
    let digest = Digest(
      Fingerprint::from_hex_string(
        "0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff",
      ).unwrap(),
      1,
    );
    assert_ser_tokens(
      &digest,
      &[
        Token::Struct {
          name: "digest",
          len: 2,
        },
        Token::Str("fingerprint"),
        Token::Str("0123456789abcdeffedcba98765432100000000000000000ffffffffffffffff"),
        Token::Str("size_bytes"),
        Token::U64(1),
        Token::StructEnd,
      ],
    );
  }

}
<|endoftext|>