<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

mod rc_collector;
mod immix_collector;

use self::rc_collector::RCCollector;
use self::immix_collector::ImmixCollector;

use spaces::immix_space::BlockInfo;
use spaces::immix_space::ImmixSpace;
use spaces::large_object_space::LargeObjectSpace;

use std::collections::{HashSet, VecMap};
use std::num::Int;

use constants::{NUM_LINES_PER_BLOCK, USE_RC_COLLECTOR, USE_EVACUATION,
                EVAC_HEADROOM, CICLE_TRIGGER_THRESHHOLD, EVAC_TRIGGER_THRESHHOLD};
use gc_object::GCObjectRef;
use spaces::CollectionType;

/// The `Collector` is a composition of the collection implementations
/// `RCCollector` and `ImmixCollector`.
///
/// It manages performs common tasks and manages the cooperation between the
/// two collectors.
pub struct Collector {
    /// The reference counting collector.
    rc_collector: RCCollector,

    /// A buffer to store all managed blocks during collection.
    all_blocks: Vec<*mut BlockInfo>,

    /// A global backup of the object map.
    ///
    /// This is only used, if the library is compiled with `Valgrind` support
    /// and needed for accurate marking of addresses as `malloclike_block` and
    /// `freelike_block`.
    object_map_backup: HashSet<GCObjectRef>,

    /// The mark histogram used during collection to calculate the required
    /// space for evacuation.
    mark_histogram: VecMap<usize>,
}

impl Collector {
    /// Create a new `Collector`.
    pub fn new() -> Collector {
        return Collector {
            rc_collector: RCCollector::new(),
            all_blocks: Vec::new(),
            object_map_backup: HashSet::new(),
            mark_histogram: VecMap::with_capacity(NUM_LINES_PER_BLOCK),
        };
    }

    /// A write barrier for the given `object` used with the `RCCollector`.
    pub fn write_barrier(&mut self, object: GCObjectRef) -> bool {
        if USE_RC_COLLECTOR {
            return self.rc_collector.write_barrier(object);
        }
        return false;
    }

    /// Store the given blocks into the buffer for use during the collection.
    pub fn extend_all_blocks(&mut self, blocks: Vec<*mut BlockInfo>) {
        self.all_blocks.extend(blocks.into_iter());
    }

    /// Prepare a collection.
    ///
    /// This function decides if a evacuating and/or cycle collecting
    /// collection will be performed. If `evacuation` is set the collectors
    /// will try to evacuate. If `cycle_collect` is set the immix tracing
    /// collector will be used.
    pub fn prepare_collection(&mut self, evacuation: bool, cycle_collect: bool,
                              available_blocks: usize, total_blocks: usize,
                              evac_headroom: usize) -> CollectionType {
        let mut perform_evac = evacuation;

        let evac_threshhold = ((total_blocks as f32) * EVAC_TRIGGER_THRESHHOLD) as usize;
        let available_evac_blocks = available_blocks + evac_headroom;
        if evacuation || available_evac_blocks < evac_threshhold {
            let hole_threshhold = self.establish_hole_threshhold(evac_headroom);
            perform_evac = USE_EVACUATION && hole_threshhold > 0
                                          && hole_threshhold < NUM_LINES_PER_BLOCK;
            if perform_evac {
                debug!("Performing evacuation with hole_threshhold={} and evac_headroom={}",
                       hole_threshhold, evac_headroom);
                for block in self.all_blocks.iter_mut() {
                    unsafe{ (**block).set_evacuation_candidate(hole_threshhold); }
                }
            }
        }

        let perform_cycle_collect = if !cycle_collect {
            let cycle_theshold = ((total_blocks as f32) * CICLE_TRIGGER_THRESHHOLD) as usize;
            available_blocks < cycle_theshold
        } else { true };

        return match (USE_RC_COLLECTOR, perform_evac, perform_cycle_collect) {
            (true, false, false) => CollectionType::RCCollection,
            (true, true, false) => CollectionType::RCEvacCollection,
            (true, false, true) => CollectionType::ImmixCollection,
            (true, true, true) => CollectionType::ImmixEvacCollection,
            (false, false, _) => CollectionType::ImmixCollection,
            (false, true, _) => CollectionType::ImmixEvacCollection,
        }
    }

    /// Perform the collection.
    ///
    /// See `Spaces.collect() how it is called.`
    pub fn collect(&mut self, collection_type: &CollectionType,
                   roots: &[GCObjectRef], immix_space: &mut ImmixSpace,
                   large_object_space: &mut LargeObjectSpace, next_live_mark: bool) {

        if USE_RC_COLLECTOR {
            self.perform_rc_collection(collection_type, roots, immix_space,
                                       large_object_space);
        }

        if collection_type.is_immix() {
            self.perform_immix_collection(collection_type, roots,
                                          immix_space, next_live_mark);
        }
    }

    /// Perform the reference counting collection.
    fn perform_rc_collection(&mut self, collection_type: &CollectionType,
                             roots: &[GCObjectRef],
                             immix_space: &mut ImmixSpace,
                             large_object_space: &mut LargeObjectSpace) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_new_objects = unsafe{ (**block).get_new_objects() };
                self.object_map_backup.extend(block_new_objects.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).remove_new_objects_from_map(); }
        }

        self.rc_collector.collect(collection_type, roots, immix_space, large_object_space);

        large_object_space.proccess_free_buffer();

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    /// Perform the immix tracing collection.
    pub fn perform_immix_collection(&mut self, collection_type: &CollectionType,
                                    roots: &[GCObjectRef],
                                    immix_space: &mut ImmixSpace,
                                    next_live_mark: bool) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                self.object_map_backup.extend(block_object_map.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
            unsafe{ (**block).clear_object_map(); }
        }

        ImmixCollector::collect(collection_type, roots, immix_space, next_live_mark);

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    /// Complete the collection.
    pub fn complete_collection(&mut self, collection_type: &CollectionType,
                               immix_space: &mut ImmixSpace,
                               large_object_space: &mut LargeObjectSpace) {
        self.mark_histogram.clear();
        let (recyclable_blocks, free_blocks) = self.sweep_all_blocks();
        immix_space.set_recyclable_blocks(recyclable_blocks);

        // XXX We should not use a constant here, but something that
        // XXX changes dynamically (see rcimmix: MAX heuristic).
        let evac_headroom = if USE_EVACUATION {
            EVAC_HEADROOM - immix_space.evac_headroom() } else { 0 };
        immix_space.extend_evac_headroom(free_blocks.iter().take(evac_headroom)
                                                    .map(|&b| b).collect());
        immix_space.return_blocks(free_blocks.iter().skip(evac_headroom)
                                             .map(|&b| b).collect());

        if collection_type.is_immix() {
            large_object_space.sweep()
        }
    }
}

impl Collector {
    /// Sweep all blocks in the buffer after the collection.
    ///
    /// This function returns a list of recyclable blocks and a list of free
    /// blocks.
    fn sweep_all_blocks(&mut self) -> (Vec<*mut BlockInfo>, Vec<*mut BlockInfo>){
        let mut unavailable_blocks = Vec::new();
        let mut recyclable_blocks = Vec::new();
        let mut free_blocks = Vec::new();
        for block in self.all_blocks.drain() {
            if unsafe{ (*block).is_empty() } {
                if cfg!(feature = "valgrind") {
                    let block_object_map = unsafe{ (*block).get_object_map() };
                    for &object in block_object_map.iter() {
                        valgrind_freelike!(object);
                    }
                }
                unsafe{ (*block).reset() ;}
                debug!("Push block {:p} into free_blocks", block);
                free_blocks.push(block);
            } else {
                unsafe{ (*block).count_holes(); }
                let (holes, marked_lines) = unsafe{ (*block).count_holes_and_marked_lines() };
                if self.mark_histogram.contains_key(&(holes as usize)) {
                    if let Some(val) = self.mark_histogram.get_mut(&(holes as usize)) {
                        *val += marked_lines;
                    }
                } else { self.mark_histogram.insert(holes as usize, marked_lines); }
                debug!("Found {} holes and {} marked lines in block {:p}",
                       holes, marked_lines, block);
                match holes {
                    0 => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        unavailable_blocks.push(block);
                    },
                    _ => {
                        debug!("Push block {:p} into recyclable_blocks", block);
                        recyclable_blocks.push(block);
                    }
                }
            }
        }
        self.all_blocks = unavailable_blocks;
        return (recyclable_blocks, free_blocks);
    }

    /// Calculate how many holes a block needs to have to be selected as a
    /// evacuation candidate.
    fn establish_hole_threshhold(&self, evac_headroom: usize) -> usize {
        let mut available_histogram : VecMap<usize> = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for block in self.all_blocks.iter() {
            let (holes, free_lines) = unsafe{ (**block).count_holes_and_available_lines() };
            if available_histogram.contains_key(&(holes as usize)) {
                if let Some(val) = available_histogram.get_mut(&(holes as usize)) {
                    *val += free_lines;
                }
            } else { available_histogram.insert(holes as usize, free_lines); }
        }
        let mut required_lines = 0;
        let mut available_lines = evac_headroom * (NUM_LINES_PER_BLOCK - 1);

        for threshold in (0..NUM_LINES_PER_BLOCK) {
            required_lines += *self.mark_histogram.get(&threshold).unwrap_or(&0);
            available_lines = available_lines.saturating_sub(*available_histogram.get(&threshold).unwrap_or(&0));
            if available_lines <= required_lines {
                return threshold;
            }
        }
        return NUM_LINES_PER_BLOCK;
    }
}
<commit_msg>Fix determination of collection type<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

mod rc_collector;
mod immix_collector;

use self::rc_collector::RCCollector;
use self::immix_collector::ImmixCollector;

use spaces::immix_space::BlockInfo;
use spaces::immix_space::ImmixSpace;
use spaces::large_object_space::LargeObjectSpace;

use std::collections::{HashSet, VecMap};
use std::num::Int;

use constants::{NUM_LINES_PER_BLOCK, USE_RC_COLLECTOR, USE_EVACUATION,
                EVAC_HEADROOM, CICLE_TRIGGER_THRESHHOLD, EVAC_TRIGGER_THRESHHOLD};
use gc_object::GCObjectRef;
use spaces::CollectionType;

/// The `Collector` is a composition of the collection implementations
/// `RCCollector` and `ImmixCollector`.
///
/// It manages performs common tasks and manages the cooperation between the
/// two collectors.
pub struct Collector {
    /// The reference counting collector.
    rc_collector: RCCollector,

    /// A buffer to store all managed blocks during collection.
    all_blocks: Vec<*mut BlockInfo>,

    /// A global backup of the object map.
    ///
    /// This is only used, if the library is compiled with `Valgrind` support
    /// and needed for accurate marking of addresses as `malloclike_block` and
    /// `freelike_block`.
    object_map_backup: HashSet<GCObjectRef>,

    /// The mark histogram used during collection to calculate the required
    /// space for evacuation.
    mark_histogram: VecMap<usize>,
}

impl Collector {
    /// Create a new `Collector`.
    pub fn new() -> Collector {
        return Collector {
            rc_collector: RCCollector::new(),
            all_blocks: Vec::new(),
            object_map_backup: HashSet::new(),
            mark_histogram: VecMap::with_capacity(NUM_LINES_PER_BLOCK),
        };
    }

    /// A write barrier for the given `object` used with the `RCCollector`.
    pub fn write_barrier(&mut self, object: GCObjectRef) -> bool {
        if USE_RC_COLLECTOR {
            return self.rc_collector.write_barrier(object);
        }
        return false;
    }

    /// Store the given blocks into the buffer for use during the collection.
    pub fn extend_all_blocks(&mut self, blocks: Vec<*mut BlockInfo>) {
        self.all_blocks.extend(blocks.into_iter());
    }

    /// Prepare a collection.
    ///
    /// This function decides if a evacuating and/or cycle collecting
    /// collection will be performed. If `evacuation` is set the collectors
    /// will try to evacuate. If `cycle_collect` is set the immix tracing
    /// collector will be used.
    pub fn prepare_collection(&mut self, evacuation: bool, cycle_collect: bool,
                              available_blocks: usize, total_blocks: usize,
                              evac_headroom: usize) -> CollectionType {
        let mut perform_evac = evacuation;

        let evac_threshhold = ((total_blocks as f32) * EVAC_TRIGGER_THRESHHOLD) as usize;
        let available_evac_blocks = available_blocks + evac_headroom;
        if evacuation || available_evac_blocks < evac_threshhold {
            let hole_threshhold = self.establish_hole_threshhold(evac_headroom);
            perform_evac = USE_EVACUATION && hole_threshhold > 0
                                          && hole_threshhold < NUM_LINES_PER_BLOCK;
            if perform_evac {
                debug!("Performing evacuation with hole_threshhold={} and evac_headroom={}",
                       hole_threshhold, evac_headroom);
                for block in self.all_blocks.iter_mut() {
                    unsafe{ (**block).set_evacuation_candidate(hole_threshhold); }
                }
            }
        }

        let cycle_theshold = ((total_blocks as f32) * CICLE_TRIGGER_THRESHHOLD) as usize;
        let perform_cycle_collect = cycle_collect && (available_blocks < cycle_theshold);

        return match (USE_RC_COLLECTOR, perform_evac, perform_cycle_collect) {
            (true, false, false) => CollectionType::RCCollection,
            (true, true, false) => CollectionType::RCEvacCollection,
            (true, false, true) => CollectionType::ImmixCollection,
            (true, true, true) => CollectionType::ImmixEvacCollection,
            (false, false, _) => CollectionType::ImmixCollection,
            (false, true, _) => CollectionType::ImmixEvacCollection,
        }
    }

    /// Perform the collection.
    ///
    /// See `Spaces.collect() how it is called.`
    pub fn collect(&mut self, collection_type: &CollectionType,
                   roots: &[GCObjectRef], immix_space: &mut ImmixSpace,
                   large_object_space: &mut LargeObjectSpace, next_live_mark: bool) {

        if USE_RC_COLLECTOR {
            self.perform_rc_collection(collection_type, roots, immix_space,
                                       large_object_space);
        }

        if collection_type.is_immix() {
            self.perform_immix_collection(collection_type, roots,
                                          immix_space, next_live_mark);
        }
    }

    /// Perform the reference counting collection.
    fn perform_rc_collection(&mut self, collection_type: &CollectionType,
                             roots: &[GCObjectRef],
                             immix_space: &mut ImmixSpace,
                             large_object_space: &mut LargeObjectSpace) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_new_objects = unsafe{ (**block).get_new_objects() };
                self.object_map_backup.extend(block_new_objects.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).remove_new_objects_from_map(); }
        }

        self.rc_collector.collect(collection_type, roots, immix_space, large_object_space);

        large_object_space.proccess_free_buffer();

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    /// Perform the immix tracing collection.
    pub fn perform_immix_collection(&mut self, collection_type: &CollectionType,
                                    roots: &[GCObjectRef],
                                    immix_space: &mut ImmixSpace,
                                    next_live_mark: bool) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                self.object_map_backup.extend(block_object_map.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
            unsafe{ (**block).clear_object_map(); }
        }

        ImmixCollector::collect(collection_type, roots, immix_space, next_live_mark);

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    /// Complete the collection.
    pub fn complete_collection(&mut self, collection_type: &CollectionType,
                               immix_space: &mut ImmixSpace,
                               large_object_space: &mut LargeObjectSpace) {
        self.mark_histogram.clear();
        let (recyclable_blocks, free_blocks) = self.sweep_all_blocks();
        immix_space.set_recyclable_blocks(recyclable_blocks);

        // XXX We should not use a constant here, but something that
        // XXX changes dynamically (see rcimmix: MAX heuristic).
        let evac_headroom = if USE_EVACUATION {
            EVAC_HEADROOM - immix_space.evac_headroom() } else { 0 };
        immix_space.extend_evac_headroom(free_blocks.iter().take(evac_headroom)
                                                    .map(|&b| b).collect());
        immix_space.return_blocks(free_blocks.iter().skip(evac_headroom)
                                             .map(|&b| b).collect());

        if collection_type.is_immix() {
            large_object_space.sweep()
        }
    }
}

impl Collector {
    /// Sweep all blocks in the buffer after the collection.
    ///
    /// This function returns a list of recyclable blocks and a list of free
    /// blocks.
    fn sweep_all_blocks(&mut self) -> (Vec<*mut BlockInfo>, Vec<*mut BlockInfo>){
        let mut unavailable_blocks = Vec::new();
        let mut recyclable_blocks = Vec::new();
        let mut free_blocks = Vec::new();
        for block in self.all_blocks.drain() {
            if unsafe{ (*block).is_empty() } {
                if cfg!(feature = "valgrind") {
                    let block_object_map = unsafe{ (*block).get_object_map() };
                    for &object in block_object_map.iter() {
                        valgrind_freelike!(object);
                    }
                }
                unsafe{ (*block).reset() ;}
                debug!("Push block {:p} into free_blocks", block);
                free_blocks.push(block);
            } else {
                unsafe{ (*block).count_holes(); }
                let (holes, marked_lines) = unsafe{ (*block).count_holes_and_marked_lines() };
                if self.mark_histogram.contains_key(&(holes as usize)) {
                    if let Some(val) = self.mark_histogram.get_mut(&(holes as usize)) {
                        *val += marked_lines;
                    }
                } else { self.mark_histogram.insert(holes as usize, marked_lines); }
                debug!("Found {} holes and {} marked lines in block {:p}",
                       holes, marked_lines, block);
                match holes {
                    0 => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        unavailable_blocks.push(block);
                    },
                    _ => {
                        debug!("Push block {:p} into recyclable_blocks", block);
                        recyclable_blocks.push(block);
                    }
                }
            }
        }
        self.all_blocks = unavailable_blocks;
        return (recyclable_blocks, free_blocks);
    }

    /// Calculate how many holes a block needs to have to be selected as a
    /// evacuation candidate.
    fn establish_hole_threshhold(&self, evac_headroom: usize) -> usize {
        let mut available_histogram : VecMap<usize> = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for block in self.all_blocks.iter() {
            let (holes, free_lines) = unsafe{ (**block).count_holes_and_available_lines() };
            if available_histogram.contains_key(&(holes as usize)) {
                if let Some(val) = available_histogram.get_mut(&(holes as usize)) {
                    *val += free_lines;
                }
            } else { available_histogram.insert(holes as usize, free_lines); }
        }
        let mut required_lines = 0;
        let mut available_lines = evac_headroom * (NUM_LINES_PER_BLOCK - 1);

        for threshold in (0..NUM_LINES_PER_BLOCK) {
            required_lines += *self.mark_histogram.get(&threshold).unwrap_or(&0);
            available_lines = available_lines.saturating_sub(*available_histogram.get(&threshold).unwrap_or(&0));
            if available_lines <= required_lines {
                return threshold;
            }
        }
        return NUM_LINES_PER_BLOCK;
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use super::*;
use serde_json;

const SHA256SUM: &str = "c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646";

pub fn get_update_json() -> serde_json::Value {
    json!(
        {
            "product-uid": "0123456789",
            "version": "1.0",
            "supported-hardware": ["board"],
            "objects":
            [
                {
                    "mode": "test",
                    "filename": "testfile",
                    "target": "/dev/device1",
                    "sha256sum": SHA256SUM,
                    "size": 10
                }
            ]
        }
    )
}

pub fn get_update_package() -> UpdatePackage {
    serde_json::from_value(get_update_json()).unwrap()
}

pub fn create_fake_object(settings: &Settings) {
    use std::fs::create_dir_all;
    use std::fs::File;
    use std::io::Write;

    let dir = &settings.update.download_dir;

    // ensure path exists
    create_dir_all(&dir).unwrap();

    let _ = File::create(&dir.join(SHA256SUM))
        .unwrap()
        .write_all(b"1234567890")
        .unwrap();
}

pub fn create_fake_settings() -> Settings {
    use tempfile::tempdir;

    let mut settings = Settings::default();
    settings.update.download_dir = tempdir().unwrap().path().to_path_buf();
    settings
}

#[test]
fn missing_object_file() {
    let u = get_update_package();
    let settings = create_fake_settings();

    assert_eq!(u.filter_objects(&settings, &ObjectStatus::Missing).len(), 1);
}

#[test]
fn complete_object_file() {
    let u = get_update_package();
    let settings = create_fake_settings();

    create_fake_object(&settings);

    assert!(
        u.filter_objects(&settings, &ObjectStatus::Missing)
            .is_empty()
    );

    assert!(
        u.filter_objects(&settings, &ObjectStatus::Incomplete)
            .is_empty()
    );

    assert!(
        u.filter_objects(&settings, &ObjectStatus::Corrupted)
            .is_empty()
    );

    assert_eq!(
        u.filter_objects(&settings, &ObjectStatus::Ready)
            .iter()
            .count(),
        1
    );
}
<commit_msg>update_package: tests: output errors of fake JSON parsing<commit_after>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use super::*;
use serde_json;

const SHA256SUM: &str = "c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646";

pub fn get_update_json() -> serde_json::Value {
    json!(
        {
            "product-uid": "0123456789",
            "version": "1.0",
            "supported-hardware": ["board"],
            "objects":
            [
                {
                    "mode": "test",
                    "filename": "testfile",
                    "target": "/dev/device1",
                    "sha256sum": SHA256SUM,
                    "size": 10
                }
            ]
        }
    )
}

pub fn get_update_package() -> UpdatePackage {
    serde_json::from_value(get_update_json())
        .map_err(|e| println!("{:?}", e))
        .unwrap()
}

pub fn create_fake_object(settings: &Settings) {
    use std::fs::create_dir_all;
    use std::fs::File;
    use std::io::Write;

    let dir = &settings.update.download_dir;

    // ensure path exists
    create_dir_all(&dir).unwrap();

    let _ = File::create(&dir.join(SHA256SUM))
        .unwrap()
        .write_all(b"1234567890")
        .unwrap();
}

pub fn create_fake_settings() -> Settings {
    use tempfile::tempdir;

    let mut settings = Settings::default();
    settings.update.download_dir = tempdir().unwrap().path().to_path_buf();
    settings
}

#[test]
fn missing_object_file() {
    let u = get_update_package();
    let settings = create_fake_settings();

    assert_eq!(u.filter_objects(&settings, &ObjectStatus::Missing).len(), 1);
}

#[test]
fn complete_object_file() {
    let u = get_update_package();
    let settings = create_fake_settings();

    create_fake_object(&settings);

    assert!(
        u.filter_objects(&settings, &ObjectStatus::Missing)
            .is_empty()
    );

    assert!(
        u.filter_objects(&settings, &ObjectStatus::Incomplete)
            .is_empty()
    );

    assert!(
        u.filter_objects(&settings, &ObjectStatus::Corrupted)
            .is_empty()
    );

    assert_eq!(
        u.filter_objects(&settings, &ObjectStatus::Ready)
            .iter()
            .count(),
        1
    );
}
<|endoftext|>


<commit_before>// * This file is part of the uutils coreutils package.
// *
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

use smallvec::SmallVec;
use std::cell::RefCell;
use std::fmt;

use crate::numeric::{Arithmetic, Montgomery};
use crate::{miller_rabin, rho, table};

type Exponent = u8;

#[derive(Clone, Debug)]
struct Decomposition(SmallVec<[(u64, Exponent); NUM_FACTORS_INLINE]>);

// The number of factors to inline directly into a `Decomposition` object.
// As a consequence of the Erdős–Kac theorem, the average number of prime factors
// of integers < 10²⁵ ≃ 2⁸³ is 4, so we can use a slightly higher value.
const NUM_FACTORS_INLINE: usize = 5;

impl Decomposition {
    fn one() -> Decomposition {
        Decomposition(SmallVec::new())
    }

    fn add(&mut self, factor: u64, exp: Exponent) {
        debug_assert!(exp > 0);

        if let Some((_, e)) = self.0.iter_mut().find(|(f, _)| *f == factor) {
            *e += exp;
        } else {
            self.0.push((factor, exp))
        }
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0
            .iter()
            .fold(1, |acc, (p, exp)| acc * p.pow(*exp as u32))
    }

    fn get(&self, p: u64) -> Option<&(u64, u8)> {
        self.0.iter().find(|(q, _)| *q == p)
    }
}

impl PartialEq for Decomposition {
    fn eq(&self, other: &Decomposition) -> bool {
        for p in &self.0 {
            if other.get(p.0) != Some(p) {
                return false;
            }
        }

        for p in &other.0 {
            if self.get(p.0) != Some(p) {
                return false;
            }
        }

        true
    }
}
impl Eq for Decomposition {}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Factors(RefCell<Decomposition>);

impl Factors {
    pub fn one() -> Factors {
        Factors(RefCell::new(Decomposition::one()))
    }

    pub fn add(&mut self, prime: u64, exp: Exponent) {
        debug_assert!(miller_rabin::is_prime(prime));
        self.0.borrow_mut().add(prime, exp)
    }

    pub fn push(&mut self, prime: u64) {
        self.add(prime, 1)
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0.borrow().product()
    }
}

impl fmt::Display for Factors {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let v = &mut (self.0).borrow_mut().0;
        v.sort_unstable();

        for (p, exp) in v.iter() {
            for _ in 0..*exp {
                write!(f, " {}", p)?
            }
        }

        Ok(())
    }
}

fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64, f: Factors) -> Factors {
    use miller_rabin::Result::*;

    // Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
    let _factor = |n, f| {
        if n < (1 << 32) {
            _factor::<Montgomery<u32>>(n, f)
        } else {
            _factor::<A>(n, f)
        }
    };

    if num == 1 {
        return f;
    }

    let n = A::new(num);
    let divisor = match miller_rabin::test::<A>(n) {
        Prime => {
            #[cfg(feature = "coz")]
            coz::progress!("factor found");
            let mut r = f;
            r.push(num);
            return r;
        }

        Composite(d) => d,
        Pseudoprime => rho::find_divisor::<A>(n),
    };

    let f = _factor(divisor, f);
    _factor(num / divisor, f)
}

pub fn factor(mut n: u64) -> Factors {
    #[cfg(feature = "coz")]
    coz::begin!("factorization");
    let mut factors = Factors::one();

    if n < 2 {
        return factors;
    }

    let n_zeros = n.trailing_zeros();
    if n_zeros > 0 {
        factors.add(2, n_zeros as Exponent);
        n >>= n_zeros;
    }

    if n == 1 {
        #[cfg(feature = "coz")]
        coz::end!("factorization");
        return factors;
    }

    table::factor(&mut n, &mut factors);

    #[allow(clippy::let_and_return)]
    let r = if n < (1 << 32) {
        _factor::<Montgomery<u32>>(n, factors)
    } else {
        _factor::<Montgomery<u64>>(n, factors)
    };

    #[cfg(feature = "coz")]
    coz::end!("factorization");

    r
}

#[cfg(test)]
mod tests {
    use super::{factor, Decomposition, Exponent, Factors};
    use quickcheck::quickcheck;
    use smallvec::smallvec;
    use std::cell::RefCell;

    #[test]
    fn factor_2044854919485649() {
        let f = Factors(RefCell::new(Decomposition(smallvec![
            (503, 1),
            (2423, 1),
            (40961, 2)
        ])));
        assert_eq!(factor(f.product()), f);
    }

    #[test]
    fn factor_recombines_small() {
        assert!((1..10_000)
            .map(|i| 2 * i + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_overflowing() {
        assert!((0..250)
            .map(|i| 2 * i + 2u64.pow(32) + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_strong_pseudoprime() {
        // This is a strong pseudoprime (wrt. miller_rabin::BASIS)
        //  and triggered a bug in rho::factor's code path handling
        //  miller_rabbin::Result::Composite
        let pseudoprime = 17179869183;
        for _ in 0..20 {
            // Repeat the test 20 times, as it only fails some fraction
            // of the time.
            assert!(factor(pseudoprime).product() == pseudoprime);
        }
    }

    quickcheck! {
        fn factor_recombines(i: u64) -> bool {
            i == 0 || factor(i).product() == i
        }

        fn recombines_factors(f: Factors) -> () {
            assert_eq!(factor(f.product()), f);
        }

        fn exponentiate_factors(f: Factors, e: Exponent) -> () {
            if e == 0 { return; }
            if let Some(fe) = f.product().checked_pow(e.into()) {
                assert_eq!(factor(fe), f ^ e);
            }
        }
    }
}

#[cfg(test)]
impl quickcheck::Arbitrary for Factors {
    fn arbitrary<G: quickcheck::Gen>(gen: &mut G) -> Self {
        use rand::Rng;
        let mut f = Factors::one();
        let mut g = 1u64;
        let mut n = u64::MAX;

        // Adam Kalai's algorithm for generating uniformly-distributed
        // integers and their factorization.
        //
        // See Generating Random Factored Numbers, Easily, J. Cryptology (2003)
        'attempt: loop {
            while n > 1 {
                n = gen.gen_range(1, n);
                if miller_rabin::is_prime(n) {
                    if let Some(h) = g.checked_mul(n) {
                        f.push(n);
                        g = h;
                    } else {
                        // We are overflowing u64, retry
                        continue 'attempt;
                    }
                }
            }

            return f;
        }
    }
}

#[cfg(test)]
impl std::ops::BitXor<Exponent> for Factors {
    type Output = Self;

    fn bitxor(self, rhs: Exponent) -> Factors {
        debug_assert_ne!(rhs, 0);
        let mut r = Factors::one();
        for (p, e) in self.0.borrow().0.iter() {
            r.add(*p, rhs * e);
        }

        debug_assert_eq!(r.product(), self.product().pow(rhs.into()));
        r
    }
}
<commit_msg>factor: Make random Factors instance generatable for tests<commit_after>// * This file is part of the uutils coreutils package.
// *
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

use smallvec::SmallVec;
use std::cell::RefCell;
use std::fmt;

use crate::numeric::{Arithmetic, Montgomery};
use crate::{miller_rabin, rho, table};

type Exponent = u8;

#[derive(Clone, Debug)]
struct Decomposition(SmallVec<[(u64, Exponent); NUM_FACTORS_INLINE]>);

// The number of factors to inline directly into a `Decomposition` object.
// As a consequence of the Erdős–Kac theorem, the average number of prime factors
// of integers < 10²⁵ ≃ 2⁸³ is 4, so we can use a slightly higher value.
const NUM_FACTORS_INLINE: usize = 5;

impl Decomposition {
    fn one() -> Decomposition {
        Decomposition(SmallVec::new())
    }

    fn add(&mut self, factor: u64, exp: Exponent) {
        debug_assert!(exp > 0);

        if let Some((_, e)) = self.0.iter_mut().find(|(f, _)| *f == factor) {
            *e += exp;
        } else {
            self.0.push((factor, exp))
        }
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0
            .iter()
            .fold(1, |acc, (p, exp)| acc * p.pow(*exp as u32))
    }

    fn get(&self, p: u64) -> Option<&(u64, u8)> {
        self.0.iter().find(|(q, _)| *q == p)
    }
}

impl PartialEq for Decomposition {
    fn eq(&self, other: &Decomposition) -> bool {
        for p in &self.0 {
            if other.get(p.0) != Some(p) {
                return false;
            }
        }

        for p in &other.0 {
            if self.get(p.0) != Some(p) {
                return false;
            }
        }

        true
    }
}
impl Eq for Decomposition {}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Factors(RefCell<Decomposition>);

impl Factors {
    pub fn one() -> Factors {
        Factors(RefCell::new(Decomposition::one()))
    }

    pub fn add(&mut self, prime: u64, exp: Exponent) {
        debug_assert!(miller_rabin::is_prime(prime));
        self.0.borrow_mut().add(prime, exp)
    }

    pub fn push(&mut self, prime: u64) {
        self.add(prime, 1)
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0.borrow().product()
    }
}

impl fmt::Display for Factors {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let v = &mut (self.0).borrow_mut().0;
        v.sort_unstable();

        for (p, exp) in v.iter() {
            for _ in 0..*exp {
                write!(f, " {}", p)?
            }
        }

        Ok(())
    }
}

fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64, f: Factors) -> Factors {
    use miller_rabin::Result::*;

    // Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
    let _factor = |n, f| {
        if n < (1 << 32) {
            _factor::<Montgomery<u32>>(n, f)
        } else {
            _factor::<A>(n, f)
        }
    };

    if num == 1 {
        return f;
    }

    let n = A::new(num);
    let divisor = match miller_rabin::test::<A>(n) {
        Prime => {
            #[cfg(feature = "coz")]
            coz::progress!("factor found");
            let mut r = f;
            r.push(num);
            return r;
        }

        Composite(d) => d,
        Pseudoprime => rho::find_divisor::<A>(n),
    };

    let f = _factor(divisor, f);
    _factor(num / divisor, f)
}

pub fn factor(mut n: u64) -> Factors {
    #[cfg(feature = "coz")]
    coz::begin!("factorization");
    let mut factors = Factors::one();

    if n < 2 {
        return factors;
    }

    let n_zeros = n.trailing_zeros();
    if n_zeros > 0 {
        factors.add(2, n_zeros as Exponent);
        n >>= n_zeros;
    }

    if n == 1 {
        #[cfg(feature = "coz")]
        coz::end!("factorization");
        return factors;
    }

    table::factor(&mut n, &mut factors);

    #[allow(clippy::let_and_return)]
    let r = if n < (1 << 32) {
        _factor::<Montgomery<u32>>(n, factors)
    } else {
        _factor::<Montgomery<u64>>(n, factors)
    };

    #[cfg(feature = "coz")]
    coz::end!("factorization");

    r
}

#[cfg(test)]
mod tests {
    use super::{factor, Decomposition, Exponent, Factors};
    use quickcheck::quickcheck;
    use smallvec::smallvec;
    use std::cell::RefCell;

    #[test]
    fn factor_2044854919485649() {
        let f = Factors(RefCell::new(Decomposition(smallvec![
            (503, 1),
            (2423, 1),
            (40961, 2)
        ])));
        assert_eq!(factor(f.product()), f);
    }

    #[test]
    fn factor_recombines_small() {
        assert!((1..10_000)
            .map(|i| 2 * i + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_overflowing() {
        assert!((0..250)
            .map(|i| 2 * i + 2u64.pow(32) + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_strong_pseudoprime() {
        // This is a strong pseudoprime (wrt. miller_rabin::BASIS)
        //  and triggered a bug in rho::factor's code path handling
        //  miller_rabbin::Result::Composite
        let pseudoprime = 17179869183;
        for _ in 0..20 {
            // Repeat the test 20 times, as it only fails some fraction
            // of the time.
            assert!(factor(pseudoprime).product() == pseudoprime);
        }
    }

    quickcheck! {
        fn factor_recombines(i: u64) -> bool {
            i == 0 || factor(i).product() == i
        }

        fn recombines_factors(f: Factors) -> () {
            assert_eq!(factor(f.product()), f);
        }

        fn exponentiate_factors(f: Factors, e: Exponent) -> () {
            if e == 0 { return; }
            if let Some(fe) = f.product().checked_pow(e.into()) {
                assert_eq!(factor(fe), f ^ e);
            }
        }
    }
}

#[cfg(test)]
use rand::{
    distributions::{Distribution, Standard},
    Rng,
};
#[cfg(test)]
impl Distribution<Factors> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Factors {
        let mut f = Factors::one();
        let mut g = 1u64;
        let mut n = u64::MAX;

        // Adam Kalai's algorithm for generating uniformly-distributed
        // integers and their factorization.
        //
        // See Generating Random Factored Numbers, Easily, J. Cryptology (2003)
        'attempt: loop {
            while n > 1 {
                n = rng.gen_range(1, n);
                if miller_rabin::is_prime(n) {
                    if let Some(h) = g.checked_mul(n) {
                        f.push(n);
                        g = h;
                    } else {
                        // We are overflowing u64, retry
                        continue 'attempt;
                    }
                }
            }

            return f;
        }
    }
}

#[cfg(test)]
impl quickcheck::Arbitrary for Factors {
    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
        g.gen()
    }
}

#[cfg(test)]
impl std::ops::BitXor<Exponent> for Factors {
    type Output = Self;

    fn bitxor(self, rhs: Exponent) -> Factors {
        debug_assert_ne!(rhs, 0);
        let mut r = Factors::one();
        for (p, e) in self.0.borrow().0.iter() {
            r.add(*p, rhs * e);
        }

        debug_assert_eq!(r.product(), self.product().pow(rhs.into()));
        r
    }
}
<|endoftext|>


<commit_before>// Make me compile without changing the function signature! Scroll down for hints :)

fn main() {
    let word = String::from("green"); // Try not changing this line :)
    if is_a_color_word(word) {
        println!("That is a color word I know!");
    } else {
        println!("That is not a color word I know.");
    }
}

fn is_a_color_word(attempt: &str) -> bool {
    attempt == "green" || attempt == "blue" || attempt == "red"
}

// Yes, it would be really easy to fix this by just changing the value bound to `guess1` to be a
// string slice instead of a `String`, wouldn't it?? There is a way to add one character to line
// 5, though, that will coerce the `String` into a string slice.
<commit_msg>Update hint to reflect new variable binding name<commit_after>// Make me compile without changing the function signature! Scroll down for hints :)

fn main() {
    let word = String::from("green"); // Try not changing this line :)
    if is_a_color_word(word) {
        println!("That is a color word I know!");
    } else {
        println!("That is not a color word I know.");
    }
}

fn is_a_color_word(attempt: &str) -> bool {
    attempt == "green" || attempt == "blue" || attempt == "red"
}

// Yes, it would be really easy to fix this by just changing the value bound to `word` to be a
// string slice instead of a `String`, wouldn't it?? There is a way to add one character to line
// 5, though, that will coerce the `String` into a string slice.
<|endoftext|>


<commit_before>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::fs::{self, File, OpenOptions};
use std::io::{self, Error, ErrorKind, Write};
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};

use chrono::{DateTime, Local};

use crate::config::{ReadableDuration, ReadableSize};

/// Opens log file with append mode. Creates a new log file if it doesn't exist.
fn open_log_file(path: impl AsRef<Path>) -> io::Result<File> {
    let path = path.as_ref();
    let parent = path.parent().ok_or_else(|| {
        Error::new(
            ErrorKind::Other,
            "Unable to get parent directory of log file",
        )
    })?;
    if !parent.is_dir() {
        fs::create_dir_all(parent)?
    }
    OpenOptions::new().append(true).create(true).open(path)
}

/// A trait that describes a file rotation operation.
pub trait Rotator: Send {
    /// Check if the option is enabled in configuration.
    /// Return true if the `rotator` is valid.
    fn is_enabled(&self) -> bool;

    /// Call by operator, initializes the states of rotators.
    fn prepare(&mut self, file: &File) -> io::Result<()>;

    /// Return if the file need to be rotated.
    fn should_rotate(&self) -> bool;

    /// Call by operator, update rotators' state while the operator try to write some data.
    fn on_write(&mut self, data: &[u8]) -> io::Result<()>;

    /// Call by operator, update rotators' state while the operator execute a rotation.
    fn on_rotate(&mut self) -> io::Result<()>;
}

/// This `FileLogger` will iterate over a series of `Rotators`,
/// once the context trigger the `Rotator`, it will execute a rotation.
///
/// After rotating, the original log file would be renamed to "{original name}.{%Y-%m-%d-%H:%M:%S}".
/// Note: log file will *not* be compressed or otherwise modified.
pub struct RotatingFileLogger {
    path: PathBuf,
    file: File,
    rename: Box<dyn Send + Fn(&Path) -> io::Result<PathBuf>>,
    rotators: Vec<Box<dyn Rotator>>,
}

/// Builder for `RotatingFileLogger`.
pub struct RotatingFileLoggerBuilder {
    rotators: Vec<Box<dyn Rotator>>,
    path: PathBuf,
    rename: Box<dyn Send + Fn(&Path) -> io::Result<PathBuf>>,
}

impl RotatingFileLoggerBuilder {
    pub fn new<F>(path: impl AsRef<Path>, rename: F) -> Self
    where
        F: 'static + Send + Fn(&Path) -> io::Result<PathBuf>,
    {
        RotatingFileLoggerBuilder {
            path: path.as_ref().to_path_buf(),
            rotators: vec![],
            rename: Box::new(rename),
        }
    }

    pub fn add_rotator<R: 'static + Rotator>(mut self, rotator: R) -> Self {
        if rotator.is_enabled() {
            self.rotators.push(Box::new(rotator));
        }
        self
    }

    pub fn build(mut self) -> io::Result<RotatingFileLogger> {
        let file = open_log_file(&self.path)?;

        for rotator in self.rotators.iter_mut() {
            rotator.prepare(&file)?;
        }

        Ok(RotatingFileLogger {
            rotators: self.rotators,
            path: self.path,
            rename: self.rename,
            file,
        })
    }
}

impl Write for RotatingFileLogger {
    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
        // Updates all roators' states.
        for rotator in self.rotators.iter_mut() {
            rotator.on_write(bytes)?;
        }
        self.file.write(bytes)
    }

    fn flush(&mut self) -> io::Result<()> {
        for rotator in self.rotators.iter() {
            if rotator.should_rotate() {
                self.file.flush()?;

                let new_path = (self.rename)(&self.path)?;
                fs::rename(&self.path, &new_path)?;
                self.file = open_log_file(&self.path)?;

                // Updates all roators' states.
                for rotator in self.rotators.iter_mut() {
                    rotator.on_rotate()?;
                }

                return Ok(());
            }
        }
        self.file.flush()
    }
}

impl Drop for RotatingFileLogger {
    fn drop(&mut self) {
        let _ = self.file.flush();
    }
}

pub struct RotateByTime {
    rotation_timespan: ReadableDuration,
    next_rotation_time: Option<SystemTime>,
}

impl RotateByTime {
    pub fn new(rotation_timespan: ReadableDuration) -> Self {
        Self {
            rotation_timespan,
            next_rotation_time: None,
        }
    }

    fn next_rotation_time(begin: SystemTime, duration: Duration) -> io::Result<SystemTime> {
        begin
            .checked_add(duration)
            .ok_or_else(|| Error::new(ErrorKind::Other, "Next rotation time is out of range."))
    }
}

impl Rotator for RotateByTime {
    fn is_enabled(&self) -> bool {
        !self.rotation_timespan.is_zero()
    }

    fn prepare(&mut self, file: &File) -> io::Result<()> {
        // Try to get the creation time first,
        // if fail to acquire, try to acquire access time.
        let metadata = file.metadata()?;
        let birth = metadata.created().or_else(|_| metadata.accessed())?;
        self.next_rotation_time = Some(Self::next_rotation_time(birth, self.rotation_timespan.0)?);
        Ok(())
    }

    fn should_rotate(&self) -> bool {
        assert!(self.next_rotation_time.is_some());
        Local::now() > DateTime::<Local>::from(self.next_rotation_time.unwrap())
    }

    fn on_write(&mut self, _: &[u8]) -> io::Result<()> {
        Ok(())
    }

    fn on_rotate(&mut self) -> io::Result<()> {
        assert!(self.next_rotation_time.is_some());
        self.next_rotation_time = Some(Self::next_rotation_time(
            SystemTime::now(),
            self.rotation_timespan.0,
        )?);
        Ok(())
    }
}

pub struct RotateBySize {
    rotation_size: ReadableSize,
    file_size: u64,
}

impl RotateBySize {
    pub fn new(rotation_size: ReadableSize) -> Self {
        RotateBySize {
            rotation_size,
            file_size: 0,
        }
    }
}

impl Rotator for RotateBySize {
    fn is_enabled(&self) -> bool {
        self.rotation_size.0 != 0
    }

    fn prepare(&mut self, file: &File) -> io::Result<()> {
        self.file_size = file.metadata()?.len();
        Ok(())
    }

    fn should_rotate(&self) -> bool {
        self.file_size > self.rotation_size.0
    }

    fn on_write(&mut self, data: &[u8]) -> io::Result<()> {
        self.file_size += data.len() as u64;
        Ok(())
    }

    fn on_rotate(&mut self) -> io::Result<()> {
        self.file_size = 0;
        Ok(())
    }
}

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

    use std::ffi::OsStr;
    use std::time::{Duration, SystemTime, UNIX_EPOCH};
    use tempfile::TempDir;

    fn file_exists(file: impl AsRef<Path>) -> bool {
        let path = file.as_ref();
        path.exists() && path.is_file()
    }

    fn rename_with_subffix(
        path: impl AsRef<Path>,
        suffix: impl AsRef<OsStr>,
    ) -> io::Result<PathBuf> {
        let mut new_path = path.as_ref().to_path_buf().into_os_string();
        new_path.push(suffix);

        Ok(PathBuf::from(new_path))
    }

    fn rename_fail() -> io::Result<PathBuf> {
        Err(Error::from(ErrorKind::NotFound))
    }

    #[test]
    fn test_should_rotate_by_time() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_should_rotate_by_time.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute before.
        let last_modified = SystemTime::now()
            .checked_sub(Duration::from_secs(60))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_subffix(path, suffix)
        })
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(30))))
        .build()
        .unwrap();

        // Rotate normally
        logger.flush().unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        assert!(file_exists(new_path));
    }

    #[test]
    fn test_should_not_rotate_by_time() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_should_not_rotate_by_time.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute later.
        let last_modified = SystemTime::now()
            .checked_add(Duration::from_secs(60))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_subffix(path, suffix)
        })
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(
            120,
        ))))
        .build()
        .unwrap();

        // Rotate normally
        logger.flush().unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        assert!(!file_exists(new_path));
    }

    #[test]
    fn test_rotate_by_size() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_should_rotate_by_size.log");
        let suffix = ".backup";

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_subffix(path, suffix)
        })
        .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
        .build()
        .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        // Should not rotate.
        logger.write_all(&[0xff; 1024]).unwrap();
        logger.flush().unwrap();
        assert!(!file_exists(new_path.clone()));

        // Triggers rotation.
        logger.write_all(&[0xff; 1024]).unwrap();
        logger.flush().unwrap();
        assert!(file_exists(new_path));
    }

    #[test]
    fn test_update_rotate_by_size() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_update_rotate_by_size.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute before.
        let last_modified = SystemTime::now()
            .checked_sub(Duration::from_secs(120))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_subffix(path, suffix)
        })
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(60))))
        .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
        .build()
        .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        logger.write_all(&[0xff; 2048]).unwrap();
        // Triggers rotate by time.
        logger.flush().unwrap();

        assert!(file_exists(new_path));

        // Should update `RotateBySize`'s state.
        assert_eq!(logger.rotators[1].should_rotate(), false);
    }

    #[test]
    fn test_update_rotate_by_time() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_update_rotate_by_time.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute before.
        let last_modified = SystemTime::now()
            .checked_sub(Duration::from_secs(120))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_subffix(path, suffix)
        })
        .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(60))))
        .build()
        .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        logger.write_all(&[0xff; 2048]).unwrap();
        // Triggers rotate by size.
        logger.flush().unwrap();

        assert!(file_exists(new_path.clone()));

        // Should update `RotateByTime`'s state.
        assert_eq!(logger.rotators[1].should_rotate(), false);

        logger.write_all(&[0xff; 2048]).unwrap();
        assert_eq!(logger.rotators[1].should_rotate(), false);
        assert!(file_exists(new_path));
    }

    #[test]
    fn test_failing_to_rotate_file_will_not_cause_panic() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_no_panic.log");
        let suffix = ".backup";

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), |_| rename_fail())
            .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
            .build()
            .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        // Rename failed.
        logger.write_all(&[0xff; 1025]).unwrap();
        assert!(logger.flush().is_err());

        // dropping the logger still should not panic.
        drop(logger);
    }
}
<commit_msg>tikv_util: Fix logging test (#7633)<commit_after>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::fs::{self, File, OpenOptions};
use std::io::{self, Error, ErrorKind, Write};
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};

use chrono::{DateTime, Local};

use crate::config::{ReadableDuration, ReadableSize};

/// Opens log file with append mode. Creates a new log file if it doesn't exist.
fn open_log_file(path: impl AsRef<Path>) -> io::Result<File> {
    let path = path.as_ref();
    let parent = path.parent().ok_or_else(|| {
        Error::new(
            ErrorKind::Other,
            "Unable to get parent directory of log file",
        )
    })?;
    if !parent.is_dir() {
        fs::create_dir_all(parent)?
    }
    OpenOptions::new().append(true).create(true).open(path)
}

/// A trait that describes a file rotation operation.
pub trait Rotator: Send {
    /// Check if the option is enabled in configuration.
    /// Return true if the `rotator` is valid.
    fn is_enabled(&self) -> bool;

    /// Call by operator, initializes the states of rotators.
    fn prepare(&mut self, file: &File) -> io::Result<()>;

    /// Return if the file need to be rotated.
    fn should_rotate(&self) -> bool;

    /// Call by operator, update rotators' state while the operator try to write some data.
    fn on_write(&mut self, data: &[u8]) -> io::Result<()>;

    /// Call by operator, update rotators' state while the operator execute a rotation.
    fn on_rotate(&mut self) -> io::Result<()>;
}

/// This `FileLogger` will iterate over a series of `Rotators`,
/// once the context trigger the `Rotator`, it will execute a rotation.
///
/// After rotating, the original log file would be renamed to "{original name}.{%Y-%m-%d-%H:%M:%S}".
/// Note: log file will *not* be compressed or otherwise modified.
pub struct RotatingFileLogger {
    path: PathBuf,
    file: File,
    rename: Box<dyn Send + Fn(&Path) -> io::Result<PathBuf>>,
    rotators: Vec<Box<dyn Rotator>>,
}

/// Builder for `RotatingFileLogger`.
pub struct RotatingFileLoggerBuilder {
    rotators: Vec<Box<dyn Rotator>>,
    path: PathBuf,
    rename: Box<dyn Send + Fn(&Path) -> io::Result<PathBuf>>,
}

impl RotatingFileLoggerBuilder {
    pub fn new<F>(path: impl AsRef<Path>, rename: F) -> Self
    where
        F: 'static + Send + Fn(&Path) -> io::Result<PathBuf>,
    {
        RotatingFileLoggerBuilder {
            path: path.as_ref().to_path_buf(),
            rotators: vec![],
            rename: Box::new(rename),
        }
    }

    pub fn add_rotator<R: 'static + Rotator>(mut self, rotator: R) -> Self {
        if rotator.is_enabled() {
            self.rotators.push(Box::new(rotator));
        }
        self
    }

    pub fn build(mut self) -> io::Result<RotatingFileLogger> {
        let file = open_log_file(&self.path)?;

        for rotator in self.rotators.iter_mut() {
            rotator.prepare(&file)?;
        }

        Ok(RotatingFileLogger {
            rotators: self.rotators,
            path: self.path,
            rename: self.rename,
            file,
        })
    }
}

impl Write for RotatingFileLogger {
    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
        // Updates all roators' states.
        for rotator in self.rotators.iter_mut() {
            rotator.on_write(bytes)?;
        }
        self.file.write(bytes)
    }

    fn flush(&mut self) -> io::Result<()> {
        for rotator in self.rotators.iter() {
            if rotator.should_rotate() {
                self.file.flush()?;

                let new_path = (self.rename)(&self.path)?;
                fs::rename(&self.path, &new_path)?;
                self.file = open_log_file(&self.path)?;

                // Updates all rotators' states.
                for rotator in self.rotators.iter_mut() {
                    rotator.on_rotate()?;
                }

                return Ok(());
            }
        }
        self.file.flush()
    }
}

impl Drop for RotatingFileLogger {
    fn drop(&mut self) {
        let _ = self.file.flush();
    }
}

pub struct RotateByTime {
    rotation_timespan: ReadableDuration,
    next_rotation_time: Option<SystemTime>,
}

impl RotateByTime {
    pub fn new(rotation_timespan: ReadableDuration) -> Self {
        Self {
            rotation_timespan,
            next_rotation_time: None,
        }
    }

    fn next_rotation_time(begin: SystemTime, duration: Duration) -> io::Result<SystemTime> {
        begin
            .checked_add(duration)
            .ok_or_else(|| Error::new(ErrorKind::Other, "Next rotation time is out of range."))
    }
}

impl Rotator for RotateByTime {
    fn is_enabled(&self) -> bool {
        !self.rotation_timespan.is_zero()
    }

    fn prepare(&mut self, file: &File) -> io::Result<()> {
        // Use the minimum of the created time and access time.
        let metadata = file.metadata()?;
        let created = metadata.created();
        let accessed = metadata.accessed();
        let birth = match (created, accessed) {
            (Err(_), a) => a?,
            (Ok(c), Ok(a)) if a < c => a,
            (Ok(c), _) => c,
        };
        self.next_rotation_time = Some(Self::next_rotation_time(birth, self.rotation_timespan.0)?);
        Ok(())
    }

    fn should_rotate(&self) -> bool {
        assert!(self.next_rotation_time.is_some());
        Local::now() > DateTime::<Local>::from(self.next_rotation_time.unwrap())
    }

    fn on_write(&mut self, _: &[u8]) -> io::Result<()> {
        Ok(())
    }

    fn on_rotate(&mut self) -> io::Result<()> {
        assert!(self.next_rotation_time.is_some());
        self.next_rotation_time = Some(Self::next_rotation_time(
            SystemTime::now(),
            self.rotation_timespan.0,
        )?);
        Ok(())
    }
}

pub struct RotateBySize {
    rotation_size: ReadableSize,
    file_size: u64,
}

impl RotateBySize {
    pub fn new(rotation_size: ReadableSize) -> Self {
        RotateBySize {
            rotation_size,
            file_size: 0,
        }
    }
}

impl Rotator for RotateBySize {
    fn is_enabled(&self) -> bool {
        self.rotation_size.0 != 0
    }

    fn prepare(&mut self, file: &File) -> io::Result<()> {
        self.file_size = file.metadata()?.len();
        Ok(())
    }

    fn should_rotate(&self) -> bool {
        self.file_size > self.rotation_size.0
    }

    fn on_write(&mut self, data: &[u8]) -> io::Result<()> {
        self.file_size += data.len() as u64;
        Ok(())
    }

    fn on_rotate(&mut self) -> io::Result<()> {
        self.file_size = 0;
        Ok(())
    }
}

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

    use std::ffi::OsStr;
    use std::time::{Duration, SystemTime, UNIX_EPOCH};
    use tempfile::TempDir;

    fn file_exists(file: impl AsRef<Path>) -> bool {
        let path = file.as_ref();
        path.exists() && path.is_file()
    }

    fn rename_with_suffix(
        path: impl AsRef<Path>,
        suffix: impl AsRef<OsStr>,
    ) -> io::Result<PathBuf> {
        let mut new_path = path.as_ref().to_path_buf().into_os_string();
        new_path.push(suffix);

        Ok(PathBuf::from(new_path))
    }

    fn rename_fail() -> io::Result<PathBuf> {
        Err(Error::from(ErrorKind::NotFound))
    }

    #[test]
    fn test_should_rotate_by_time() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_should_rotate_by_time.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute before.
        let last_modified = SystemTime::now()
            .checked_sub(Duration::from_secs(60))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_suffix(path, suffix)
        })
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(30))))
        .build()
        .unwrap();

        // Rotate normally
        logger.flush().unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        assert!(file_exists(new_path));
    }

    #[test]
    fn test_should_not_rotate_by_time() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_should_not_rotate_by_time.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute later.
        let last_modified = SystemTime::now()
            .checked_add(Duration::from_secs(60))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_suffix(path, suffix)
        })
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(
            120,
        ))))
        .build()
        .unwrap();

        // Rotate normally
        logger.flush().unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        assert!(!file_exists(new_path));
    }

    #[test]
    fn test_rotate_by_size() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_should_rotate_by_size.log");
        let suffix = ".backup";

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_suffix(path, suffix)
        })
        .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
        .build()
        .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        // Should not rotate.
        logger.write_all(&[0xff; 1024]).unwrap();
        logger.flush().unwrap();
        assert!(!file_exists(new_path.clone()));

        // Triggers rotation.
        logger.write_all(&[0xff; 1024]).unwrap();
        logger.flush().unwrap();
        assert!(file_exists(new_path));
    }

    #[test]
    fn test_update_rotate_by_size() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_update_rotate_by_size.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute before.
        let last_modified = SystemTime::now()
            .checked_sub(Duration::from_secs(120))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_suffix(path, suffix)
        })
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(60))))
        .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
        .build()
        .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        logger.write_all(&[0xff; 2048]).unwrap();
        // Triggers rotate by time.
        logger.flush().unwrap();

        assert!(file_exists(new_path));

        // Should update `RotateBySize`'s state.
        assert_eq!(logger.rotators[1].should_rotate(), false);
    }

    #[test]
    fn test_update_rotate_by_time() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_update_rotate_by_time.log");
        let suffix = ".backup";

        // Set the last modification time to 1 minute before.
        let last_modified = SystemTime::now()
            .checked_sub(Duration::from_secs(120))
            .unwrap()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Create a file.
        open_log_file(path.clone()).unwrap();

        // Modify last_modified time.
        let accessed = last_modified;
        utime::set_file_times(path.clone(), accessed, last_modified).unwrap();

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), move |path| {
            rename_with_suffix(path, suffix)
        })
        .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
        .add_rotator(RotateByTime::new(ReadableDuration(Duration::from_secs(60))))
        .build()
        .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        logger.write_all(&[0xff; 2048]).unwrap();
        // Triggers rotate by size.
        logger.flush().unwrap();

        assert!(file_exists(new_path.clone()));

        // Should update `RotateByTime`'s state.
        assert_eq!(logger.rotators[1].should_rotate(), false);

        logger.write_all(&[0xff; 2048]).unwrap();
        assert_eq!(logger.rotators[1].should_rotate(), false);
        assert!(file_exists(new_path));
    }

    #[test]
    fn test_failing_to_rotate_file_will_not_cause_panic() {
        let tmp_dir = TempDir::new().unwrap();
        let path = tmp_dir.path().join("test_no_panic.log");
        let suffix = ".backup";

        let mut logger = RotatingFileLoggerBuilder::new(path.clone(), |_| rename_fail())
            .add_rotator(RotateBySize::new(ReadableSize::kb(1)))
            .build()
            .unwrap();

        let mut new_path = PathBuf::from(path).into_os_string();
        new_path.push(suffix);

        // Rename failed.
        logger.write_all(&[0xff; 1025]).unwrap();
        assert!(logger.flush().is_err());

        // dropping the logger still should not panic.
        drop(logger);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

use std::io;
use std::io::prelude::*;

pub fn main() {
    let mut x = 0;
    let mut y = 0;
    let mut rx = 0;
    let mut ry = 0;
    let mut houses = HashSet::new();
    houses.insert((x,y));

    let stdin = io::stdin();
    for line in stdin.lock().lines() {
      let line = line.unwrap();
      for c in line.chars() {
            match c.to_string().as_ref() {
                ">" => x += 1,
                "<" => x -= 1,
                "^" => y += 1,
                "v" => y -= 1,
                _ => panic!()
            }
            houses.insert((x,y));
      }
    }
    println!("Part 1: {}", houses.len());
}<commit_msg>finish parth 2 of day 3<commit_after>use std::io;
use std::io::prelude::*;
use std::collections::HashSet;

struct Santa {
    x: i32,
    y: i32
}

fn step(c: char, s: &mut Santa) {
    match c {
        '>' => s.x += 1,
        '<' => s.x -= 1,
        '^' => s.y += 1,
        'v' => s.y -= 1,
        _ => panic!()
    }
}

pub fn main() {
    let mut p1_santa = Santa {x: 0, y: 0};
    let mut p2_santa = Santa {x: 0, y: 0};
    let mut p2_robo  = Santa {x: 0, y: 0};

    let mut part1_visited = HashSet::new();
    let mut part2_visited = HashSet::new();

    part1_visited.insert((0,0));
    part2_visited.insert((0,0));

    let mut roboturn: bool = false;

    let stdin = io::stdin();
    for line in stdin.lock().lines() {
      let line = line.unwrap();
      for c in line.chars() {
            step(c, &mut p1_santa);
            if roboturn {
                step(c, &mut p2_robo);
            } else {
                step(c, &mut p2_santa);
            }
            roboturn = !roboturn;
            part1_visited.insert((p1_santa.x, p1_santa.y));
            part2_visited.insert((p2_santa.x, p2_santa.y));
            part2_visited.insert((p2_robo.x, p2_robo.y));
      }
    }
    println!("Part 1: {}", part1_visited.len());
    println!("Part 2: {}", part2_visited.len());
}<|endoftext|>


<commit_before>extern crate byteorder;
extern crate gimli;

use byteorder::ByteOrder;
use gimli::{AttributeValue, DebugAbbrev, DebugInfo, DebugLine, DebugLineOffset, DW_AT_stmt_list,
            LineNumberProgramHeader, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("./fixtures/self/");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    for unit in debug_info.units() {
        let unit = unit.expect("Should parse compilation unit");
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    for unit in debug_info.units() {
        let unit = unit.expect("Should parse the unit OK");

        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        if let Some(value) = unit_entry.attr_value(DW_AT_stmt_list) {
            // For whatever reason, rustc generated DW_FORM_data4 typed
            // attributes for DW_AT_stmt_list attributes, when it seems to me
            // like the more correct choice would be DW_FORM_sec_offset (the
            // spec seems to agree -- see pages 214-215). Because of this, we
            // have to turn the data into an offset.
            let offset = match value {
                AttributeValue::Data(data) => LittleEndian::read_u32(data),
                otherwise => panic!("unexpected value form: {:?}", otherwise),
            };
            let offset = DebugLineOffset(offset as u64);

            LineNumberProgramHeader::new(debug_line, offset)
                .expect("should parse line number program header");
        }
    }
}
<commit_msg>Use `attr_value` instead of manually iterating attributes<commit_after>extern crate byteorder;
extern crate gimli;

use byteorder::ByteOrder;
use gimli::{AttributeValue, DebugAbbrev, DebugInfo, DebugLine, DebugLineOffset, DW_AT_stmt_list,
            LineNumberProgramHeader, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("./fixtures/self/");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    for unit in debug_info.units() {
        let unit = unit.expect("Should parse compilation unit");
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    for unit in debug_info.units() {
        let unit = unit.expect("Should parse the unit OK");

        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        if let Some(value) = unit_entry.attr_value(DW_AT_stmt_list) {
            // rustc generates DWARF 2 by default, which doesn't have
            // DW_FORM_sec_offset, so instead we get DW_FORM_data4
            // values. Because of this, we have to turn the data into an offset
            // manually.
            let offset = match value {
                AttributeValue::Data(data) => LittleEndian::read_u32(data),
                otherwise => panic!("unexpected value form: {:?}", otherwise),
            };
            let offset = DebugLineOffset(offset as u64);

            LineNumberProgramHeader::new(debug_line, offset)
                .expect("should parse line number program header");
        }
    }
}
<|endoftext|>