<commit_before>//! SRT-format subtitle support.

use std::io::File;
use std::num::Float;

use err::{SubStudyError, SubStudyResult};
use decode::smart_decode;

/// Format seconds using the standard SRT time format.
pub fn format_time(time: f32) -> String {
    let (h, rem) = ((time / 3600.0).trunc(), time % 3600.0);
    let (m, s) = ((rem / 60.0).trunc(), rem % 60.0);
    (format!("{:02f}:{:02f}:{:0>6.3f}", h, m, s)).replace(".", ",")
}

/// A single SRT-format subtitle, minus some of the optional fields used in
/// various versions of the file format.
#[deriving(Show, PartialEq, Clone)]
pub struct Subtitle {
    /// The index of this subtitle.  We should normalize these to start
    /// with 1 on output.
    pub index: uint,

    /// The start time of this subtitle, in seconds.
    pub begin: f32,

    /// The end time of this subtitle, in seconds.
    pub end: f32,

    /// The lines of text in this subtitle.
    pub lines: Vec<String>
}

impl Subtitle {
    /// Return a string representation of this subtitle.
    pub fn to_string(&self) -> String {
        format!("{}\n{} --> {}\n{}\n", self.index,
                format_time(self.begin),
                format_time(self.end),
                self.lines.connect("\n"))
    }
}

/// The contents of an SRT-format subtitle file.
#[deriving(Show, PartialEq)]
pub struct SubtitleFile {
    /// The subtitles in this file.
    pub subtitles: Vec<Subtitle>
}

impl SubtitleFile {
    /// Parse raw subtitle text into an appropriate structure.
    pub fn from_str(data: &str) -> SubStudyResult<SubtitleFile> {
        match grammar::subtitle_file(data) {
            Ok(file) => Ok(file),
            Err(msg) => Err(SubStudyError::Parse(msg))
        }
    }

    /// Parse the subtitle file found at the specified path.
    pub fn from_path(path: &Path) -> SubStudyResult<SubtitleFile> {
        let mut file = try!(File::open(path));
        let bytes = try!(file.read_to_end());
        let data = try!(smart_decode(bytes.as_slice()));
        SubtitleFile::from_str(data.as_slice())
    }

    /// Convert subtitles to a string.
    pub fn to_string(&self) -> String {
        let subs: Vec<String> =
            self.subtitles.iter().map(|s| s.to_string()).collect();
        // The BOM (byte-order mark) is generally discouraged on Linux, but
        // it's sometimes needed to get good results under Windows.  We
        // include it here because Wikipedia says that SRT files files
        // default to various legacy encoding, but that the BOM can be used
        // for Unicode.
        format!("\uFEFF{}", subs.connect("\n"))
    }
}

#[cfg(test)]
mod test {
    use srt::{SubtitleFile,Subtitle};

    #[test]
    fn subtitle_file_from_path() {
        let path = Path::new("fixtures/sample.es.srt");
        let srt = SubtitleFile::from_path(&path).unwrap();
        assert_eq!(5, srt.subtitles.len());

        let sub = &srt.subtitles[0];
        assert_eq!(16, sub.index);
        assert_eq!(62.328, sub.begin);
        assert_eq!(64.664, sub.end);
        assert_eq!(vec!("¡Si! ¡Aang ha vuelto!".to_string()), sub.lines);

        let sub2 = &srt.subtitles[2];
        assert_eq!(vec!("Tu diste la señal a la armada".to_string(),
                        "del fuego con la bengala,".to_string()),
                   sub2.lines);
    }

    #[test]
    fn subtitle_to_string() {
        let sub = Subtitle{index: 4, begin: 61.5, end: 63.75,
                           lines: vec!("Line 1".to_string(),
                                       "Line 2".to_string())};
        let expected = r"4
00:01:01,500 --> 00:01:03,750
Line 1
Line 2
".to_string();
        assert_eq!(expected, sub.to_string());
    }

    #[test]
    fn subtitle_file_to_string() {
        let data = "\uFEFF16
00:01:02,328 --> 00:01:04,664
Line 1.1

17
00:01:12,839 --> 00:01:13,839
Line 2.1
";
        let srt = SubtitleFile::from_str(data).unwrap();
        assert_eq!(data, srt.to_string().as_slice());
    }
}

// Our parser expression grammar.  We'd like to move this to another file.
peg! grammar(r#"
use srt::{Subtitle,SubtitleFile};

#[pub]
subtitle_file -> SubtitleFile
    = bom? blank_lines? result:subtitles blank_lines? {
        SubtitleFile{subtitles: result}
    }

subtitles -> Vec<Subtitle>
    = subs:subtitle ** blank_lines { subs }

subtitle -> Subtitle
    = index:digits newline t:times newline l:lines {
        let (b, e) = t;
        Subtitle{index: index, begin: b, end: e, lines: l}
    }

times -> (f32, f32)
    = begin:time " --> " end:time { (begin, end) }

time -> f32
    = hh:digits ":" mm:digits ":" ss:comma_float {
        (hh as f32)*3600.0 + (mm as f32)*60.0 + ss
    }

lines -> Vec<String>
    = lines:line ** newline { lines }

line -> String
    = [^\r\n]+ { match_str.to_string() }

digits -> uint
    = [0-9]+ { from_str::<uint>(match_str).unwrap() }

comma_float -> f32
    = [0-9]+ "," [0-9]+ {
        let fixed: String = match_str.replace(",", ".");
        from_str::<f32>(fixed.as_slice()).unwrap()
    }

bom
    = "\uFEFF"

newline
    = "\r"? "\n"

blank_lines
    = newline+

"#)
<commit_msg>Remove format specifier "f" which no longer works<commit_after>//! SRT-format subtitle support.

use std::io::File;
use std::num::Float;

use err::{SubStudyError, SubStudyResult};
use decode::smart_decode;

/// Format seconds using the standard SRT time format.
pub fn format_time(time: f32) -> String {
    let (h, rem) = ((time / 3600.0).trunc(), time % 3600.0);
    let (m, s) = ((rem / 60.0).trunc(), rem % 60.0);
    (format!("{:02}:{:02}:{:0>6.3}", h, m, s)).replace(".", ",")
}

/// A single SRT-format subtitle, minus some of the optional fields used in
/// various versions of the file format.
#[deriving(Show, PartialEq, Clone)]
pub struct Subtitle {
    /// The index of this subtitle.  We should normalize these to start
    /// with 1 on output.
    pub index: uint,

    /// The start time of this subtitle, in seconds.
    pub begin: f32,

    /// The end time of this subtitle, in seconds.
    pub end: f32,

    /// The lines of text in this subtitle.
    pub lines: Vec<String>
}

impl Subtitle {
    /// Return a string representation of this subtitle.
    pub fn to_string(&self) -> String {
        format!("{}\n{} --> {}\n{}\n", self.index,
                format_time(self.begin),
                format_time(self.end),
                self.lines.connect("\n"))
    }
}

/// The contents of an SRT-format subtitle file.
#[deriving(Show, PartialEq)]
pub struct SubtitleFile {
    /// The subtitles in this file.
    pub subtitles: Vec<Subtitle>
}

impl SubtitleFile {
    /// Parse raw subtitle text into an appropriate structure.
    pub fn from_str(data: &str) -> SubStudyResult<SubtitleFile> {
        match grammar::subtitle_file(data) {
            Ok(file) => Ok(file),
            Err(msg) => Err(SubStudyError::Parse(msg))
        }
    }

    /// Parse the subtitle file found at the specified path.
    pub fn from_path(path: &Path) -> SubStudyResult<SubtitleFile> {
        let mut file = try!(File::open(path));
        let bytes = try!(file.read_to_end());
        let data = try!(smart_decode(bytes.as_slice()));
        SubtitleFile::from_str(data.as_slice())
    }

    /// Convert subtitles to a string.
    pub fn to_string(&self) -> String {
        let subs: Vec<String> =
            self.subtitles.iter().map(|s| s.to_string()).collect();
        // The BOM (byte-order mark) is generally discouraged on Linux, but
        // it's sometimes needed to get good results under Windows.  We
        // include it here because Wikipedia says that SRT files files
        // default to various legacy encoding, but that the BOM can be used
        // for Unicode.
        format!("\uFEFF{}", subs.connect("\n"))
    }
}

#[cfg(test)]
mod test {
    use srt::{SubtitleFile,Subtitle};

    #[test]
    fn subtitle_file_from_path() {
        let path = Path::new("fixtures/sample.es.srt");
        let srt = SubtitleFile::from_path(&path).unwrap();
        assert_eq!(5, srt.subtitles.len());

        let sub = &srt.subtitles[0];
        assert_eq!(16, sub.index);
        assert_eq!(62.328, sub.begin);
        assert_eq!(64.664, sub.end);
        assert_eq!(vec!("¡Si! ¡Aang ha vuelto!".to_string()), sub.lines);

        let sub2 = &srt.subtitles[2];
        assert_eq!(vec!("Tu diste la señal a la armada".to_string(),
                        "del fuego con la bengala,".to_string()),
                   sub2.lines);
    }

    #[test]
    fn subtitle_to_string() {
        let sub = Subtitle{index: 4, begin: 61.5, end: 63.75,
                           lines: vec!("Line 1".to_string(),
                                       "Line 2".to_string())};
        let expected = r"4
00:01:01,500 --> 00:01:03,750
Line 1
Line 2
".to_string();
        assert_eq!(expected, sub.to_string());
    }

    #[test]
    fn subtitle_file_to_string() {
        let data = "\uFEFF16
00:01:02,328 --> 00:01:04,664
Line 1.1

17
00:01:12,839 --> 00:01:13,839
Line 2.1
";
        let srt = SubtitleFile::from_str(data).unwrap();
        assert_eq!(data, srt.to_string().as_slice());
    }
}

// Our parser expression grammar.  We'd like to move this to another file.
peg! grammar(r#"
use srt::{Subtitle,SubtitleFile};

#[pub]
subtitle_file -> SubtitleFile
    = bom? blank_lines? result:subtitles blank_lines? {
        SubtitleFile{subtitles: result}
    }

subtitles -> Vec<Subtitle>
    = subs:subtitle ** blank_lines { subs }

subtitle -> Subtitle
    = index:digits newline t:times newline l:lines {
        let (b, e) = t;
        Subtitle{index: index, begin: b, end: e, lines: l}
    }

times -> (f32, f32)
    = begin:time " --> " end:time { (begin, end) }

time -> f32
    = hh:digits ":" mm:digits ":" ss:comma_float {
        (hh as f32)*3600.0 + (mm as f32)*60.0 + ss
    }

lines -> Vec<String>
    = lines:line ** newline { lines }

line -> String
    = [^\r\n]+ { match_str.to_string() }

digits -> uint
    = [0-9]+ { from_str::<uint>(match_str).unwrap() }

comma_float -> f32
    = [0-9]+ "," [0-9]+ {
        let fixed: String = match_str.replace(",", ".");
        from_str::<f32>(fixed.as_slice()).unwrap()
    }

bom
    = "\uFEFF"

newline
    = "\r"? "\n"

blank_lines
    = newline+

"#)
<|endoftext|>


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

use getrandom::getrandom;

use poly::{eval, generate, y_intercept};

/// Split a secret into N shares, of which K are required to re-combine. Returns
/// a map of share IDs to share values.
pub fn split(n: u8, k: u8, secret: &[u8]) -> HashMap<u8, Vec<u8>> {
    // Generate a random K-degree polynomial for each byte of the secret.
    let polys = secret
        .iter()
        .map(|b| generate((k - 1) as usize, *b, &mut getrandom))
        .collect::<Vec<Vec<u8>>>();

    // Collect the evaluation of each polynomial with the share ID as the input.
    (1..=n)
        .map(|id| (id, polys.iter().map(|p| eval(p, id)).collect()))
        .collect()
}

/// Combine a map of share IDs into the original secret.
///
/// N.B.: There is no way to know if this is successful or not.
pub fn combine<S: ::std::hash::BuildHasher>(shares: &HashMap<u8, Vec<u8>, S>) -> Vec<u8> {
    let len = shares.values().next().unwrap().len();
    if shares.values().any(|v| v.len() != len) {
        panic!("mismatched share lengths")
    }

    (0..len)
        .map(|i| y_intercept(shares.iter().map(|(id, v)| (*id, v[i])).collect()))
        .collect()
}

#[cfg(test)]
mod test {
    extern crate itertools;

    use super::*;

    use self::itertools::Itertools;

    #[test]
    fn test_split() {
        let secret = vec![1, 2, 3, 4, 5];
        let splits = split(5, 3, &secret);

        for i in 5..=3 {
            for keys in splits.keys().combinations(i) {
                let mut subset: HashMap<u8, Vec<u8>> = HashMap::new();
                for key in keys {
                    subset.insert(*key, splits.get(key).unwrap().to_vec());
                }
                assert_eq!(combine(&subset), secret);
            }
        }

        for i in 2..=1 {
            for keys in splits.keys().combinations(i) {
                let mut subset: HashMap<u8, Vec<u8>> = HashMap::new();
                for key in keys {
                    subset.insert(*key, splits.get(key).unwrap().to_vec());
                }
                assert_ne!(combine(&subset), secret);
            }
        }
    }

    #[test]
    fn test_combine() {
        let mut shares: HashMap<u8, Vec<u8>> = HashMap::new();
        shares.insert(1, vec![64, 163, 216, 189, 193]);
        shares.insert(3, vec![194, 250, 117, 212, 82]);
        shares.insert(5, vec![95, 17, 153, 111, 252]);

        assert_eq!(combine(&shares), vec![1, 2, 3, 4, 5])
    }
}
<commit_msg>Use destructuring instead of dereferencing.<commit_after>use std::collections::HashMap;

use getrandom::getrandom;

use poly::{eval, generate, y_intercept};

/// Split a secret into N shares, of which K are required to re-combine. Returns
/// a map of share IDs to share values.
pub fn split(n: u8, k: u8, secret: &[u8]) -> HashMap<u8, Vec<u8>> {
    // Generate a random K-degree polynomial for each byte of the secret.
    let polys = secret
        .iter()
        .map(|&b| generate((k - 1) as usize, b, &mut getrandom))
        .collect::<Vec<Vec<u8>>>();

    // Collect the evaluation of each polynomial with the share ID as the input.
    (1..=n)
        .map(|id| (id, polys.iter().map(|p| eval(p, id)).collect()))
        .collect()
}

/// Combine a map of share IDs into the original secret.
///
/// N.B.: There is no way to know if this is successful or not.
pub fn combine<S: ::std::hash::BuildHasher>(shares: &HashMap<u8, Vec<u8>, S>) -> Vec<u8> {
    let len = shares.values().next().unwrap().len();
    if shares.values().any(|v| v.len() != len) {
        panic!("mismatched share lengths")
    }

    (0..len)
        .map(|i| y_intercept(shares.iter().map(|(&id, v)| (id, v[i])).collect()))
        .collect()
}

#[cfg(test)]
mod test {
    extern crate itertools;

    use super::*;

    use self::itertools::Itertools;

    #[test]
    fn test_split() {
        let secret = vec![1, 2, 3, 4, 5];
        let splits = split(5, 3, &secret);

        for i in 5..=3 {
            for keys in splits.keys().combinations(i) {
                let mut subset: HashMap<u8, Vec<u8>> = HashMap::new();
                for key in keys {
                    subset.insert(*key, splits.get(key).unwrap().to_vec());
                }
                assert_eq!(combine(&subset), secret);
            }
        }

        for i in 2..=1 {
            for keys in splits.keys().combinations(i) {
                let mut subset: HashMap<u8, Vec<u8>> = HashMap::new();
                for key in keys {
                    subset.insert(*key, splits.get(key).unwrap().to_vec());
                }
                assert_ne!(combine(&subset), secret);
            }
        }
    }

    #[test]
    fn test_combine() {
        let mut shares: HashMap<u8, Vec<u8>> = HashMap::new();
        shares.insert(1, vec![64, 163, 216, 189, 193]);
        shares.insert(3, vec![194, 250, 117, 212, 82]);
        shares.insert(5, vec![95, 17, 153, 111, 252]);

        assert_eq!(combine(&shares), vec![1, 2, 3, 4, 5])
    }
}
<|endoftext|>


<commit_before>// This file implements a futures-compatible executor which schedules futures
// onto the JavaScript event loop.
//
// TODO: Verify that this works correctly for multiple threads.
// TODO: verify that this works correctly with pinned futures
// TODO: use FuturesUnordered (similar to LocalPool)

use futures_core::future::{FutureObj, LocalFutureObj};
use futures_executor::enter;
use futures_core::task::{Spawn, SpawnError};
use futures_util::task::ArcWake;
use std::future::Future;
use std::task::{Poll, Context};
use std::pin::Pin;
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use std::sync::Arc;
use std::collections::VecDeque;
use std::cmp;
use webcore::try_from::TryInto;
use webcore::value::Reference;

// TODO: Determine optimal values for these constants
// Initial capacity of the event queue
const INITIAL_QUEUE_CAPACITY: usize = 10;

// Iterations to wait before allowing the queue to shrink
const QUEUE_SHRINK_DELAY: usize = 10;

pub(crate) type BoxedFuture = LocalFutureObj< 'static, () >;

#[derive(Debug)]
struct TaskInner {
    future: BoxedFuture,
    executor: EventLoopExecutor,
}

#[derive(Debug)]
struct Task {
    is_queued: Cell< bool >,
    inner: RefCell< TaskInner >,
}

// TODO fix these
// It is safe because JavaScript is (currently) always single-threaded
unsafe impl Send for Task {}
unsafe impl Sync for Task {}

impl Task {
    fn new( executor: EventLoopExecutor, future: BoxedFuture ) -> Arc< Self > {
        Arc::new( Self {
            is_queued: Cell::new( true ),
            inner: RefCell::new( TaskInner {
                future,
                executor,
            } ),
        } )
    }

    fn poll( arc: Arc< Self > ) {
        let mut lock = arc.inner.borrow_mut();

        // This is needed in order to borrow disjoint struct fields
        let lock = &mut *lock;

        // Clear `is_queued` flag so that it will re-queue if poll calls waker.wake()
        arc.is_queued.set( false );

        let poll = {
            // TODO is there some way of saving these so they don't need to be recreated all the time ?
            let waker = ArcWake::into_waker( arc.clone() );
            let cx = &mut Context::from_waker( &waker );

            // TODO what if poll panics ?
            // TODO is this Pin correct ?
            Pin::new( &mut lock.future ).poll( cx )
        };

        if let Poll::Pending = poll {
            // It was woken up during the poll, so we requeue it
            if arc.is_queued.get() {
                lock.executor.0.push_task( arc.clone() );
            }
        }
    }

    #[inline]
    fn push_task( arc: &Arc< Self > ) {
        if !arc.is_queued.replace( true ) {
            if let Ok( lock ) = arc.inner.try_borrow() {
                lock.executor.0.push_task( arc.clone() );
            }
        }
    }
}

impl ArcWake for Task {
    #[inline]
    fn wake_by_ref( arc_self: &Arc< Self > ) {
        Task::push_task( arc_self );
    }
}

#[derive(Debug)]
struct EventLoopInner {
    // This avoids unnecessary allocations and interop overhead
    // by using a Rust queue of pending tasks.
    queue: VecDeque< Arc< Task > >,
    // TODO handle overflow
    past_sum: usize,
    past_length: usize,
    shrink_counter: usize,
}

#[derive(Debug)]
struct EventLoopQueue {
    inner: RefCell< EventLoopInner >,
    is_draining: Cell< bool >,
}

impl EventLoopQueue {
    // See if it's worth trying to reclaim some space from the queue
    fn estimate_realloc_capacity( &self ) -> Option< ( usize, usize ) > {
        let mut inner = self.inner.borrow_mut();

        let cap = inner.queue.capacity();

        inner.past_sum += inner.queue.len();
        inner.past_length += 1;

        let average = inner.past_sum / inner.past_length;

        // It will resize the queue if the average length is less than a quarter of the
        // capacity.
        //
        // The check for INITIAL_QUEUE_CAPACITY is necessary in the situation
        // where the queue is at its initial capacity, but the length is very low.
        if average < cap / 4 && cap >= INITIAL_QUEUE_CAPACITY * 2 {
            // It only resizes if the above condition is met for QUEUE_SHRINK_DELAY iterations.
            inner.shrink_counter += 1;

            if inner.shrink_counter >= QUEUE_SHRINK_DELAY {
                inner.shrink_counter = 0;
                return Some( ( cap, cmp::max( average * 2, INITIAL_QUEUE_CAPACITY ) ) );
            }

        } else {
            inner.shrink_counter = 0;
        }

        None
    }

    // Poll the queue until it is empty
    fn drain( &self ) {
        let _enter = enter().expect( "EventLoopExecutor is already draining" );

        if !self.is_draining.replace( true ) {
            let maybe_realloc_capacity = self.estimate_realloc_capacity();

            // Poll all the pending tasks
            loop {
                let mut inner = self.inner.borrow_mut();

                match inner.queue.pop_front() {
                    Some( task ) => {
                        // This is necessary because the polled task might queue more tasks
                        drop( inner );
                        Task::poll( task );
                    },
                    None => {
                        // We decided to reclaim some space
                        if let Some( ( old_capacity, realloc_capacity ) ) = maybe_realloc_capacity {
                            inner.queue = VecDeque::with_capacity( realloc_capacity );

                            let new_capacity = inner.queue.capacity();

                            // This makes sure that we are actually shrinking the capacity
                            assert!( new_capacity < old_capacity );

                            // This is necessary because the estimate_realloc_capacity method
                            // relies upon the behavior of the VecDeque's capacity
                            assert!( new_capacity < realloc_capacity * 2 );
                        }

                        self.is_draining.set( false );

                        break;
                    },
                }
            }
        }
    }
}

// A proxy for the JavaScript event loop.
#[derive(Debug)]
struct EventLoop {
    queue: Rc< EventLoopQueue >,
    // TODO is this thread-safe ?
    waker: Reference,
}

impl EventLoop {
    // Waits for next microtask tick
    fn queue_microtask( &self ) {
        js! { @(no_return) @{&self.waker}(); }
    }

    // Pushes a task onto the queue
    fn push_task( &self, task: Arc< Task > ) {
        let mut inner = self.queue.inner.borrow_mut();

        inner.queue.push_back( task );

        // If the queue was previously empty, then we need to schedule
        // the queue to be drained.
        //
        // The check for `is_draining` is necessary in the situation where
        // the `drain` method pops the last task from the queue, but that
        // task then re-queues another task.
        if inner.queue.len() == 1 && !self.queue.is_draining.get() {
            self.queue_microtask();
        }
    }
}

// Not currently necessary, but may become relevant in the future
// TODO what about when the thread is killed, is this guaranteed to be called ?
impl Drop for EventLoop {
    #[inline]
    fn drop( &mut self ) {
        js! { @(no_return)
            @{&self.waker}.drop();
        }
    }
}

#[derive(Debug, Clone)]
struct EventLoopExecutor( Rc< EventLoop > );

impl EventLoopExecutor {
    fn new() -> Self {
        let queue = VecDeque::with_capacity( INITIAL_QUEUE_CAPACITY );
        // This is necessary because the estimate_realloc_capacity method
        // relies upon the behavior of the VecDeque's capacity
        assert!( queue.capacity() < INITIAL_QUEUE_CAPACITY * 2 );

        let queue = Rc::new( EventLoopQueue {
            inner: RefCell::new( EventLoopInner {
                queue: queue,
                past_sum: 0,
                past_length: 0,
                shrink_counter: 0,
            } ),

            is_draining: Cell::new( false ),
        } );

        let waker = {
            let queue = queue.clone();

            js!(
                var callback = @{move || queue.drain()};

                var dropped = false;

                function wrapper() {
                    if ( !dropped ) {
                        callback();
                    }
                }

                var nextTick;

                // Modern browsers can use `MutationObserver` which allows
                // us to schedule a micro-task without allocating a promise.
                // https://dom.spec.whatwg.org/#notify-mutation-observers
                if ( typeof MutationObserver === "function" ) {
                    var node = document.createTextNode( "0" );
                    var state = false;

                    new MutationObserver( wrapper ).observe( node, { characterData: true } );

                    nextTick = function () {
                        state = !state;
                        node.data = ( state ? "1" : "0" );
                    };

                // Node.js and other environments
                } else {
                    var promise = Promise.resolve( null );

                    nextTick = function () {
                        promise.then( wrapper );
                    };
                }

                nextTick.drop = function () {
                    dropped = true;
                    callback.drop();
                };

                return nextTick;
            ).try_into().unwrap()
        };

        EventLoopExecutor( Rc::new( EventLoop { queue, waker } ) )
    }

    #[inline]
    fn spawn_local( &self, future: BoxedFuture ) {
        self.0.push_task( Task::new( self.clone(), future ) );
    }
}

impl Spawn for EventLoopExecutor {
    #[inline]
    fn spawn_obj( &mut self,  future: FutureObj< 'static, () > ) -> Result< (), SpawnError > {
        self.spawn_local( future.into() );
        Ok( () )
    }
}

pub(crate) fn spawn_local( future: BoxedFuture ) {
    thread_local! {
        static EVENT_LOOP: EventLoopExecutor = EventLoopExecutor::new();
    }

    EVENT_LOOP.with( |event_loop| event_loop.spawn_local( future ) )
}
<commit_msg>Update ArcWaker::into_waker() call<commit_after>// This file implements a futures-compatible executor which schedules futures
// onto the JavaScript event loop.
//
// TODO: Verify that this works correctly for multiple threads.
// TODO: verify that this works correctly with pinned futures
// TODO: use FuturesUnordered (similar to LocalPool)

use futures_core::future::{FutureObj, LocalFutureObj};
use futures_executor::enter;
use futures_core::task::{Spawn, SpawnError};
use futures_util::task::{self, ArcWake};
use std::future::Future;
use std::task::{Poll, Context};
use std::pin::Pin;
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use std::sync::Arc;
use std::collections::VecDeque;
use std::cmp;
use webcore::try_from::TryInto;
use webcore::value::Reference;

// TODO: Determine optimal values for these constants
// Initial capacity of the event queue
const INITIAL_QUEUE_CAPACITY: usize = 10;

// Iterations to wait before allowing the queue to shrink
const QUEUE_SHRINK_DELAY: usize = 10;

pub(crate) type BoxedFuture = LocalFutureObj< 'static, () >;

#[derive(Debug)]
struct TaskInner {
    future: BoxedFuture,
    executor: EventLoopExecutor,
}

#[derive(Debug)]
struct Task {
    is_queued: Cell< bool >,
    inner: RefCell< TaskInner >,
}

// TODO fix these
// It is safe because JavaScript is (currently) always single-threaded
unsafe impl Send for Task {}
unsafe impl Sync for Task {}

impl Task {
    fn new( executor: EventLoopExecutor, future: BoxedFuture ) -> Arc< Self > {
        Arc::new( Self {
            is_queued: Cell::new( true ),
            inner: RefCell::new( TaskInner {
                future,
                executor,
            } ),
        } )
    }

    fn poll( arc: Arc< Self > ) {
        let mut lock = arc.inner.borrow_mut();

        // This is needed in order to borrow disjoint struct fields
        let lock = &mut *lock;

        // Clear `is_queued` flag so that it will re-queue if poll calls waker.wake()
        arc.is_queued.set( false );

        let poll = {
            // TODO is there some way of saving these so they don't need to be recreated all the time ?
            let waker = task::waker( arc.clone() );
            let cx = &mut Context::from_waker( &waker );

            // TODO what if poll panics ?
            // TODO is this Pin correct ?
            Pin::new( &mut lock.future ).poll( cx )
        };

        if let Poll::Pending = poll {
            // It was woken up during the poll, so we requeue it
            if arc.is_queued.get() {
                lock.executor.0.push_task( arc.clone() );
            }
        }
    }

    #[inline]
    fn push_task( arc: &Arc< Self > ) {
        if !arc.is_queued.replace( true ) {
            if let Ok( lock ) = arc.inner.try_borrow() {
                lock.executor.0.push_task( arc.clone() );
            }
        }
    }
}

impl ArcWake for Task {
    #[inline]
    fn wake_by_ref( arc_self: &Arc< Self > ) {
        Task::push_task( arc_self );
    }
}

#[derive(Debug)]
struct EventLoopInner {
    // This avoids unnecessary allocations and interop overhead
    // by using a Rust queue of pending tasks.
    queue: VecDeque< Arc< Task > >,
    // TODO handle overflow
    past_sum: usize,
    past_length: usize,
    shrink_counter: usize,
}

#[derive(Debug)]
struct EventLoopQueue {
    inner: RefCell< EventLoopInner >,
    is_draining: Cell< bool >,
}

impl EventLoopQueue {
    // See if it's worth trying to reclaim some space from the queue
    fn estimate_realloc_capacity( &self ) -> Option< ( usize, usize ) > {
        let mut inner = self.inner.borrow_mut();

        let cap = inner.queue.capacity();

        inner.past_sum += inner.queue.len();
        inner.past_length += 1;

        let average = inner.past_sum / inner.past_length;

        // It will resize the queue if the average length is less than a quarter of the
        // capacity.
        //
        // The check for INITIAL_QUEUE_CAPACITY is necessary in the situation
        // where the queue is at its initial capacity, but the length is very low.
        if average < cap / 4 && cap >= INITIAL_QUEUE_CAPACITY * 2 {
            // It only resizes if the above condition is met for QUEUE_SHRINK_DELAY iterations.
            inner.shrink_counter += 1;

            if inner.shrink_counter >= QUEUE_SHRINK_DELAY {
                inner.shrink_counter = 0;
                return Some( ( cap, cmp::max( average * 2, INITIAL_QUEUE_CAPACITY ) ) );
            }

        } else {
            inner.shrink_counter = 0;
        }

        None
    }

    // Poll the queue until it is empty
    fn drain( &self ) {
        let _enter = enter().expect( "EventLoopExecutor is already draining" );

        if !self.is_draining.replace( true ) {
            let maybe_realloc_capacity = self.estimate_realloc_capacity();

            // Poll all the pending tasks
            loop {
                let mut inner = self.inner.borrow_mut();

                match inner.queue.pop_front() {
                    Some( task ) => {
                        // This is necessary because the polled task might queue more tasks
                        drop( inner );
                        Task::poll( task );
                    },
                    None => {
                        // We decided to reclaim some space
                        if let Some( ( old_capacity, realloc_capacity ) ) = maybe_realloc_capacity {
                            inner.queue = VecDeque::with_capacity( realloc_capacity );

                            let new_capacity = inner.queue.capacity();

                            // This makes sure that we are actually shrinking the capacity
                            assert!( new_capacity < old_capacity );

                            // This is necessary because the estimate_realloc_capacity method
                            // relies upon the behavior of the VecDeque's capacity
                            assert!( new_capacity < realloc_capacity * 2 );
                        }

                        self.is_draining.set( false );

                        break;
                    },
                }
            }
        }
    }
}

// A proxy for the JavaScript event loop.
#[derive(Debug)]
struct EventLoop {
    queue: Rc< EventLoopQueue >,
    // TODO is this thread-safe ?
    waker: Reference,
}

impl EventLoop {
    // Waits for next microtask tick
    fn queue_microtask( &self ) {
        js! { @(no_return) @{&self.waker}(); }
    }

    // Pushes a task onto the queue
    fn push_task( &self, task: Arc< Task > ) {
        let mut inner = self.queue.inner.borrow_mut();

        inner.queue.push_back( task );

        // If the queue was previously empty, then we need to schedule
        // the queue to be drained.
        //
        // The check for `is_draining` is necessary in the situation where
        // the `drain` method pops the last task from the queue, but that
        // task then re-queues another task.
        if inner.queue.len() == 1 && !self.queue.is_draining.get() {
            self.queue_microtask();
        }
    }
}

// Not currently necessary, but may become relevant in the future
// TODO what about when the thread is killed, is this guaranteed to be called ?
impl Drop for EventLoop {
    #[inline]
    fn drop( &mut self ) {
        js! { @(no_return)
            @{&self.waker}.drop();
        }
    }
}

#[derive(Debug, Clone)]
struct EventLoopExecutor( Rc< EventLoop > );

impl EventLoopExecutor {
    fn new() -> Self {
        let queue = VecDeque::with_capacity( INITIAL_QUEUE_CAPACITY );
        // This is necessary because the estimate_realloc_capacity method
        // relies upon the behavior of the VecDeque's capacity
        assert!( queue.capacity() < INITIAL_QUEUE_CAPACITY * 2 );

        let queue = Rc::new( EventLoopQueue {
            inner: RefCell::new( EventLoopInner {
                queue: queue,
                past_sum: 0,
                past_length: 0,
                shrink_counter: 0,
            } ),

            is_draining: Cell::new( false ),
        } );

        let waker = {
            let queue = queue.clone();

            js!(
                var callback = @{move || queue.drain()};

                var dropped = false;

                function wrapper() {
                    if ( !dropped ) {
                        callback();
                    }
                }

                var nextTick;

                // Modern browsers can use `MutationObserver` which allows
                // us to schedule a micro-task without allocating a promise.
                // https://dom.spec.whatwg.org/#notify-mutation-observers
                if ( typeof MutationObserver === "function" ) {
                    var node = document.createTextNode( "0" );
                    var state = false;

                    new MutationObserver( wrapper ).observe( node, { characterData: true } );

                    nextTick = function () {
                        state = !state;
                        node.data = ( state ? "1" : "0" );
                    };

                // Node.js and other environments
                } else {
                    var promise = Promise.resolve( null );

                    nextTick = function () {
                        promise.then( wrapper );
                    };
                }

                nextTick.drop = function () {
                    dropped = true;
                    callback.drop();
                };

                return nextTick;
            ).try_into().unwrap()
        };

        EventLoopExecutor( Rc::new( EventLoop { queue, waker } ) )
    }

    #[inline]
    fn spawn_local( &self, future: BoxedFuture ) {
        self.0.push_task( Task::new( self.clone(), future ) );
    }
}

impl Spawn for EventLoopExecutor {
    #[inline]
    fn spawn_obj( &mut self,  future: FutureObj< 'static, () > ) -> Result< (), SpawnError > {
        self.spawn_local( future.into() );
        Ok( () )
    }
}

pub(crate) fn spawn_local( future: BoxedFuture ) {
    thread_local! {
        static EVENT_LOOP: EventLoopExecutor = EventLoopExecutor::new();
    }

    EVENT_LOOP.with( |event_loop| event_loop.spawn_local( future ) )
}
<|endoftext|>


<commit_before>use ::xflow::xfstruct::*;
// use ::xflow::errors::*;

pub struct ValidationError {
    pub code:    i32,
    pub message: String,
    pub paths:   Vec<String>,
}

impl ValidationError {
    /// Constructs a new `ValidationError`
    ///
    /// # Example
    /// ```
    /// use xfdocs::xflow::validation::{ValidationError};
    /// let err = ValidationError::new(1, "sample error".to_string(), Vec::<String>::new());
    /// println!("Validation error {}", err.message);
    /// ```
    pub fn new(code:i32, message:String, paths:Vec<String>) -> ValidationError {
        ValidationError {
            code:    code,
            message: message,
            paths:   paths
        }
    }
}

pub struct Validation {
    pub errors: Vec<ValidationError>,
}

impl Validation {

    pub fn new() -> Validation {
        Validation {
            errors: Vec::<ValidationError>::new()
        }
    }

   pub fn validate(&self, xflow:&XFlowStruct) {
       Validation::all_edges_have_nodes(xflow);
       Validation::has_one_entry_node(xflow);
       Validation::has_terminal_nodes(xflow);
   }

    pub fn all_edges_have_nodes(xflow:&XFlowStruct) -> Vec<ValidationError> {
        let mut errors = Vec::<ValidationError>::new();

        let mut node_ids = xflow.nodes.iter().map({|node|
            node.id.clone()
        }).collect::<Vec<i32>>();

        node_ids.sort();
        node_ids.dedup();

        for edge in xflow.edges.iter() {

            if !node_ids.contains(&edge.0) {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("Edge {:?} has no connecting node {:?}", edge, edge.0),
                    paths:   vec![format!("/edges/{:?}", edge)],
                });
            };

            if !node_ids.contains(&edge.1) {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("Edge {:?} has no connecting node {:?}", edge, edge.1),
                    paths:   vec![format!("/edges/{:?}", edge)],
                });
            };
        }

       errors
   }

    pub fn has_one_entry_node(xflow:&XFlowStruct) -> Vec<ValidationError> {
        let mut errors = Vec::<ValidationError>::new();

        let res = xflow.get_nodes_by("flow", "start");
        match res.len() {
            0 => {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("XFlow has no entry nodes"),
                    paths:   vec![format!("/nodes")],
                });
            },
            1 => {},
            _ => {
                //
                // XXX: Add multiple paths
                //
                errors.push(ValidationError {
                    code:    1,
                    message: format!("XFlow has multiple entry nodes"),
                    paths:   vec!["/nodes".to_string()],
                });
            }
        }

        errors
    }

    pub fn has_terminal_nodes(xflow:&XFlowStruct) -> Vec<ValidationError> {
        let mut errors = Vec::<ValidationError>::new();

        let res = xflow.get_nodes_by("flow", "end");
        match res.len() {
            0 => {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("XFlow has no terminal nodes"),
                    paths:   vec![format!("/nodes")],
                });
            }
            _ => {}
        }

        errors

    }

//
//
//        all_edges_have_nodes(flow),
//        has_one_entry_node(flow),
//        has_terminal_nodes(flow),
//        all_node_actions_have_matching_requirements(flow),
//        expressions_reference_known_variables(flow),
//        all_return_values_exist(flow),
//        variables_are_defined_only_once(flow),
//        all_nodes_have_at_least_one_edge(flow)
}
<commit_msg>Add stub validation method<commit_after>use ::xflow::xfstruct::*;
// use ::xflow::errors::*;

pub struct ValidationError {
    pub code:    i32,
    pub message: String,
    pub paths:   Vec<String>,
}

impl ValidationError {
    /// Constructs a new `ValidationError`
    ///
    /// # Example
    /// ```
    /// use xfdocs::xflow::validation::{ValidationError};
    /// let err = ValidationError::new(1, "sample error".to_string(), Vec::<String>::new());
    /// println!("Validation error {}", err.message);
    /// ```
    pub fn new(code:i32, message:String, paths:Vec<String>) -> ValidationError {
        ValidationError {
            code:    code,
            message: message,
            paths:   paths
        }
    }
}

pub struct Validation {
    pub errors: Vec<ValidationError>,
}

impl Validation {

    pub fn new() -> Validation {
        Validation {
            errors: Vec::<ValidationError>::new()
        }
    }

   pub fn validate(&self, xflow:&XFlowStruct) {
       Validation::all_edges_have_nodes(xflow);
       Validation::has_one_entry_node(xflow);
       Validation::has_terminal_nodes(xflow);
   }

    pub fn all_edges_have_nodes(xflow:&XFlowStruct) -> Vec<ValidationError> {
        let mut errors = Vec::<ValidationError>::new();

        let mut node_ids = xflow.nodes.iter().map({|node|
            node.id.clone()
        }).collect::<Vec<i32>>();

        node_ids.sort();
        node_ids.dedup();

        for edge in xflow.edges.iter() {

            if !node_ids.contains(&edge.0) {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("Edge {:?} has no connecting node {:?}", edge, edge.0),
                    paths:   vec![format!("/edges/{:?}", edge)],
                });
            };

            if !node_ids.contains(&edge.1) {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("Edge {:?} has no connecting node {:?}", edge, edge.1),
                    paths:   vec![format!("/edges/{:?}", edge)],
                });
            };
        }

       errors
   }

    pub fn has_one_entry_node(xflow:&XFlowStruct) -> Vec<ValidationError> {
        let mut errors = Vec::<ValidationError>::new();

        let res = xflow.get_nodes_by("flow", "start");
        match res.len() {
            0 => {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("XFlow has no entry nodes"),
                    paths:   vec![format!("/nodes")],
                });
            },
            1 => {},
            _ => {
                //
                // XXX: Add multiple paths
                //
                errors.push(ValidationError {
                    code:    1,
                    message: format!("XFlow has multiple entry nodes"),
                    paths:   vec!["/nodes".to_string()],
                });
            }
        }

        errors
    }

    pub fn has_terminal_nodes(xflow:&XFlowStruct) -> Vec<ValidationError> {
        let mut errors = Vec::<ValidationError>::new();

        let res = xflow.get_nodes_by("flow", "end");
        match res.len() {
            0 => {
                errors.push(ValidationError {
                    code:    1,
                    message: format!("XFlow has no terminal nodes"),
                    paths:   vec![format!("/nodes")],
                });
            }
            _ => {}
        }

        errors

    }

    pub fn all_nodes_have_at_least_one_edge(xflow:&XFlowStruct) -> Vec<ValidationError> {
        let mut errors = Vec::<ValidationError>::new();

        errors
    }

//     X  all_edges_have_nodes(flow),
//     X  has_one_entry_node(flow),
//     X  has_terminal_nodes(flow),
//        all_node_actions_have_matching_requirements(flow),
//        expressions_reference_known_variables(flow),
//        all_return_values_exist(flow),
//        variables_are_defined_only_once(flow),
//        all_nodes_have_at_least_one_edge(flow)
}
<|endoftext|>


<commit_before>#[allow(non_snake_case)]
#[repr(C)]
#[repr(packed)]
pub struct Descriptor {
    bLength: u8,
    bDescriptorType: u8,
    bcdCCID: u16,
    bMaxSlotIndex: u8,
    bVoltageSupport: u8,
    dwProtocols: u32,
    dwDefaultClock: u32,
    dwMaximumClock: u32,
    bNumClockSupported: u8,
    dwDataRate: u32,
    dwMaxDataRate: u32,
    bNumDataRatesSupported: u8,
    dwMaxIFSD: u32,
    dwSynchProtocols: u32,
    dwMechanical: u32,
    dwFeatures: u32,
    dwMaxCCIDMessageLength: u32,
    bClassGetResponse: u8,
    bClassEnvelope: u8,
    wLcdLayout: u16,
    bPINSupport: u8,
    bMaxCCIDBusySlots: u8,
}
<commit_msg>USB: Use #[repr(C, packed)] syntax<commit_after>#[allow(non_snake_case)]
#[repr(C, packed)]
pub struct Descriptor {
    bLength: u8,
    bDescriptorType: u8,
    bcdCCID: u16,
    bMaxSlotIndex: u8,
    bVoltageSupport: u8,
    dwProtocols: u32,
    dwDefaultClock: u32,
    dwMaximumClock: u32,
    bNumClockSupported: u8,
    dwDataRate: u32,
    dwMaxDataRate: u32,
    bNumDataRatesSupported: u8,
    dwMaxIFSD: u32,
    dwSynchProtocols: u32,
    dwMechanical: u32,
    dwFeatures: u32,
    dwMaxCCIDMessageLength: u32,
    bClassGetResponse: u8,
    bClassEnvelope: u8,
    wLcdLayout: u16,
    bPINSupport: u8,
    bMaxCCIDBusySlots: u8,
}
<|endoftext|>


<commit_before>//! An analysis to determine which locals require allocas and
//! which do not.

use rustc_data_structures::bit_set::BitSet;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::traversal;
use rustc::ty;
use rustc::ty::layout::{LayoutOf, HasTyCtxt};
use super::FunctionCx;
use crate::traits::*;

pub fn non_ssa_locals<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
    fx: &FunctionCx<'a, 'tcx, Bx>
) -> BitSet<mir::Local> {
    let mir = fx.mir;
    let mut analyzer = LocalAnalyzer::new(fx);

    analyzer.visit_body(mir);

    for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
        let ty = fx.monomorphize(&ty);
        debug!("local {} has type {:?}", index, ty);
        let layout = fx.cx.layout_of(ty);
        if fx.cx.is_backend_immediate(layout) {
            // These sorts of types are immediates that we can store
            // in an Value without an alloca.
        } else if fx.cx.is_backend_scalar_pair(layout) {
            // We allow pairs and uses of any of their 2 fields.
        } else {
            // These sorts of types require an alloca. Note that
            // is_llvm_immediate() may *still* be true, particularly
            // for newtypes, but we currently force some types
            // (e.g., structs) into an alloca unconditionally, just so
            // that we don't have to deal with having two pathways
            // (gep vs extractvalue etc).
            analyzer.not_ssa(mir::Local::new(index));
        }
    }

    analyzer.non_ssa_locals
}

struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> {
    fx: &'mir FunctionCx<'a, 'tcx, Bx>,
    dominators: Dominators<mir::BasicBlock>,
    non_ssa_locals: BitSet<mir::Local>,
    // The location of the first visited direct assignment to each
    // local, or an invalid location (out of bounds `block` index).
    first_assignment: IndexVec<mir::Local, Location>
}

impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
        let invalid_location =
            mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
        let mut analyzer = LocalAnalyzer {
            fx,
            dominators: fx.mir.dominators(),
            non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
            first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls)
        };

        // Arguments get assigned to by means of the function being called
        for arg in fx.mir.args_iter() {
            analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
        }

        analyzer
    }

    fn first_assignment(&self, local: mir::Local) -> Option<Location> {
        let location = self.first_assignment[local];
        if location.block.index() < self.fx.mir.basic_blocks().len() {
            Some(location)
        } else {
            None
        }
    }

    fn not_ssa(&mut self, local: mir::Local) {
        debug!("marking {:?} as non-SSA", local);
        self.non_ssa_locals.insert(local);
    }

    fn assign(&mut self, local: mir::Local, location: Location) {
        if self.first_assignment(local).is_some() {
            self.not_ssa(local);
        } else {
            self.first_assignment[local] = location;
        }
    }
}

impl<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
    for LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn visit_assign(&mut self,
                    place: &mir::Place<'tcx>,
                    rvalue: &mir::Rvalue<'tcx>,
                    location: Location) {
        debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);

        if let mir::Place::Base(mir::PlaceBase::Local(index)) = *place {
            self.assign(index, location);
            if !self.fx.rvalue_creates_operand(rvalue) {
                self.not_ssa(index);
            }
        } else {
            self.visit_place(
                place,
                PlaceContext::MutatingUse(MutatingUseContext::Store),
                location
            );
        }

        self.visit_rvalue(rvalue, location);
    }

    fn visit_terminator_kind(&mut self,
                             kind: &mir::TerminatorKind<'tcx>,
                             location: Location) {
        let check = match *kind {
            mir::TerminatorKind::Call {
                func: mir::Operand::Constant(ref c),
                ref args, ..
            } => match c.ty.sty {
                ty::FnDef(did, _) => Some((did, args)),
                _ => None,
            },
            _ => None,
        };
        if let Some((def_id, args)) = check {
            if Some(def_id) == self.fx.cx.tcx().lang_items().box_free_fn() {
                // box_free(x) shares with `drop x` the property that it
                // is not guaranteed to be statically dominated by the
                // definition of x, so x must always be in an alloca.
                if let mir::Operand::Move(ref place) = args[0] {
                    self.visit_place(
                        place,
                        PlaceContext::MutatingUse(MutatingUseContext::Drop),
                        location
                    );
                }
            }
        }

        self.super_terminator_kind(kind, location);
    }

    fn visit_place(&mut self,
                   place: &mir::Place<'tcx>,
                   context: PlaceContext,
                   location: Location) {
        debug!("visit_place(place={:?}, context={:?})", place, context);
        let mut context = context;
        let cx = self.fx.cx;

        place.iterate(|place_base, place_projections| {
            for proj in place_projections {
                // Allow uses of projections that are ZSTs or from scalar fields.
                let is_consume = match context {
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
                    _ => false
                };
                if is_consume {
                    let base_ty = proj.base.ty(self.fx.mir, cx.tcx());
                    let base_ty = self.fx.monomorphize(&base_ty);

                    // ZSTs don't require any actual memory access.
                    let elem_ty = base_ty
                        .projection_ty(cx.tcx(), &proj.elem)
                        .ty;
                    let elem_ty = self.fx.monomorphize(&elem_ty);
                    if cx.layout_of(elem_ty).is_zst() {
                        return;
                    }

                    if let mir::ProjectionElem::Field(..) = proj.elem {
                        let layout = cx.layout_of(base_ty.ty);
                        if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
                            // Recurse with the same context, instead of `Projection`,
                            // potentially stopping at non-operand projections,
                            // which would trigger `not_ssa` on locals.
                            continue;
                        }
                    }
                }

                // A deref projection only reads the pointer, never needs the place.
                if let mir::ProjectionElem::Deref = proj.elem {
                    return self.visit_place(
                        &proj.base,
                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                        location
                    );
                }

                context = if context.is_mutating_use() {
                    PlaceContext::MutatingUse(MutatingUseContext::Projection)
                } else {
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
                };
            }

            // Default base visit behavior
            if let mir::PlaceBase::Local(local) = place_base {
                self.visit_local(local, context, location);
            }
        });
    }

    fn visit_local(&mut self,
                   &local: &mir::Local,
                   context: PlaceContext,
                   location: Location) {
        match context {
            PlaceContext::MutatingUse(MutatingUseContext::Call) => {
                self.assign(local, location);
            }

            PlaceContext::NonUse(_) |
            PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
                // Reads from uninitialized variables (e.g., in dead code, after
                // optimizations) require locals to be in (uninitialized) memory.
                // N.B., there can be uninitialized reads of a local visited after
                // an assignment to that local, if they happen on disjoint paths.
                let ssa_read = match self.first_assignment(local) {
                    Some(assignment_location) => {
                        assignment_location.dominates(location, &self.dominators)
                    }
                    None => false
                };
                if !ssa_read {
                    self.not_ssa(local);
                }
            }

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
            PlaceContext::MutatingUse(MutatingUseContext::Store) |
            PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
            PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
            PlaceContext::MutatingUse(MutatingUseContext::Projection) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
                self.not_ssa(local);
            }

            PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
                let ty = self.fx.mir.local_decls[local].ty;
                let ty = self.fx.monomorphize(&ty);

                // Only need the place if we're actually dropping it.
                if self.fx.cx.type_needs_drop(ty) {
                    self.not_ssa(local);
                }
            }
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CleanupKind {
    NotCleanup,
    Funclet,
    Internal { funclet: mir::BasicBlock }
}

impl CleanupKind {
    pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
        match self {
            CleanupKind::NotCleanup => None,
            CleanupKind::Funclet => Some(for_bb),
            CleanupKind::Internal { funclet } => Some(funclet),
        }
    }
}

pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Body<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
    fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                              mir: &mir::Body<'tcx>) {
        for (bb, data) in mir.basic_blocks().iter_enumerated() {
            match data.terminator().kind {
                TerminatorKind::Goto { .. } |
                TerminatorKind::Resume |
                TerminatorKind::Abort |
                TerminatorKind::Return |
                TerminatorKind::GeneratorDrop |
                TerminatorKind::Unreachable |
                TerminatorKind::SwitchInt { .. } |
                TerminatorKind::Yield { .. } |
                TerminatorKind::FalseEdges { .. } |
                TerminatorKind::FalseUnwind { .. } => {
                    /* nothing to do */
                }
                TerminatorKind::Call { cleanup: unwind, .. } |
                TerminatorKind::Assert { cleanup: unwind, .. } |
                TerminatorKind::DropAndReplace { unwind, .. } |
                TerminatorKind::Drop { unwind, .. } => {
                    if let Some(unwind) = unwind {
                        debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
                               bb, data, unwind);
                        result[unwind] = CleanupKind::Funclet;
                    }
                }
            }
        }
    }

    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                       mir: &mir::Body<'tcx>) {
        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());

        let mut set_successor = |funclet: mir::BasicBlock, succ| {
            match funclet_succs[funclet] {
                ref mut s @ None => {
                    debug!("set_successor: updating successor of {:?} to {:?}",
                           funclet, succ);
                    *s = Some(succ);
                },
                Some(s) => if s != succ {
                    span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
                              funclet, s, succ);
                }
            }
        };

        for (bb, data) in traversal::reverse_postorder(mir) {
            let funclet = match result[bb] {
                CleanupKind::NotCleanup => continue,
                CleanupKind::Funclet => bb,
                CleanupKind::Internal { funclet } => funclet,
            };

            debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
                   bb, data, result[bb], funclet);

            for &succ in data.terminator().successors() {
                let kind = result[succ];
                debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
                       funclet, succ, kind);
                match kind {
                    CleanupKind::NotCleanup => {
                        result[succ] = CleanupKind::Internal { funclet };
                    }
                    CleanupKind::Funclet => {
                        if funclet != succ {
                            set_successor(funclet, succ);
                        }
                    }
                    CleanupKind::Internal { funclet: succ_funclet } => {
                        if funclet != succ_funclet {
                            // `succ` has 2 different funclet going into it, so it must
                            // be a funclet by itself.

                            debug!("promoting {:?} to a funclet and updating {:?}", succ,
                                   succ_funclet);
                            result[succ] = CleanupKind::Funclet;
                            set_successor(succ_funclet, succ);
                            set_successor(funclet, succ);
                        }
                    }
                }
            }
        }
    }

    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());

    discover_masters(&mut result, mir);
    propagate(&mut result, mir);
    debug!("cleanup_kinds: result={:?}", result);
    result
}
<commit_msg>Rollup merge of #61641 - spastorino:revert-to-recursion-on-local-analyzer, r=oli-obk<commit_after>//! An analysis to determine which locals require allocas and
//! which do not.

use rustc_data_structures::bit_set::BitSet;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::traversal;
use rustc::ty;
use rustc::ty::layout::{LayoutOf, HasTyCtxt};
use super::FunctionCx;
use crate::traits::*;

pub fn non_ssa_locals<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
    fx: &FunctionCx<'a, 'tcx, Bx>
) -> BitSet<mir::Local> {
    let mir = fx.mir;
    let mut analyzer = LocalAnalyzer::new(fx);

    analyzer.visit_body(mir);

    for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
        let ty = fx.monomorphize(&ty);
        debug!("local {} has type {:?}", index, ty);
        let layout = fx.cx.layout_of(ty);
        if fx.cx.is_backend_immediate(layout) {
            // These sorts of types are immediates that we can store
            // in an Value without an alloca.
        } else if fx.cx.is_backend_scalar_pair(layout) {
            // We allow pairs and uses of any of their 2 fields.
        } else {
            // These sorts of types require an alloca. Note that
            // is_llvm_immediate() may *still* be true, particularly
            // for newtypes, but we currently force some types
            // (e.g., structs) into an alloca unconditionally, just so
            // that we don't have to deal with having two pathways
            // (gep vs extractvalue etc).
            analyzer.not_ssa(mir::Local::new(index));
        }
    }

    analyzer.non_ssa_locals
}

struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> {
    fx: &'mir FunctionCx<'a, 'tcx, Bx>,
    dominators: Dominators<mir::BasicBlock>,
    non_ssa_locals: BitSet<mir::Local>,
    // The location of the first visited direct assignment to each
    // local, or an invalid location (out of bounds `block` index).
    first_assignment: IndexVec<mir::Local, Location>
}

impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
        let invalid_location =
            mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
        let mut analyzer = LocalAnalyzer {
            fx,
            dominators: fx.mir.dominators(),
            non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
            first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls)
        };

        // Arguments get assigned to by means of the function being called
        for arg in fx.mir.args_iter() {
            analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
        }

        analyzer
    }

    fn first_assignment(&self, local: mir::Local) -> Option<Location> {
        let location = self.first_assignment[local];
        if location.block.index() < self.fx.mir.basic_blocks().len() {
            Some(location)
        } else {
            None
        }
    }

    fn not_ssa(&mut self, local: mir::Local) {
        debug!("marking {:?} as non-SSA", local);
        self.non_ssa_locals.insert(local);
    }

    fn assign(&mut self, local: mir::Local, location: Location) {
        if self.first_assignment(local).is_some() {
            self.not_ssa(local);
        } else {
            self.first_assignment[local] = location;
        }
    }
}

impl<'mir, 'a: 'mir, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
    for LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn visit_assign(&mut self,
                    place: &mir::Place<'tcx>,
                    rvalue: &mir::Rvalue<'tcx>,
                    location: Location) {
        debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);

        if let mir::Place::Base(mir::PlaceBase::Local(index)) = *place {
            self.assign(index, location);
            if !self.fx.rvalue_creates_operand(rvalue) {
                self.not_ssa(index);
            }
        } else {
            self.visit_place(
                place,
                PlaceContext::MutatingUse(MutatingUseContext::Store),
                location
            );
        }

        self.visit_rvalue(rvalue, location);
    }

    fn visit_terminator_kind(&mut self,
                             kind: &mir::TerminatorKind<'tcx>,
                             location: Location) {
        let check = match *kind {
            mir::TerminatorKind::Call {
                func: mir::Operand::Constant(ref c),
                ref args, ..
            } => match c.ty.sty {
                ty::FnDef(did, _) => Some((did, args)),
                _ => None,
            },
            _ => None,
        };
        if let Some((def_id, args)) = check {
            if Some(def_id) == self.fx.cx.tcx().lang_items().box_free_fn() {
                // box_free(x) shares with `drop x` the property that it
                // is not guaranteed to be statically dominated by the
                // definition of x, so x must always be in an alloca.
                if let mir::Operand::Move(ref place) = args[0] {
                    self.visit_place(
                        place,
                        PlaceContext::MutatingUse(MutatingUseContext::Drop),
                        location
                    );
                }
            }
        }

        self.super_terminator_kind(kind, location);
    }

    fn visit_place(&mut self,
                   place: &mir::Place<'tcx>,
                   context: PlaceContext,
                   location: Location) {
        debug!("visit_place(place={:?}, context={:?})", place, context);
        let cx = self.fx.cx;

        if let mir::Place::Projection(ref proj) = *place {
            // Allow uses of projections that are ZSTs or from scalar fields.
            let is_consume = match context {
                PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
                PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
                _ => false
            };
            if is_consume {
                let base_ty = proj.base.ty(self.fx.mir, cx.tcx());
                let base_ty = self.fx.monomorphize(&base_ty);

                // ZSTs don't require any actual memory access.
                let elem_ty = base_ty
                    .projection_ty(cx.tcx(), &proj.elem)
                    .ty;
                let elem_ty = self.fx.monomorphize(&elem_ty);
                if cx.layout_of(elem_ty).is_zst() {
                    return;
                }

                if let mir::ProjectionElem::Field(..) = proj.elem {
                    let layout = cx.layout_of(base_ty.ty);
                    if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
                        // Recurse with the same context, instead of `Projection`,
                        // potentially stopping at non-operand projections,
                        // which would trigger `not_ssa` on locals.
                        self.visit_place(&proj.base, context, location);
                        return;
                    }
                }
            }

            // A deref projection only reads the pointer, never needs the place.
            if let mir::ProjectionElem::Deref = proj.elem {
                return self.visit_place(
                    &proj.base,
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                    location
                );
            }
        }

        self.super_place(place, context, location);
    }

    fn visit_local(&mut self,
                   &local: &mir::Local,
                   context: PlaceContext,
                   location: Location) {
        match context {
            PlaceContext::MutatingUse(MutatingUseContext::Call) => {
                self.assign(local, location);
            }

            PlaceContext::NonUse(_) |
            PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
                // Reads from uninitialized variables (e.g., in dead code, after
                // optimizations) require locals to be in (uninitialized) memory.
                // N.B., there can be uninitialized reads of a local visited after
                // an assignment to that local, if they happen on disjoint paths.
                let ssa_read = match self.first_assignment(local) {
                    Some(assignment_location) => {
                        assignment_location.dominates(location, &self.dominators)
                    }
                    None => false
                };
                if !ssa_read {
                    self.not_ssa(local);
                }
            }

            PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
            PlaceContext::MutatingUse(MutatingUseContext::Store) |
            PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
            PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
            PlaceContext::MutatingUse(MutatingUseContext::Projection) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) |
            PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
                self.not_ssa(local);
            }

            PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
                let ty = self.fx.mir.local_decls[local].ty;
                let ty = self.fx.monomorphize(&ty);

                // Only need the place if we're actually dropping it.
                if self.fx.cx.type_needs_drop(ty) {
                    self.not_ssa(local);
                }
            }
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CleanupKind {
    NotCleanup,
    Funclet,
    Internal { funclet: mir::BasicBlock }
}

impl CleanupKind {
    pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
        match self {
            CleanupKind::NotCleanup => None,
            CleanupKind::Funclet => Some(for_bb),
            CleanupKind::Internal { funclet } => Some(funclet),
        }
    }
}

pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Body<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
    fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                              mir: &mir::Body<'tcx>) {
        for (bb, data) in mir.basic_blocks().iter_enumerated() {
            match data.terminator().kind {
                TerminatorKind::Goto { .. } |
                TerminatorKind::Resume |
                TerminatorKind::Abort |
                TerminatorKind::Return |
                TerminatorKind::GeneratorDrop |
                TerminatorKind::Unreachable |
                TerminatorKind::SwitchInt { .. } |
                TerminatorKind::Yield { .. } |
                TerminatorKind::FalseEdges { .. } |
                TerminatorKind::FalseUnwind { .. } => {
                    /* nothing to do */
                }
                TerminatorKind::Call { cleanup: unwind, .. } |
                TerminatorKind::Assert { cleanup: unwind, .. } |
                TerminatorKind::DropAndReplace { unwind, .. } |
                TerminatorKind::Drop { unwind, .. } => {
                    if let Some(unwind) = unwind {
                        debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
                               bb, data, unwind);
                        result[unwind] = CleanupKind::Funclet;
                    }
                }
            }
        }
    }

    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                       mir: &mir::Body<'tcx>) {
        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());

        let mut set_successor = |funclet: mir::BasicBlock, succ| {
            match funclet_succs[funclet] {
                ref mut s @ None => {
                    debug!("set_successor: updating successor of {:?} to {:?}",
                           funclet, succ);
                    *s = Some(succ);
                },
                Some(s) => if s != succ {
                    span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
                              funclet, s, succ);
                }
            }
        };

        for (bb, data) in traversal::reverse_postorder(mir) {
            let funclet = match result[bb] {
                CleanupKind::NotCleanup => continue,
                CleanupKind::Funclet => bb,
                CleanupKind::Internal { funclet } => funclet,
            };

            debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
                   bb, data, result[bb], funclet);

            for &succ in data.terminator().successors() {
                let kind = result[succ];
                debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
                       funclet, succ, kind);
                match kind {
                    CleanupKind::NotCleanup => {
                        result[succ] = CleanupKind::Internal { funclet };
                    }
                    CleanupKind::Funclet => {
                        if funclet != succ {
                            set_successor(funclet, succ);
                        }
                    }
                    CleanupKind::Internal { funclet: succ_funclet } => {
                        if funclet != succ_funclet {
                            // `succ` has 2 different funclet going into it, so it must
                            // be a funclet by itself.

                            debug!("promoting {:?} to a funclet and updating {:?}", succ,
                                   succ_funclet);
                            result[succ] = CleanupKind::Funclet;
                            set_successor(succ_funclet, succ);
                            set_successor(funclet, succ);
                        }
                    }
                }
            }
        }
    }

    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());

    discover_masters(&mut result, mir);
    propagate(&mut result, mir);
    debug!("cleanup_kinds: result={:?}", result);
    result
}
<|endoftext|>


<commit_before>pub const VGA_BUFFER: *mut u8 = 0xb8000 as *mut u8;

#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
struct ColorCode(u8);

impl ColorCode {
    fn new(foreground: Color, background: Color) -> ColorCode {
        ColorCode((background as u8) << 4 | (foreground as u8))
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar {
    ascii_character: u8,
    color_code: ColorCode,
}

const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;

#[repr(transparent)]
struct Buffer {
    chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

pub struct Writer {
    column_position: usize,
    color_code: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column_position >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column_position;

                let color_code = self.color_code;
                self.buffer.chars[row][col] = ScreenChar {
                    ascii_character: byte,
                    color_code,
                };
                self.column_position += 1;
            }
        }
    }

    fn new_line(&mut self) {
        todo!()
    }

    pub fn write_string(&mut self, s: &str) {
        for byte in s.bytes() {
            match byte {
                // printable ASCII byte or newline
                0x20..=0x7e | b'\n' => self.write_byte(byte),
                // not part of printable ASCII range
                _ => self.write_byte(0xfe),
            }
        }
    }
}

pub fn print_something() {
    let mut writer = Writer {
        column_position: 0,
        color_code: ColorCode::new(Color::Yellow, Color::Black),
        buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
    };

    writer.write_byte(b'H');
    writer.write_string("ello ");
    writer.write_string("Wörld!");
}
<commit_msg>vga: nuke VGA_BUFFER<commit_after>
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
struct ColorCode(u8);

impl ColorCode {
    fn new(foreground: Color, background: Color) -> ColorCode {
        ColorCode((background as u8) << 4 | (foreground as u8))
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar {
    ascii_character: u8,
    color_code: ColorCode,
}

const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;

#[repr(transparent)]
struct Buffer {
    chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

pub struct Writer {
    column_position: usize,
    color_code: ColorCode,
    buffer: &'static mut Buffer,
}

impl Writer {
    pub fn write_byte(&mut self, byte: u8) {
        match byte {
            b'\n' => self.new_line(),
            byte => {
                if self.column_position >= BUFFER_WIDTH {
                    self.new_line();
                }

                let row = BUFFER_HEIGHT - 1;
                let col = self.column_position;

                let color_code = self.color_code;
                self.buffer.chars[row][col] = ScreenChar {
                    ascii_character: byte,
                    color_code,
                };
                self.column_position += 1;
            }
        }
    }

    fn new_line(&mut self) {
        todo!()
    }

    pub fn write_string(&mut self, s: &str) {
        for byte in s.bytes() {
            match byte {
                // printable ASCII byte or newline
                0x20..=0x7e | b'\n' => self.write_byte(byte),
                // not part of printable ASCII range
                _ => self.write_byte(0xfe),
            }
        }
    }
}

pub fn print_something() {
    let mut writer = Writer {
        column_position: 0,
        color_code: ColorCode::new(Color::Yellow, Color::Black),
        buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
    };

    writer.write_byte(b'H');
    writer.write_string("ello ");
    writer.write_string("Wörld!");
}
<|endoftext|>


<commit_before>//! Object safe interface for file watching and reading.
use std::fmt;

use paths::{AbsPath, AbsPathBuf};

/// A set of files on the file system.
#[derive(Debug, Clone)]
pub enum Entry {
    /// The `Entry` is represented by a raw set of files.
    Files(Vec<AbsPathBuf>),
    /// The `Entry` is represented by `Directories`.
    Directories(Directories),
}

/// Specifies a set of files on the file system.
///
/// A file is included if:
///   * it has included extension
///   * it is under an `include` path
///   * it is not under `exclude` path
///
/// If many include/exclude paths match, the longest one wins.
///
/// If a path is in both `include` and `exclude`, the `exclude` one wins.
#[derive(Debug, Clone, Default)]
pub struct Directories {
    pub extensions: Vec<String>,
    pub include: Vec<AbsPathBuf>,
    pub exclude: Vec<AbsPathBuf>,
}

/// [`Handle`]'s configuration.
#[derive(Debug)]
pub struct Config {
    /// Version number to associate progress updates to the right config
    /// version.
    pub version: u32,
    /// Set of initially loaded files.
    pub load: Vec<Entry>,
    /// Index of watched entries in `load`.
    ///
    /// If a path in a watched entry is modified,the [`Handle`] should notify it.
    pub watch: Vec<usize>,
}

/// Message about an action taken by a [`Handle`].
pub enum Message {
    /// Indicate a gradual progress.
    ///
    /// This is supposed to be the number of loaded files.
    Progress { n_total: usize, n_done: usize, config_version: u32 },
    /// The handle loaded the following files' content.
    Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
}

/// Type that will receive [`Messages`](Message) from a [`Handle`].
pub type Sender = Box<dyn Fn(Message) + Send>;

/// Interface for reading and watching files.
pub trait Handle: fmt::Debug {
    /// Spawn a new handle with the given `sender`.
    fn spawn(sender: Sender) -> Self
    where
        Self: Sized;

    /// Set this handle's configuration.
    fn set_config(&mut self, config: Config);

    /// The file's content at `path` has been modified, and should be reloaded.
    fn invalidate(&mut self, path: AbsPathBuf);

    /// Load the content of the given file, returning [`None`] if it does not
    /// exists.
    fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
}

impl Entry {
    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git],
    /// })
    /// ```
    pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, base/target],
    /// })
    /// ```
    pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "target"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, /tests, /examples, /benches],
    /// })
    /// ```
    pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"]))
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// See [`Directories::contains_file`].
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(files) => files.iter().any(|it| it == path),
            Entry::Directories(dirs) => dirs.contains_file(path),
        }
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// - If `self` is `Entry::Files`, returns `false`
    /// - Else, see [`Directories::contains_dir`].
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(_) => false,
            Entry::Directories(dirs) => dirs.contains_dir(path),
        }
    }
}

impl Directories {
    /// Returns `true` if `path` is included in `self`.
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        let ext = path.extension().unwrap_or_default();
        if self.extensions.iter().all(|it| it.as_str() != ext) {
            return false;
        }
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// Since `path` is supposed to be a directory, this will not take extension
    /// into account.
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// It is included if
    ///   - An element in `self.include` is a prefix of `path`.
    ///   - This path is longer than any element in `self.exclude` that is a prefix
    ///     of `path`. In case of equality, exclusion wins.
    fn includes_path(&self, path: &AbsPath) -> bool {
        let include = self.include.iter().fold(None::<&AbsPathBuf>, |include, incl| {
            if !path.starts_with(incl) {
                return include;
            }

            Some(match include {
                Some(prev) if prev.starts_with(incl) => prev,
                _ => incl,
            })
        });

        let include = match include {
            Some(it) => it,
            None => return false,
        };

        !self.exclude.iter().any(|excl| path.starts_with(excl) && excl.starts_with(include))
    }
}

/// Returns :
/// ```text
/// Directories {
///     extensions: ["rs"],
///     include: [base],
///     exclude: [base/<exclude>],
/// }
/// ```
fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories {
    let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>();
    Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude }
}

impl fmt::Debug for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Message::Loaded { files } => {
                f.debug_struct("Loaded").field("n_files", &files.len()).finish()
            }
            Message::Progress { n_total, n_done, config_version } => f
                .debug_struct("Progress")
                .field("n_total", n_total)
                .field("n_done", n_done)
                .field("config_version", config_version)
                .finish(),
        }
    }
}

#[test]
fn handle_is_object_safe() {
    fn _assert(_: &dyn Handle) {}
}
<commit_msg>Chore: add comments to explicitly express two-step check<commit_after>//! Object safe interface for file watching and reading.
use std::fmt;

use paths::{AbsPath, AbsPathBuf};

/// A set of files on the file system.
#[derive(Debug, Clone)]
pub enum Entry {
    /// The `Entry` is represented by a raw set of files.
    Files(Vec<AbsPathBuf>),
    /// The `Entry` is represented by `Directories`.
    Directories(Directories),
}

/// Specifies a set of files on the file system.
///
/// A file is included if:
///   * it has included extension
///   * it is under an `include` path
///   * it is not under `exclude` path
///
/// If many include/exclude paths match, the longest one wins.
///
/// If a path is in both `include` and `exclude`, the `exclude` one wins.
#[derive(Debug, Clone, Default)]
pub struct Directories {
    pub extensions: Vec<String>,
    pub include: Vec<AbsPathBuf>,
    pub exclude: Vec<AbsPathBuf>,
}

/// [`Handle`]'s configuration.
#[derive(Debug)]
pub struct Config {
    /// Version number to associate progress updates to the right config
    /// version.
    pub version: u32,
    /// Set of initially loaded files.
    pub load: Vec<Entry>,
    /// Index of watched entries in `load`.
    ///
    /// If a path in a watched entry is modified,the [`Handle`] should notify it.
    pub watch: Vec<usize>,
}

/// Message about an action taken by a [`Handle`].
pub enum Message {
    /// Indicate a gradual progress.
    ///
    /// This is supposed to be the number of loaded files.
    Progress { n_total: usize, n_done: usize, config_version: u32 },
    /// The handle loaded the following files' content.
    Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
}

/// Type that will receive [`Messages`](Message) from a [`Handle`].
pub type Sender = Box<dyn Fn(Message) + Send>;

/// Interface for reading and watching files.
pub trait Handle: fmt::Debug {
    /// Spawn a new handle with the given `sender`.
    fn spawn(sender: Sender) -> Self
    where
        Self: Sized;

    /// Set this handle's configuration.
    fn set_config(&mut self, config: Config);

    /// The file's content at `path` has been modified, and should be reloaded.
    fn invalidate(&mut self, path: AbsPathBuf);

    /// Load the content of the given file, returning [`None`] if it does not
    /// exists.
    fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
}

impl Entry {
    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git],
    /// })
    /// ```
    pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, base/target],
    /// })
    /// ```
    pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "target"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, /tests, /examples, /benches],
    /// })
    /// ```
    pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"]))
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// See [`Directories::contains_file`].
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(files) => files.iter().any(|it| it == path),
            Entry::Directories(dirs) => dirs.contains_file(path),
        }
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// - If `self` is `Entry::Files`, returns `false`
    /// - Else, see [`Directories::contains_dir`].
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(_) => false,
            Entry::Directories(dirs) => dirs.contains_dir(path),
        }
    }
}

impl Directories {
    /// Returns `true` if `path` is included in `self`.
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        // First, check the file extension...
        let ext = path.extension().unwrap_or_default();
        if self.extensions.iter().all(|it| it.as_str() != ext) {
            return false;
        }

        // Then, check for path inclusion...
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// Since `path` is supposed to be a directory, this will not take extension
    /// into account.
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// It is included if
    ///   - An element in `self.include` is a prefix of `path`.
    ///   - This path is longer than any element in `self.exclude` that is a prefix
    ///     of `path`. In case of equality, exclusion wins.
    fn includes_path(&self, path: &AbsPath) -> bool {
        let include = self.include.iter().fold(None::<&AbsPathBuf>, |include, incl| {
            if !path.starts_with(incl) {
                return include;
            }

            Some(match include {
                Some(prev) if prev.starts_with(incl) => prev,
                _ => incl,
            })
        });

        let include = match include {
            Some(it) => it,
            None => return false,
        };

        !self.exclude.iter().any(|excl| path.starts_with(excl) && excl.starts_with(include))
    }
}

/// Returns :
/// ```text
/// Directories {
///     extensions: ["rs"],
///     include: [base],
///     exclude: [base/<exclude>],
/// }
/// ```
fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories {
    let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>();
    Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude }
}

impl fmt::Debug for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Message::Loaded { files } => {
                f.debug_struct("Loaded").field("n_files", &files.len()).finish()
            }
            Message::Progress { n_total, n_done, config_version } => f
                .debug_struct("Progress")
                .field("n_total", n_total)
                .field("n_done", n_done)
                .field("config_version", config_version)
                .finish(),
        }
    }
}

#[test]
fn handle_is_object_safe() {
    fn _assert(_: &dyn Handle) {}
}
<|endoftext|>