<commit_before>use std::io::{self, BufRead, Read};
use std::marker::PhantomData;

use super::{ROOT_STATE, PatIdx, StateIdx};

/// An abstraction over automatons and their corresponding iterators.
/// The type parameter `P` is the type of the pattern that was used to
/// construct this Automaton.
pub trait Automaton<P>: Sized {
    /// Return the next state given the current state and next character.
    fn next_state(&self, si: StateIdx, b: u8) -> StateIdx;

    /// Return true if and only if the given state and current pattern index
    /// indicate a match.
    fn has_match(&self, si: StateIdx, outi: PatIdx) -> bool;

    /// Build a match given the current state, pattern index and input index.
    fn get_match(&self, si: StateIdx, outi: PatIdx, texti: usize) -> Match;

    /// Attempt to skip through the input.
    ///
    /// This returns the index into `text` at which the next match attempt
    /// should start. (If no skipping occurred, then the return value should
    /// be equal to `at`.)
    ///
    /// Finally, if no match is possible, then return `text.len()`.
    fn skip_to(&self, si: StateIdx, text: &[u8], at: usize) -> usize;

    /// Returns true if and only if this automaton can skip through the input.
    fn is_skippable(&self) -> bool;

    /// Returns all of the patterns matched by this automaton.
    ///
    /// The order of the patterns is the order in which they were added.
    fn patterns(&self) -> &[P];

    /// Returns the pattern indexed at `i`.
    ///
    /// The index corresponds to the position at which the pattern was added
    /// to the automaton, starting at `0`.
    fn pattern(&self, i: usize) -> &P;

    /// Return the number of patterns in the automaton.
    #[inline]
    fn len(&self) -> usize {
        self.patterns().len()
    }

    /// Returns true if the automaton has no patterns.
    #[inline]
    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator of non-overlapping matches in `s`.
    fn find<'a, 's>(
        &'a self,
        s: &'s str,
    ) -> Matches<'a, 's, P, Self> {
        Matches {
            aut: self,
            text: s.as_bytes(),
            texti: 0,
            si: ROOT_STATE,
            _m: PhantomData,
        }
    }

    /// Returns an iterator of overlapping matches in `s`.
    fn find_overlapping<'a, 's>(
        &'a self,
        s: &'s str,
    ) -> MatchesOverlapping<'a, 's, P, Self> {
        MatchesOverlapping {
            aut: self,
            text: s.as_bytes(),
            texti: 0,
            si: ROOT_STATE,
            outi: 0,
            _m: PhantomData,
        }
    }

    /// Returns an iterator of non-overlapping matches in the given reader.
    fn stream_find<'a, R: io::Read>(
        &'a self,
        rdr: R,
    ) -> StreamMatches<'a, R, P, Self> {
        StreamMatches {
            aut: self,
            buf: io::BufReader::new(rdr),
            texti: 0,
            si: ROOT_STATE,
            _m: PhantomData,
        }
    }

    /// Returns an iterator of overlapping matches in the given reader.
    fn stream_find_overlapping<'a, R: io::Read>(
        &'a self,
        rdr: R,
    ) -> StreamMatchesOverlapping<'a, R, P, Self> {
        StreamMatchesOverlapping {
            aut: self,
            buf: io::BufReader::new(rdr),
            texti: 0,
            si: ROOT_STATE,
            outi: 0,
            _m: PhantomData,
        }
    }
}

/// Records a match in the search text.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Match {
    /// The pattern index.
    ///
    /// This corresponds to the ordering in which the matched pattern was
    /// added to the automaton, starting at `0`.
    pub pati: usize,
    /// The starting byte offset of the match in the search text.
    pub start: usize,
    /// The ending byte offset of the match in the search text.
    ///
    /// (This can be re-captiulated with `pati` and adding the pattern's
    /// length to `start`, but it is convenient to have it here.)
    pub end: usize,
}

/// An iterator of non-overlapping matches for in-memory text.
///
/// This iterator yields `Match` values.
///
/// `'a` is the lifetime of the automaton, `'s` is the lifetime of the
/// search text, and `P` is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct Matches<'a, 's, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    text: &'s [u8],
    texti: usize,
    si: StateIdx,
    _m: PhantomData<P>,
}

// When there's an initial lone start byte, it is usually worth it
// to use `memchr` to skip along the input. The problem is that
// the skipping function is called in the inner match loop, which
// can be quite costly if the skipping condition is never met.
// Therefore, we lift the case analysis outside of the inner loop at
// the cost of repeating code.
//
// `step_to_match` is the version of the inner loop without skipping,
// and `skip_to_match` is the version with skipping.
fn step_to_match<P, A: Automaton<P>>(
    aut: &A,
    text: &[u8],
    mut texti: usize,
    mut si: StateIdx
) -> Option<(usize, StateIdx)> {
    while texti < text.len() {
        si = aut.next_state(si, text[texti]);
        if aut.has_match(si, 0) {
            return Some((texti, si));
        }
        texti += 1;
    }
    None
}

fn skip_to_match<P, A: Automaton<P>>(
    aut: &A,
    text: &[u8],
    mut texti: usize,
    mut si: StateIdx
) -> Option<(usize, StateIdx)> {
    texti = aut.skip_to(si, text, texti);
    while texti < text.len() {
        si = aut.next_state(si, text[texti]);
        if aut.has_match(si, 0) {
            return Some((texti, si));
        }
        texti = aut.skip_to(si, text, texti + 1);
    }
    None
}

impl<'a, 's, P, A: Automaton<P>> Iterator for Matches<'a, 's, P, A> {
    type Item = Match;

    fn next(&mut self) -> Option<Match> {
        if self.aut.is_skippable() {
            if let Some((texti, si)) = skip_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti + 1;
                self.si = ROOT_STATE;
                return Some(self.aut.get_match(si, 0, texti));
            }
        } else {
            if let Some((texti, si)) = step_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti + 1;
                self.si = ROOT_STATE;
                return Some(self.aut.get_match(si, 0, texti));
            }
        }
        None
    }
}

/// An iterator of non-overlapping matches for streaming text.
///
/// This iterator yields `io::Result<Match>` values.
///
/// `'a` is the lifetime of the automaton, `R` is the type of the underlying
/// `io::Read`er, and P is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct StreamMatches<'a, R, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    buf: io::BufReader<R>,
    texti: usize,
    si: StateIdx,
    _m: PhantomData<P>,
}

impl<'a, R: io::Read, P, A: Automaton<P>> Iterator for StreamMatches<'a, R, P, A> {
    type Item = io::Result<Match>;

    fn next(&mut self) -> Option<io::Result<Match>> {
        let mut m = None;
        let mut consumed = 0;
'LOOP:  loop {
            self.buf.consume(consumed);
            let bs = match self.buf.fill_buf() {
                Err(err) => return Some(Err(err)),
                Ok(bs) if bs.len() == 0 => break,
                Ok(bs) => bs,
            };
            consumed = bs.len(); // is shortened if we find a match
            for (i, &b) in bs.iter().enumerate() {
                self.si = self.aut.next_state(self.si, b);
                if self.aut.has_match(self.si, 0) {
                    m = Some(Ok(self.aut.get_match(self.si, 0, self.texti)));
                    consumed = i + 1;
                    self.texti += 1;
                    self.si = ROOT_STATE;
                    break 'LOOP;
                }
                self.texti += 1;
            }
        }
        self.buf.consume(consumed);
        m
    }
}

/// An iterator of overlapping matches for in-memory text.
///
/// This iterator yields `Match` values.
///
/// `'a` is the lifetime of the automaton, `'s` is the lifetime of the
/// search text, and `P` is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct MatchesOverlapping<'a, 's, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    text: &'s [u8],
    texti: usize,
    si: StateIdx,
    outi: usize,
    _m: PhantomData<P>,
}

impl<'a, 's, P, A: Automaton<P>> Iterator for MatchesOverlapping<'a, 's, P, A> {
    type Item = Match;

    fn next(&mut self) -> Option<Match> {
        if self.aut.has_match(self.si, self.outi) {
            let m = self.aut.get_match(self.si, self.outi, self.texti);
            self.outi += 1;
            if !self.aut.has_match(self.si, self.outi) {
                self.texti += 1;
            }
            return Some(m);
        }

        self.outi = 0;
        if self.aut.is_skippable() {
            if let Some((texti, si)) = skip_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti;
                self.si = si;
                return self.next();
            }
        } else {
            if let Some((texti, si)) = step_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti;
                self.si = si;
                return self.next();
            }
        }
        None
    }
}

/// An iterator of overlapping matches for streaming text.
///
/// This iterator yields `io::Result<Match>` values.
///
/// `'a` is the lifetime of the automaton, `R` is the type of the underlying
/// `io::Read`er, and P is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct StreamMatchesOverlapping<'a, R, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    buf: io::BufReader<R>,
    texti: usize,
    si: StateIdx,
    outi: usize,
    _m: PhantomData<P>,
}

impl<
    'a,
    R: io::Read,
    P,
    A: Automaton<P>,
> Iterator for StreamMatchesOverlapping<'a, R, P, A> {
    type Item = io::Result<Match>;

    fn next(&mut self) -> Option<io::Result<Match>> {
        if self.aut.has_match(self.si, self.outi) {
            let m = self.aut.get_match(self.si, self.outi, self.texti);
            self.outi += 1;
            if !self.aut.has_match(self.si, self.outi) {
                self.texti += 1;
            }
            return Some(Ok(m));
        }
        let mut m = None;
        let mut consumed = 0;
        self.outi = 0;
'LOOP:  loop {
            self.buf.consume(consumed);
            let bs = match self.buf.fill_buf() {
                Err(err) => return Some(Err(err)),
                Ok(bs) if bs.len() == 0 => break,
                Ok(bs) => bs,
            };
            consumed = bs.len(); // is shortened if we find a match
            for (i, &b) in bs.iter().enumerate() {
                self.si = self.aut.next_state(self.si, b);
                if self.aut.has_match(self.si, self.outi) {
                    m = Some(Ok(self.aut.get_match(self.si, self.outi,
                                                   self.texti)));
                    consumed = i + 1;
                    self.outi += 1;
                    if !self.aut.has_match(self.si, self.outi) {
                        self.texti += 1;
                    }
                    break 'LOOP;
                }
                self.texti += 1;
            }
        }
        self.buf.consume(consumed);
        m
    }
}
<commit_msg>Allow searching in byte slices (and more).<commit_after>use std::io::{self, BufRead, Read};
use std::marker::PhantomData;

use super::{ROOT_STATE, PatIdx, StateIdx};

/// An abstraction over automatons and their corresponding iterators.
/// The type parameter `P` is the type of the pattern that was used to
/// construct this Automaton.
pub trait Automaton<P>: Sized {
    /// Return the next state given the current state and next character.
    fn next_state(&self, si: StateIdx, b: u8) -> StateIdx;

    /// Return true if and only if the given state and current pattern index
    /// indicate a match.
    fn has_match(&self, si: StateIdx, outi: PatIdx) -> bool;

    /// Build a match given the current state, pattern index and input index.
    fn get_match(&self, si: StateIdx, outi: PatIdx, texti: usize) -> Match;

    /// Attempt to skip through the input.
    ///
    /// This returns the index into `text` at which the next match attempt
    /// should start. (If no skipping occurred, then the return value should
    /// be equal to `at`.)
    ///
    /// Finally, if no match is possible, then return `text.len()`.
    fn skip_to(&self, si: StateIdx, text: &[u8], at: usize) -> usize;

    /// Returns true if and only if this automaton can skip through the input.
    fn is_skippable(&self) -> bool;

    /// Returns all of the patterns matched by this automaton.
    ///
    /// The order of the patterns is the order in which they were added.
    fn patterns(&self) -> &[P];

    /// Returns the pattern indexed at `i`.
    ///
    /// The index corresponds to the position at which the pattern was added
    /// to the automaton, starting at `0`.
    fn pattern(&self, i: usize) -> &P;

    /// Return the number of patterns in the automaton.
    #[inline]
    fn len(&self) -> usize {
        self.patterns().len()
    }

    /// Returns true if the automaton has no patterns.
    #[inline]
    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator of non-overlapping matches in `s`.
    fn find<'a, 's, Q: ?Sized + AsRef<[u8]>>(
        &'a self,
        s: &'s Q,
    ) -> Matches<'a, 's, P, Self> {
        Matches {
            aut: self,
            text: s.as_ref(),
            texti: 0,
            si: ROOT_STATE,
            _m: PhantomData,
        }
    }

    /// Returns an iterator of overlapping matches in `s`.
    fn find_overlapping<'a, 's, Q: ?Sized + AsRef<[u8]>>(
        &'a self,
        s: &'s Q,
    ) -> MatchesOverlapping<'a, 's, P, Self> {
        MatchesOverlapping {
            aut: self,
            text: s.as_ref(),
            texti: 0,
            si: ROOT_STATE,
            outi: 0,
            _m: PhantomData,
        }
    }

    /// Returns an iterator of non-overlapping matches in the given reader.
    fn stream_find<'a, R: io::Read>(
        &'a self,
        rdr: R,
    ) -> StreamMatches<'a, R, P, Self> {
        StreamMatches {
            aut: self,
            buf: io::BufReader::new(rdr),
            texti: 0,
            si: ROOT_STATE,
            _m: PhantomData,
        }
    }

    /// Returns an iterator of overlapping matches in the given reader.
    fn stream_find_overlapping<'a, R: io::Read>(
        &'a self,
        rdr: R,
    ) -> StreamMatchesOverlapping<'a, R, P, Self> {
        StreamMatchesOverlapping {
            aut: self,
            buf: io::BufReader::new(rdr),
            texti: 0,
            si: ROOT_STATE,
            outi: 0,
            _m: PhantomData,
        }
    }
}

/// Records a match in the search text.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Match {
    /// The pattern index.
    ///
    /// This corresponds to the ordering in which the matched pattern was
    /// added to the automaton, starting at `0`.
    pub pati: usize,
    /// The starting byte offset of the match in the search text.
    pub start: usize,
    /// The ending byte offset of the match in the search text.
    ///
    /// (This can be re-captiulated with `pati` and adding the pattern's
    /// length to `start`, but it is convenient to have it here.)
    pub end: usize,
}

/// An iterator of non-overlapping matches for in-memory text.
///
/// This iterator yields `Match` values.
///
/// `'a` is the lifetime of the automaton, `'s` is the lifetime of the
/// search text, and `P` is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct Matches<'a, 's, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    text: &'s [u8],
    texti: usize,
    si: StateIdx,
    _m: PhantomData<P>,
}

// When there's an initial lone start byte, it is usually worth it
// to use `memchr` to skip along the input. The problem is that
// the skipping function is called in the inner match loop, which
// can be quite costly if the skipping condition is never met.
// Therefore, we lift the case analysis outside of the inner loop at
// the cost of repeating code.
//
// `step_to_match` is the version of the inner loop without skipping,
// and `skip_to_match` is the version with skipping.
fn step_to_match<P, A: Automaton<P>>(
    aut: &A,
    text: &[u8],
    mut texti: usize,
    mut si: StateIdx
) -> Option<(usize, StateIdx)> {
    while texti < text.len() {
        si = aut.next_state(si, text[texti]);
        if aut.has_match(si, 0) {
            return Some((texti, si));
        }
        texti += 1;
    }
    None
}

fn skip_to_match<P, A: Automaton<P>>(
    aut: &A,
    text: &[u8],
    mut texti: usize,
    mut si: StateIdx
) -> Option<(usize, StateIdx)> {
    texti = aut.skip_to(si, text, texti);
    while texti < text.len() {
        si = aut.next_state(si, text[texti]);
        if aut.has_match(si, 0) {
            return Some((texti, si));
        }
        texti = aut.skip_to(si, text, texti + 1);
    }
    None
}

impl<'a, 's, P, A: Automaton<P>> Iterator for Matches<'a, 's, P, A> {
    type Item = Match;

    fn next(&mut self) -> Option<Match> {
        if self.aut.is_skippable() {
            if let Some((texti, si)) = skip_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti + 1;
                self.si = ROOT_STATE;
                return Some(self.aut.get_match(si, 0, texti));
            }
        } else {
            if let Some((texti, si)) = step_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti + 1;
                self.si = ROOT_STATE;
                return Some(self.aut.get_match(si, 0, texti));
            }
        }
        None
    }
}

/// An iterator of non-overlapping matches for streaming text.
///
/// This iterator yields `io::Result<Match>` values.
///
/// `'a` is the lifetime of the automaton, `R` is the type of the underlying
/// `io::Read`er, and P is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct StreamMatches<'a, R, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    buf: io::BufReader<R>,
    texti: usize,
    si: StateIdx,
    _m: PhantomData<P>,
}

impl<'a, R: io::Read, P, A: Automaton<P>> Iterator for StreamMatches<'a, R, P, A> {
    type Item = io::Result<Match>;

    fn next(&mut self) -> Option<io::Result<Match>> {
        let mut m = None;
        let mut consumed = 0;
'LOOP:  loop {
            self.buf.consume(consumed);
            let bs = match self.buf.fill_buf() {
                Err(err) => return Some(Err(err)),
                Ok(bs) if bs.len() == 0 => break,
                Ok(bs) => bs,
            };
            consumed = bs.len(); // is shortened if we find a match
            for (i, &b) in bs.iter().enumerate() {
                self.si = self.aut.next_state(self.si, b);
                if self.aut.has_match(self.si, 0) {
                    m = Some(Ok(self.aut.get_match(self.si, 0, self.texti)));
                    consumed = i + 1;
                    self.texti += 1;
                    self.si = ROOT_STATE;
                    break 'LOOP;
                }
                self.texti += 1;
            }
        }
        self.buf.consume(consumed);
        m
    }
}

/// An iterator of overlapping matches for in-memory text.
///
/// This iterator yields `Match` values.
///
/// `'a` is the lifetime of the automaton, `'s` is the lifetime of the
/// search text, and `P` is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct MatchesOverlapping<'a, 's, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    text: &'s [u8],
    texti: usize,
    si: StateIdx,
    outi: usize,
    _m: PhantomData<P>,
}

impl<'a, 's, P, A: Automaton<P>> Iterator for MatchesOverlapping<'a, 's, P, A> {
    type Item = Match;

    fn next(&mut self) -> Option<Match> {
        if self.aut.has_match(self.si, self.outi) {
            let m = self.aut.get_match(self.si, self.outi, self.texti);
            self.outi += 1;
            if !self.aut.has_match(self.si, self.outi) {
                self.texti += 1;
            }
            return Some(m);
        }

        self.outi = 0;
        if self.aut.is_skippable() {
            if let Some((texti, si)) = skip_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti;
                self.si = si;
                return self.next();
            }
        } else {
            if let Some((texti, si)) = step_to_match(self.aut, self.text, self.texti, self.si) {
                self.texti = texti;
                self.si = si;
                return self.next();
            }
        }
        None
    }
}

/// An iterator of overlapping matches for streaming text.
///
/// This iterator yields `io::Result<Match>` values.
///
/// `'a` is the lifetime of the automaton, `R` is the type of the underlying
/// `io::Read`er, and P is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct StreamMatchesOverlapping<'a, R, P, A: 'a + Automaton<P>> {
    aut: &'a A,
    buf: io::BufReader<R>,
    texti: usize,
    si: StateIdx,
    outi: usize,
    _m: PhantomData<P>,
}

impl<
    'a,
    R: io::Read,
    P,
    A: Automaton<P>,
> Iterator for StreamMatchesOverlapping<'a, R, P, A> {
    type Item = io::Result<Match>;

    fn next(&mut self) -> Option<io::Result<Match>> {
        if self.aut.has_match(self.si, self.outi) {
            let m = self.aut.get_match(self.si, self.outi, self.texti);
            self.outi += 1;
            if !self.aut.has_match(self.si, self.outi) {
                self.texti += 1;
            }
            return Some(Ok(m));
        }
        let mut m = None;
        let mut consumed = 0;
        self.outi = 0;
'LOOP:  loop {
            self.buf.consume(consumed);
            let bs = match self.buf.fill_buf() {
                Err(err) => return Some(Err(err)),
                Ok(bs) if bs.len() == 0 => break,
                Ok(bs) => bs,
            };
            consumed = bs.len(); // is shortened if we find a match
            for (i, &b) in bs.iter().enumerate() {
                self.si = self.aut.next_state(self.si, b);
                if self.aut.has_match(self.si, self.outi) {
                    m = Some(Ok(self.aut.get_match(self.si, self.outi,
                                                   self.texti)));
                    consumed = i + 1;
                    self.outi += 1;
                    if !self.aut.has_match(self.si, self.outi) {
                        self.texti += 1;
                    }
                    break 'LOOP;
                }
                self.texti += 1;
            }
        }
        self.buf.consume(consumed);
        m
    }
}
<|endoftext|>


<commit_before>use rustbox;
use yoga;
use yoga_wrapper;

use yoga::Renders;
use yoga::Renderable;

use measurer;
use renderer;

pub struct Backend<'rbox> {
    renderer: renderer::Renderer<'rbox>,
    measurer: measurer::Measurer,
}

impl<'rbox> Backend<'rbox> {
    pub fn new(rustbox: &rustbox::RustBox) -> Backend {
        Backend {
            renderer: renderer::Renderer::new(rustbox),
            measurer: measurer::Measurer {},
        }
    }
}

impl<'rbox, 'meas> yoga::Backend<'meas> for Backend<'rbox> {
    type Color = i32;
    type Renderer = renderer::Renderer<'rbox>;
    type Measurer = measurer::Measurer;

    fn render(&mut self, node: &yoga::Renderable) {
        let renderer = self.get_renderer();
        renderer.render(node);
        renderer.rustbox.present();
    }

    fn get_renderer(&mut self) -> &mut renderer::Renderer<'rbox> {
        &mut self.renderer
    }

    fn create_context<'text>(&'meas self, text: &'text str) -> yoga_wrapper::Context<'text, 'meas> {
        yoga_wrapper::Context::new(text, &self.measurer)
    }
}
<commit_msg>chore(backend): no need for measurer<commit_after>use rustbox;
use yoga;
use yoga_wrapper;

use yoga::Renders;
use yoga::Renderable;

use measurer;
use renderer;

pub struct Backend<'rbox> {
    renderer: renderer::Renderer<'rbox>,
    measurer: measurer::Measurer,
}

impl<'rbox> Backend<'rbox> {
    pub fn new(rustbox: &rustbox::RustBox) -> Backend {
        Backend {
            renderer: renderer::Renderer::new(rustbox),
            measurer: measurer::Measurer {},
        }
    }
}

impl<'rbox, 'meas> yoga::Backend<'meas> for Backend<'rbox> {
    type Color = i32;
    type Renderer = renderer::Renderer<'rbox>;

    fn render(&mut self, node: &yoga::Renderable) {
        let renderer = self.get_renderer();
        renderer.render(node);
        renderer.rustbox.present();
    }

    fn get_renderer(&mut self) -> &mut renderer::Renderer<'rbox> {
        &mut self.renderer
    }

    fn create_context<'text>(&'meas self, text: &'text str) -> yoga_wrapper::Context<'text, 'meas> {
        yoga_wrapper::Context::new(text, &self.measurer)
    }
}
<|endoftext|>


<commit_before>//! A Gnu Hash table as 4 sections:
//!
//!  1. Header
//!  2. Bloom Filter
//!  3. Hash Buckets
//!  4. Chains
//!
//! The header has is an array of four `u32`s:
//!
//!  1. nbuckets
//!  2. symndx
//!  3. maskwords
//!  4. shift2
//!
//! See more:
//!  * http://www.linker-aliens.org/blogs/ali/entry/gnu_hash_elf_sections
//!    or https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2
//!  * https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/

/// GNU hash function: accepts a symbol name and returns a value that may be
/// used to compute a bucket index.
///
/// Consequently, if the hashing function returns the value `x` for some name,
/// `buckets[x % nbuckets]` gives an index, `y`, into both the symbol table
/// and the chain table.
pub fn hash(symbol: &str) -> u32 {
    const HASH_SEED: u32 = 5381;
    symbol
        .bytes()
        .fold(HASH_SEED, |hash, b| hash.wrapping_mul(33).wrapping_add(u32::from(b)))
}

#[cfg(test)]
mod tests {
    use super::hash;
    #[test]
    fn test_hash() {
        assert_eq!(hash(""), 0x0000_1505);
        assert_eq!(hash("printf"), 0x156b_2bb8);
        assert_eq!(hash("exit"), 0x7c96_7e3f);
        assert_eq!(hash("syscall"), 0xbac2_12a0);
        assert_eq!(hash("flapenguin.me"), 0x8ae9_f18e);
    }
}

macro_rules! elf_gnu_hash_impl {
    ($IntTy:ty) => {
        use crate::elf::sym::Sym;
        use crate::strtab::Strtab;
        use core::fmt;
        use core::mem;
        use core::slice;

        const INT_SIZE: usize = mem::size_of::<$IntTy>();
        const U32_SIZE: usize = mem::size_of::<u32>();
        /// Size of a bits mask in bloom filter
        const ELFCLASS_BITS: u32 = INT_SIZE as u32 * 8;

        /// A better hash table for the ELF used by GNU systems in GNU-compatible software.
        pub struct GnuHash<'a> {
            /// Index of the first symbol in the `.dynsym` table which is accessible with
            /// the hash table
            symindex: u32,
            /// Shift count used in the bloom filter
            shift2: u32,
            /// 2 bit bloom filter on `chains`
            // Either 32 or 64-bit depending on the class of object
            bloom_filter: &'a [$IntTy],
            /// GNU hash table bucket array; indexes start at 0. This array holds symbol
            /// table indexes and contains the index of hashes in `chains`
            buckets: &'a [u32],
            /// Hash values; indexes start at 0. This array holds symbol table indexes.
            chains: &'a [u32], // => chains[dynsyms.len() - symindex]
            dynsyms: &'a [Sym],
        }

        impl fmt::Debug for GnuHash<'_> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.debug_struct("GnuHash")
                    .field("nbuckets", &self.buckets.len())
                    .field("symindex", &self.symindex)
                    .field("maskwords", &(self.bloom_filter.len() - 1))
                    .field("shift2", &self.shift2)
                    .field("bloom_filter", &self.bloom_filter.as_ptr())
                    .field("bucket", &self.buckets.as_ptr())
                    .field("chains", &self.chains.as_ptr())
                    .finish()
            }
        }

        impl<'a> GnuHash<'a> {
            /// Initialize a GnuHash from a pointer to `.hash` (or `.gnu.hash`) section
            /// and total number of dynamic symbols.
            pub unsafe fn from_raw_table(hashtab: &'a [u8], dynsyms: &'a [Sym]) -> Result<Self, &'static str> {
                if hashtab.as_ptr() as usize % INT_SIZE != 0 {
                    return Err("hashtab is not aligned with 64-bit");
                }

                if hashtab.len() <= 16 {
                    return Err("failed to read in number of buckets");
                }

                let [nbuckets, symindex, maskwords, shift2] =
                    (hashtab.as_ptr() as *const u32 as *const [u32; 4]).read();

                if !maskwords.is_power_of_two() {
                    return Err("maskwords must be a power of two");
                }

                let hashtab = &hashtab[16..];
                {
                    // SAFETY: Condition to check for an overflow
                    //   size_of(chains) + size_of(buckets) + size_of(bloom_filter) == size_of(hashtab)

                    if dynsyms.len() <= symindex as usize {
                        return Err("symindex must be smaller than dynsyms.len()");
                    }
                    let chains_size = (dynsyms.len() - symindex as usize).checked_mul(U32_SIZE);
                    let buckets_size = (nbuckets as usize).checked_mul(U32_SIZE);
                    let bloom_size = (maskwords as usize).checked_mul(INT_SIZE);

                    let total_size = match (chains_size, buckets_size, bloom_size) {
                        (Some(a), Some(b), Some(c)) => a.checked_add(b).and_then(|t| t.checked_add(c)),
                        _ => None,
                    };
                    match total_size {
                        Some(size) if size == hashtab.len() => {}
                        _ => return Err("index out of bound or non-complete hash section"),
                    }
                }

                let bloom_filter_ptr = hashtab.as_ptr() as *const $IntTy;
                let buckets_ptr = bloom_filter_ptr.add(maskwords as usize) as *const u32;
                let chains_ptr = buckets_ptr.add(nbuckets as usize);
                let bloom_filter = slice::from_raw_parts(bloom_filter_ptr, maskwords as usize);
                let buckets = slice::from_raw_parts(buckets_ptr, nbuckets as usize);
                let chains = slice::from_raw_parts(chains_ptr, dynsyms.len() - symindex as usize);
                Ok(Self {
                    symindex,
                    shift2,
                    bloom_filter,
                    buckets,
                    chains,
                    dynsyms,
                })
            }

            /// Locate the hash chain, and corresponding hash value element.
            fn lookup(&self, symbol: &str, hash: u32, dynstrtab: &Strtab) -> Option<&'a Sym> {
                const MASK_LOWEST_BIT: u32 = 0xffff_fffe;
                let bucket = self.buckets[hash as usize % self.buckets.len()];

                // Empty hash chain, symbol not present
                if bucket < self.symindex {
                    return None;
                }
                // Walk the chain until the symbol is found or the chain is exhausted.
                let chain_idx = bucket - self.symindex;
                let hash = hash & MASK_LOWEST_BIT;
                let chains = &self.chains.get((chain_idx as usize)..);
                let dynsyms = &self.dynsyms.get((bucket as usize)..);
                if let (Some(chains), Some(dynsyms)) = (chains, dynsyms) {
                    for (hash2, symb) in chains.iter().zip(dynsyms.iter()) {
                        if (hash == (hash2 & MASK_LOWEST_BIT)) && (symbol == &dynstrtab[symb.st_name as usize]) {
                            return Some(symb);
                        }
                        // Done if at end of chain
                        if hash2 & 1 == 1 {
                            break;
                        }
                    }
                }
                None
            }

            /// Check if symbol maybe is in the hash table, or definitely not in it.
            #[inline]
            fn check_maybe_match(&self, hash: u32) -> bool {
                const MASK: u32 = ELFCLASS_BITS - 1;
                let hash2 = hash >> self.shift2;
                // `x & (N - 1)` is equivalent to `x % N` iff `N = 2^y`.
                let bitmask: $IntTy = 1 << (hash & (MASK)) | 1 << (hash2 & MASK);
                let bloom_idx = (hash / ELFCLASS_BITS) & (self.bloom_filter.len() as u32 - 1);
                let bitmask_word = self.bloom_filter[bloom_idx as usize];
                (bitmask_word & bitmask) == bitmask
            }

            /// Given a symbol, a hash of that symbol, a dynamic string table and
            /// a `dynstrtab` to cross-reference names, maybe returns a Sym.
            pub fn find(&self, symbol: &str, dynstrtab: &Strtab) -> Option<&'a Sym> {
                let hash = self::hash(symbol);
                self.find_with_hash(symbol, hash, dynstrtab)
            }

            /// This function will not check if the passed `hash` is really
            /// the hash of `symbol`
            pub fn find_with_hash(&self, symbol: &str, hash: u32, dynstrtab: &Strtab) -> Option<&'a Sym> {
                if self.check_maybe_match(hash) {
                    #[cold] // HACK: This is trick for `unlikely` in C
                    self.lookup(symbol, hash, dynstrtab)
                } else {
                    None
                }
            }
        }
    };
}
<commit_msg>Use ? to return early<commit_after>//! A Gnu Hash table as 4 sections:
//!
//!  1. Header
//!  2. Bloom Filter
//!  3. Hash Buckets
//!  4. Chains
//!
//! The header has is an array of four `u32`s:
//!
//!  1. nbuckets
//!  2. symndx
//!  3. maskwords
//!  4. shift2
//!
//! See more:
//!  * http://www.linker-aliens.org/blogs/ali/entry/gnu_hash_elf_sections
//!    or https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2
//!  * https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/

/// GNU hash function: accepts a symbol name and returns a value that may be
/// used to compute a bucket index.
///
/// Consequently, if the hashing function returns the value `x` for some name,
/// `buckets[x % nbuckets]` gives an index, `y`, into both the symbol table
/// and the chain table.
pub fn hash(symbol: &str) -> u32 {
    const HASH_SEED: u32 = 5381;
    symbol
        .bytes()
        .fold(HASH_SEED, |hash, b| hash.wrapping_mul(33).wrapping_add(u32::from(b)))
}

#[cfg(test)]
mod tests {
    use super::hash;
    #[test]
    fn test_hash() {
        assert_eq!(hash(""), 0x0000_1505);
        assert_eq!(hash("printf"), 0x156b_2bb8);
        assert_eq!(hash("exit"), 0x7c96_7e3f);
        assert_eq!(hash("syscall"), 0xbac2_12a0);
        assert_eq!(hash("flapenguin.me"), 0x8ae9_f18e);
    }
}

macro_rules! elf_gnu_hash_impl {
    ($IntTy:ty) => {
        use crate::elf::sym::Sym;
        use crate::strtab::Strtab;
        use core::fmt;
        use core::mem;
        use core::slice;

        const INT_SIZE: usize = mem::size_of::<$IntTy>();
        const U32_SIZE: usize = mem::size_of::<u32>();
        /// Size of a bits mask in bloom filter
        const ELFCLASS_BITS: u32 = INT_SIZE as u32 * 8;

        /// A better hash table for the ELF used by GNU systems in GNU-compatible software.
        pub struct GnuHash<'a> {
            /// Index of the first symbol in the `.dynsym` table which is accessible with
            /// the hash table
            symindex: u32,
            /// Shift count used in the bloom filter
            shift2: u32,
            /// 2 bit bloom filter on `chains`
            // Either 32 or 64-bit depending on the class of object
            bloom_filter: &'a [$IntTy],
            /// GNU hash table bucket array; indexes start at 0. This array holds symbol
            /// table indexes and contains the index of hashes in `chains`
            buckets: &'a [u32],
            /// Hash values; indexes start at 0. This array holds symbol table indexes.
            chains: &'a [u32], // => chains[dynsyms.len() - symindex]
            dynsyms: &'a [Sym],
        }

        impl fmt::Debug for GnuHash<'_> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.debug_struct("GnuHash")
                    .field("nbuckets", &self.buckets.len())
                    .field("symindex", &self.symindex)
                    .field("maskwords", &(self.bloom_filter.len() - 1))
                    .field("shift2", &self.shift2)
                    .field("bloom_filter", &self.bloom_filter.as_ptr())
                    .field("bucket", &self.buckets.as_ptr())
                    .field("chains", &self.chains.as_ptr())
                    .finish()
            }
        }

        impl<'a> GnuHash<'a> {
            /// Initialize a GnuHash from a pointer to `.hash` (or `.gnu.hash`) section
            /// and total number of dynamic symbols.
            pub unsafe fn from_raw_table(hashtab: &'a [u8], dynsyms: &'a [Sym]) -> Result<Self, &'static str> {
                if hashtab.as_ptr() as usize % INT_SIZE != 0 {
                    return Err("hashtab is not aligned with 64-bit");
                }

                if hashtab.len() <= 16 {
                    return Err("failed to read in number of buckets");
                }

                let [nbuckets, symindex, maskwords, shift2] =
                    (hashtab.as_ptr() as *const u32 as *const [u32; 4]).read();

                if !maskwords.is_power_of_two() {
                    return Err("maskwords must be a power of two");
                }

                let hashtab = &hashtab[16..];
                {
                    // SAFETY: Condition to check for an overflow
                    //   size_of(chains) + size_of(buckets) + size_of(bloom_filter) == size_of(hashtab)

                    if dynsyms.len() <= symindex as usize {
                        return Err("symindex must be smaller than dynsyms.len()");
                    }
                    let chains_size = (dynsyms.len() - symindex as usize).checked_mul(U32_SIZE);
                    let buckets_size = (nbuckets as usize).checked_mul(U32_SIZE);
                    let bloom_size = (maskwords as usize).checked_mul(INT_SIZE);

                    let total_size = match (chains_size, buckets_size, bloom_size) {
                        (Some(a), Some(b), Some(c)) => a.checked_add(b).and_then(|t| t.checked_add(c)),
                        _ => None,
                    };
                    match total_size {
                        Some(size) if size == hashtab.len() => {}
                        _ => return Err("index out of bound or non-complete hash section"),
                    }
                }

                let bloom_filter_ptr = hashtab.as_ptr() as *const $IntTy;
                let buckets_ptr = bloom_filter_ptr.add(maskwords as usize) as *const u32;
                let chains_ptr = buckets_ptr.add(nbuckets as usize);
                let bloom_filter = slice::from_raw_parts(bloom_filter_ptr, maskwords as usize);
                let buckets = slice::from_raw_parts(buckets_ptr, nbuckets as usize);
                let chains = slice::from_raw_parts(chains_ptr, dynsyms.len() - symindex as usize);
                Ok(Self {
                    symindex,
                    shift2,
                    bloom_filter,
                    buckets,
                    chains,
                    dynsyms,
                })
            }

            /// Locate the hash chain, and corresponding hash value element.
            fn lookup(&self, symbol: &str, hash: u32, dynstrtab: &Strtab) -> Option<&'a Sym> {
                const MASK_LOWEST_BIT: u32 = 0xffff_fffe;
                let bucket = self.buckets[hash as usize % self.buckets.len()];

                // Empty hash chain, symbol not present
                if bucket < self.symindex {
                    return None;
                }
                // Walk the chain until the symbol is found or the chain is exhausted.
                let chain_idx = bucket - self.symindex;
                let hash = hash & MASK_LOWEST_BIT;
                let chains = &self.chains.get((chain_idx as usize)..)?;
                let dynsyms = &self.dynsyms.get((bucket as usize)..)?;
                for (hash2, symb) in chains.iter().zip(dynsyms.iter()) {
                    if (hash == (hash2 & MASK_LOWEST_BIT)) && (symbol == &dynstrtab[symb.st_name as usize]) {
                        return Some(symb);
                    }
                    // Chain ends with an element with the lowest bit set to 1.
                    if hash2 & 1 == 1 {
                        break;
                    }
                }
                None
            }

            /// Check if symbol maybe is in the hash table, or definitely not in it.
            #[inline]
            fn check_maybe_match(&self, hash: u32) -> bool {
                const MASK: u32 = ELFCLASS_BITS - 1;
                let hash2 = hash >> self.shift2;
                // `x & (N - 1)` is equivalent to `x % N` iff `N = 2^y`.
                let bitmask: $IntTy = 1 << (hash & (MASK)) | 1 << (hash2 & MASK);
                let bloom_idx = (hash / ELFCLASS_BITS) & (self.bloom_filter.len() as u32 - 1);
                let bitmask_word = self.bloom_filter[bloom_idx as usize];
                (bitmask_word & bitmask) == bitmask
            }

            /// Given a symbol, a hash of that symbol, a dynamic string table and
            /// a `dynstrtab` to cross-reference names, maybe returns a Sym.
            pub fn find(&self, symbol: &str, dynstrtab: &Strtab) -> Option<&'a Sym> {
                let hash = self::hash(symbol);
                self.find_with_hash(symbol, hash, dynstrtab)
            }

            /// This function will not check if the passed `hash` is really
            /// the hash of `symbol`
            pub fn find_with_hash(&self, symbol: &str, hash: u32, dynstrtab: &Strtab) -> Option<&'a Sym> {
                if self.check_maybe_match(hash) {
                    #[cold] // HACK: This is trick for `unlikely` in C
                    self.lookup(symbol, hash, dynstrtab)
                } else {
                    None
                }
            }
        }
    };
}
<|endoftext|>


<commit_before>use crate::alphabet::Alphabet;
use crate::engine::fast_portable::{decode_table, encode_table};
use crate::engine::{fast_portable, Config, DecodeEstimate, Engine};
use crate::{DecodeError, PAD_BYTE};
use alloc::ops::BitOr;
use std::ops::{BitAnd, Shl, Shr};

/// Comparatively simple implementation that can be used as something to compare against in tests
pub struct Naive {
    encode_table: [u8; 64],
    decode_table: [u8; 256],
    config: NaiveConfig,
}

impl Naive {
    const ENCODE_INPUT_CHUNK_SIZE: usize = 3;
    const DECODE_INPUT_CHUNK_SIZE: usize = 4;

    pub const fn from(alphabet: &Alphabet, config: NaiveConfig) -> Naive {
        Naive {
            encode_table: encode_table(alphabet),
            decode_table: decode_table(alphabet),
            config,
        }
    }

    fn decode_byte_into_u32(&self, offset: usize, byte: u8) -> Result<u32, DecodeError> {
        let decoded = self.decode_table[byte as usize];

        if decoded == fast_portable::INVALID_VALUE {
            return Err(DecodeError::InvalidByte(offset, byte));
        }

        Ok(decoded as u32)
    }
}

impl Engine for Naive {
    type Config = NaiveConfig;
    type DecodeEstimate = NaiveEstimate;

    fn encode(&self, input: &[u8], output: &mut [u8]) -> usize {
        // complete chunks first

        const LOW_SIX_BITS: u32 = 0x3F;

        let rem = input.len() % Naive::ENCODE_INPUT_CHUNK_SIZE;
        // will never underflow
        let complete_chunk_len = input.len() - rem;

        let mut input_index = 0_usize;
        let mut output_index = 0_usize;
        if let Some(last_complete_chunk_index) =
            complete_chunk_len.checked_sub(Naive::ENCODE_INPUT_CHUNK_SIZE)
        {
            while input_index <= last_complete_chunk_index {
                let chunk = &input[input_index..input_index + Naive::ENCODE_INPUT_CHUNK_SIZE];

                // populate low 24 bits from 3 bytes
                let chunk_int: u32 =
                    (chunk[0] as u32).shl(16) | (chunk[1] as u32).shl(8) | (chunk[2] as u32);
                // encode 4x 6-bit output bytes
                output[output_index] = self.encode_table[chunk_int.shr(18) as usize];
                output[output_index + 1] =
                    self.encode_table[chunk_int.shr(12_u8).bitand(LOW_SIX_BITS) as usize];
                output[output_index + 2] =
                    self.encode_table[chunk_int.shr(6_u8).bitand(LOW_SIX_BITS) as usize];
                output[output_index + 3] =
                    self.encode_table[chunk_int.bitand(LOW_SIX_BITS) as usize];

                input_index += Naive::ENCODE_INPUT_CHUNK_SIZE;
                output_index += 4;
            }
        }

        // then leftovers
        if rem == 2 {
            let chunk = &input[input_index..input_index + 2];

            // high six bits of chunk[0]
            output[output_index] = self.encode_table[chunk[0].shr(2) as usize];
            // bottom 2 bits of [0], high 4 bits of [1]
            output[output_index + 1] =
                self.encode_table[(chunk[0].shl(4_u8).bitor(chunk[1].shr(4_u8)) as u32)
                    .bitand(LOW_SIX_BITS) as usize];
            // bottom 4 bits of [1], with the 2 bottom bits as zero
            output[output_index + 2] =
                self.encode_table[(chunk[1].shl(2_u8) as u32).bitand(LOW_SIX_BITS) as usize];

            output_index += 3;
        } else if rem == 1 {
            let byte = input[input_index];
            output[output_index] = self.encode_table[byte.shr(2) as usize];
            output[output_index + 1] =
                self.encode_table[(byte.shl(4_u8) as u32).bitand(LOW_SIX_BITS) as usize];
            output_index += 2;
        }

        output_index
    }

    fn decoded_length_estimate(&self, input_len: usize) -> Self::DecodeEstimate {
        NaiveEstimate::from(input_len)
    }

    fn decode(
        &self,
        input: &[u8],
        output: &mut [u8],
        estimate: Self::DecodeEstimate,
    ) -> Result<usize, DecodeError> {
        match estimate.rem {
            1 => {
                // trailing whitespace is so common that it's worth it to check the last byte to
                // possibly return a better error message
                if let Some(b) = input.last() {
                    if *b != PAD_BYTE
                        && self.decode_table[*b as usize] == fast_portable::INVALID_VALUE
                    {
                        return Err(DecodeError::InvalidByte(input.len() - 1, *b));
                    }
                }

                return Err(DecodeError::InvalidLength);
            }
            _ => {}
        }

        let mut input_index = 0_usize;
        let mut output_index = 0_usize;
        const BOTTOM_BYTE: u32 = 0xFF;

        // can only use the main loop on non-trailing chunks
        if input.len() > Naive::DECODE_INPUT_CHUNK_SIZE {
            // skip the last chunk, whether it's partial or full, since it might
            // have padding, and start at the beginning of the chunk before that
            let last_complete_chunk_start_index = estimate.complete_chunk_len
                - if estimate.rem == 0 {
                    // Trailing chunk is also full chunk, so there must be at least 2 chunks, and
                    // this won't underflow
                    Naive::DECODE_INPUT_CHUNK_SIZE * 2
                } else {
                    // Trailing chunk is partial, so it's already excluded in
                    // complete_chunk_len
                    Naive::DECODE_INPUT_CHUNK_SIZE
                };

            while input_index <= last_complete_chunk_start_index {
                let chunk = &input[input_index..input_index + Naive::DECODE_INPUT_CHUNK_SIZE];
                let decoded_int: u32 = self.decode_byte_into_u32(input_index, chunk[0])?.shl(18)
                    | self
                        .decode_byte_into_u32(input_index + 1, chunk[1])?
                        .shl(12)
                    | self.decode_byte_into_u32(input_index + 2, chunk[2])?.shl(6)
                    | self.decode_byte_into_u32(input_index + 3, chunk[3])?;

                output[output_index] = decoded_int.shr(16_u8).bitand(BOTTOM_BYTE) as u8;
                output[output_index + 1] = decoded_int.shr(8_u8).bitand(BOTTOM_BYTE) as u8;
                output[output_index + 2] = decoded_int.bitand(BOTTOM_BYTE) as u8;

                input_index += Naive::DECODE_INPUT_CHUNK_SIZE;
                output_index += 3;
            }
        }

        // handle incomplete chunk -- simplified version of FastPortable
        let mut leftover_bits: u32 = 0;
        let mut morsels_in_leftover = 0;
        let mut padding_bytes = 0;
        let mut first_padding_index: usize = 0;
        let mut last_symbol = 0_u8;
        let start_of_leftovers = input_index;
        for (index, byte) in input[start_of_leftovers..].iter().enumerate() {
            // '=' padding
            if *byte == PAD_BYTE {
                // There can be bad padding in a few ways:
                // 1 - Padding with non-padding characters after it
                // 2 - Padding after zero or one non-padding characters before it
                //     in the current quad.
                // 3 - More than two characters of padding. If 3 or 4 padding chars
                //     are in the same quad, that implies it will be caught by #2.
                //     If it spreads from one quad to another, it will be an invalid byte
                //     in the first quad.

                if index < 2 {
                    // Check for case #2.
                    let bad_padding_index = start_of_leftovers
                        + if padding_bytes > 0 {
                            // If we've already seen padding, report the first padding index.
                            // This is to be consistent with the faster logic above: it will report an
                            // error on the first padding character (since it doesn't expect to see
                            // anything but actual encoded data).
                            first_padding_index
                        } else {
                            // haven't seen padding before, just use where we are now
                            index
                        };
                    return Err(DecodeError::InvalidByte(bad_padding_index, *byte));
                }

                if padding_bytes == 0 {
                    first_padding_index = index;
                }

                padding_bytes += 1;
                continue;
            }

            // Check for case #1.
            // To make '=' handling consistent with the main loop, don't allow
            // non-suffix '=' in trailing chunk either. Report error as first
            // erroneous padding.
            if padding_bytes > 0 {
                return Err(DecodeError::InvalidByte(
                    start_of_leftovers + first_padding_index,
                    PAD_BYTE,
                ));
            }
            last_symbol = *byte;

            // can use up to 4 * 6 = 24 bits of the u32, if last chunk has no padding.
            // Pack the leftovers from left to right.
            let shift = 32 - (morsels_in_leftover + 1) * 6;
            let morsel = self.decode_table[*byte as usize];
            if morsel == fast_portable::INVALID_VALUE {
                return Err(DecodeError::InvalidByte(start_of_leftovers + index, *byte));
            }

            leftover_bits |= (morsel as u32) << shift;
            morsels_in_leftover += 1;
        }

        let leftover_bits_ready_to_append = match morsels_in_leftover {
            0 => 0,
            2 => 8,
            3 => 16,
            4 => 24,
            _ => unreachable!(
                "Impossible: must only have 0 to 4 input bytes in last chunk, with no invalid lengths"
            ),
        };

        // if there are bits set outside the bits we care about, last symbol encodes trailing
        // bits that will not be included in the output
        let mask = !0 >> leftover_bits_ready_to_append;
        if !self.config.decode_allow_trailing_bits && (leftover_bits & mask) != 0 {
            // last morsel is at `morsels_in_leftover` - 1
            return Err(DecodeError::InvalidLastSymbol(
                start_of_leftovers + morsels_in_leftover - 1,
                last_symbol,
            ));
        }

        let mut leftover_bits_appended_to_buf = 0;
        while leftover_bits_appended_to_buf < leftover_bits_ready_to_append {
            // `as` simply truncates the higher bits, which is what we want here
            let selected_bits = (leftover_bits >> (24 - leftover_bits_appended_to_buf)) as u8;
            output[output_index] = selected_bits;
            output_index += 1;

            leftover_bits_appended_to_buf += 8;
        }

        Ok(output_index)
    }

    fn config(&self) -> &Self::Config {
        &self.config
    }
}

pub struct NaiveEstimate {
    /// remainder from dividing input by `Naive::DECODE_CHUNK_SIZE`
    rem: usize,
    /// Number of complete `Naive::DECODE_CHUNK_SIZE`-length chunks
    complete_chunk_len: usize,
}

impl NaiveEstimate {
    fn from(input_len: usize) -> NaiveEstimate {
        let rem = input_len % Naive::DECODE_INPUT_CHUNK_SIZE;
        let complete_chunk_len = input_len - rem;

        NaiveEstimate {
            rem,
            complete_chunk_len,
        }
    }
}

impl DecodeEstimate for NaiveEstimate {
    fn decoded_length_estimate(&self) -> usize {
        (self.complete_chunk_len + 1) * 3
    }
}

#[derive(Clone, Copy, Debug)]
pub struct NaiveConfig {
    pub padding: bool,
    pub decode_allow_trailing_bits: bool,
}

impl Config for NaiveConfig {
    fn encode_padding(&self) -> bool {
        self.padding
    }
}
<commit_msg>use 'if' block for equality check<commit_after>use crate::alphabet::Alphabet;
use crate::engine::fast_portable::{decode_table, encode_table};
use crate::engine::{fast_portable, Config, DecodeEstimate, Engine};
use crate::{DecodeError, PAD_BYTE};
use alloc::ops::BitOr;
use std::ops::{BitAnd, Shl, Shr};

/// Comparatively simple implementation that can be used as something to compare against in tests
pub struct Naive {
    encode_table: [u8; 64],
    decode_table: [u8; 256],
    config: NaiveConfig,
}

impl Naive {
    const ENCODE_INPUT_CHUNK_SIZE: usize = 3;
    const DECODE_INPUT_CHUNK_SIZE: usize = 4;

    pub const fn from(alphabet: &Alphabet, config: NaiveConfig) -> Naive {
        Naive {
            encode_table: encode_table(alphabet),
            decode_table: decode_table(alphabet),
            config,
        }
    }

    fn decode_byte_into_u32(&self, offset: usize, byte: u8) -> Result<u32, DecodeError> {
        let decoded = self.decode_table[byte as usize];

        if decoded == fast_portable::INVALID_VALUE {
            return Err(DecodeError::InvalidByte(offset, byte));
        }

        Ok(decoded as u32)
    }
}

impl Engine for Naive {
    type Config = NaiveConfig;
    type DecodeEstimate = NaiveEstimate;

    fn encode(&self, input: &[u8], output: &mut [u8]) -> usize {
        // complete chunks first

        const LOW_SIX_BITS: u32 = 0x3F;

        let rem = input.len() % Naive::ENCODE_INPUT_CHUNK_SIZE;
        // will never underflow
        let complete_chunk_len = input.len() - rem;

        let mut input_index = 0_usize;
        let mut output_index = 0_usize;
        if let Some(last_complete_chunk_index) =
            complete_chunk_len.checked_sub(Naive::ENCODE_INPUT_CHUNK_SIZE)
        {
            while input_index <= last_complete_chunk_index {
                let chunk = &input[input_index..input_index + Naive::ENCODE_INPUT_CHUNK_SIZE];

                // populate low 24 bits from 3 bytes
                let chunk_int: u32 =
                    (chunk[0] as u32).shl(16) | (chunk[1] as u32).shl(8) | (chunk[2] as u32);
                // encode 4x 6-bit output bytes
                output[output_index] = self.encode_table[chunk_int.shr(18) as usize];
                output[output_index + 1] =
                    self.encode_table[chunk_int.shr(12_u8).bitand(LOW_SIX_BITS) as usize];
                output[output_index + 2] =
                    self.encode_table[chunk_int.shr(6_u8).bitand(LOW_SIX_BITS) as usize];
                output[output_index + 3] =
                    self.encode_table[chunk_int.bitand(LOW_SIX_BITS) as usize];

                input_index += Naive::ENCODE_INPUT_CHUNK_SIZE;
                output_index += 4;
            }
        }

        // then leftovers
        if rem == 2 {
            let chunk = &input[input_index..input_index + 2];

            // high six bits of chunk[0]
            output[output_index] = self.encode_table[chunk[0].shr(2) as usize];
            // bottom 2 bits of [0], high 4 bits of [1]
            output[output_index + 1] =
                self.encode_table[(chunk[0].shl(4_u8).bitor(chunk[1].shr(4_u8)) as u32)
                    .bitand(LOW_SIX_BITS) as usize];
            // bottom 4 bits of [1], with the 2 bottom bits as zero
            output[output_index + 2] =
                self.encode_table[(chunk[1].shl(2_u8) as u32).bitand(LOW_SIX_BITS) as usize];

            output_index += 3;
        } else if rem == 1 {
            let byte = input[input_index];
            output[output_index] = self.encode_table[byte.shr(2) as usize];
            output[output_index + 1] =
                self.encode_table[(byte.shl(4_u8) as u32).bitand(LOW_SIX_BITS) as usize];
            output_index += 2;
        }

        output_index
    }

    fn decoded_length_estimate(&self, input_len: usize) -> Self::DecodeEstimate {
        NaiveEstimate::from(input_len)
    }

    fn decode(
        &self,
        input: &[u8],
        output: &mut [u8],
        estimate: Self::DecodeEstimate,
    ) -> Result<usize, DecodeError> {
        if estimate.rem == 1 {
            // trailing whitespace is so common that it's worth it to check the last byte to
            // possibly return a better error message
            if let Some(b) = input.last() {
                if *b != PAD_BYTE && self.decode_table[*b as usize] == fast_portable::INVALID_VALUE
                {
                    return Err(DecodeError::InvalidByte(input.len() - 1, *b));
                }
            }

            return Err(DecodeError::InvalidLength);
        }

        let mut input_index = 0_usize;
        let mut output_index = 0_usize;
        const BOTTOM_BYTE: u32 = 0xFF;

        // can only use the main loop on non-trailing chunks
        if input.len() > Naive::DECODE_INPUT_CHUNK_SIZE {
            // skip the last chunk, whether it's partial or full, since it might
            // have padding, and start at the beginning of the chunk before that
            let last_complete_chunk_start_index = estimate.complete_chunk_len
                - if estimate.rem == 0 {
                    // Trailing chunk is also full chunk, so there must be at least 2 chunks, and
                    // this won't underflow
                    Naive::DECODE_INPUT_CHUNK_SIZE * 2
                } else {
                    // Trailing chunk is partial, so it's already excluded in
                    // complete_chunk_len
                    Naive::DECODE_INPUT_CHUNK_SIZE
                };

            while input_index <= last_complete_chunk_start_index {
                let chunk = &input[input_index..input_index + Naive::DECODE_INPUT_CHUNK_SIZE];
                let decoded_int: u32 = self.decode_byte_into_u32(input_index, chunk[0])?.shl(18)
                    | self
                        .decode_byte_into_u32(input_index + 1, chunk[1])?
                        .shl(12)
                    | self.decode_byte_into_u32(input_index + 2, chunk[2])?.shl(6)
                    | self.decode_byte_into_u32(input_index + 3, chunk[3])?;

                output[output_index] = decoded_int.shr(16_u8).bitand(BOTTOM_BYTE) as u8;
                output[output_index + 1] = decoded_int.shr(8_u8).bitand(BOTTOM_BYTE) as u8;
                output[output_index + 2] = decoded_int.bitand(BOTTOM_BYTE) as u8;

                input_index += Naive::DECODE_INPUT_CHUNK_SIZE;
                output_index += 3;
            }
        }

        // handle incomplete chunk -- simplified version of FastPortable
        let mut leftover_bits: u32 = 0;
        let mut morsels_in_leftover = 0;
        let mut padding_bytes = 0;
        let mut first_padding_index: usize = 0;
        let mut last_symbol = 0_u8;
        let start_of_leftovers = input_index;
        for (index, byte) in input[start_of_leftovers..].iter().enumerate() {
            // '=' padding
            if *byte == PAD_BYTE {
                // There can be bad padding in a few ways:
                // 1 - Padding with non-padding characters after it
                // 2 - Padding after zero or one non-padding characters before it
                //     in the current quad.
                // 3 - More than two characters of padding. If 3 or 4 padding chars
                //     are in the same quad, that implies it will be caught by #2.
                //     If it spreads from one quad to another, it will be an invalid byte
                //     in the first quad.

                if index < 2 {
                    // Check for case #2.
                    let bad_padding_index = start_of_leftovers
                        + if padding_bytes > 0 {
                            // If we've already seen padding, report the first padding index.
                            // This is to be consistent with the faster logic above: it will report an
                            // error on the first padding character (since it doesn't expect to see
                            // anything but actual encoded data).
                            first_padding_index
                        } else {
                            // haven't seen padding before, just use where we are now
                            index
                        };
                    return Err(DecodeError::InvalidByte(bad_padding_index, *byte));
                }

                if padding_bytes == 0 {
                    first_padding_index = index;
                }

                padding_bytes += 1;
                continue;
            }

            // Check for case #1.
            // To make '=' handling consistent with the main loop, don't allow
            // non-suffix '=' in trailing chunk either. Report error as first
            // erroneous padding.
            if padding_bytes > 0 {
                return Err(DecodeError::InvalidByte(
                    start_of_leftovers + first_padding_index,
                    PAD_BYTE,
                ));
            }
            last_symbol = *byte;

            // can use up to 4 * 6 = 24 bits of the u32, if last chunk has no padding.
            // Pack the leftovers from left to right.
            let shift = 32 - (morsels_in_leftover + 1) * 6;
            let morsel = self.decode_table[*byte as usize];
            if morsel == fast_portable::INVALID_VALUE {
                return Err(DecodeError::InvalidByte(start_of_leftovers + index, *byte));
            }

            leftover_bits |= (morsel as u32) << shift;
            morsels_in_leftover += 1;
        }

        let leftover_bits_ready_to_append = match morsels_in_leftover {
            0 => 0,
            2 => 8,
            3 => 16,
            4 => 24,
            _ => unreachable!(
                "Impossible: must only have 0 to 4 input bytes in last chunk, with no invalid lengths"
            ),
        };

        // if there are bits set outside the bits we care about, last symbol encodes trailing
        // bits that will not be included in the output
        let mask = !0 >> leftover_bits_ready_to_append;
        if !self.config.decode_allow_trailing_bits && (leftover_bits & mask) != 0 {
            // last morsel is at `morsels_in_leftover` - 1
            return Err(DecodeError::InvalidLastSymbol(
                start_of_leftovers + morsels_in_leftover - 1,
                last_symbol,
            ));
        }

        let mut leftover_bits_appended_to_buf = 0;
        while leftover_bits_appended_to_buf < leftover_bits_ready_to_append {
            // `as` simply truncates the higher bits, which is what we want here
            let selected_bits = (leftover_bits >> (24 - leftover_bits_appended_to_buf)) as u8;
            output[output_index] = selected_bits;
            output_index += 1;

            leftover_bits_appended_to_buf += 8;
        }

        Ok(output_index)
    }

    fn config(&self) -> &Self::Config {
        &self.config
    }
}

pub struct NaiveEstimate {
    /// remainder from dividing input by `Naive::DECODE_CHUNK_SIZE`
    rem: usize,
    /// Number of complete `Naive::DECODE_CHUNK_SIZE`-length chunks
    complete_chunk_len: usize,
}

impl NaiveEstimate {
    fn from(input_len: usize) -> NaiveEstimate {
        let rem = input_len % Naive::DECODE_INPUT_CHUNK_SIZE;
        let complete_chunk_len = input_len - rem;

        NaiveEstimate {
            rem,
            complete_chunk_len,
        }
    }
}

impl DecodeEstimate for NaiveEstimate {
    fn decoded_length_estimate(&self) -> usize {
        (self.complete_chunk_len + 1) * 3
    }
}

#[derive(Clone, Copy, Debug)]
pub struct NaiveConfig {
    pub padding: bool,
    pub decode_allow_trailing_bits: bool,
}

impl Config for NaiveConfig {
    fn encode_padding(&self) -> bool {
        self.padding
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

/// Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
/// Assumes Von Nuemann's neighborhood.

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport
}

impl Default for Stage {
    fn default() -> Self { Stage::Collision }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down
}

impl Direction {

    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32)
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>> {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {

    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Transport,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(neighbor) => {
                    let opposite = direction.opposite();
                    let head_on = self.particle(&direction) &&
                                  neighbor.particle(&opposite) &&
                                  !self.particle(&direction.perpendicular()) &&
                                  !neighbor.particle(&opposite.perpendicular());

                    if head_on {
                        new.set_particle(&direction.perpendicular(), self.particle(&direction));
                    }
                    else {
                        let particle = new.particle(&direction) || self.particle(&direction);
                        new.set_particle(&direction, particle);
                    }
                },
                // Rebound
                None => {
                    if self.particle(&direction) {
                        let opposite = direction.opposite();
                        new.set_particle(&opposite, true);
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Collision,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();

                    if neighbor.particle(&opposite) {
                        new.set_particle(&opposite, neighbor.particle(&opposite));
                    }
                },
                None => {
                    if self.particle(&direction) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {

        match *direction {
            Direction::Up => self.particles[0],
            Direction::Left => self.particles[1],
            Direction::Right => self.particles[2],
            Direction::Down => self.particles[3]
        }
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {

        match *direction {
            Direction::Up => { self.particles[0] = exists },
            Direction::Left => { self.particles[1] = exists },
            Direction::Right => { self.particles[2] = exists },
            Direction::Down => { self.particles[3] = exists }
        }
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {

    pub fn new() -> Self {
        HPPRulesTestConsumer { }
    }

    fn particles_count<C: Cell>(&self,
                                 cells: &Vec<C>,
                                 direction: &Direction) -> i32 {

        cells.iter()
             .map(|c| to_cell(c))
             .filter(|c: &HPP| c.particle(direction) == true)
             .count() as i32
    }

    fn find_cell<C: Cell>(&self,
                          cells: &Vec<C>,
                          x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
                         .find(|c| c.coord().x() == x && c.coord().y() == y)
                         .unwrap();

        to_cell(found)
    }

    fn test_collision<G: Grid>(&self, grid: &G) {

        let left_particle_count =
            self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count =
            self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count =
            self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count =
            self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right =
            self.find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up =
            self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down =
            self.find_cell(&grid.cells(), 2, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {

        let simple_move_to_right = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right), true);

        let move_to_down_after_head_on = self.find_cell(&grid.cells(), 2, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0 .. 3 {
            print!("|");

            for x in 0 .. 3 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle =
                    directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl Consumer for HPPRulesTestConsumer {

    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, true, false, false];
    let right_particle = [false, false, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: right_particle, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (2, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    let right_particles_count =
        grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(&Direction::Right) == true)
            .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction
}

impl HPPSpreadTestConsumer {

    pub fn new() -> Self {
        HPPSpreadTestConsumer { cur_y: 0, cur_direction: Direction::Down }
    }

    fn particles_count<C: Cell>(&self,
                                 cells: &Vec<C>,
                                 direction: &Direction) -> i32 {

        cells.iter()
             .map(|c| to_cell(c))
             .filter(|c: &HPP| c.particle(direction) == true)
             .count() as i32
    }

    fn find_cell<C: Cell>(&self,
                          cells: &Vec<C>,
                          x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
                         .find(|c| c.coord().x() == x && c.coord().y() == y)
                         .unwrap();

        to_cell(found)
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0 .. 5 {
            print!("|");

            for x in 0 .. 5 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle =
                    directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count =
            self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        }
        else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {

        let particles_count =
            self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(
            grid.cells().iter()
                        .map(|c| to_cell(c))
                        .filter(|c: &HPP| c.coord().y() == self.cur_y)
                        .all(|c| c.particle(&self.cur_direction) == true)
        );

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {

    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0 .. 5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0)
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}<commit_msg>rustfmt<commit_after>#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

/// Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
/// Assumes Von Nuemann's neighborhood.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport,
}

impl Default for Stage {
    fn default() -> Self {
        Stage::Collision
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down,
}

impl Direction {
    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right,
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32),
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {
    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Transport, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(neighbor) => {
                    let opposite = direction.opposite();
                    let head_on = self.particle(&direction) && neighbor.particle(&opposite) &&
                                  !self.particle(&direction.perpendicular()) &&
                                  !neighbor.particle(&opposite.perpendicular());

                    if head_on {
                        new.set_particle(&direction.perpendicular(), self.particle(&direction));
                    } else {
                        let particle = new.particle(&direction) || self.particle(&direction);
                        new.set_particle(&direction, particle);
                    }
                }
                // Rebound
                None => {
                    if self.particle(&direction) {
                        let opposite = direction.opposite();
                        new.set_particle(&opposite, true);
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Collision, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();

                    if neighbor.particle(&opposite) {
                        new.set_particle(&opposite, neighbor.particle(&opposite));
                    }
                }
                None => {
                    if self.particle(&direction) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {

        match *direction {
            Direction::Up => self.particles[0],
            Direction::Left => self.particles[1],
            Direction::Right => self.particles[2],
            Direction::Down => self.particles[3],
        }
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {

        match *direction {
            Direction::Up => self.particles[0] = exists,
            Direction::Left => self.particles[1] = exists,
            Direction::Right => self.particles[2] = exists,
            Direction::Down => self.particles[3] = exists,
        }
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {
    pub fn new() -> Self {
        HPPRulesTestConsumer {}
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn test_collision<G: Grid>(&self, grid: &G) {

        let left_particle_count = self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = self.find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down = self.find_cell(&grid.cells(), 2, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {

        let simple_move_to_right = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right),
                   true);

        let move_to_down_after_head_on = self.find_cell(&grid.cells(), 2, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..3 {
            print!("|");

            for x in 0..3 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl Consumer for HPPRulesTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, true, false, false];
    let right_particle = [false, false, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: right_particle, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (2, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    let right_particles_count = grid.cells()
        .iter()
        .map(|c| to_cell(c))
        .filter(|c: &HPP| c.particle(&Direction::Right) == true)
        .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction,
}

impl HPPSpreadTestConsumer {
    pub fn new() -> Self {
        HPPSpreadTestConsumer {
            cur_y: 0,
            cur_direction: Direction::Down,
        }
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..5 {
            print!("|");

            for x in 0..5 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        } else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.coord().y() == self.cur_y)
            .all(|c| c.particle(&self.cur_direction) == true));

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0..5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0),
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}
<|endoftext|>