<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 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;
    }
}
<commit_msg>collector: More debug output on collection<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) {
        debug!("Perform collection (evacuation={}, cycle_collect={})",
               collection_type.is_evac(), collection_type.is_immix());

        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>use alga::general::{ClosedAdd, ClosedMul};
use num::{One, Zero};
use std::iter;
use std::marker::PhantomData;
use std::ops::{Add, Mul, Range};
use std::slice;

use allocator::Allocator;
use constraint::{AreMultipliable, DimEq, SameNumberOfRows, ShapeConstraint};
use sparse::{CsMatrix, CsStorage, CsStorageMut, CsVector};
use storage::{Storage, StorageMut};
use {DefaultAllocator, Dim, Matrix, MatrixMN, Real, Scalar, Vector, VectorN, U1};

impl<N: Scalar, R: Dim, C: Dim, S: CsStorage<N, R, C>> CsMatrix<N, R, C, S> {
    fn scatter<R2: Dim, C2: Dim>(
        &self,
        j: usize,
        beta: N,
        timestamps: &mut [usize],
        timestamp: usize,
        workspace: &mut [N],
        mut nz: usize,
        res: &mut CsMatrix<N, R2, C2>,
    ) -> usize
    where
        N: ClosedAdd + ClosedMul,
        DefaultAllocator: Allocator<usize, C2>,
    {
        for (i, val) in self.data.column_entries(j) {
            if timestamps[i] < timestamp {
                timestamps[i] = timestamp;
                res.data.i[nz] = i;
                nz += 1;
                workspace[i] = val * beta;
            } else {
                workspace[i] += val * beta;
            }
        }

        nz
    }
}

/*
impl<N: Scalar, R, S> CsVector<N, R, S> {
    pub fn axpy(&mut self, alpha: N, x: CsVector<N, R, S>, beta: N) {
        // First, compute the number of non-zero entries.
        let mut nnzero = 0;

        // Allocate a size large enough.
        self.data.set_column_len(0, nnzero);

        // Fill with the axpy.
        let mut i = self.len();
        let mut j = x.len();
        let mut k = nnzero - 1;
        let mut rid1 = self.data.row_index(0, i - 1);
        let mut rid2 = x.data.row_index(0, j - 1);

        while k > 0 {
            if rid1 == rid2 {
                self.data.set_row_index(0, k, rid1);
                self[k] = alpha * x[j] + beta * self[k];
                i -= 1;
                j -= 1;
            } else if rid1 < rid2 {
                self.data.set_row_index(0, k, rid1);
                self[k] = beta * self[i];
                i -= 1;
            } else {
                self.data.set_row_index(0, k, rid2);
                self[k] = alpha * x[j];
                j -= 1;
            }

            k -= 1;
        }
    }
}
*/

impl<N: Scalar + Zero + ClosedAdd + ClosedMul, D: Dim, S: StorageMut<N, D>> Vector<N, D, S> {
    pub fn axpy_cs<D2: Dim, S2>(&mut self, alpha: N, x: &CsVector<N, D2, S2>, beta: N)
    where
        S2: CsStorage<N, D2>,
        ShapeConstraint: DimEq<D, D2>,
    {
        if beta.is_zero() {
            for i in 0..x.len() {
                unsafe {
                    let k = x.data.row_index_unchecked(i);
                    let y = self.vget_unchecked_mut(k);
                    *y = alpha * *x.data.get_value_unchecked(i);
                }
            }
        } else {
            // Needed to be sure even components not present on `x` are multiplied.
            *self *= beta;

            for i in 0..x.len() {
                unsafe {
                    let k = x.data.row_index_unchecked(i);
                    let y = self.vget_unchecked_mut(k);
                    *y += alpha * *x.data.get_value_unchecked(i);
                }
            }
        }
    }

    /*
    pub fn gemv_sparse<R2: Dim, C2: Dim, S2>(&mut self, alpha: N, a: &CsMatrix<N, R2, C2, S2>, x: &DVector<N>, beta: N)
        where
            S2: CsStorage<N, R2, C2> {
        let col2 = a.column(0);
        let val = unsafe { *x.vget_unchecked(0) };
        self.axpy_sparse(alpha * val, &col2, beta);

        for j in 1..ncols2 {
            let col2 = a.column(j);
            let val = unsafe { *x.vget_unchecked(j) };

            self.axpy_sparse(alpha * val, &col2, N::one());
        }
    }
    */
}

impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Mul<&'b CsMatrix<N, R2, C2, S2>>
    for &'a CsMatrix<N, R1, C1, S1>
where
    N: Scalar + ClosedAdd + ClosedMul + Zero,
    R1: Dim,
    C1: Dim,
    R2: Dim,
    C2: Dim,
    S1: CsStorage<N, R1, C1>,
    S2: CsStorage<N, R2, C2>,
    ShapeConstraint: AreMultipliable<R1, C1, R2, C2>,
    DefaultAllocator: Allocator<usize, C2> + Allocator<usize, R1> + Allocator<N, R1>,
{
    type Output = CsMatrix<N, R1, C2>;

    fn mul(self, rhs: &'b CsMatrix<N, R2, C2, S2>) -> CsMatrix<N, R1, C2> {
        let (nrows1, ncols1) = self.data.shape();
        let (nrows2, ncols2) = rhs.data.shape();
        assert_eq!(
            ncols1.value(),
            nrows2.value(),
            "Mismatched dimensions for matrix multiplication."
        );

        let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len());
        let mut workspace = VectorN::<N, R1>::zeros_generic(nrows1, U1);
        let mut nz = 0;

        for j in 0..ncols2.value() {
            res.data.p[j] = nz;
            let new_size_bound = nz + nrows1.value();
            res.data.i.resize(new_size_bound, 0);
            res.data.vals.resize(new_size_bound, N::zero());

            for (i, beta) in rhs.data.column_entries(j) {
                for (k, val) in self.data.column_entries(i) {
                    workspace[k] += val * beta;
                }
            }

            for (i, val) in workspace.as_mut_slice().iter_mut().enumerate() {
                if !val.is_zero() {
                    res.data.i[nz] = i;
                    res.data.vals[nz] = *val;
                    *val = N::zero();
                    nz += 1;
                }
            }
        }

        res.data.i.truncate(nz);
        res.data.i.shrink_to_fit();
        res.data.vals.truncate(nz);
        res.data.vals.shrink_to_fit();
        res
    }
}

impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Add<&'b CsMatrix<N, R2, C2, S2>>
    for &'a CsMatrix<N, R1, C1, S1>
where
    N: Scalar + ClosedAdd + ClosedMul + One,
    R1: Dim,
    C1: Dim,
    R2: Dim,
    C2: Dim,
    S1: CsStorage<N, R1, C1>,
    S2: CsStorage<N, R2, C2>,
    ShapeConstraint: DimEq<R1, R2> + DimEq<C1, C2>,
    DefaultAllocator: Allocator<usize, C2> + Allocator<usize, R1> + Allocator<N, R1>,
{
    type Output = CsMatrix<N, R1, C2>;

    fn add(self, rhs: &'b CsMatrix<N, R2, C2, S2>) -> CsMatrix<N, R1, C2> {
        let (nrows1, ncols1) = self.data.shape();
        let (nrows2, ncols2) = rhs.data.shape();
        assert_eq!(
            (nrows1.value(), ncols1.value()),
            (nrows2.value(), ncols2.value()),
            "Mismatched dimensions for matrix sum."
        );

        let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len());
        let mut timestamps = VectorN::zeros_generic(nrows1, U1);
        let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows1, U1) };
        let mut nz = 0;

        for j in 0..ncols2.value() {
            res.data.p[j] = nz;

            nz = self.scatter(
                j,
                N::one(),
                timestamps.as_mut_slice(),
                j + 1,
                workspace.as_mut_slice(),
                nz,
                &mut res,
            );

            nz = rhs.scatter(
                j,
                N::one(),
                timestamps.as_mut_slice(),
                j + 1,
                workspace.as_mut_slice(),
                nz,
                &mut res,
            );

            // Keep the output sorted.
            let range = res.data.p[j]..nz;
            res.data.i[range.clone()].sort();

            for p in range {
                res.data.vals[p] = workspace[res.data.i[p]]
            }
        }

        res.data.i.truncate(nz);
        res.data.i.shrink_to_fit();
        res.data.vals.truncate(nz);
        res.data.vals.shrink_to_fit();
        res
    }
}

impl<'a, 'b, N, R, C, S> Mul<N> for CsMatrix<N, R, C, S>
where
    N: Scalar + ClosedAdd + ClosedMul + Zero,
    R: Dim,
    C: Dim,
    S: CsStorageMut<N, R, C>,
{
    type Output = Self;

    fn mul(mut self, rhs: N) -> Self {
        for e in self.values_mut() {
            *e *= rhs
        }

        self
    }
}
<commit_msg>Add comment about cs matrix multiplication implementation.<commit_after>use alga::general::{ClosedAdd, ClosedMul};
use num::{One, Zero};
use std::iter;
use std::marker::PhantomData;
use std::ops::{Add, Mul, Range};
use std::slice;

use allocator::Allocator;
use constraint::{AreMultipliable, DimEq, SameNumberOfRows, ShapeConstraint};
use sparse::{CsMatrix, CsStorage, CsStorageMut, CsVector};
use storage::{Storage, StorageMut};
use {DefaultAllocator, Dim, Matrix, MatrixMN, Real, Scalar, Vector, VectorN, U1};

impl<N: Scalar, R: Dim, C: Dim, S: CsStorage<N, R, C>> CsMatrix<N, R, C, S> {
    fn scatter<R2: Dim, C2: Dim>(
        &self,
        j: usize,
        beta: N,
        timestamps: &mut [usize],
        timestamp: usize,
        workspace: &mut [N],
        mut nz: usize,
        res: &mut CsMatrix<N, R2, C2>,
    ) -> usize
    where
        N: ClosedAdd + ClosedMul,
        DefaultAllocator: Allocator<usize, C2>,
    {
        for (i, val) in self.data.column_entries(j) {
            if timestamps[i] < timestamp {
                timestamps[i] = timestamp;
                res.data.i[nz] = i;
                nz += 1;
                workspace[i] = val * beta;
            } else {
                workspace[i] += val * beta;
            }
        }

        nz
    }
}

/*
impl<N: Scalar, R, S> CsVector<N, R, S> {
    pub fn axpy(&mut self, alpha: N, x: CsVector<N, R, S>, beta: N) {
        // First, compute the number of non-zero entries.
        let mut nnzero = 0;

        // Allocate a size large enough.
        self.data.set_column_len(0, nnzero);

        // Fill with the axpy.
        let mut i = self.len();
        let mut j = x.len();
        let mut k = nnzero - 1;
        let mut rid1 = self.data.row_index(0, i - 1);
        let mut rid2 = x.data.row_index(0, j - 1);

        while k > 0 {
            if rid1 == rid2 {
                self.data.set_row_index(0, k, rid1);
                self[k] = alpha * x[j] + beta * self[k];
                i -= 1;
                j -= 1;
            } else if rid1 < rid2 {
                self.data.set_row_index(0, k, rid1);
                self[k] = beta * self[i];
                i -= 1;
            } else {
                self.data.set_row_index(0, k, rid2);
                self[k] = alpha * x[j];
                j -= 1;
            }

            k -= 1;
        }
    }
}
*/

impl<N: Scalar + Zero + ClosedAdd + ClosedMul, D: Dim, S: StorageMut<N, D>> Vector<N, D, S> {
    pub fn axpy_cs<D2: Dim, S2>(&mut self, alpha: N, x: &CsVector<N, D2, S2>, beta: N)
    where
        S2: CsStorage<N, D2>,
        ShapeConstraint: DimEq<D, D2>,
    {
        if beta.is_zero() {
            for i in 0..x.len() {
                unsafe {
                    let k = x.data.row_index_unchecked(i);
                    let y = self.vget_unchecked_mut(k);
                    *y = alpha * *x.data.get_value_unchecked(i);
                }
            }
        } else {
            // Needed to be sure even components not present on `x` are multiplied.
            *self *= beta;

            for i in 0..x.len() {
                unsafe {
                    let k = x.data.row_index_unchecked(i);
                    let y = self.vget_unchecked_mut(k);
                    *y += alpha * *x.data.get_value_unchecked(i);
                }
            }
        }
    }

    /*
    pub fn gemv_sparse<R2: Dim, C2: Dim, S2>(&mut self, alpha: N, a: &CsMatrix<N, R2, C2, S2>, x: &DVector<N>, beta: N)
        where
            S2: CsStorage<N, R2, C2> {
        let col2 = a.column(0);
        let val = unsafe { *x.vget_unchecked(0) };
        self.axpy_sparse(alpha * val, &col2, beta);

        for j in 1..ncols2 {
            let col2 = a.column(j);
            let val = unsafe { *x.vget_unchecked(j) };

            self.axpy_sparse(alpha * val, &col2, N::one());
        }
    }
    */
}

impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Mul<&'b CsMatrix<N, R2, C2, S2>>
    for &'a CsMatrix<N, R1, C1, S1>
where
    N: Scalar + ClosedAdd + ClosedMul + Zero,
    R1: Dim,
    C1: Dim,
    R2: Dim,
    C2: Dim,
    S1: CsStorage<N, R1, C1>,
    S2: CsStorage<N, R2, C2>,
    ShapeConstraint: AreMultipliable<R1, C1, R2, C2>,
    DefaultAllocator: Allocator<usize, C2> + Allocator<usize, R1> + Allocator<N, R1>,
{
    type Output = CsMatrix<N, R1, C2>;

    fn mul(self, rhs: &'b CsMatrix<N, R2, C2, S2>) -> CsMatrix<N, R1, C2> {
        let (nrows1, ncols1) = self.data.shape();
        let (nrows2, ncols2) = rhs.data.shape();
        assert_eq!(
            ncols1.value(),
            nrows2.value(),
            "Mismatched dimensions for matrix multiplication."
        );

        let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len());
        let mut workspace = VectorN::<N, R1>::zeros_generic(nrows1, U1);
        let mut nz = 0;

        for j in 0..ncols2.value() {
            res.data.p[j] = nz;
            let new_size_bound = nz + nrows1.value();
            res.data.i.resize(new_size_bound, 0);
            res.data.vals.resize(new_size_bound, N::zero());

            for (i, beta) in rhs.data.column_entries(j) {
                for (k, val) in self.data.column_entries(i) {
                    workspace[k] += val * beta;
                }
            }

            for (i, val) in workspace.as_mut_slice().iter_mut().enumerate() {
                if !val.is_zero() {
                    res.data.i[nz] = i;
                    res.data.vals[nz] = *val;
                    *val = N::zero();
                    nz += 1;
                }
            }
        }

        // NOTE: the following has a lower complexity, but is slower in many cases, likely because
        // of branching inside of the inner loop.
        //
        // let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len());
        // let mut timestamps = VectorN::zeros_generic(nrows1, U1);
        // let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows1, U1) };
        // let mut nz = 0;
        //
        // for j in 0..ncols2.value() {
        //     res.data.p[j] = nz;
        //     let new_size_bound = nz + nrows1.value();
        //     res.data.i.resize(new_size_bound, 0);
        //     res.data.vals.resize(new_size_bound, N::zero());
        //
        //     for (i, val) in rhs.data.column_entries(j) {
        //         nz = self.scatter(
        //             i,
        //             val,
        //             timestamps.as_mut_slice(),
        //             j + 1,
        //             workspace.as_mut_slice(),
        //             nz,
        //             &mut res,
        //         );
        //     }
        //
        //     // Keep the output sorted.
        //     let range = res.data.p[j]..nz;
        //     res.data.i[range.clone()].sort();
        //
        //     for p in range {
        //         res.data.vals[p] = workspace[res.data.i[p]]
        //     }
        // }

        res.data.i.truncate(nz);
        res.data.i.shrink_to_fit();
        res.data.vals.truncate(nz);
        res.data.vals.shrink_to_fit();
        res
    }
}

impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Add<&'b CsMatrix<N, R2, C2, S2>>
    for &'a CsMatrix<N, R1, C1, S1>
where
    N: Scalar + ClosedAdd + ClosedMul + One,
    R1: Dim,
    C1: Dim,
    R2: Dim,
    C2: Dim,
    S1: CsStorage<N, R1, C1>,
    S2: CsStorage<N, R2, C2>,
    ShapeConstraint: DimEq<R1, R2> + DimEq<C1, C2>,
    DefaultAllocator: Allocator<usize, C2> + Allocator<usize, R1> + Allocator<N, R1>,
{
    type Output = CsMatrix<N, R1, C2>;

    fn add(self, rhs: &'b CsMatrix<N, R2, C2, S2>) -> CsMatrix<N, R1, C2> {
        let (nrows1, ncols1) = self.data.shape();
        let (nrows2, ncols2) = rhs.data.shape();
        assert_eq!(
            (nrows1.value(), ncols1.value()),
            (nrows2.value(), ncols2.value()),
            "Mismatched dimensions for matrix sum."
        );

        let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len());
        let mut timestamps = VectorN::zeros_generic(nrows1, U1);
        let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows1, U1) };
        let mut nz = 0;

        for j in 0..ncols2.value() {
            res.data.p[j] = nz;

            nz = self.scatter(
                j,
                N::one(),
                timestamps.as_mut_slice(),
                j + 1,
                workspace.as_mut_slice(),
                nz,
                &mut res,
            );

            nz = rhs.scatter(
                j,
                N::one(),
                timestamps.as_mut_slice(),
                j + 1,
                workspace.as_mut_slice(),
                nz,
                &mut res,
            );

            // Keep the output sorted.
            let range = res.data.p[j]..nz;
            res.data.i[range.clone()].sort();

            for p in range {
                res.data.vals[p] = workspace[res.data.i[p]]
            }
        }

        res.data.i.truncate(nz);
        res.data.i.shrink_to_fit();
        res.data.vals.truncate(nz);
        res.data.vals.shrink_to_fit();
        res
    }
}

impl<'a, 'b, N, R, C, S> Mul<N> for CsMatrix<N, R, C, S>
where
    N: Scalar + ClosedAdd + ClosedMul + Zero,
    R: Dim,
    C: Dim,
    S: CsStorageMut<N, R, C>,
{
    type Output = Self;

    fn mul(mut self, rhs: N) -> Self {
        for e in self.values_mut() {
            *e *= rhs
        }

        self
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use controller::Reader;
use utils;

// Each line in the database file is a 'Record' that contains the following data to be read
// and serialized.
pub struct Record {
    // Unique (?) id given to each record.
    pub id: u64,
    // The payload of each record that is Base64 encoded and JSON serialized.
    pub payload: String,
    // Any necessary metedata needed to identify the record.
    // TODO Base64 encoded and JSON encoded?
    pub metadata: String,
}

pub struct Factory {
    reader: Reader,
}

pub type RecordResult<T, E> = Result<T, E>;

//TODO Add some factory traits here if necessary
pub trait RecordFactory {
    fn new(reader: Reader) -> Self;

    fn create(&self, data: String) -> RecordResult<Record, String>;

    fn create_from_enc(&self, data: String) -> RecordResult<Record, String>;
}

impl RecordFactory for Factory {
    fn new(reader: Reader) -> Factory {
        Factory {
            reader: reader,
        }
    }

    //TODO Remove the below flag when `create` is used
    #[allow(dead_code, unused_must_use, unused_variables)]
    fn create(&self, data: String) -> RecordResult<Record, String> {
        let vec_of_data = utils::string_slice(data);

        let id_num = FromStr::from_str(vec_of_data[0].as_slice());
        let id_value: u64 = match id_num {
            Some(value)     => value,
            None            => -1 // This is a failure value
        };

        Ok(Record {
            id: id_value,
            payload: vec_of_data[1].clone(),
            metadata: vec_of_data[2].clone(),
        })
    }

    fn create_from_enc(&self, data: String) -> RecordResult<Record, String> {
        let vec_of_data = utils::string_slice(data);

        let id_num = FromStr::from_str(vec_of_data[0].as_slice());
        let id_value: u64 = match id_num {
            Some(value)     => value,
            None            => -1, // This is a failure value
        };

        let enc_payload = String::from_str(vec_of_data[1].as_slice());
        let payload = utils::decode_record(enc_payload);
        let enc_metadata = String::from_str(vec_of_data[2].as_slice());
        let metadata = utils::decode_record(enc_metadata);

        Ok(Record {
            id: id_value,
            payload: payload,
            metadata: metadata,
        })
    }
}

#[test]
fn test_create_record() {
    let input = String::from_str("1 payload metadata");
    let expected = Record {
        id: 1,
        payload: String::from_str("payload"),
        metadata: String::from_str("metadata"),
    };
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
    let factory: Factory = RecordFactory::new(reader);
    let output: Record = factory.create(input).ok().expect("Parsing failed.");
    assert_eq!(expected.id, output.id);
    assert_eq!(expected.payload, output.payload);
    assert_eq!(expected.metadata, output.metadata);
}

#[test]
fn test_create_from_encoded() {
    let input = String::from_str("5 cGF5bG9hZA== bWV0YWRhdGE=");
    let expected = Record {
        id: 5,
        payload: String::from_str("payload"),
        metadata: String::from_str("metadata"),
    };
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
    let factory: Factory = RecordFactory::new(reader);
    let output: Record = factory.create_from_enc(input).ok().expect("Parsing failed.");
    assert_eq!(expected.id, output.id);
    assert_eq!(expected.payload, output.payload);
    assert_eq!(expected.metadata, output.metadata);
}
<commit_msg>Remove completed TODO<commit_after>use std::str::FromStr;
use controller::Reader;
use utils;

// Each line in the database file is a 'Record' that contains the following data to be read
// and serialized.
pub struct Record {
    // Unique (?) id given to each record.
    pub id: u64,
    // The payload of each record that is Base64 encoded and JSON serialized.
    pub payload: String,
    // Any necessary metedata needed to identify the record.
    // TODO Base64 encoded and JSON encoded?
    pub metadata: String,
}

pub struct Factory {
    reader: Reader,
}

pub type RecordResult<T, E> = Result<T, E>;

//TODO Add some factory traits here if necessary
pub trait RecordFactory {
    fn new(reader: Reader) -> Self;

    fn create(&self, data: String) -> RecordResult<Record, String>;

    fn create_from_enc(&self, data: String) -> RecordResult<Record, String>;
}

impl RecordFactory for Factory {
    fn new(reader: Reader) -> Factory {
        Factory {
            reader: reader,
        }
    }

    fn create(&self, data: String) -> RecordResult<Record, String> {
        let vec_of_data = utils::string_slice(data);

        let id_num = FromStr::from_str(vec_of_data[0].as_slice());
        let id_value: u64 = match id_num {
            Some(value)     => value,
            None            => -1 // This is a failure value
        };

        Ok(Record {
            id: id_value,
            payload: vec_of_data[1].clone(),
            metadata: vec_of_data[2].clone(),
        })
    }

    fn create_from_enc(&self, data: String) -> RecordResult<Record, String> {
        let vec_of_data = utils::string_slice(data);

        let id_num = FromStr::from_str(vec_of_data[0].as_slice());
        let id_value: u64 = match id_num {
            Some(value)     => value,
            None            => -1, // This is a failure value
        };

        let enc_payload = String::from_str(vec_of_data[1].as_slice());
        let payload = utils::decode_record(enc_payload);
        let enc_metadata = String::from_str(vec_of_data[2].as_slice());
        let metadata = utils::decode_record(enc_metadata);

        Ok(Record {
            id: id_value,
            payload: payload,
            metadata: metadata,
        })
    }
}

#[test]
fn test_create_record() {
    let input = String::from_str("1 payload metadata");
    let expected = Record {
        id: 1,
        payload: String::from_str("payload"),
        metadata: String::from_str("metadata"),
    };
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
    let factory: Factory = RecordFactory::new(reader);
    let output: Record = factory.create(input).ok().expect("Parsing failed.");
    assert_eq!(expected.id, output.id);
    assert_eq!(expected.payload, output.payload);
    assert_eq!(expected.metadata, output.metadata);
}

#[test]
fn test_create_from_encoded() {
    let input = String::from_str("5 cGF5bG9hZA== bWV0YWRhdGE=");
    let expected = Record {
        id: 5,
        payload: String::from_str("payload"),
        metadata: String::from_str("metadata"),
    };
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
    let factory: Factory = RecordFactory::new(reader);
    let output: Record = factory.create_from_enc(input).ok().expect("Parsing failed.");
    assert_eq!(expected.id, output.id);
    assert_eq!(expected.payload, output.payload);
    assert_eq!(expected.metadata, output.metadata);
}
<|endoftext|>


<commit_before>
// This pass generates x86-64 assembly from IR.

#![allow(non_upper_case_globals)]

use crate::regalloc::*;
use crate::util::*;
use crate::*;
use std::collections::HashMap;

lazy_static! {
    static ref LABEL: Mutex<usize> = Mutex::new(0);
    static ref ESCAPED: Mutex<HashMap<char,char>> = Mutex::new(HashMap::new());
}

const argreg8: [&'static str; 6] = ["dil", "sil", "dl", "cl", "r8b", "r9b"];
const argreg32: [&'static str; 6] = ["edi", "esi", "edx", "ecx", "r8d", "r9d"];
const argreg64: [&'static str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];

fn label() -> usize {
    *LABEL.lock().unwrap()
}

fn init_escaped() {
    let mut escaped = ESCAPED.lock().unwrap();
    if escaped.len() > 0 {
        return;
    }

    escaped.insert(char::from(8), 'b');  // \b
    escaped.insert(char::from(12), 'f'); // \f
    escaped.insert(char::from(10), 'n'); // \n
    escaped.insert(char::from(13), 'r'); // \r
    escaped.insert(char::from(9), 't');  // \t
    escaped.insert('\\', '\\');
    escaped.insert('\'', '\'');
    escaped.insert('\"', '"');
}

fn escaped(c: char) -> Option<char> {
    let mut ret = None;
    match ESCAPED.lock() {
        Ok(escaped) => {
            match escaped.get(&c) {
                Some(esc) => {
                    ret = Some(*esc);
                }
                None => { }
            }
        }
        Err(_) => { }
    }
    return ret;
}

fn inc_label() {
    let mut label = LABEL.lock().unwrap();
    *label += 1;
}

fn escape(s: & String) -> String {
    init_escaped();

    let mut buf = String::new();
    let mut chars = s.chars();
    while let Some(c) = chars.next() {
        if let Some(esc) = escaped(c) {
            buf.push('\\');
            buf.push(esc);
        } else if c.is_ascii_graphic() || c == ' ' {
            buf.push(c);
        } else {
            buf.push_str(&format!("\\{:03o}", u32::from(c)));
        }
    }

    //buf.push(char::from(0));
    return buf;
}

fn emit_cmp(ir: &IR, insn: &str) {
    let lhs = ir.lhs as usize;
    let rhs = ir.rhs as usize;

    println!("  cmp {}, {}", regs[lhs], regs[rhs]);
    println!("  {} {}", insn, regs8[lhs]);
    println!("  movzb {}, {}", regs[lhs], regs8[lhs]);
}

fn gen(fun: &IR) {
    let ret = format!(".Lend{}", label());
    inc_label();

    println!(".global {}", fun.name);
    println!("{}:", fun.name);
    println!("  push rbp");
    println!("  mov rbp, rsp");
    println!("  sub rsp, {}", roundup(fun.stacksize, 16));
    println!("  push r12");
    println!("  push r13");
    println!("  push r14");
    println!("  push r15");

    for i in 0..fun.ir.len() {
        let ir = &fun.ir[i as usize];
        let lhs = ir.lhs as usize;
        let rhs = ir.rhs as usize;

        match ir.op {
            IRType::IMM => {
                println!("  mov {}, {}", regs[lhs], rhs);
            }
            IRType::BPREL => {
                println!("  lea {}, [rbp-{}]", regs[lhs], rhs);
            }
            IRType::MOV => {
                println!("  mov {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::RETURN => {
                println!("  mov rax, {}", regs[lhs]);
                println!("  jmp {}", ret);
            }
            IRType::CALL => {
                for i in 0..ir.nargs {
                    println!("  mov {}, {}", argreg64[i], regs[ir.args[i] as usize]);
                }

                println!("  push r10");
                println!("  push r11");
                println!("  mov rax, 0");
                println!("  call {}", ir.name);
                println!("  pop r11");
                println!("  pop r10");

                println!("  mov {}, rax", regs[lhs]);
            }
            IRType::LABEL => {
                println!(".L{}:", lhs);
            }
            IRType::LABEL_ADDR => {
                println!("  lea {}, {}", regs[lhs], ir.name);
            }
            IRType::NEG => {
                println!("  neg {}", regs[lhs]);
            }
            IRType::EQ => {
                emit_cmp(ir, "sete")
            }
            IRType::NE => {
                emit_cmp(ir, "setne")
            }
            IRType::LT => {
                emit_cmp(ir, "setl")
            }
            IRType::LE => {
                emit_cmp(ir, "setle")
            }
            IRType::AND => {
                println!("  and {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::OR => {
                println!("  or {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::XOR => {
                println!("  xor {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::SHL => {
                println!("  mov cl, {}", regs8[rhs]);
                println!("  shl {}, cl", regs[lhs]);
            }
            IRType::SHR => {
                println!("  mov cl, {}", regs8[rhs]);
                println!("  shr {}, cl", regs[lhs]);
            }
            IRType::JMP => {
                println!("  jmp .L{}", lhs);
            }
            IRType::IF => {
                println!("  cmp {}, 0", regs[lhs]);
                println!("  jne .L{}", rhs);
            }
            IRType::UNLESS => {
                println!("  cmp {}, 0", regs[lhs]);
                println!("  je .L{}", rhs);
            }
            IRType::LOAD8 => {
                println!("  mov {}, [{}]", regs8[lhs], regs[rhs]);
                println!("  movzb {}, {}", regs[lhs], regs8[lhs]);
            }
            IRType::LOAD32 => {
                println!("  mov {}, [{}]", regs32[lhs], regs[rhs]);
            }
            IRType::LOAD64 => {
                println!("  mov {}, [{}]", regs[lhs], regs[rhs]);
            }
            IRType::STORE8 => {
                println!("  mov [{}], {}", regs[lhs], regs8[rhs]);

                // fall through in 9cc
                println!("  mov [{}], {}", regs[lhs], regs32[rhs]);
            }
            IRType::STORE32 => {
                println!("  mov [{}], {}", regs[lhs], regs32[rhs]);
            }
            IRType::STORE64 => {
                println!("  mov [{}], {}", regs[lhs], regs[rhs]);
            }
            IRType::STORE8_ARG => {
                println!("  mov [rbp-{}], {}", lhs, argreg8[rhs]);
            }
            IRType::STORE32_ARG => {
                println!("  mov [rbp-{}], {}", lhs, argreg32[rhs]);
            }
            IRType::STORE64_ARG => {
                println!("  mov [rbp-{}], {}", lhs, argreg64[rhs]);
            }
            IRType::ADD => {
                println!("  add {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::ADD_IMM => {
                println!("  add {}, {}", regs[lhs], rhs);
            }
            IRType::SUB => {
                println!("  sub {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::SUB_IMM => {
                println!("  sub {}, {}", regs[lhs], rhs);
            }
            IRType::MUL => {
                println!("  mov rax, {}", regs[rhs]);
                println!("  mul {}", regs[lhs]);
                println!("  mov {}, rax", regs[lhs]);
            }
            IRType::MUL_IMM => {
                println!("  mov rax, {}", rhs);
                println!("  mul {}", regs[lhs]);
                println!("  mov {}, rax", regs[lhs]);
            }
            IRType::DIV => {
                println!(" mov rax, {}", regs[lhs]);
                println!(" cqo");
                println!(" div {}", regs[rhs]);
                println!(" mov {}, rax", regs[lhs]);
            }
            IRType::MOD => {
                println!("  mov rax, {}", regs[lhs]);
                println!("  cqo");
                println!("  div {}", regs[rhs]);
                println!("  mov {}, rdx", regs[lhs]);
            }
            IRType::NOP => {}
            ref i => {
                panic!(format!("unknown operator {:?}", i));
            }
        }
    }
    println!("{}:", ret);
    println!("  pop r15");
    println!("  pop r14");
    println!("  pop r13");
    println!("  pop r12");
    println!("  mov rsp, rbp");
    println!("  pop rbp");
    println!("  ret");
}

pub fn gen_x86(globals: Vec<Var>, fns: &Vec<IR>) {
    println!(".intel_syntax noprefix");

    println!(".data");
    for var in globals.iter() {
        if var.is_extern {
            continue;
        }
        println!("{}:", var.name);
        println!("  .ascii \"{}\"", escape(&var.data));
    }

    println!(".text");
    for i in 0..fns.len() {
        gen(&fns[i]);
    }
}
<commit_msg>Make assembly emitter more concise.<commit_after>
// This pass generates x86-64 assembly from IR.

#![allow(non_upper_case_globals)]

use crate::regalloc::*;
use crate::util::*;
use crate::*;
use std::collections::HashMap;

macro_rules! emit {
    ($fmt:expr) => {
        print!("\t");
        println!($fmt);
    };
    ($fmt:expr,$($x:tt)*) => {
        print!("\t");
        println!($fmt, $( $x )*);
    };
}

lazy_static! {
    static ref NLABEL: Mutex<usize> = Mutex::new(0);
    static ref ESCAPED: Mutex<HashMap<char,char>> = Mutex::new(HashMap::new());
}

fn bump_nlabel() -> usize {
    let mut nlabel = NLABEL.lock().unwrap();
    let ret = *nlabel;
    *nlabel += 1;
    return ret;
}

const argreg8: [&'static str; 6] = ["dil", "sil", "dl", "cl", "r8b", "r9b"];
const argreg32: [&'static str; 6] = ["edi", "esi", "edx", "ecx", "r8d", "r9d"];
const argreg64: [&'static str; 6] = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];

fn init_escaped() {
    let mut escaped = ESCAPED.lock().unwrap();
    if escaped.len() > 0 {
        return;
    }

    escaped.insert(char::from(8), 'b');  // \b
    escaped.insert(char::from(12), 'f'); // \f
    escaped.insert(char::from(10), 'n'); // \n
    escaped.insert(char::from(13), 'r'); // \r
    escaped.insert(char::from(9), 't');  // \t
    escaped.insert('\\', '\\');
    escaped.insert('\'', '\'');
    escaped.insert('\"', '"');
}

fn escaped(c: char) -> Option<char> {
    let mut ret = None;
    match ESCAPED.lock() {
        Ok(escaped) => {
            match escaped.get(&c) {
                Some(esc) => {
                    ret = Some(*esc);
                }
                None => { }
            }
        }
        Err(_) => { }
    }
    return ret;
}

fn escape(s: & String) -> String {
    init_escaped();

    let mut buf = String::new();
    let mut chars = s.chars();
    while let Some(c) = chars.next() {
        if let Some(esc) = escaped(c) {
            buf.push('\\');
            buf.push(esc);
        } else if c.is_ascii_graphic() || c == ' ' {
            buf.push(c);
        } else {
            buf.push_str(&format!("\\{:03o}", u32::from(c)));
        }
    }

    //buf.push(char::from(0));
    return buf;
}

fn emit_cmp(insn: &str, ir: &IR) {
    let lhs = ir.lhs as usize;
    let rhs = ir.rhs as usize;

    emit!("cmp {}, {}", regs[lhs], regs[rhs]);
    emit!("{} {}", insn, regs8[lhs]);
    emit!("movzb {}, {}", regs[lhs], regs8[lhs]);
}

fn gen(fun: &IR) {
    let ret = format!(".Lend{}", bump_nlabel());

    println!(".global {}", fun.name);
    println!("{}:", fun.name);
    emit!("push rbp");
    emit!("mov rbp, rsp");
    emit!("sub rsp, {}", roundup(fun.stacksize, 16));
    emit!("push r12");
    emit!("push r13");
    emit!("push r14");
    emit!("push r15");

    for i in 0..fun.ir.len() {
        let ir = &fun.ir[i as usize];
        let lhs = ir.lhs as usize;
        let rhs = ir.rhs as usize;

        match ir.op {
            IRType::IMM => {
                emit!("mov {}, {}", regs[lhs], rhs);
            }
            IRType::BPREL => {
                emit!("lea {}, [rbp-{}]", regs[lhs], rhs);
            }
            IRType::MOV => {
                emit!("mov {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::RETURN => {
                emit!("mov rax, {}", regs[lhs]);
                emit!("jmp {}", ret);
            }
            IRType::CALL => {
                for i in 0..ir.nargs {
                    emit!("mov {}, {}", argreg64[i], regs[ir.args[i] as usize]);
                }

                emit!("push r10");
                emit!("push r11");
                emit!("mov rax, 0");
                emit!("call {}", ir.name);
                emit!("pop r11");
                emit!("pop r10");

                emit!("mov {}, rax", regs[lhs]);
            }
            IRType::LABEL => {
                println!(".L{}:", lhs);
            }
            IRType::LABEL_ADDR => {
                emit!("lea {}, {}", regs[lhs], ir.name);
            }
            IRType::NEG => {
                emit!("neg {}", regs[lhs]);
            }
            IRType::EQ => {
                emit_cmp("sete", ir)
            }
            IRType::NE => {
                emit_cmp("setne", ir)
            }
            IRType::LT => {
                emit_cmp("setl", ir)
            }
            IRType::LE => {
                emit_cmp("setle", ir)
            }
            IRType::AND => {
                emit!("and {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::OR => {
                emit!("or {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::XOR => {
                emit!("xor {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::SHL => {
                emit!("mov cl, {}", regs8[rhs]);
                emit!("shl {}, cl", regs[lhs]);
            }
            IRType::SHR => {
                emit!("mov cl, {}", regs8[rhs]);
                emit!("shr {}, cl", regs[lhs]);
            }
            IRType::JMP => {
                emit!("jmp .L{}", lhs);
            }
            IRType::IF => {
                emit!("cmp {}, 0", regs[lhs]);
                emit!("jne .L{}", rhs);
            }
            IRType::UNLESS => {
                emit!("cmp {}, 0", regs[lhs]);
                emit!("je .L{}", rhs);
            }
            IRType::LOAD8 => {
                emit!("mov {}, [{}]", regs8[lhs], regs[rhs]);
                emit!("movzb {}, {}", regs[lhs], regs8[lhs]);
            }
            IRType::LOAD32 => {
                emit!("mov {}, [{}]", regs32[lhs], regs[rhs]);
            }
            IRType::LOAD64 => {
                emit!("mov {}, [{}]", regs[lhs], regs[rhs]);
            }
            IRType::STORE8 => {
                emit!("mov [{}], {}", regs[lhs], regs8[rhs]);

                // fall through in 9cc
                emit!("mov [{}], {}", regs[lhs], regs32[rhs]);
            }
            IRType::STORE32 => {
                emit!("mov [{}], {}", regs[lhs], regs32[rhs]);
            }
            IRType::STORE64 => {
                emit!("mov [{}], {}", regs[lhs], regs[rhs]);
            }
            IRType::STORE8_ARG => {
                emit!("mov [rbp-{}], {}", lhs, argreg8[rhs]);
            }
            IRType::STORE32_ARG => {
                emit!("mov [rbp-{}], {}", lhs, argreg32[rhs]);
            }
            IRType::STORE64_ARG => {
                emit!("mov [rbp-{}], {}", lhs, argreg64[rhs]);
            }
            IRType::ADD => {
                emit!("add {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::ADD_IMM => {
                emit!("add {}, {}", regs[lhs], rhs);
            }
            IRType::SUB => {
                emit!("sub {}, {}", regs[lhs], regs[rhs]);
            }
            IRType::SUB_IMM => {
                emit!("sub {}, {}", regs[lhs], rhs);
            }
            IRType::MUL => {
                emit!("mov rax, {}", regs[rhs]);
                emit!("mul {}", regs[lhs]);
                emit!("mov {}, rax", regs[lhs]);
            }
            IRType::MUL_IMM => {
                emit!("mov rax, {}", rhs);
                emit!("mul {}", regs[lhs]);
                emit!("mov {}, rax", regs[lhs]);
            }
            IRType::DIV => {
                emit!("mov rax, {}", regs[lhs]);
                emit!("cqo");
                emit!("div {}", regs[rhs]);
                emit!("mov {}, rax", regs[lhs]);
            }
            IRType::MOD => {
                emit!("mov rax, {}", regs[lhs]);
                emit!("cqo");
                emit!("div {}", regs[rhs]);
                emit!("mov {}, rdx", regs[lhs]);
            }
            IRType::NOP => {}
            ref i => {
                panic!("unknown operator {:?}", i);
            }
        }
    }
    println!("{}:", ret);
    emit!("pop r15");
    emit!("pop r14");
    emit!("pop r13");
    emit!("pop r12");
    emit!("mov rsp, rbp");
    emit!("pop rbp");
    emit!("ret");
}

pub fn gen_x86(globals: Vec<Var>, fns: &Vec<IR>) {
    println!(".intel_syntax noprefix");

    println!(".data");
    for var in globals.iter() {
        if var.is_extern {
            continue;
        }
        println!("{}:", var.name);
        emit!(".ascii \"{}\"", escape(&var.data));
    }

    println!(".text");
    for i in 0..fns.len() {
        gen(&fns[i]);
    }
}
<|endoftext|>


<commit_before>
#[derive(Show)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum AddOp {
  Add,
  Subtract,
}

#[derive(Show)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum MultOp {
  Multiply,
  Divide,
  Modulo,
}

#[derive(Clone)]
#[derive(Show)]
#[derive(PartialEq)]
pub struct AddTerm(pub AddOp, pub Expr);

#[derive(Clone)]
#[derive(Show)]
#[derive(PartialEq)]
pub struct MultTerm(pub MultOp, pub Expr);

#[derive(Show)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum Expr {
  Variable(String),
  Num(i32),
  AddSub(Vec<AddTerm>), //a + b - c + d becomes [(+ a) (+ b) (- c) (+ d)]
  MultDiv(Vec<MultTerm>),
}

//for now this is it's own type and not a statement
#[derive(Show)]
#[derive(Clone)]
#[derive(PartialEq)]
pub struct Block(pub Vec<Statement>);

#[derive(Show)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum Statement {
  Assign(String, Expr),
  Output(Expr),
  If(Expr, Comparator, Expr, Block, Option<Block>),
  While(Expr, Comparator, Expr, Block),
}

#[derive(Show)]
#[derive(Clone)]
#[derive(Eq)]
#[derive(PartialEq)]
pub enum Comparator {
  CEq,  // ==
  CGt,  // >
  CLt,  // <
  CNeq, // !=
  CGeq, // >=
  CLeq, // <=
}

#[derive(Show)]
#[derive(Eq)]
#[derive(PartialEq)]
#[derive(Clone)]
pub enum Token {
  Equals,
  Ident(String),
  Number(i32),
  PlusSign,
  MinusSign,
  MultSign,
  DivideSign,
  ModuloSign,
  OutputCmd,
  NewLine,
  OpenParen,
  CloseParen,
  OpenBrace,
  CloseBrace,
  IfKeyword,
  ElseKeyword,
  WhileKeyword,
  Cmp(Comparator),
}

<commit_msg>Changed all #[derive(Show)] to #[derive(Debug)]<commit_after>
#[derive(Debug)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum AddOp {
  Add,
  Subtract,
}

#[derive(Debug)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum MultOp {
  Multiply,
  Divide,
  Modulo,
}

#[derive(Clone)]
#[derive(Debug)]
#[derive(PartialEq)]
pub struct AddTerm(pub AddOp, pub Expr);

#[derive(Clone)]
#[derive(Debug)]
#[derive(PartialEq)]
pub struct MultTerm(pub MultOp, pub Expr);

#[derive(Debug)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum Expr {
  Variable(String),
  Num(i32),
  AddSub(Vec<AddTerm>), //a + b - c + d becomes [(+ a) (+ b) (- c) (+ d)]
  MultDiv(Vec<MultTerm>),
}

//for now this is it's own type and not a statement
#[derive(Debug)]
#[derive(Clone)]
#[derive(PartialEq)]
pub struct Block(pub Vec<Statement>);

#[derive(Debug)]
#[derive(Clone)]
#[derive(PartialEq)]
pub enum Statement {
  Assign(String, Expr),
  Output(Expr),
  If(Expr, Comparator, Expr, Block, Option<Block>),
  While(Expr, Comparator, Expr, Block),
}

#[derive(Debug)]
#[derive(Clone)]
#[derive(Eq)]
#[derive(PartialEq)]
pub enum Comparator {
  CEq,  // ==
  CGt,  // >
  CLt,  // <
  CNeq, // !=
  CGeq, // >=
  CLeq, // <=
}

#[derive(Debug)]
#[derive(Eq)]
#[derive(PartialEq)]
#[derive(Clone)]
pub enum Token {
  Equals,
  Ident(String),
  Number(i32),
  PlusSign,
  MinusSign,
  MultSign,
  DivideSign,
  ModuloSign,
  OutputCmd,
  NewLine,
  OpenParen,
  CloseParen,
  OpenBrace,
  CloseBrace,
  IfKeyword,
  ElseKeyword,
  WhileKeyword,
  Cmp(Comparator),
}

<|endoftext|>


<commit_before>extern crate rustbox;
extern crate chrono;

use rustbox::*;
use chrono::*;

use screen::common::*;
use utility::string::*;
use model::ShowItem;
use reply_model::*;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
    scrollY: usize,
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show {
            rustbox: &rustbox,
            scrollY: 0,
        }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        self.print_header(&format!("{} - {} [{}/{}]",
                                   item.title,
                                   title,
                                   item.page,
                                   item.max_page));
        self.print_body(2, &item);
    }

    fn print_header(&mut self, text: &str) {
        let title_len = jks_len(text);
        let padding = (if self.rustbox.width() >= title_len {
            self.rustbox.width() - title_len
        } else {
            0
        }) / 2;

        let header_bottom = seq_str_gen(0, self.rustbox.width(), "─", "");

        clearline(&self.rustbox, self.rustbox.width(), 0, 0);
        self.rustbox.print(padding,
                           0,
                           rustbox::RB_BOLD,
                           Color::White,
                           Color::Black,
                           text);
        self.rustbox.print(0,
                           1,
                           rustbox::RB_BOLD,
                           Color::Yellow,
                           Color::Black,
                           &header_bottom);
    }

    pub fn print_body(&mut self, offset_y: usize, item: &ShowItem) {
        let width = self.body_width();
        let rows = self.body_height();
        let rustbox = self.rustbox;
        let scrollY = self.scrollY;

        let mut y = offset_y;
        let replier_max_width = 14;
        let time_max_width = 5;
        let now = Local::now();

        let separator_width = if rustbox.width() >= 2 {
            rustbox.width() - 2
        } else {
            0
        };
        let separator_padding_width = if rustbox.width() > separator_width {
            rustbox.width() - separator_width
        } else {
            0
        } / 2;

        let separator_padding = seq_str_gen(0, separator_padding_width, " ", "");

        let separator_bottom = make_separator_bottom(separator_width, &separator_padding);

        for (i, reply) in item.replies.iter().take(rows).enumerate() {

            let mut m = print_reply(&reply.body, 0, scrollY, y, &rustbox);

            if scrollY + 1 < y + m {

                let replier_name = reply.username.clone();

                let published_at = reply.published_at.clone();

                let published_at_dt = match Local.datetime_from_str(&published_at,
                                                                    "%d/%m/%Y %H:%M") {
                    Ok(v) => v,
                    Err(e) => now,
                };
                let time = published_at_format(&(now - published_at_dt));

                let separator_top = make_separator_top(separator_width,
                                                       &separator_padding,
                                                       replier_max_width,
                                                       &replier_name,
                                                       time_max_width,
                                                       &time);

                rustbox.print(0,
                              m + y - scrollY,
                              rustbox::RB_NORMAL,
                              Color::Green,
                              Color::Black,
                              &separator_top);
            }
            m += 1;

            if scrollY + 1 < y + m {
                rustbox.print(0,
                              m + y - scrollY,
                              rustbox::RB_NORMAL,
                              Color::Green,
                              Color::Black,
                              &separator_bottom);
            }
            m += 1;
            y += m;
        }
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp < 10000 {
            self.scrollY = tmp + value;
            return true;
        }
        false
    }

    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }
    fn print_default(&self, x: usize, y: usize, text: &str) {
        self.rustbox.print(x, y, rustbox::RB_BOLD, Color::White, Color::Black, text);
    }
}

fn print_default(rustbox: &rustbox::RustBox, x: usize, y: usize, s: String) {
    rustbox.print(0, y, rustbox::RB_NORMAL, Color::White, Color::Black, &s);
}

fn print_reply(vec: &Vec<NodeType>,
               depth: usize,
               scrollY: usize,
               y: usize,
               rustbox: &rustbox::RustBox)
               -> usize {
    let padding = seq_str_gen(0, depth, "├─", "");
    let mut m = 0;
    let mut recursive_offset = 0;
    let mut total_y = 0;
    let mut line = String::new();

    // clean up lines (end)
    let vec2 = {
        let vec_length = vec.len();
        let vec_check_cleanup = vec.clone();

        // check if last 4 elements match the EMPTY PATTERN
        let is_last4_empty = vec_check_cleanup.iter()
                                              .rev()
                                              .take(4)
                                              .enumerate()
                                              .all(|(j, node)| match node.clone() {
                                                  NodeType::Br(n) => j == 1 || j == 2 || j == 3,
                                                  NodeType::Text(n) => j == 0 && n.data.is_empty(),
                                                  _ => false,
                                              });

        let vec_short_length = if vec_length > 4 && is_last4_empty {
            vec_length - 4
        } else {
            vec_length
        };

        vec.iter().take(vec_short_length)
    };

    // clean up lines (start)
    let vec3 = {
        let vec2_cloned = vec2.clone();
        let mut result: Vec<NodeType> = Vec::new();
        for (j, node) in vec2_cloned.enumerate() {
            let node2 = node.clone();
            let node3 = node.clone();
            match node2 {
                NodeType::Br(n) => {
                    if !result.is_empty() {
                        result.push(node3);
                    }
                }
                _ => result.push(node3),
            }
        }
        result.clone()
    };

    let mut is_first = true;
    for (j, node) in vec3.iter().enumerate() {
        total_y = y + m + recursive_offset;
        if scrollY + 1 < total_y {
            let node2 = node.clone();
            match node2 {
                NodeType::Text(n) => {
                    if n.data != "" {
                        line = format!("{}{}", line, n.data);
                    }
                }
                NodeType::Image(n) => {
                    if n.data != "" {
                        line = format!("{}[img {}]", line, n.data);
                    }
                }
                NodeType::BlockQuote(n) => {
                    recursive_offset += print_reply(&n.data, depth + 1, scrollY, total_y, &rustbox);
                    is_first = false;
                }
                NodeType::Br(n) => {
                    if !line.is_empty() {
                        print_default(rustbox,
                                      0,
                                      total_y - scrollY,
                                      format!(" {}{}", padding, line));
                        line = String::new();
                        is_first = false;
                    }

                    // prevent first line empty
                    if !is_first {
                        m += 1;
                    }

                }
            }
        }
    }

    if !line.is_empty() {
        total_y = y + m + recursive_offset;
        print_default(rustbox,
                      0,
                      total_y - scrollY,
                      format!(" {}{}  ", padding, line));
        line = String::new();
        m += 1;
    }

    m + recursive_offset
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = seq_str_gen(0, replier_name_left_spacing_width, "─", "");
    let replier_name_right_spacing = seq_str_gen(0, replier_name_right_spacing_width, "─", "");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = seq_str_gen(0, time_left_spacing_width, "─", "");
    let time_right_spacing = seq_str_gen(0, time_right_spacing_width, "─", "");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = seq_str_gen(0, separator_top_middle_width, " ", "");
    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = seq_str_gen(0, separator_bottom_middle_width, "─", "");

    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}

fn seq_str_gen(start: usize, end: usize, sym: &str, join_sym: &str) -> String {
    (start..end).map(|_| sym.clone()).collect::<Vec<_>>().join(&join_sym)
}
<commit_msg>show - extract separator builder<commit_after>extern crate rustbox;
extern crate chrono;

use rustbox::*;
use chrono::*;

use screen::common::*;
use utility::string::*;
use model::ShowItem;
use reply_model::*;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
    scrollY: usize,
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show {
            rustbox: &rustbox,
            scrollY: 0,
        }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        self.print_header(&format!("{} - {} [{}/{}]",
                                   item.title,
                                   title,
                                   item.page,
                                   item.max_page));
        self.print_body(2, &item);
    }

    fn print_header(&mut self, text: &str) {
        let title_len = jks_len(text);
        let padding = (if self.rustbox.width() >= title_len {
            self.rustbox.width() - title_len
        } else {
            0
        }) / 2;

        let header_bottom = seq_str_gen(0, self.rustbox.width(), "─", "");

        clearline(&self.rustbox, self.rustbox.width(), 0, 0);
        self.rustbox.print(padding,
                           0,
                           rustbox::RB_BOLD,
                           Color::White,
                           Color::Black,
                           text);
        self.rustbox.print(0,
                           1,
                           rustbox::RB_BOLD,
                           Color::Yellow,
                           Color::Black,
                           &header_bottom);
    }

    fn build_separator_arguments(&mut self) -> (usize, usize, String){
        let width = self.body_width();
        let rustbox = self.rustbox;

        let separator_width = if rustbox.width() >= 2 {
            rustbox.width() - 2
        } else {
            0
        };
        let separator_padding_width = if rustbox.width() > separator_width {
            rustbox.width() - separator_width
        } else {
            0
        } / 2;

        let separator_padding = seq_str_gen(0, separator_padding_width, " ", "");

        (separator_width, separator_padding_width, separator_padding)
    }

    fn build_separator_top(&mut self, replier_name: &str, time: &str) -> String {
        let replier_max_width = 14;
        let time_max_width = 5;
        let (separator_width, separator_padding_width, separator_padding) = self.build_separator_arguments();
        make_separator_top(separator_width,
                                               &separator_padding,
                                               replier_max_width,
                                               &replier_name,
                                               time_max_width,
                                               &time)
    }

    fn build_separator_bottom(&mut self) -> String {
        let (separator_width, separator_padding_width, separator_padding) = self.build_separator_arguments();
        make_separator_bottom(separator_width, &separator_padding)
    }

    pub fn print_body(&mut self, offset_y: usize, item: &ShowItem) {
        let width = self.body_width();
        let rows = self.body_height();
        let rustbox = self.rustbox;
        let scrollY = self.scrollY;

        let mut y = offset_y;
        let now = Local::now();

        for (i, reply) in item.replies.iter().take(rows).enumerate() {

            let mut m = print_reply(&reply.body, 0, scrollY, y, &rustbox);

            if scrollY + 1 < y + m {

                let replier_name = reply.username.clone();

                let published_at = reply.published_at.clone();

                let published_at_dt = match Local.datetime_from_str(&published_at,
                                                                    "%d/%m/%Y %H:%M") {
                    Ok(v) => v,
                    Err(e) => now,
                };
                let time = published_at_format(&(now - published_at_dt));

                rustbox.print(0,
                              m + y - scrollY,
                              rustbox::RB_NORMAL,
                              Color::Green,
                              Color::Black,
                              &self.build_separator_top(&replier_name, &time));
            }
            m += 1;

            if scrollY + 1 < y + m {
                rustbox.print(0,
                              m + y - scrollY,
                              rustbox::RB_NORMAL,
                              Color::Green,
                              Color::Black,
                              &self.build_separator_bottom());
            }
            m += 1;
            y += m;
        }
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp < 10000 {
            self.scrollY = tmp + value;
            return true;
        }
        false
    }

    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }
    fn print_default(&self, x: usize, y: usize, text: &str) {
        self.rustbox.print(x, y, rustbox::RB_BOLD, Color::White, Color::Black, text);
    }
}

fn print_default(rustbox: &rustbox::RustBox, x: usize, y: usize, s: String) {
    rustbox.print(0, y, rustbox::RB_NORMAL, Color::White, Color::Black, &s);
}

fn print_reply(vec: &Vec<NodeType>,
               depth: usize,
               scrollY: usize,
               y: usize,
               rustbox: &rustbox::RustBox)
               -> usize {
    let padding = seq_str_gen(0, depth, "├─", "");
    let mut m = 0;
    let mut recursive_offset = 0;
    let mut total_y = 0;
    let mut line = String::new();

    // clean up lines (end)
    let vec2 = {
        let vec_length = vec.len();
        let vec_check_cleanup = vec.clone();

        // check if last 4 elements match the EMPTY PATTERN
        let is_last4_empty = vec_check_cleanup.iter()
                                              .rev()
                                              .take(4)
                                              .enumerate()
                                              .all(|(j, node)| match node.clone() {
                                                  NodeType::Br(n) => j == 1 || j == 2 || j == 3,
                                                  NodeType::Text(n) => j == 0 && n.data.is_empty(),
                                                  _ => false,
                                              });

        let vec_short_length = if vec_length > 4 && is_last4_empty {
            vec_length - 4
        } else {
            vec_length
        };

        vec.iter().take(vec_short_length)
    };

    // clean up lines (start)
    let vec3 = {
        let vec2_cloned = vec2.clone();
        let mut result: Vec<NodeType> = Vec::new();
        for (j, node) in vec2_cloned.enumerate() {
            let node2 = node.clone();
            let node3 = node.clone();
            match node2 {
                NodeType::Br(n) => {
                    if !result.is_empty() {
                        result.push(node3);
                    }
                }
                _ => result.push(node3),
            }
        }
        result.clone()
    };

    let mut is_first = true;
    for (j, node) in vec3.iter().enumerate() {
        total_y = y + m + recursive_offset;
        if scrollY + 1 < total_y {
            let node2 = node.clone();
            match node2 {
                NodeType::Text(n) => {
                    if n.data != "" {
                        line = format!("{}{}", line, n.data);
                    }
                }
                NodeType::Image(n) => {
                    if n.data != "" {
                        line = format!("{}[img {}]", line, n.data);
                    }
                }
                NodeType::BlockQuote(n) => {
                    recursive_offset += print_reply(&n.data, depth + 1, scrollY, total_y, &rustbox);
                    is_first = false;
                }
                NodeType::Br(n) => {
                    if !line.is_empty() {
                        print_default(rustbox,
                                      0,
                                      total_y - scrollY,
                                      format!(" {}{}", padding, line));
                        line = String::new();
                        is_first = false;
                    }

                    // prevent first line empty
                    if !is_first {
                        m += 1;
                    }

                }
            }
        }
    }

    if !line.is_empty() {
        total_y = y + m + recursive_offset;
        print_default(rustbox,
                      0,
                      total_y - scrollY,
                      format!(" {}{}  ", padding, line));
        line = String::new();
        m += 1;
    }

    m + recursive_offset
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = seq_str_gen(0, replier_name_left_spacing_width, "─", "");
    let replier_name_right_spacing = seq_str_gen(0, replier_name_right_spacing_width, "─", "");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = seq_str_gen(0, time_left_spacing_width, "─", "");
    let time_right_spacing = seq_str_gen(0, time_right_spacing_width, "─", "");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = seq_str_gen(0, separator_top_middle_width, " ", "");
    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = seq_str_gen(0, separator_bottom_middle_width, "─", "");

    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}

fn seq_str_gen(start: usize, end: usize, sym: &str, join_sym: &str) -> String {
    (start..end).map(|_| sym.clone()).collect::<Vec<_>>().join(&join_sym)
}
<|endoftext|>