<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A work queue for scheduling units of work across threads in a fork-join fashion.
//!
//! Data associated with queues is simply a pair of unsigned integers. It is expected that a
//! higher-level API on top of this could allow safe fork-join parallelism.

use native;
use rand;
use rand::{Rng, XorShiftRng};
use std::cast;
use std::mem;
use std::sync::atomics::{AtomicUint, SeqCst};
use std::sync::deque::{Abort, BufferPool, Data, Empty, Stealer, Worker};
use std::task::TaskOpts;

/// A unit of work.
///
/// The type parameter `QUD` stands for "queue user data" and represents global custom data for the
/// entire work queue, and the type parameter `WUD` stands for "work user data" and represents
/// custom data specific to each unit of work.
pub struct WorkUnit<QUD,WUD> {
    /// The function to execute.
    pub fun: extern "Rust" fn(WUD, &mut WorkerProxy<QUD,WUD>),
    /// Arbitrary data.
    pub data: WUD,
}

/// Messages from the supervisor to the worker.
enum WorkerMsg<QUD,WUD> {
    /// Tells the worker to start work.
    StartMsg(Worker<WorkUnit<QUD,WUD>>, *mut AtomicUint, *QUD),

    /// Tells the worker to stop. It can be restarted again with a `StartMsg`.
    StopMsg,

    /// Tells the worker thread to terminate.
    ExitMsg,
}

/// Messages to the supervisor.
enum SupervisorMsg<QUD,WUD> {
    FinishedMsg,
    ReturnDequeMsg(uint, Worker<WorkUnit<QUD,WUD>>),
}

/// Information that the supervisor thread keeps about the worker threads.
struct WorkerInfo<QUD,WUD> {
    /// The communication channel to the workers.
    chan: Sender<WorkerMsg<QUD,WUD>>,
    /// The buffer pool for this deque.
    pool: BufferPool<WorkUnit<QUD,WUD>>,
    /// The worker end of the deque, if we have it.
    deque: Option<Worker<WorkUnit<QUD,WUD>>>,
    /// The thief end of the work-stealing deque.
    thief: Stealer<WorkUnit<QUD,WUD>>,
}

/// Information specific to each worker thread that the thread keeps.
struct WorkerThread<QUD,WUD> {
    /// The index of this worker.
    index: uint,
    /// The communication port from the supervisor.
    port: Receiver<WorkerMsg<QUD,WUD>>,
    /// The communication channel on which messages are sent to the supervisor.
    chan: Sender<SupervisorMsg<QUD,WUD>>,
    /// The thief end of the work-stealing deque for all other workers.
    other_deques: Vec<Stealer<WorkUnit<QUD,WUD>>>,
    /// The random number generator for this worker.
    rng: XorShiftRng,
}

static SPIN_COUNT: uint = 1000;

impl<QUD:Send,WUD:Send> WorkerThread<QUD,WUD> {
    /// The main logic. This function starts up the worker and listens for
    /// messages.
    pub fn start(&mut self) {
        loop {
            // Wait for a start message.
            let (mut deque, ref_count, queue_data) = match self.port.recv() {
                StartMsg(deque, ref_count, queue_data) => (deque, ref_count, queue_data),
                StopMsg => fail!("unexpected stop message"),
                ExitMsg => return,
            };

            // We're off!
            //
            // FIXME(pcwalton): Can't use labeled break or continue cross-crate due to a Rust bug.
            loop {
                // FIXME(pcwalton): Nasty workaround for the lack of labeled break/continue
                // cross-crate.
                let mut work_unit = unsafe {
                    mem::uninit()
                };
                match deque.pop() {
                    Some(work) => work_unit = work,
                    None => {
                        // Become a thief.
                        let mut i = 0;
                        let mut should_continue = true;
                        loop {
                            let victim = (self.rng.next_u32() as uint) % self.other_deques.len();
                            match self.other_deques.get_mut(victim).steal() {
                                Empty | Abort => {
                                    // Continue.
                                }
                                Data(work) => {
                                    work_unit = work;
                                    break
                                }
                            }

                            if i == SPIN_COUNT {
                                match self.port.try_recv() {
                                    Ok(StopMsg) => {
                                        should_continue = false;
                                        break
                                    }
                                    Ok(ExitMsg) => return,
                                    Ok(_) => fail!("unexpected message"),
                                    _ => {}
                                }

                                i = 0
                            } else {
                                i += 1
                            }
                        }

                        if !should_continue {
                            break
                        }
                    }
                }

                // At this point, we have some work. Perform it.
                let mut proxy = WorkerProxy {
                    worker: &mut deque,
                    ref_count: ref_count,
                    queue_data: queue_data,
                };
                (work_unit.fun)(work_unit.data, &mut proxy);

                // The work is done. Now decrement the count of outstanding work items. If this was
                // the last work unit in the queue, then send a message on the channel.
                unsafe {
                    if (*ref_count).fetch_sub(1, SeqCst) == 1 {
                        self.chan.send(FinishedMsg)
                    }
                }
            }

            // Give the deque back to the supervisor.
            self.chan.send(ReturnDequeMsg(self.index, deque))
        }
    }
}

/// A handle to the work queue that individual work units have.
pub struct WorkerProxy<'a,QUD,WUD> {
    pub worker: &'a mut Worker<WorkUnit<QUD,WUD>>,
    pub ref_count: *mut AtomicUint,
    pub queue_data: *QUD,
}

impl<'a,QUD,WUD:Send> WorkerProxy<'a,QUD,WUD> {
    /// Enqueues a block into the work queue.
    #[inline]
    pub fn push(&mut self, work_unit: WorkUnit<QUD,WUD>) {
        unsafe {
            drop((*self.ref_count).fetch_add(1, SeqCst));
        }
        self.worker.push(work_unit);
    }

    /// Retrieves the queue user data.
    #[inline]
    pub fn user_data<'a>(&'a self) -> &'a QUD {
        unsafe {
            cast::transmute(self.queue_data)
        }
    }
}

/// A work queue on which units of work can be submitted.
pub struct WorkQueue<QUD,WUD> {
    /// Information about each of the workers.
    workers: Vec<WorkerInfo<QUD,WUD>>,
    /// A port on which deques can be received from the workers.
    port: Receiver<SupervisorMsg<QUD,WUD>>,
    /// The amount of work that has been enqueued.
    pub work_count: uint,
    /// Arbitrary user data.
    pub data: QUD,
}

impl<QUD:Send,WUD:Send> WorkQueue<QUD,WUD> {
    /// Creates a new work queue and spawns all the threads associated with
    /// it.
    pub fn new(task_name: &'static str, thread_count: uint, user_data: QUD) -> WorkQueue<QUD,WUD> {
        // Set up data structures.
        let (supervisor_chan, supervisor_port) = channel();
        let (mut infos, mut threads) = (vec!(), vec!());
        for i in range(0, thread_count) {
            let (worker_chan, worker_port) = channel();
            let mut pool = BufferPool::new();
            let (worker, thief) = pool.deque();
            infos.push(WorkerInfo {
                chan: worker_chan,
                pool: pool,
                deque: Some(worker),
                thief: thief,
            });
            threads.push(WorkerThread {
                index: i,
                port: worker_port,
                chan: supervisor_chan.clone(),
                other_deques: vec!(),
                rng: rand::weak_rng(),
            });
        }

        // Connect workers to one another.
        for i in range(0, thread_count) {
            for j in range(0, thread_count) {
                if i != j {
                    threads.get_mut(i).other_deques.push(infos.get(j).thief.clone())
                }
            }
            assert!(threads.get(i).other_deques.len() == thread_count - 1)
        }

        // Spawn threads.
        for thread in threads.move_iter() {
            let mut opts = TaskOpts::new();
            opts.name = Some(task_name.into_maybe_owned());
            native::task::spawn_opts(opts, proc() {
                let mut thread = thread;
                thread.start()
            })
        }

        WorkQueue {
            workers: infos,
            port: supervisor_port,
            work_count: 0,
            data: user_data,
        }
    }

    /// Enqueues a block into the work queue.
    #[inline]
    pub fn push(&mut self, work_unit: WorkUnit<QUD,WUD>) {
        match self.workers.get_mut(0).deque {
            None => {
                fail!("tried to push a block but we don't have the deque?!")
            }
            Some(ref mut deque) => deque.push(work_unit),
        }
        self.work_count += 1
    }

    /// Synchronously runs all the enqueued tasks and waits for them to complete.
    pub fn run(&mut self) {
        // Tell the workers to start.
        let mut work_count = AtomicUint::new(self.work_count);
        for worker in self.workers.mut_iter() {
            worker.chan.send(StartMsg(worker.deque.take_unwrap(), &mut work_count, &self.data))
        }

        // Wait for the work to finish.
        drop(self.port.recv());
        self.work_count = 0;

        // Tell everyone to stop.
        for worker in self.workers.iter() {
            worker.chan.send(StopMsg)
        }

        // Get our deques back.
        for _ in range(0, self.workers.len()) {
            match self.port.recv() {
                ReturnDequeMsg(index, deque) => self.workers.get_mut(index).deque = Some(deque),
                FinishedMsg => fail!("unexpected finished message!"),
            }
        }
    }

    pub fn shutdown(&mut self) {
        for worker in self.workers.iter() {
            worker.chan.send(ExitMsg)
        }
    }
}

<commit_msg>auto merge of #2500 : bjz/servo/workqueue, r=pcwalton<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A work queue for scheduling units of work across threads in a fork-join fashion.
//!
//! Data associated with queues is simply a pair of unsigned integers. It is expected that a
//! higher-level API on top of this could allow safe fork-join parallelism.

use native;
use rand;
use rand::{Rng, XorShiftRng};
use std::cast;
use std::mem;
use std::sync::atomics::{AtomicUint, SeqCst};
use std::sync::deque::{Abort, BufferPool, Data, Empty, Stealer, Worker};
use std::task::TaskOpts;

/// A unit of work.
///
/// # Type parameters
///
/// - `QueueData`: global custom data for the entire work queue.
/// - `WorkData`: custom data specific to each unit of work.
pub struct WorkUnit<QueueData, WorkData> {
    /// The function to execute.
    pub fun: extern "Rust" fn(WorkData, &mut WorkerProxy<QueueData, WorkData>),
    /// Arbitrary data.
    pub data: WorkData,
}

/// Messages from the supervisor to the worker.
enum WorkerMsg<QueueData, WorkData> {
    /// Tells the worker to start work.
    StartMsg(Worker<WorkUnit<QueueData, WorkData>>, *mut AtomicUint, *QueueData),
    /// Tells the worker to stop. It can be restarted again with a `StartMsg`.
    StopMsg,
    /// Tells the worker thread to terminate.
    ExitMsg,
}

/// Messages to the supervisor.
enum SupervisorMsg<QueueData, WorkData> {
    FinishedMsg,
    ReturnDequeMsg(uint, Worker<WorkUnit<QueueData, WorkData>>),
}

/// Information that the supervisor thread keeps about the worker threads.
struct WorkerInfo<QueueData, WorkData> {
    /// The communication channel to the workers.
    chan: Sender<WorkerMsg<QueueData, WorkData>>,
    /// The buffer pool for this deque.
    pool: BufferPool<WorkUnit<QueueData, WorkData>>,
    /// The worker end of the deque, if we have it.
    deque: Option<Worker<WorkUnit<QueueData, WorkData>>>,
    /// The thief end of the work-stealing deque.
    thief: Stealer<WorkUnit<QueueData, WorkData>>,
}

/// Information specific to each worker thread that the thread keeps.
struct WorkerThread<QueueData, WorkData> {
    /// The index of this worker.
    index: uint,
    /// The communication port from the supervisor.
    port: Receiver<WorkerMsg<QueueData, WorkData>>,
    /// The communication channel on which messages are sent to the supervisor.
    chan: Sender<SupervisorMsg<QueueData, WorkData>>,
    /// The thief end of the work-stealing deque for all other workers.
    other_deques: Vec<Stealer<WorkUnit<QueueData, WorkData>>>,
    /// The random number generator for this worker.
    rng: XorShiftRng,
}

static SPIN_COUNT: uint = 1000;

impl<QueueData: Send, WorkData: Send> WorkerThread<QueueData, WorkData> {
    /// The main logic. This function starts up the worker and listens for
    /// messages.
    fn start(&mut self) {
        loop {
            // Wait for a start message.
            let (mut deque, ref_count, queue_data) = match self.port.recv() {
                StartMsg(deque, ref_count, queue_data) => (deque, ref_count, queue_data),
                StopMsg => fail!("unexpected stop message"),
                ExitMsg => return,
            };

            // We're off!
            //
            // FIXME(pcwalton): Can't use labeled break or continue cross-crate due to a Rust bug.
            loop {
                // FIXME(pcwalton): Nasty workaround for the lack of labeled break/continue
                // cross-crate.
                let mut work_unit = unsafe {
                    mem::uninit()
                };
                match deque.pop() {
                    Some(work) => work_unit = work,
                    None => {
                        // Become a thief.
                        let mut i = 0;
                        let mut should_continue = true;
                        loop {
                            let victim = (self.rng.next_u32() as uint) % self.other_deques.len();
                            match self.other_deques.get_mut(victim).steal() {
                                Empty | Abort => {
                                    // Continue.
                                }
                                Data(work) => {
                                    work_unit = work;
                                    break
                                }
                            }

                            if i == SPIN_COUNT {
                                match self.port.try_recv() {
                                    Ok(StopMsg) => {
                                        should_continue = false;
                                        break
                                    }
                                    Ok(ExitMsg) => return,
                                    Ok(_) => fail!("unexpected message"),
                                    _ => {}
                                }

                                i = 0
                            } else {
                                i += 1
                            }
                        }

                        if !should_continue {
                            break
                        }
                    }
                }

                // At this point, we have some work. Perform it.
                let mut proxy = WorkerProxy {
                    worker: &mut deque,
                    ref_count: ref_count,
                    queue_data: queue_data,
                };
                (work_unit.fun)(work_unit.data, &mut proxy);

                // The work is done. Now decrement the count of outstanding work items. If this was
                // the last work unit in the queue, then send a message on the channel.
                unsafe {
                    if (*ref_count).fetch_sub(1, SeqCst) == 1 {
                        self.chan.send(FinishedMsg)
                    }
                }
            }

            // Give the deque back to the supervisor.
            self.chan.send(ReturnDequeMsg(self.index, deque))
        }
    }
}

/// A handle to the work queue that individual work units have.
pub struct WorkerProxy<'a, QueueData, WorkData> {
    worker: &'a mut Worker<WorkUnit<QueueData, WorkData>>,
    ref_count: *mut AtomicUint,
    queue_data: *QueueData,
}

impl<'a, QueueData, WorkData: Send> WorkerProxy<'a, QueueData, WorkData> {
    /// Enqueues a block into the work queue.
    #[inline]
    pub fn push(&mut self, work_unit: WorkUnit<QueueData, WorkData>) {
        unsafe {
            drop((*self.ref_count).fetch_add(1, SeqCst));
        }
        self.worker.push(work_unit);
    }

    /// Retrieves the queue user data.
    #[inline]
    pub fn user_data<'a>(&'a self) -> &'a QueueData {
        unsafe {
            cast::transmute(self.queue_data)
        }
    }
}

/// A work queue on which units of work can be submitted.
pub struct WorkQueue<QueueData, WorkData> {
    /// Information about each of the workers.
    workers: Vec<WorkerInfo<QueueData, WorkData>>,
    /// A port on which deques can be received from the workers.
    port: Receiver<SupervisorMsg<QueueData, WorkData>>,
    /// The amount of work that has been enqueued.
    work_count: uint,
    /// Arbitrary user data.
    pub data: QueueData,
}

impl<QueueData: Send, WorkData: Send> WorkQueue<QueueData, WorkData> {
    /// Creates a new work queue and spawns all the threads associated with
    /// it.
    pub fn new(task_name: &'static str, thread_count: uint, user_data: QueueData) -> WorkQueue<QueueData, WorkData> {
        // Set up data structures.
        let (supervisor_chan, supervisor_port) = channel();
        let (mut infos, mut threads) = (vec!(), vec!());
        for i in range(0, thread_count) {
            let (worker_chan, worker_port) = channel();
            let mut pool = BufferPool::new();
            let (worker, thief) = pool.deque();
            infos.push(WorkerInfo {
                chan: worker_chan,
                pool: pool,
                deque: Some(worker),
                thief: thief,
            });
            threads.push(WorkerThread {
                index: i,
                port: worker_port,
                chan: supervisor_chan.clone(),
                other_deques: vec!(),
                rng: rand::weak_rng(),
            });
        }

        // Connect workers to one another.
        for i in range(0, thread_count) {
            for j in range(0, thread_count) {
                if i != j {
                    threads.get_mut(i).other_deques.push(infos.get(j).thief.clone())
                }
            }
            assert!(threads.get(i).other_deques.len() == thread_count - 1)
        }

        // Spawn threads.
        for thread in threads.move_iter() {
            let mut opts = TaskOpts::new();
            opts.name = Some(task_name.into_maybe_owned());
            native::task::spawn_opts(opts, proc() {
                let mut thread = thread;
                thread.start()
            })
        }

        WorkQueue {
            workers: infos,
            port: supervisor_port,
            work_count: 0,
            data: user_data,
        }
    }

    /// Enqueues a block into the work queue.
    #[inline]
    pub fn push(&mut self, work_unit: WorkUnit<QueueData, WorkData>) {
        match self.workers.get_mut(0).deque {
            None => {
                fail!("tried to push a block but we don't have the deque?!")
            }
            Some(ref mut deque) => deque.push(work_unit),
        }
        self.work_count += 1
    }

    /// Synchronously runs all the enqueued tasks and waits for them to complete.
    pub fn run(&mut self) {
        // Tell the workers to start.
        let mut work_count = AtomicUint::new(self.work_count);
        for worker in self.workers.mut_iter() {
            worker.chan.send(StartMsg(worker.deque.take_unwrap(), &mut work_count, &self.data))
        }

        // Wait for the work to finish.
        drop(self.port.recv());
        self.work_count = 0;

        // Tell everyone to stop.
        for worker in self.workers.iter() {
            worker.chan.send(StopMsg)
        }

        // Get our deques back.
        for _ in range(0, self.workers.len()) {
            match self.port.recv() {
                ReturnDequeMsg(index, deque) => self.workers.get_mut(index).deque = Some(deque),
                FinishedMsg => fail!("unexpected finished message!"),
            }
        }
    }

    pub fn shutdown(&mut self) {
        for worker in self.workers.iter() {
            worker.chan.send(ExitMsg)
        }
    }
}

<|endoftext|>


<commit_before>use ansi_term::Style;

use fs::{File, FileTarget};

pub use self::cell::{TextCell, TextCellContents, DisplayWidth};
pub use self::colours::Colours;
pub use self::details::Details;
pub use self::grid_details::GridDetails;
pub use self::grid::Grid;
pub use self::lines::Lines;

mod grid;
pub mod details;
mod lines;
mod grid_details;
pub mod column;
mod cell;
mod colours;
mod tree;

pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents {
    let mut bits = Vec::new();

    if file.dir.is_none() {
        if let Some(parent) = file.path.parent() {
            let coconut = parent.components().count();

            if coconut == 1 && parent.has_root() {
                bits.push(colours.symlink_path.paint("/"));
            }
            else if coconut >= 1 {
                bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                bits.push(colours.symlink_path.paint("/"));
            }
        }
    }

    if !file.name.is_empty() {
        bits.push(file_colour(colours, file).paint(file.name.clone()));
    }

    if links && file.is_link() {
        match file.link_target() {
            FileTarget::Ok(target) => {
                bits.push(Style::default().paint(" "));
                bits.push(colours.punctuation.paint("->"));
                bits.push(Style::default().paint(" "));

                if let Some(parent) = target.path.parent() {
                    let coconut = parent.components().count();

                    if coconut == 1 && parent.has_root() {
                        bits.push(colours.symlink_path.paint("/"));
                    }
                    else if coconut >= 1 {
                        bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                        bits.push(colours.symlink_path.paint("/"));
                    }
                }

                if !target.name.is_empty() {
                    bits.push(file_colour(colours, &target).paint(target.name));
                }
            },

            FileTarget::Broken(broken_path) => {
                bits.push(Style::default().paint(" "));
                bits.push(colours.broken_arrow.paint("->"));
                bits.push(Style::default().paint(" "));
                bits.push(colours.broken_filename.paint(broken_path.display().to_string()));
            },

            FileTarget::Err(_) => {
                // Do nothing -- the error gets displayed on the next line
            }
        }
    } else if classify {
        if file.is_executable_file() {
            bits.push(Style::default().paint("*"));
        } else if file.is_directory() {
            bits.push(Style::default().paint("/"));
        } else if file.is_pipe() {
            bits.push(Style::default().paint("|"));
        } else if file.is_link() {
            bits.push(Style::default().paint("@"));
        } else if file.is_socket() {
            bits.push(Style::default().paint("="));
        }
    }

    bits.into()
}

pub fn file_colour(colours: &Colours, file: &File) -> Style {
    match file {
        f if f.is_directory()        => colours.filetypes.directory,
        f if f.is_executable_file()  => colours.filetypes.executable,
        f if f.is_link()             => colours.filetypes.symlink,
        f if f.is_pipe()             => colours.filetypes.pipe,
        f if f.is_char_device()
           | f.is_block_device()     => colours.filetypes.device,
        f if f.is_socket()           => colours.filetypes.socket,
        f if !f.is_file()            => colours.filetypes.special,
        f if f.is_immediate()        => colours.filetypes.immediate,
        f if f.is_image()            => colours.filetypes.image,
        f if f.is_video()            => colours.filetypes.video,
        f if f.is_music()            => colours.filetypes.music,
        f if f.is_lossless()         => colours.filetypes.lossless,
        f if f.is_crypto()           => colours.filetypes.crypto,
        f if f.is_document()         => colours.filetypes.document,
        f if f.is_compressed()       => colours.filetypes.compressed,
        f if f.is_temp()             => colours.filetypes.temp,
        f if f.is_compiled()         => colours.filetypes.compiled,
        _                            => colours.filetypes.normal,
    }
}
<commit_msg>Move “coloured file name” into its own function<commit_after>use ansi_term::{ANSIString, Style};

use fs::{File, FileTarget};

pub use self::cell::{TextCell, TextCellContents, DisplayWidth};
pub use self::colours::Colours;
pub use self::details::Details;
pub use self::grid_details::GridDetails;
pub use self::grid::Grid;
pub use self::lines::Lines;

mod grid;
pub mod details;
mod lines;
mod grid_details;
pub mod column;
mod cell;
mod colours;
mod tree;

pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents {
    let mut bits = Vec::new();

    // TODO: This long function could do with some splitting up.

    if file.dir.is_none() {
        if let Some(parent) = file.path.parent() {
            let coconut = parent.components().count();

            if coconut == 1 && parent.has_root() {
                bits.push(colours.symlink_path.paint("/"));
            }
            else if coconut >= 1 {
                bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                bits.push(colours.symlink_path.paint("/"));
            }
        }
    }

    if !file.name.is_empty() {
        bits.push(coloured_file_name(file, colours));
    }

    if links && file.is_link() {
        match file.link_target() {
            FileTarget::Ok(target) => {
                bits.push(Style::default().paint(" "));
                bits.push(colours.punctuation.paint("->"));
                bits.push(Style::default().paint(" "));

                if let Some(parent) = target.path.parent() {
                    let coconut = parent.components().count();

                    if coconut == 1 && parent.has_root() {
                        bits.push(colours.symlink_path.paint("/"));
                    }
                    else if coconut >= 1 {
                        bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
                        bits.push(colours.symlink_path.paint("/"));
                    }
                }

                if !target.name.is_empty() {
                    bits.push(file_colour(colours, &target).paint(target.name));
                }
            },

            FileTarget::Broken(broken_path) => {
                bits.push(Style::default().paint(" "));
                bits.push(colours.broken_arrow.paint("->"));
                bits.push(Style::default().paint(" "));
                bits.push(colours.broken_filename.paint(broken_path.display().to_string()));
            },

            FileTarget::Err(_) => {
                // Do nothing -- the error gets displayed on the next line
            }
        }
    } else if classify {
        if file.is_executable_file() {
            bits.push(Style::default().paint("*"));
        } else if file.is_directory() {
            bits.push(Style::default().paint("/"));
        } else if file.is_pipe() {
            bits.push(Style::default().paint("|"));
        } else if file.is_link() {
            bits.push(Style::default().paint("@"));
        } else if file.is_socket() {
            bits.push(Style::default().paint("="));
        }
    }

    bits.into()
}

fn coloured_file_name<'a>(file: &File, colours: &Colours) -> ANSIString<'a> {
    file_colour(colours, file).paint(file.name.clone())
}

pub fn file_colour(colours: &Colours, file: &File) -> Style {
    match file {
        f if f.is_directory()        => colours.filetypes.directory,
        f if f.is_executable_file()  => colours.filetypes.executable,
        f if f.is_link()             => colours.filetypes.symlink,
        f if f.is_pipe()             => colours.filetypes.pipe,
        f if f.is_char_device()
           | f.is_block_device()     => colours.filetypes.device,
        f if f.is_socket()           => colours.filetypes.socket,
        f if !f.is_file()            => colours.filetypes.special,
        f if f.is_immediate()        => colours.filetypes.immediate,
        f if f.is_image()            => colours.filetypes.image,
        f if f.is_video()            => colours.filetypes.video,
        f if f.is_music()            => colours.filetypes.music,
        f if f.is_lossless()         => colours.filetypes.lossless,
        f if f.is_crypto()           => colours.filetypes.crypto,
        f if f.is_document()         => colours.filetypes.document,
        f if f.is_compressed()       => colours.filetypes.compressed,
        f if f.is_temp()             => colours.filetypes.temp,
        f if f.is_compiled()         => colours.filetypes.compiled,
        _                            => colours.filetypes.normal,
    }
}
<|endoftext|>


<commit_before>//! Parser module is responsible for parsing FITS files.

named!(fits<&[u8], (Vec<&[u8]>, Vec<&[u8]>) >,
       pair!(primary_header, many0!( take!(2880) )));

named!(primary_header<&[u8], Vec<&[u8]> >, many_m_n!(2, 2, take!(2880)));

named!(end_header<&[u8],(&[u8], Vec<&[u8]>)>, pair!(tag!("END"), count!(tag!(" "), 77)));

named!(blank_header<&[u8],Vec<&[u8]> >, count!(tag!(" "), 80));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::{fits, end_header, blank_header};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, (header, blocks)) => {
                assert_eq!(header.len(), 2);
                assert_eq!(blocks.len(), 3675);
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_header_should_parse_an_END_header(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_header(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_header_should_parse_a_BLANK_header(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_header(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<commit_msg>Change many_m_n into count<commit_after>//! Parser module is responsible for parsing FITS files.

named!(fits<&[u8], (Vec<&[u8]>, Vec<&[u8]>) >,
       pair!(primary_header, many0!( take!(2880) )));

named!(primary_header<&[u8], Vec<&[u8]> >, count!(take!(2880), 2));

named!(end_header<&[u8],(&[u8], Vec<&[u8]>)>, pair!(tag!("END"), count!(tag!(" "), 77)));

named!(blank_header<&[u8],Vec<&[u8]> >, count!(tag!(" "), 80));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::{fits, end_header, blank_header};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, (header, blocks)) => {
                assert_eq!(header.len(), 2);
                assert_eq!(blocks.len(), 3675);
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_header_should_parse_an_END_header(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_header(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_header_should_parse_a_BLANK_header(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_header(data);

        match result {
            IResult::Done(_,_) => assert!(true),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<|endoftext|>


<commit_before>macro_rules! push {
    ($s:ident, $t:expr) => {{
        // println!("parser push: {:?}", $t);
        Ok($s.vm.borrow_mut().alloc($t))
    }}
}

mod error;

use std::rc::Rc;
use std::cell::RefCell;
use core::*;
use lexer::*;
use self::error::Error;

const UNIT_INDX: usize = 5;

pub struct Parser<'a> {
    lexer: Lexer<'a, 'a>,
    vm: Rc<RefCell<Heliqs>>,
    // state: State,
    current_token: RefCell<CurrentToken>,
    // aexp_tokens: Vec<Vec<Tokn>>,
}

#[derive(Eq, PartialEq, Clone, Debug)]
enum CurrentToken {
    SOT, // Start Of Tokens
    Has(Tokn),
    EOT, // End Of Tokens
}

impl<'a> Parser<'a> {
    pub fn new(lexer: Lexer<'a, 'a>, vm: Rc<RefCell<Heliqs>>) -> Self {
        Parser {
            lexer: lexer,
            vm: vm,
            // state: State::Aexp,
            current_token: RefCell::new(CurrentToken::SOT),
            // aexp_tokens: vec![vec![]],
        }
    }

    // Unitは常に1つにする(index=0)
    fn add_unit(&mut self) {
        let _unit = self.vm.borrow_mut().alloc(Epiq::Unit);
    }

    fn add_prim(&mut self, name: &str) {
        let prim = self.vm.borrow_mut().alloc(Epiq::Prim(name.to_string()));
        self.vm.borrow_mut().define(name, prim);
    }

    pub fn parse(&mut self) {

        self.add_prim("decr");
        self.add_prim("ltoreq");
        self.add_prim("eq");
        self.add_prim("plus");
        self.add_prim("minus");

        self.add_unit();

        self.consume_token();

        let _ = self.parse_aexp();
    }

    fn consume_token(&mut self) {
        let res = self.lexer.tokenize();
        match res {
            TokenizeResult::Ok(t) => {
                let mut token = self.current_token.borrow_mut();
                *token = CurrentToken::Has(t);
            },
            TokenizeResult::Err(_e) => {},
            TokenizeResult::EOF => {
                let mut token = self.current_token.borrow_mut();
                *token = CurrentToken::EOT;
            },
        }
    }

    fn parse_aexp(&mut self) -> Result<usize, Error> {
        let res = self.current_token.borrow().clone();

        match res {
            CurrentToken::Has(Tokn::Atsm) => {
                self.consume_token();
                self.parse_resolve()
            },

            CurrentToken::Has(Tokn::Sgqt) => {
                self.consume_token();
                self.parse_otag(Tokn::Sgqt)
            },

            CurrentToken::Has(Tokn::Pipe) => {
                self.consume_token();
                self.parse_otag(Tokn::Pipe)
            },
            CurrentToken::Has(Tokn::Crrt) => {
                self.consume_token();
                self.parse_otag(Tokn::Crrt)
            },
            CurrentToken::Has(Tokn::Smcl) => {
                self.consume_token();
                // push!(self, Epiq::Unit)
                Ok(5)
            }
            CurrentToken::Has(Tokn::Lbkt) => {
                self.consume_token();
                self.parse_list()
            },
            CurrentToken::Has(Tokn::Dbqt) => {
                self.consume_token();
                self.parse_text()
            },
            _ => self.parse_literal(),
        }
    }

    fn parse_resolve(&mut self) -> Result<usize, Error> {
        // let pidx = (self.parse_aexp())?;
        let qidx = (self.parse_aexp())?;
        let id = self.vm.borrow_mut().alloc(Epiq::Rslv(UNIT_INDX, qidx));
        push!(self, Epiq::Eval(UNIT_INDX, id))
    }

    // Pipe QTag Pval QVal
    fn parse_otag(&mut self, tokn: Tokn) -> Result<usize, Error> {
        let current_token = self.current_token.borrow().clone();
        match current_token {
            CurrentToken::Has(Tokn::Otag(ref otag)) => {
                self.consume_token();

                match tokn {
                    Tokn::Pipe => {
                        let pidx = (self.parse_aexp())?;
                        let qidx = (self.parse_aexp())?;
                        self.match_otag(pidx, qidx, otag)
                    },
                    Tokn::Crrt => {
                        match otag.as_ref() {
                            // ^Tと^Fは特別扱い
                            "T" => push!(self, Epiq::Tval),
                            "F" => push!(self, Epiq::Fval),
                            _ =>  {
                                // 現在は^Tか^Fしか認めていないが、将来のため
                                let pidx = (self.parse_aexp())?;
                                let qidx = (self.parse_aexp())?;
                                push!(self, Epiq::Mpiq{o: otag.clone(), p: pidx, q: qidx})
                            },
                        }
                    },
                    Tokn::Sgqt => {
                        // 引数は一つ、それをqとみなす
                        let qidx = (self.parse_aexp())?;
                        self.match_otag(UNIT_INDX, qidx, otag)
                    },
                    _ => Err(Error::UnknownError(255)),
                }
            },

            CurrentToken::Has(ref t) => Err(Error::TokenError(t.clone())),
            _ => Err(Error::UnknownError(1)),
        }
    }

    fn match_otag(&mut self, pidx: NodeId, qidx: NodeId, otag: &str) -> Result<usize, Error> {
        match otag {
            ">" => push!(self, Epiq::Eval(pidx, qidx)),
            ":" => push!(self, Epiq::Lpiq(pidx, qidx)),
            "!" => push!(self, Epiq::Appl(pidx, qidx)),
            "@" => push!(self, Epiq::Rslv(pidx, qidx)),
            "?" => push!(self, Epiq::Cond(pidx, qidx)),
            "%" => push!(self, Epiq::Envn(pidx, qidx)),
            "#" => push!(self, Epiq::Bind(pidx, qidx)),
            "." => push!(self, Epiq::Accs(pidx, qidx)),
            r"\" => push!(self, Epiq::Lmbd(pidx, qidx)),

            _ => {
                push!(self, Epiq::Tpiq{o: otag.to_string(), p: pidx, q: qidx})
            },
        }
    }

    fn parse_list(&mut self) -> Result<usize, Error> {
        let current_token = self.current_token.borrow().clone();
        // 閉じbracketが出るまで再帰呼出
        match current_token {
            CurrentToken::Has(Tokn::Rbkt) => {
                self.consume_token();
                push!(self, Epiq::Unit)
            },
            _ => {
                let pidx = (self.parse_aexp())?;
                let qidx = (self.parse_list())?;
                push!(self, Epiq::Lpiq(pidx, qidx))
            }
        }
    }

    fn parse_text(&mut self) -> Result<usize, Error> {
        let current_token1 = self.current_token.borrow().clone();
        match current_token1 {
            CurrentToken::Has(Tokn::Chvc(ref s)) => {
                self.consume_token();
                let current_token2 = self.current_token.borrow().clone();
                match current_token2 {
                    CurrentToken::Has(Tokn::Dbqt) => {
                        self.consume_token();
                        push!(self, Epiq::Text(s.clone()))
                    },
                    _ => Err(Error::UnknownError(13)),
                }
            },
            _ => Err(Error::UnknownError(12)),
        }
    }

    fn parse_literal(&mut self) -> Result<usize, Error> {
        let current_token = self.current_token.borrow().clone();
        match current_token {
            CurrentToken::Has(Tokn::Chvc(ref s)) => {
                self.consume_token();
                push!(self, Epiq::Name(s.clone()))
            },
            CurrentToken::Has(Tokn::Nmbr(ref s)) => {
                self.consume_token();
                push!(self, Epiq::Uit8(s.parse::<i64>().unwrap()))
            },
            _ => Err(Error::UnknownError(10)),
        }
    }
}
<commit_msg>fix using magic number<commit_after>macro_rules! push {
    ($s:ident, $t:expr) => {{
        // println!("parser push: {:?}", $t);
        Ok($s.vm.borrow_mut().alloc($t))
    }}
}

mod error;

use std::rc::Rc;
use std::cell::RefCell;
use core::*;
use lexer::*;
use self::error::Error;

const UNIT_INDX: usize = 5;

pub struct Parser<'a> {
    lexer: Lexer<'a, 'a>,
    vm: Rc<RefCell<Heliqs>>,
    // state: State,
    current_token: RefCell<CurrentToken>,
    // aexp_tokens: Vec<Vec<Tokn>>,
}

#[derive(Eq, PartialEq, Clone, Debug)]
enum CurrentToken {
    SOT, // Start Of Tokens
    Has(Tokn),
    EOT, // End Of Tokens
}

impl<'a> Parser<'a> {
    pub fn new(lexer: Lexer<'a, 'a>, vm: Rc<RefCell<Heliqs>>) -> Self {
        Parser {
            lexer: lexer,
            vm: vm,
            // state: State::Aexp,
            current_token: RefCell::new(CurrentToken::SOT),
            // aexp_tokens: vec![vec![]],
        }
    }

    // Unitは常に1つにする(index=0)
    fn add_unit(&mut self) {
        let _unit = self.vm.borrow_mut().alloc(Epiq::Unit);
    }

    fn add_prim(&mut self, name: &str) {
        let prim = self.vm.borrow_mut().alloc(Epiq::Prim(name.to_string()));
        self.vm.borrow_mut().define(name, prim);
    }

    pub fn parse(&mut self) {

        self.add_prim("decr");
        self.add_prim("ltoreq");
        self.add_prim("eq");
        self.add_prim("plus");
        self.add_prim("minus");

        self.add_unit();

        self.consume_token();

        let _ = self.parse_aexp();
    }

    fn consume_token(&mut self) {
        let res = self.lexer.tokenize();
        match res {
            TokenizeResult::Ok(t) => {
                let mut token = self.current_token.borrow_mut();
                *token = CurrentToken::Has(t);
            },
            TokenizeResult::Err(_e) => {},
            TokenizeResult::EOF => {
                let mut token = self.current_token.borrow_mut();
                *token = CurrentToken::EOT;
            },
        }
    }

    fn parse_aexp(&mut self) -> Result<usize, Error> {
        let res = self.current_token.borrow().clone();

        match res {
            CurrentToken::Has(Tokn::Atsm) => {
                self.consume_token();
                self.parse_resolve()
            },

            CurrentToken::Has(Tokn::Sgqt) => {
                self.consume_token();
                self.parse_otag(Tokn::Sgqt)
            },

            CurrentToken::Has(Tokn::Pipe) => {
                self.consume_token();
                self.parse_otag(Tokn::Pipe)
            },
            CurrentToken::Has(Tokn::Crrt) => {
                self.consume_token();
                self.parse_otag(Tokn::Crrt)
            },
            CurrentToken::Has(Tokn::Smcl) => {
                self.consume_token();
                Ok(UNIT_INDX)
            }
            CurrentToken::Has(Tokn::Lbkt) => {
                self.consume_token();
                self.parse_list()
            },
            CurrentToken::Has(Tokn::Dbqt) => {
                self.consume_token();
                self.parse_text()
            },
            _ => self.parse_literal(),
        }
    }

    fn parse_resolve(&mut self) -> Result<usize, Error> {
        // let pidx = (self.parse_aexp())?;
        let qidx = (self.parse_aexp())?;
        let id = self.vm.borrow_mut().alloc(Epiq::Rslv(UNIT_INDX, qidx));
        push!(self, Epiq::Eval(UNIT_INDX, id))
    }

    // Pipe QTag Pval QVal
    fn parse_otag(&mut self, tokn: Tokn) -> Result<usize, Error> {
        let current_token = self.current_token.borrow().clone();
        match current_token {
            CurrentToken::Has(Tokn::Otag(ref otag)) => {
                self.consume_token();

                match tokn {
                    Tokn::Pipe => {
                        let pidx = (self.parse_aexp())?;
                        let qidx = (self.parse_aexp())?;
                        self.match_otag(pidx, qidx, otag)
                    },
                    Tokn::Crrt => {
                        match otag.as_ref() {
                            // ^Tと^Fは特別扱い
                            "T" => push!(self, Epiq::Tval),
                            "F" => push!(self, Epiq::Fval),
                            _ =>  {
                                // 現在は^Tか^Fしか認めていないが、将来のため
                                let pidx = (self.parse_aexp())?;
                                let qidx = (self.parse_aexp())?;
                                push!(self, Epiq::Mpiq{o: otag.clone(), p: pidx, q: qidx})
                            },
                        }
                    },
                    Tokn::Sgqt => {
                        // 引数は一つ、それをqとみなす
                        let qidx = (self.parse_aexp())?;
                        self.match_otag(UNIT_INDX, qidx, otag)
                    },
                    _ => Err(Error::UnknownError(255)),
                }
            },

            CurrentToken::Has(ref t) => Err(Error::TokenError(t.clone())),
            _ => Err(Error::UnknownError(1)),
        }
    }

    fn match_otag(&mut self, pidx: NodeId, qidx: NodeId, otag: &str) -> Result<usize, Error> {
        match otag {
            ">" => push!(self, Epiq::Eval(pidx, qidx)),
            ":" => push!(self, Epiq::Lpiq(pidx, qidx)),
            "!" => push!(self, Epiq::Appl(pidx, qidx)),
            "@" => push!(self, Epiq::Rslv(pidx, qidx)),
            "?" => push!(self, Epiq::Cond(pidx, qidx)),
            "%" => push!(self, Epiq::Envn(pidx, qidx)),
            "#" => push!(self, Epiq::Bind(pidx, qidx)),
            "." => push!(self, Epiq::Accs(pidx, qidx)),
            r"\" => push!(self, Epiq::Lmbd(pidx, qidx)),

            _ => {
                push!(self, Epiq::Tpiq{o: otag.to_string(), p: pidx, q: qidx})
            },
        }
    }

    fn parse_list(&mut self) -> Result<usize, Error> {
        let current_token = self.current_token.borrow().clone();
        // 閉じbracketが出るまで再帰呼出
        match current_token {
            CurrentToken::Has(Tokn::Rbkt) => {
                self.consume_token();
                push!(self, Epiq::Unit)
            },
            _ => {
                let pidx = (self.parse_aexp())?;
                let qidx = (self.parse_list())?;
                push!(self, Epiq::Lpiq(pidx, qidx))
            }
        }
    }

    fn parse_text(&mut self) -> Result<usize, Error> {
        let current_token1 = self.current_token.borrow().clone();
        match current_token1 {
            CurrentToken::Has(Tokn::Chvc(ref s)) => {
                self.consume_token();
                let current_token2 = self.current_token.borrow().clone();
                match current_token2 {
                    CurrentToken::Has(Tokn::Dbqt) => {
                        self.consume_token();
                        push!(self, Epiq::Text(s.clone()))
                    },
                    _ => Err(Error::UnknownError(13)),
                }
            },
            _ => Err(Error::UnknownError(12)),
        }
    }

    fn parse_literal(&mut self) -> Result<usize, Error> {
        let current_token = self.current_token.borrow().clone();
        match current_token {
            CurrentToken::Has(Tokn::Chvc(ref s)) => {
                self.consume_token();
                push!(self, Epiq::Name(s.clone()))
            },
            CurrentToken::Has(Tokn::Nmbr(ref s)) => {
                self.consume_token();
                push!(self, Epiq::Uit8(s.parse::<i64>().unwrap()))
            },
            _ => Err(Error::UnknownError(10)),
        }
    }
}
<|endoftext|>


<commit_before>//! A collection of methods that modify the background of the outputs.
use super::super::{Container, ContainerType, LayoutTree, TreeError};
use super::super::commands::CommandResult;

use uuid::Uuid;
use rustwlc::{WlcView};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BackgroundErr {
    /// A background (the `WlcView`) is already attached to the output (`UUID`)
    AlreadyAttached(Uuid, WlcView)
}

impl LayoutTree {
    /// Attempts to attach the `bg` to the `outputs`.
    ///
    /// If any of them already have a background attached,
    /// ``
    pub fn attach_background(&mut self, bg: WlcView, outputs: &[Uuid])
                             -> CommandResult {
        for output_id in outputs {
            match *try!(self.lookup_mut(*output_id)) {
                Container::Output { ref mut background, .. } => {
                    if background.is_none() {
                        *background = Some(bg);
                    } else {
                        error!("HERE");
                        error!("{:?}", background.clone());
                        return Err(TreeError::Background(
                            BackgroundErr::AlreadyAttached(*output_id, background.clone().unwrap())))
                    }
                },
                _ => return Err(TreeError::UuidWrongType(*output_id,
                                                         vec![ContainerType::Output]))
            }
        }
        Ok(())
    }
}
<commit_msg>Added hack/fix for wlc bug<commit_after>//! A collection of methods that modify the background of the outputs.
use super::super::{Container, ContainerType, LayoutTree, TreeError};
use super::super::commands::CommandResult;

use uuid::Uuid;
use rustwlc::{WlcView};

use std::collections::HashSet;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BackgroundErr {
    /// A background (the `WlcView`) is already attached to the output (`UUID`)
    AlreadyAttached(Uuid, WlcView)
}

impl LayoutTree {
    /// Attempts to attach the `bg` to the `outputs`.
    ///
    /// If any of them already have a background attached,
    /// ``
    pub fn attach_background(&mut self, bg: WlcView, outputs: &[Uuid])
                             -> CommandResult {
        // TODO Remove this to remove when the wlc bug is fixed
        // https://github.com/Cloudef/wlc/issues/221
        let mut to_remove = HashSet::with_capacity(outputs.len());
        for output_id in outputs {
            match *try!(self.lookup_mut(*output_id)) {
                Container::Output { ref mut background, .. } => {
                    if background.is_none() {
                        *background = Some(bg);
                    } else {
                        // TODO This can't be used right now, see this bug:
                        // https://github.com/Cloudef/wlc/issues/221
                        /*return Err(TreeError::Background(
                            BackgroundErr::AlreadyAttached(*output_id, background.clone().unwrap())))*/
                        if let Some(view) = background.take() {
                            to_remove.insert(view);
                        }
                        *background = Some(bg);
                    }
                },
                _ => return Err(TreeError::UuidWrongType(*output_id,
                                                         vec![ContainerType::Output]))
            }
        }
        for view in to_remove {
            view.close();
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ptr::NonNull;

use jit::{cached_code, JitState};
use page::Page;
use state_flags::CachedStateFlags;

static mut MODULE_PTR: NonNull<JitState> =
    unsafe { NonNull::new_unchecked(mem::align_of::<JitState>() as *mut _) };

fn get_module<'a>() -> &'a mut JitState { unsafe { MODULE_PTR.as_mut() } }

#[no_mangle]
/// Called from JS, not C
pub fn rust_setup() {
    let x = Box::new(JitState::create_and_initialise());
    unsafe {
        MODULE_PTR = NonNull::new(Box::into_raw(x)).expect("assigning module ptr");
    }

    use std::panic;

    panic::set_hook(Box::new(|panic_info| {
        if let Some(location) = panic_info.location() {
            console_log!(
                "panic occurred in file '{}' at line {}",
                location.file(),
                location.line()
            );
        }
        else {
            console_log!("panic occurred but can't get location information...");
        }
    }));
}

#[no_mangle]
pub fn jit_find_cache_entry(phys_address: u32, state_flags: u32) -> u32 {
    let cached_code {
        wasm_table_index,
        initial_state,
    } = ::jit::jit_find_cache_entry(phys_address, CachedStateFlags::of_u32(state_flags));
    wasm_table_index as u32 | (initial_state as u32) << 16
}

#[no_mangle]
/// Called from JS, not C
pub fn codegen_finalize_finished(
    wasm_table_index: u16,
    phys_addr: u32,
    end_addr: u32,
    first_opcode: u32,
    state_flags: u32,
) {
    ::jit::codegen_finalize_finished(
        get_module(),
        wasm_table_index,
        phys_addr,
        end_addr,
        first_opcode,
        CachedStateFlags::of_u32(state_flags),
    )
}

#[no_mangle]
pub fn jit_increase_hotness_and_maybe_compile(phys_address: u32, cs_offset: u32, state_flags: u32) {
    ::jit::jit_increase_hotness_and_maybe_compile(
        get_module(),
        phys_address,
        cs_offset,
        CachedStateFlags::of_u32(state_flags),
    )
}

#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_force_generate_unsafe(phys_addr: u32, cs_offset: u32, state_flags: u32) {
    ::jit::jit_force_generate_unsafe(
        get_module(),
        phys_addr,
        cs_offset,
        CachedStateFlags::of_u32(state_flags),
    )
}

#[no_mangle]
pub fn jit_dirty_cache(start_addr: u32, end_addr: u32) {
    ::jit::jit_dirty_cache(get_module(), start_addr, end_addr);
}

#[no_mangle]
pub fn jit_dirty_cache_small(start_addr: u32, end_addr: u32) {
    ::jit::jit_dirty_cache_small(get_module(), start_addr, end_addr);
}

#[no_mangle]
pub fn jit_dirty_cache_single(addr: u32) { ::jit::jit_dirty_cache_single(get_module(), addr); }

#[no_mangle]
pub fn jit_page_has_code(page: u32) -> bool {
    ::jit::jit_page_has_code(get_module(), Page::page_of(page << 12))
}

#[no_mangle]
/// Called from JS, not C
pub fn jit_empty_cache() { ::jit::jit_empty_cache(get_module()) }

#[no_mangle]
/// Called from JS, not C
pub fn jit_get_op_ptr() -> *const u8 { ::jit::jit_get_op_ptr(get_module()) }

#[no_mangle]
/// Called from JS, not C
pub fn jit_get_op_len() -> u32 { ::jit::jit_get_op_len(get_module()) }

#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_unused_cache_stat() -> u32 { ::jit::jit_unused_cache_stat() }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_entry_length(i: u32) -> u32 { ::jit::jit_get_entry_length(i) }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_entry_address(i: u32) -> u32 { ::jit::jit_get_entry_address(i) }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_entry_pending(i: u32) -> bool { ::jit::jit_get_entry_pending(i) }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_wasm_table_index_free_list_count() -> u32 {
    ::jit::jit_get_wasm_table_index_free_list_count(get_module())
}

#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_unused_cache_stat() -> u32 { 0 }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_entry_length(_: u32) -> u32 { 0 }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_entry_address(_: u32) -> u32 { 0 }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_entry_pending(_: u32) -> bool { false }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_wasm_table_index_free_list_count() -> u32 { 0 }
<commit_msg>Improve hook handler: Print more information<commit_after>use std::mem;
use std::ptr::NonNull;

use jit::{cached_code, JitState};
use page::Page;
use state_flags::CachedStateFlags;

static mut MODULE_PTR: NonNull<JitState> =
    unsafe { NonNull::new_unchecked(mem::align_of::<JitState>() as *mut _) };

fn get_module<'a>() -> &'a mut JitState { unsafe { MODULE_PTR.as_mut() } }

#[no_mangle]
/// Called from JS, not C
pub fn rust_setup() {
    let x = Box::new(JitState::create_and_initialise());
    unsafe {
        MODULE_PTR = NonNull::new(Box::into_raw(x)).expect("assigning module ptr");
    }

    use std::panic;

    panic::set_hook(Box::new(|panic_info| {
        console_log!("{}", panic_info.to_string());
    }));
}

#[no_mangle]
pub fn jit_find_cache_entry(phys_address: u32, state_flags: u32) -> u32 {
    let cached_code {
        wasm_table_index,
        initial_state,
    } = ::jit::jit_find_cache_entry(phys_address, CachedStateFlags::of_u32(state_flags));
    wasm_table_index as u32 | (initial_state as u32) << 16
}

#[no_mangle]
/// Called from JS, not C
pub fn codegen_finalize_finished(
    wasm_table_index: u16,
    phys_addr: u32,
    end_addr: u32,
    first_opcode: u32,
    state_flags: u32,
) {
    ::jit::codegen_finalize_finished(
        get_module(),
        wasm_table_index,
        phys_addr,
        end_addr,
        first_opcode,
        CachedStateFlags::of_u32(state_flags),
    )
}

#[no_mangle]
pub fn jit_increase_hotness_and_maybe_compile(phys_address: u32, cs_offset: u32, state_flags: u32) {
    ::jit::jit_increase_hotness_and_maybe_compile(
        get_module(),
        phys_address,
        cs_offset,
        CachedStateFlags::of_u32(state_flags),
    )
}

#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_force_generate_unsafe(phys_addr: u32, cs_offset: u32, state_flags: u32) {
    ::jit::jit_force_generate_unsafe(
        get_module(),
        phys_addr,
        cs_offset,
        CachedStateFlags::of_u32(state_flags),
    )
}

#[no_mangle]
pub fn jit_dirty_cache(start_addr: u32, end_addr: u32) {
    ::jit::jit_dirty_cache(get_module(), start_addr, end_addr);
}

#[no_mangle]
pub fn jit_dirty_cache_small(start_addr: u32, end_addr: u32) {
    ::jit::jit_dirty_cache_small(get_module(), start_addr, end_addr);
}

#[no_mangle]
pub fn jit_dirty_cache_single(addr: u32) { ::jit::jit_dirty_cache_single(get_module(), addr); }

#[no_mangle]
pub fn jit_page_has_code(page: u32) -> bool {
    ::jit::jit_page_has_code(get_module(), Page::page_of(page << 12))
}

#[no_mangle]
/// Called from JS, not C
pub fn jit_empty_cache() { ::jit::jit_empty_cache(get_module()) }

#[no_mangle]
/// Called from JS, not C
pub fn jit_get_op_ptr() -> *const u8 { ::jit::jit_get_op_ptr(get_module()) }

#[no_mangle]
/// Called from JS, not C
pub fn jit_get_op_len() -> u32 { ::jit::jit_get_op_len(get_module()) }

#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_unused_cache_stat() -> u32 { ::jit::jit_unused_cache_stat() }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_entry_length(i: u32) -> u32 { ::jit::jit_get_entry_length(i) }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_entry_address(i: u32) -> u32 { ::jit::jit_get_entry_address(i) }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_entry_pending(i: u32) -> bool { ::jit::jit_get_entry_pending(i) }
#[no_mangle]
#[cfg(debug_assertions)]
pub fn jit_get_wasm_table_index_free_list_count() -> u32 {
    ::jit::jit_get_wasm_table_index_free_list_count(get_module())
}

#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_unused_cache_stat() -> u32 { 0 }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_entry_length(_: u32) -> u32 { 0 }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_entry_address(_: u32) -> u32 { 0 }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_entry_pending(_: u32) -> bool { false }
#[no_mangle]
#[cfg(not(debug_assertions))]
pub fn jit_get_wasm_table_index_free_list_count() -> u32 { 0 }
<|endoftext|>


<commit_before>use super::{SeasonState, SeriesConfig};
use backend::{AnimeEntry, AnimeInfo, SyncBackend};
use error::SeriesError;
use input::{self, Answer};
use regex::Regex;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::ops::Range;
use std::path::{Path, PathBuf};
use toml;

pub struct FolderData {
    pub series_info: SeriesInfo,
    pub savefile: SaveData,
    pub path: PathBuf,
}

impl FolderData {
    pub fn load_dir(path: &Path) -> Result<FolderData, SeriesError> {
        let mut savefile = SaveData::from_dir(path)?;
        let series_info = FolderData::load_series_info(path, &mut savefile)?;

        Ok(FolderData {
            series_info,
            savefile,
            path: PathBuf::from(path),
        })
    }

    fn load_series_info(path: &Path, savefile: &mut SaveData) -> Result<SeriesInfo, SeriesError> {
        let mut ep_data = parse_episode_files_until_valid(path, &mut savefile.episode_matcher)?;

        if let Some(info) = SeriesInfo::select_from_save(&mut ep_data, savefile) {
            return Ok(info);
        }

        let info = prompt_select_series_info(ep_data)?;
        savefile.files_title = Some(info.name.clone());

        Ok(info)
    }

    pub fn populate_season_data<B>(&mut self, config: &SeriesConfig<B>) -> Result<(), SeriesError>
    where
        B: SyncBackend,
    {
        let num_seasons = self.seasons().len();

        if num_seasons > config.season_num {
            return Ok(());
        }

        for cur_season in num_seasons..=config.season_num {
            let info = AnimeInfo::default();
            let entry = AnimeEntry::new(info);

            let mut season = SeasonState {
                state: entry,
                needs_info: true,
                needs_sync: config.offline_mode,
            };

            season.sync_info_from_remote(config, &self, cur_season)?;

            self.seasons_mut().push(season);
        }

        Ok(())
    }

    pub fn calculate_season_offset(&self, mut range: Range<usize>) -> u32 {
        let num_seasons = self.savefile.season_states.len();
        range.start = num_seasons.min(range.start);
        range.end = num_seasons.min(range.end);

        let mut offset = 0;

        for i in range {
            let season = &self.savefile.season_states[i];

            match season.state.info.episodes {
                Some(eps) => offset += eps,
                None => return offset,
            }
        }

        offset
    }

    pub fn try_remove_dir(&self) {
        let path = self.path.to_string_lossy();

        println!("WARNING: {} will be deleted", path);
        println!("is this ok? (y/N)");

        match input::read_yn(Answer::No) {
            Ok(true) => match fs::remove_dir_all(&self.path) {
                Ok(_) => (),
                Err(err) => {
                    eprintln!("failed to remove directory: {}", err);
                }
            },
            Ok(false) => (),
            Err(err) => {
                eprintln!("failed to read input: {}", err);
            }
        }
    }

    pub fn get_episode(&self, episode: u32) -> Result<&PathBuf, SeriesError> {
        self.series_info.get_episode(episode)
    }

    pub fn save(&self) -> Result<(), SeriesError> {
        self.savefile.write_to_file()
    }

    pub fn seasons(&self) -> &Vec<SeasonState> {
        &self.savefile.season_states
    }

    pub fn seasons_mut(&mut self) -> &mut Vec<SeasonState> {
        &mut self.savefile.season_states
    }
}

#[derive(Serialize, Deserialize)]
pub struct SaveData {
    pub episode_matcher: Option<String>,
    pub files_title: Option<String>,
    pub season_states: Vec<SeasonState>,
    #[serde(skip)]
    pub path: PathBuf,
}

impl SaveData {
    const DATA_FILE_NAME: &'static str = ".anup";

    pub fn new(path: PathBuf) -> SaveData {
        SaveData {
            episode_matcher: None,
            files_title: None,
            season_states: Vec::new(),
            path,
        }
    }

    pub fn from_dir(path: &Path) -> Result<SaveData, SeriesError> {
        let path = PathBuf::from(path).join(SaveData::DATA_FILE_NAME);

        if !path.exists() {
            return Ok(SaveData::new(path));
        }

        let file_contents = fs::read_to_string(&path)?;

        let mut save_data: SaveData = toml::from_str(&file_contents)?;
        save_data.path = path;

        Ok(save_data)
    }

    pub fn write_to_file(&self) -> Result<(), SeriesError> {
        let toml = toml::to_string(self)?;
        fs::write(&self.path, toml)?;

        Ok(())
    }
}

pub struct SeriesInfo {
    pub name: String,
    pub episodes: HashMap<u32, PathBuf>,
}

impl SeriesInfo {
    pub fn get_episode(&self, episode: u32) -> Result<&PathBuf, SeriesError> {
        self.episodes
            .get(&episode)
            .ok_or_else(|| SeriesError::EpisodeNotFound(episode))
    }

    pub fn select_from_save(
        ep_data: &mut SeriesEpisodes,
        savefile: &SaveData,
    ) -> Option<SeriesInfo> {
        if let Some(name) = &savefile.files_title {
            let entry = ep_data.remove_entry(name);

            if let Some((name, episodes)) = entry {
                return Some(SeriesInfo { name, episodes });
            }
        }

        None
    }
}

struct EpisodeFile {
    series_name: String,
    episode_num: u32,
}

impl EpisodeFile {
    fn parse(path: &Path, matcher: &Regex) -> Result<EpisodeFile, SeriesError> {
        // Replace certain characters with spaces since they can prevent proper series
        // identification or prevent it from being found on a sync backend
        let filename = path
            .file_name()
            .and_then(|path| path.to_str())
            .ok_or(SeriesError::UnableToGetFilename)?;

        let caps = matcher
            .captures(&filename)
            .ok_or(SeriesError::EpisodeRegexCaptureFailed)?;

        let series_name = {
            let raw_name = caps
                .name("name")
                .map(|c| c.as_str())
                .ok_or_else(|| SeriesError::UnknownRegexCapture("name".into()))?;

            raw_name
                .replace('.', " ")
                .replace(" -", ":") // Dashes typically represent a colon in file names
                .replace('_', " ")
                .trim()
                .to_string()
        };

        let episode = caps
            .name("episode")
            .ok_or_else(|| SeriesError::UnknownRegexCapture("episode".into()))
            .and_then(|cap| {
                cap.as_str()
                    .parse()
                    .map_err(SeriesError::EpisodeNumParseFailed)
            })?;

        Ok(EpisodeFile {
            series_name,
            episode_num: episode,
        })
    }
}

type EpisodePaths = HashMap<u32, PathBuf>;
type SeriesEpisodes = HashMap<String, EpisodePaths>;

pub fn prompt_select_series_info(info: SeriesEpisodes) -> Result<SeriesInfo, SeriesError> {
    if info.is_empty() {
        return Err(SeriesError::NoSeriesFound);
    }

    let mut info = info
        .into_iter()
        .map(|(name, eps)| SeriesInfo {
            name,
            episodes: eps,
        }).collect::<Vec<_>>();

    if info.len() == 1 {
        return Ok(info.remove(0));
    }

    println!("multiple series found in directory");
    println!("please enter the number next to the episode files you want to use:");

    for (i, series) in info.iter().enumerate() {
        println!("{} [{}]", 1 + i, series.name);
    }

    let index = input::read_range(1, info.len())? - 1;
    let series = info.remove(index);

    Ok(series)
}

// This default pattern will match episodes in several common formats, such as:
// [Group] Series Name - 01.mkv
// [Group]_Series_Name_-_01.mkv
// [Group].Series.Name.-.01.mkv
// [Group] Series Name - 01 [tag 1][tag 2].mkv
// [Group]_Series_Name_-_01_[tag1][tag2].mkv
// [Group].Series.Name.-.01.[tag1][tag2].mkv
// Series Name - 01.mkv
// Series_Name_-_01.mkv
// Series.Name.-.01.mkv
const EP_FORMAT_REGEX: &str =
    r"(?:\[.+?\](?:\s+|_+|\.+))?(?P<name>.+?)(?:\s*|_*|\.*)-(?:\s*|_*|\.*)(?P<episode>\d+).*?\..+?";

fn format_episode_parser_regex<'a, S>(pattern: Option<S>) -> Result<Cow<'a, Regex>, SeriesError>
where
    S: AsRef<str>,
{
    lazy_static! {
        static ref EP_FORMAT: Regex = Regex::new(EP_FORMAT_REGEX).unwrap();
    }

    match pattern {
        Some(pattern) => {
            let pattern = pattern
                .as_ref()
                .replace("{name}", "(?P<name>.+?)")
                .replace("{episode}", r"(?P<episode>\d+)");

            let regex = Regex::new(&pattern)?;
            Ok(Cow::Owned(regex))
        }
        None => Ok(Cow::Borrowed(&*EP_FORMAT)),
    }
}

pub fn parse_episode_files<S>(
    path: &Path,
    pattern: Option<S>,
) -> Result<SeriesEpisodes, SeriesError>
where
    S: AsRef<str>,
{
    if !path.is_dir() {
        return Err(SeriesError::NotADirectory(path.to_string_lossy().into()));
    }

    let pattern = format_episode_parser_regex(pattern)?;
    let mut data = HashMap::new();

    for entry in fs::read_dir(path).map_err(SeriesError::Io)? {
        let entry = entry.map_err(SeriesError::Io)?.path();

        if !entry.is_file() {
            continue;
        }

        let episode = match EpisodeFile::parse(&entry, pattern.as_ref()) {
            Ok(episode) => episode,
            Err(SeriesError::EpisodeRegexCaptureFailed) => continue,
            Err(err) => return Err(err),
        };

        let series = data.entry(episode.series_name).or_insert_with(HashMap::new);
        series.insert(episode.episode_num, entry);
    }

    if data.is_empty() {
        return Err(SeriesError::NoSeriesFound);
    }

    Ok(data)
}

pub fn parse_episode_files_until_valid<S>(
    path: &Path,
    pattern: &mut Option<S>,
) -> Result<SeriesEpisodes, SeriesError>
where
    S: AsRef<str> + From<String>,
{
    loop {
        match parse_episode_files(path, pattern.as_ref()) {
            Ok(data) => break Ok(data),
            Err(SeriesError::NoSeriesFound) => {
                println!("no series found");
                println!("you will now be prompted to enter a custom regex pattern");
                println!("when entering the pattern, please mark the series name and episode number with {{name}} and {{episode}}, respectively");
                println!("example:");
                println!("  filename: [SubGroup] Series Name - Ep01.mkv");
                println!(r"  pattern: \[.+?\] {{name}} - Ep{{episode}}.mkv");
                println!("please enter your custom pattern:");

                *pattern = Some(input::read_line()?.into());
            }
            Err(err @ SeriesError::Regex(_)) | Err(err @ SeriesError::UnknownRegexCapture(_)) => {
                eprintln!("error parsing regex pattern: {}", err);
                println!("please try again:");

                *pattern = Some(input::read_line()?.into());
            }
            Err(err) => return Err(err),
        }
    }
}
<commit_msg>[series] Use swap_remove() instead of remove() in prompt_select_series_info()<commit_after>use super::{SeasonState, SeriesConfig};
use backend::{AnimeEntry, AnimeInfo, SyncBackend};
use error::SeriesError;
use input::{self, Answer};
use regex::Regex;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::ops::Range;
use std::path::{Path, PathBuf};
use toml;

pub struct FolderData {
    pub series_info: SeriesInfo,
    pub savefile: SaveData,
    pub path: PathBuf,
}

impl FolderData {
    pub fn load_dir(path: &Path) -> Result<FolderData, SeriesError> {
        let mut savefile = SaveData::from_dir(path)?;
        let series_info = FolderData::load_series_info(path, &mut savefile)?;

        Ok(FolderData {
            series_info,
            savefile,
            path: PathBuf::from(path),
        })
    }

    fn load_series_info(path: &Path, savefile: &mut SaveData) -> Result<SeriesInfo, SeriesError> {
        let mut ep_data = parse_episode_files_until_valid(path, &mut savefile.episode_matcher)?;

        if let Some(info) = SeriesInfo::select_from_save(&mut ep_data, savefile) {
            return Ok(info);
        }

        let info = prompt_select_series_info(ep_data)?;
        savefile.files_title = Some(info.name.clone());

        Ok(info)
    }

    pub fn populate_season_data<B>(&mut self, config: &SeriesConfig<B>) -> Result<(), SeriesError>
    where
        B: SyncBackend,
    {
        let num_seasons = self.seasons().len();

        if num_seasons > config.season_num {
            return Ok(());
        }

        for cur_season in num_seasons..=config.season_num {
            let info = AnimeInfo::default();
            let entry = AnimeEntry::new(info);

            let mut season = SeasonState {
                state: entry,
                needs_info: true,
                needs_sync: config.offline_mode,
            };

            season.sync_info_from_remote(config, &self, cur_season)?;

            self.seasons_mut().push(season);
        }

        Ok(())
    }

    pub fn calculate_season_offset(&self, mut range: Range<usize>) -> u32 {
        let num_seasons = self.savefile.season_states.len();
        range.start = num_seasons.min(range.start);
        range.end = num_seasons.min(range.end);

        let mut offset = 0;

        for i in range {
            let season = &self.savefile.season_states[i];

            match season.state.info.episodes {
                Some(eps) => offset += eps,
                None => return offset,
            }
        }

        offset
    }

    pub fn try_remove_dir(&self) {
        let path = self.path.to_string_lossy();

        println!("WARNING: {} will be deleted", path);
        println!("is this ok? (y/N)");

        match input::read_yn(Answer::No) {
            Ok(true) => match fs::remove_dir_all(&self.path) {
                Ok(_) => (),
                Err(err) => {
                    eprintln!("failed to remove directory: {}", err);
                }
            },
            Ok(false) => (),
            Err(err) => {
                eprintln!("failed to read input: {}", err);
            }
        }
    }

    pub fn get_episode(&self, episode: u32) -> Result<&PathBuf, SeriesError> {
        self.series_info.get_episode(episode)
    }

    pub fn save(&self) -> Result<(), SeriesError> {
        self.savefile.write_to_file()
    }

    pub fn seasons(&self) -> &Vec<SeasonState> {
        &self.savefile.season_states
    }

    pub fn seasons_mut(&mut self) -> &mut Vec<SeasonState> {
        &mut self.savefile.season_states
    }
}

#[derive(Serialize, Deserialize)]
pub struct SaveData {
    pub episode_matcher: Option<String>,
    pub files_title: Option<String>,
    pub season_states: Vec<SeasonState>,
    #[serde(skip)]
    pub path: PathBuf,
}

impl SaveData {
    const DATA_FILE_NAME: &'static str = ".anup";

    pub fn new(path: PathBuf) -> SaveData {
        SaveData {
            episode_matcher: None,
            files_title: None,
            season_states: Vec::new(),
            path,
        }
    }

    pub fn from_dir(path: &Path) -> Result<SaveData, SeriesError> {
        let path = PathBuf::from(path).join(SaveData::DATA_FILE_NAME);

        if !path.exists() {
            return Ok(SaveData::new(path));
        }

        let file_contents = fs::read_to_string(&path)?;

        let mut save_data: SaveData = toml::from_str(&file_contents)?;
        save_data.path = path;

        Ok(save_data)
    }

    pub fn write_to_file(&self) -> Result<(), SeriesError> {
        let toml = toml::to_string(self)?;
        fs::write(&self.path, toml)?;

        Ok(())
    }
}

pub struct SeriesInfo {
    pub name: String,
    pub episodes: HashMap<u32, PathBuf>,
}

impl SeriesInfo {
    pub fn get_episode(&self, episode: u32) -> Result<&PathBuf, SeriesError> {
        self.episodes
            .get(&episode)
            .ok_or_else(|| SeriesError::EpisodeNotFound(episode))
    }

    pub fn select_from_save(
        ep_data: &mut SeriesEpisodes,
        savefile: &SaveData,
    ) -> Option<SeriesInfo> {
        if let Some(name) = &savefile.files_title {
            let entry = ep_data.remove_entry(name);

            if let Some((name, episodes)) = entry {
                return Some(SeriesInfo { name, episodes });
            }
        }

        None
    }
}

struct EpisodeFile {
    series_name: String,
    episode_num: u32,
}

impl EpisodeFile {
    fn parse(path: &Path, matcher: &Regex) -> Result<EpisodeFile, SeriesError> {
        // Replace certain characters with spaces since they can prevent proper series
        // identification or prevent it from being found on a sync backend
        let filename = path
            .file_name()
            .and_then(|path| path.to_str())
            .ok_or(SeriesError::UnableToGetFilename)?;

        let caps = matcher
            .captures(&filename)
            .ok_or(SeriesError::EpisodeRegexCaptureFailed)?;

        let series_name = {
            let raw_name = caps
                .name("name")
                .map(|c| c.as_str())
                .ok_or_else(|| SeriesError::UnknownRegexCapture("name".into()))?;

            raw_name
                .replace('.', " ")
                .replace(" -", ":") // Dashes typically represent a colon in file names
                .replace('_', " ")
                .trim()
                .to_string()
        };

        let episode = caps
            .name("episode")
            .ok_or_else(|| SeriesError::UnknownRegexCapture("episode".into()))
            .and_then(|cap| {
                cap.as_str()
                    .parse()
                    .map_err(SeriesError::EpisodeNumParseFailed)
            })?;

        Ok(EpisodeFile {
            series_name,
            episode_num: episode,
        })
    }
}

type EpisodePaths = HashMap<u32, PathBuf>;
type SeriesEpisodes = HashMap<String, EpisodePaths>;

pub fn prompt_select_series_info(info: SeriesEpisodes) -> Result<SeriesInfo, SeriesError> {
    if info.is_empty() {
        return Err(SeriesError::NoSeriesFound);
    }

    let mut info = info
        .into_iter()
        .map(|(name, eps)| SeriesInfo {
            name,
            episodes: eps,
        }).collect::<Vec<_>>();

    if info.len() == 1 {
        return Ok(info.swap_remove(0));
    }

    println!("multiple series found in directory");
    println!("please enter the number next to the episode files you want to use:");

    for (i, series) in info.iter().enumerate() {
        println!("{} [{}]", 1 + i, series.name);
    }

    let index = input::read_range(1, info.len())? - 1;
    let series = info.swap_remove(index);

    Ok(series)
}

// This default pattern will match episodes in several common formats, such as:
// [Group] Series Name - 01.mkv
// [Group]_Series_Name_-_01.mkv
// [Group].Series.Name.-.01.mkv
// [Group] Series Name - 01 [tag 1][tag 2].mkv
// [Group]_Series_Name_-_01_[tag1][tag2].mkv
// [Group].Series.Name.-.01.[tag1][tag2].mkv
// Series Name - 01.mkv
// Series_Name_-_01.mkv
// Series.Name.-.01.mkv
const EP_FORMAT_REGEX: &str =
    r"(?:\[.+?\](?:\s+|_+|\.+))?(?P<name>.+?)(?:\s*|_*|\.*)-(?:\s*|_*|\.*)(?P<episode>\d+).*?\..+?";

fn format_episode_parser_regex<'a, S>(pattern: Option<S>) -> Result<Cow<'a, Regex>, SeriesError>
where
    S: AsRef<str>,
{
    lazy_static! {
        static ref EP_FORMAT: Regex = Regex::new(EP_FORMAT_REGEX).unwrap();
    }

    match pattern {
        Some(pattern) => {
            let pattern = pattern
                .as_ref()
                .replace("{name}", "(?P<name>.+?)")
                .replace("{episode}", r"(?P<episode>\d+)");

            let regex = Regex::new(&pattern)?;
            Ok(Cow::Owned(regex))
        }
        None => Ok(Cow::Borrowed(&*EP_FORMAT)),
    }
}

pub fn parse_episode_files<S>(
    path: &Path,
    pattern: Option<S>,
) -> Result<SeriesEpisodes, SeriesError>
where
    S: AsRef<str>,
{
    if !path.is_dir() {
        return Err(SeriesError::NotADirectory(path.to_string_lossy().into()));
    }

    let pattern = format_episode_parser_regex(pattern)?;
    let mut data = HashMap::new();

    for entry in fs::read_dir(path).map_err(SeriesError::Io)? {
        let entry = entry.map_err(SeriesError::Io)?.path();

        if !entry.is_file() {
            continue;
        }

        let episode = match EpisodeFile::parse(&entry, pattern.as_ref()) {
            Ok(episode) => episode,
            Err(SeriesError::EpisodeRegexCaptureFailed) => continue,
            Err(err) => return Err(err),
        };

        let series = data.entry(episode.series_name).or_insert_with(HashMap::new);
        series.insert(episode.episode_num, entry);
    }

    if data.is_empty() {
        return Err(SeriesError::NoSeriesFound);
    }

    Ok(data)
}

pub fn parse_episode_files_until_valid<S>(
    path: &Path,
    pattern: &mut Option<S>,
) -> Result<SeriesEpisodes, SeriesError>
where
    S: AsRef<str> + From<String>,
{
    loop {
        match parse_episode_files(path, pattern.as_ref()) {
            Ok(data) => break Ok(data),
            Err(SeriesError::NoSeriesFound) => {
                println!("no series found");
                println!("you will now be prompted to enter a custom regex pattern");
                println!("when entering the pattern, please mark the series name and episode number with {{name}} and {{episode}}, respectively");
                println!("example:");
                println!("  filename: [SubGroup] Series Name - Ep01.mkv");
                println!(r"  pattern: \[.+?\] {{name}} - Ep{{episode}}.mkv");
                println!("please enter your custom pattern:");

                *pattern = Some(input::read_line()?.into());
            }
            Err(err @ SeriesError::Regex(_)) | Err(err @ SeriesError::UnknownRegexCapture(_)) => {
                eprintln!("error parsing regex pattern: {}", err);
                println!("please try again:");

                *pattern = Some(input::read_line()?.into());
            }
            Err(err) => return Err(err),
        }
    }
}
<|endoftext|>


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

mod immix_space;
mod large_object_space;
mod collector;

use self::immix_space::ImmixSpace;
use self::large_object_space::LargeObjectSpace;
use self::collector::Collector;

use constants::LARGE_OBJECT;
use gc_object::{GCRTTI, GCObjectRef};
use stack::Stack;

/// The type of collection that will be performed.
pub enum CollectionType {
    /// A simple reference counting collection.
    RCCollection,

    /// A reference counting collection with proactive opportunistic
    /// evacuation.
    RCEvacCollection,

    /// A reference counting collection followed by the immix tracing (cycle)
    /// collection.
    ImmixCollection,

    /// A reference counting collection followed by the immix tracing (cycle)
    /// collection. Both with opportunistict evacuation.
    ImmixEvacCollection,
}

impl CollectionType {
    /// Returns if this `CollectionType` is an evacuating collection.
    pub fn is_evac(&self) -> bool {
        use self::CollectionType::{RCEvacCollection, ImmixEvacCollection};
        return match *self {
            RCEvacCollection | ImmixEvacCollection => true,
            _ => false,
        }
    }

    /// Returns if this `CollectionType` is a cycle collecting collection.
    pub fn is_immix(&self) -> bool {
        use self::CollectionType::{ImmixCollection, ImmixEvacCollection};
        return match *self {
            ImmixCollection | ImmixEvacCollection => true,
            _ => false,
        }
    }
}

/// The `Spaces` contains the different garbage collector spaces in which
/// objects can be allocated that are managed by some collector.
pub struct Spaces {
    /// The stack of the one execution thread.
    stack: Stack,

    /// The default immix space.
    immix_space: ImmixSpace,

    /// The large object space for objects greater than `LARGE_OBJECT`.
    large_object_space: LargeObjectSpace,

    /// The collectors.
    collector: Collector,

    /// The current live mark.
    ///
    /// During allocation of objects this value is used as the `mark` state of
    /// new objects. During allocation the value is negated and used to mark
    /// objects during the tracing mark phase. This way the newly allocated
    /// objects are always initialized with the last `mark` state with will be
    /// flipped if they are reached is the mark phase.
    current_live_mark: bool,
}

impl Spaces {
    /// Create a new `Spaces`.
    pub fn new() -> Spaces {
        return Spaces {
            stack: Stack::new(),
            immix_space: ImmixSpace::new(),
            large_object_space: LargeObjectSpace::new(),
            collector: Collector::new(),
            current_live_mark: false,
        };
    }

    /// Return if the given address is valid in any of the managed spaces.
    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        return self.immix_space.is_gc_object(object)
               || self.large_object_space.is_gc_object(object);
    }

    /// Set an address of an object reference as static root.
    pub fn set_static_root(&mut self, address: *const GCObjectRef) {
        self.stack.set_static_root(address);
    }

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

    /// Allocate a new object described by the `rtti` or returns `None` if
    /// there is no memory left to fullfill the allocation request.
    pub fn allocate(&mut self, rtti: *const GCRTTI) -> Option<GCObjectRef>{
        let size = unsafe{ (*rtti).object_size() };
        debug!("Request to allocate an object of size {}", size);
        return if size < LARGE_OBJECT { self.immix_space.allocate(rtti) }
               else { self.large_object_space.allocate(rtti) };
    }

    /// Collect the roots using `Stack::enumerate_roots()` and filter them for
    /// validity in `LargeObjectSpace` or `ImmixSpace`.
    fn collect_roots(&self) -> Vec<GCObjectRef> {
        let los_filter = self.large_object_space.is_gc_object_filter();
        let immix_filter = self.immix_space.is_gc_object_filter();
        return self.stack.enumerate_roots().into_iter()
                         .filter(|o| los_filter(*o) || immix_filter(*o))
                         .collect();
    }

    /// Trigger a garbage collection.
    ///
    /// This will always run the referece counting collector. If `evacuation`
    /// is set the collectors will try to evacuate. If `cycle_collect` is set
    /// the immix tracing collector will be used.
    pub fn collect(&mut self, evacuation: bool, cycle_collect: bool) {
        debug!("Requested collection (evacuation={}, cycle_collect={})",
               evacuation, cycle_collect);

        let roots = self.collect_roots();
        self.collector.extend_all_blocks(self.immix_space.get_all_blocks());

        for root in roots.iter().map(|o| *o) {
            unsafe{ (*root).set_pinned(true); }
        }

        let collection_type = self.collector.prepare_collection(evacuation,
                                cycle_collect,
                                self.immix_space.available_blocks(),
                                self.immix_space.evac_headroom());
        self.collector.collect(&collection_type, roots.as_ref(),
                               &mut self.immix_space,
                               &mut self.large_object_space,
                               !self.current_live_mark);
        self.collector.complete_collection(&collection_type, &mut self.immix_space,
                                           &mut self.large_object_space);

        for root in roots.iter() {
            unsafe{ (**root).set_pinned(false); }
        }
        if collection_type.is_immix() {
            self.current_live_mark = !self.current_live_mark;
            self.immix_space.set_current_live_mark(self.current_live_mark);
            self.large_object_space.set_current_live_mark(self.current_live_mark);

        }
        valgrind_assert_no_leaks!();
    }
}

<commit_msg>Spaces: unify root pinning<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

mod immix_space;
mod large_object_space;
mod collector;

use self::immix_space::ImmixSpace;
use self::large_object_space::LargeObjectSpace;
use self::collector::Collector;

use constants::LARGE_OBJECT;
use gc_object::{GCRTTI, GCObjectRef};
use stack::Stack;

/// The type of collection that will be performed.
pub enum CollectionType {
    /// A simple reference counting collection.
    RCCollection,

    /// A reference counting collection with proactive opportunistic
    /// evacuation.
    RCEvacCollection,

    /// A reference counting collection followed by the immix tracing (cycle)
    /// collection.
    ImmixCollection,

    /// A reference counting collection followed by the immix tracing (cycle)
    /// collection. Both with opportunistict evacuation.
    ImmixEvacCollection,
}

impl CollectionType {
    /// Returns if this `CollectionType` is an evacuating collection.
    pub fn is_evac(&self) -> bool {
        use self::CollectionType::{RCEvacCollection, ImmixEvacCollection};
        return match *self {
            RCEvacCollection | ImmixEvacCollection => true,
            _ => false,
        }
    }

    /// Returns if this `CollectionType` is a cycle collecting collection.
    pub fn is_immix(&self) -> bool {
        use self::CollectionType::{ImmixCollection, ImmixEvacCollection};
        return match *self {
            ImmixCollection | ImmixEvacCollection => true,
            _ => false,
        }
    }
}

/// The `Spaces` contains the different garbage collector spaces in which
/// objects can be allocated that are managed by some collector.
pub struct Spaces {
    /// The stack of the one execution thread.
    stack: Stack,

    /// The default immix space.
    immix_space: ImmixSpace,

    /// The large object space for objects greater than `LARGE_OBJECT`.
    large_object_space: LargeObjectSpace,

    /// The collectors.
    collector: Collector,

    /// The current live mark.
    ///
    /// During allocation of objects this value is used as the `mark` state of
    /// new objects. During allocation the value is negated and used to mark
    /// objects during the tracing mark phase. This way the newly allocated
    /// objects are always initialized with the last `mark` state with will be
    /// flipped if they are reached is the mark phase.
    current_live_mark: bool,
}

impl Spaces {
    /// Create a new `Spaces`.
    pub fn new() -> Spaces {
        return Spaces {
            stack: Stack::new(),
            immix_space: ImmixSpace::new(),
            large_object_space: LargeObjectSpace::new(),
            collector: Collector::new(),
            current_live_mark: false,
        };
    }

    /// Return if the given address is valid in any of the managed spaces.
    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        return self.immix_space.is_gc_object(object)
               || self.large_object_space.is_gc_object(object);
    }

    /// Set an address of an object reference as static root.
    pub fn set_static_root(&mut self, address: *const GCObjectRef) {
        self.stack.set_static_root(address);
    }

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

    /// Allocate a new object described by the `rtti` or returns `None` if
    /// there is no memory left to fullfill the allocation request.
    pub fn allocate(&mut self, rtti: *const GCRTTI) -> Option<GCObjectRef>{
        let size = unsafe{ (*rtti).object_size() };
        debug!("Request to allocate an object of size {}", size);
        return if size < LARGE_OBJECT { self.immix_space.allocate(rtti) }
               else { self.large_object_space.allocate(rtti) };
    }

    /// Collect the roots using `Stack::enumerate_roots()` and filter them for
    /// validity in `LargeObjectSpace` or `ImmixSpace`.
    fn collect_roots(&self) -> Vec<GCObjectRef> {
        let los_filter = self.large_object_space.is_gc_object_filter();
        let immix_filter = self.immix_space.is_gc_object_filter();
        return self.stack.enumerate_roots().into_iter()
                         .filter(|o| los_filter(*o) || immix_filter(*o))
                         .collect();
    }

    /// Trigger a garbage collection.
    ///
    /// This will always run the referece counting collector. If `evacuation`
    /// is set the collectors will try to evacuate. If `cycle_collect` is set
    /// the immix tracing collector will be used.
    pub fn collect(&mut self, evacuation: bool, cycle_collect: bool) {
        debug!("Requested collection (evacuation={}, cycle_collect={})",
               evacuation, cycle_collect);

        let roots = self.collect_roots();
        self.collector.extend_all_blocks(self.immix_space.get_all_blocks());

        for root in roots.iter().map(|o| *o) {
            unsafe{ (*root).set_pinned(true); }
        }

        let collection_type = self.collector.prepare_collection(evacuation,
                                cycle_collect,
                                self.immix_space.available_blocks(),
                                self.immix_space.evac_headroom());
        self.collector.collect(&collection_type, roots.as_ref(),
                               &mut self.immix_space,
                               &mut self.large_object_space,
                               !self.current_live_mark);
        self.collector.complete_collection(&collection_type, &mut self.immix_space,
                                           &mut self.large_object_space);

        for root in roots.iter().map(|o| *o) {
            unsafe{ (*root).set_pinned(false); }
        }

        if collection_type.is_immix() {
            self.current_live_mark = !self.current_live_mark;
            self.immix_space.set_current_live_mark(self.current_live_mark);
            self.large_object_space.set_current_live_mark(self.current_live_mark);

        }
        valgrind_assert_no_leaks!();
    }
}

<|endoftext|>