<commit_before>/*!
 * Processing the markdown and resolving references.
 */

use std::{
    collections::HashSet,
    fs::File,
    io::{Read, Write},
    path::{Path, PathBuf},
};

use pulldown_cmark::{html, Event, LinkType, Parser, Tag};
use url::Url;

use crate::config::ResolvedConfig;

struct RenderAdapter<'a, 'b, 'c: 'a, I: Iterator<Item = Event<'b>>> {
    ctx: &'a mut ProcessorContext<'a, 'c>,
    iter: I,
}

impl<'a, 'b, 'c: 'a, I: Iterator<Item = Event<'b>>> Iterator for RenderAdapter<'a, 'b, 'c, I> {
    type Item = Event<'b>;

    fn next(&mut self) -> Option<Self::Item> {
        let item = self.iter.next()?;
        let styles = &mut self.ctx.styles;
        let render_stack = &mut *self.ctx.render_stack;
        let finished = self.ctx.finished;
        let out_dir = &self.ctx.config.roots.output;
        let base_dir = &self.ctx.config.roots.source;
        let filename = self.ctx.filename;
        Some(match item {
            Event::Start(tag) => match tag {
                i @ Tag::Image(..) => {
                    styles.insert("image");
                    Event::Start(i)
                }
                p @ Tag::Paragraph => {
                    styles.insert("paragraph");
                    Event::Start(p)
                }
                Tag::Link(ty, url, title) => {
                    styles.insert("link");
                    match ty {
                        LinkType::Inline => {
                            if let Ok(parsed) = Url::parse(&url) {
                                // check if scheme is hyperref, if so add to stack and rewrite url
                                if parsed.scheme() == "hyperref" {
                                    let parsed_path: &Path = parsed.path().as_ref();
                                    let fname: PathBuf = if parsed_path.is_absolute() {
                                        out_dir.join(parsed_path.strip_prefix("/").unwrap())
                                    } else {
                                        filename
                                            .parent()
                                            .unwrap_or("/".as_ref())
                                            .join(parsed.path())
                                    }
                                    .with_extension("md");
                                    #[cfg(target_os = "windows")]
                                    // replace with backslashes so that \\?\ isn't broken
                                    let fname: PathBuf =
                                        fname.to_str().unwrap().replace("/", "\\").into();
                                    if let Ok(fname) = fname.canonicalize() {
                                        let fname_for_url = fname.strip_prefix(&base_dir).unwrap();
                                        #[cfg(target_os = "windows")]
                                    // windows is dumb again
                                    let fname_for_url: PathBuf =
                                        fname_for_url.to_str().unwrap().replace("\\", "/").into();
                                        // figure out new location
                                        let new_location = format!(
                                            "/{}",
                                            fname_for_url.with_extension("html").to_str().unwrap(),
                                        );
                                        let input = RenderingInput::Other(fname);
                                        if !render_stack.contains(&input)
                                            && !finished.contains(&input)
                                        {
                                            match input {
                                                RenderingInput::Other(ref fname) => println!(
                                                    "walk: {}",
                                                    fname.to_str().unwrap_or("unknown")
                                                ),
                                                _ => {}
                                            }
                                            render_stack.push(input);
                                        }
                                        Event::Start(Tag::Link(ty, new_location.into(), title))
                                        // link.url = new_location.into_bytes();
                                    } else {
                                        println!("Couldn't resolve hyperref: {}", url);
                                        Event::Start(Tag::Link(ty, url, title))
                                    }
                                } else {
                                    Event::Start(Tag::Link(ty, url, title))
                                }
                            } else {
                                Event::Start(Tag::Link(ty, url, title))
                            }
                        }
                        _ => Event::Start(Tag::Link(ty, url, title)),
                    }
                }
                t => Event::Start(t),
            },
            ev => ev,
        })
    }
}

/// Processing context for a single file
pub struct ProcessorContext<'a, 'b: 'a> {
    styles: &'a mut HashSet<&'b str>,
    filename: &'a Path,
    config: &'a ResolvedConfig,
    finished: &'a HashSet<RenderingInput>,
    render_stack: &'a mut Vec<RenderingInput>,
}

/// Rendering input
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub enum RenderingInput {
    Index,
    Keep,
    Other(PathBuf),
}

/// Processes files
pub struct Processor {
    /// Stuff is derived from this
    config: ResolvedConfig,
    // next items to render
    render_stack: Vec<RenderingInput>,
    // items that have already been rendered
    finished: HashSet<RenderingInput>,
}

impl Processor {
    pub fn new(config: ResolvedConfig) -> Self {
        Self {
            config,
            render_stack: Default::default(),
            finished: Default::default(),
        }
    }

    pub fn render_toplevel(&mut self, force: bool) -> anyhow::Result<()> {
        self.render_stack.extend(std::array::IntoIter::new([
            RenderingInput::Index,
            RenderingInput::Keep,
        ]));
        self.render_all(force)?;
        Ok(())
    }

    fn render_all(&mut self, force: bool) -> anyhow::Result<()> {
        while !self.render_stack.is_empty() {
            let stack = std::mem::take(&mut self.render_stack);
            for input in stack {
                println!("RENDER: {:?}", input);
                self.render(input, force)?;
            }
        }
        Ok(())
    }

    pub fn render(&mut self, input: RenderingInput, force: bool) -> anyhow::Result<()> {
        let out_dir = &self.config.roots.output;
        let base_dir = &self.config.roots.source;
        let style_chunks_root = &self.config.lib.styles.chunks_root;
        let prelude_html = &self.config.lib.prelude_location;
        let filename = match input {
            RenderingInput::Index => &self.config.inputs.index,
            RenderingInput::Keep => &self.config.inputs.keep,
            RenderingInput::Other(ref o) => o,
        };

        if !filename.exists() {
            println!(
                "nonexistent: {}, nothing to do",
                filename.to_str().unwrap_or("unknown")
            );
            return Ok(());
        }

        // create out dir if doesn't exist
        if !out_dir.exists() {
            std::fs::create_dir_all(out_dir)?;
        }
        // // canonicalize paths
        // let (filename, base_dir, out_dir) = (
        //     filename.as_ref().canonicalize()?,
        //     base_dir.as_ref().canonicalize()?,
        //     out_dir.as_ref().canonicalize()?,
        // );
        // as-ref paths
        // let (filename, base_dir, out_dir) = (filename.as_ref(), base_dir.as_ref(), out_dir.as_ref());

        // NOTE: can't canonicalize here since the output path may not exist
        let out_path = out_dir
            .join(filename.strip_prefix(&base_dir)?)
            .with_extension("html");
        println!("out: {}", out_path.to_str().unwrap_or("unknown"));

        let buf = {
            let mut s = String::new();
            let mut f = File::open(&filename)?;
            f.read_to_string(&mut s)?;
            Ok::<_, std::io::Error>(s)
        }?;

        let mut styles = {
            let mut h = HashSet::new();
            h.insert("_global");
            h
        };

        let html = {
            let parser = Parser::new(&buf);
            let mut ctx = ProcessorContext {
                filename,
                styles: &mut styles,
                config: &self.config,
                finished: &self.finished,
                render_stack: &mut self.render_stack,
            };
            let adapter = RenderAdapter {
                ctx: &mut ctx,
                iter: parser,
            };

            let mut s = String::new();
            html::push_html(&mut s, adapter);
            s
        };

        let styles = {
            let mut new_styles = Vec::new();
            for sname in styles.into_iter() {
                let path = style_chunks_root.join(sname).with_extension("css");
                // skip missing files
                if let Ok(path) = AsRef::<Path>::as_ref(&path).canonicalize() {
                    let css_out_path = out_dir.join("css").join(sname).with_extension("css");
                    if let Some(p) = css_out_path.parent() {
                        std::fs::create_dir_all(p)?;
                    }
                    // Minify style first
                    let minified_css = {
                        let mut s = String::new();
                        let mut f = File::open(&path)?;
                        f.read_to_string(&mut s)?;
                        let minified = html_minifier::css::minify(&s)
                            .map_err(|_| anyhow::anyhow!("minify failed"))?;
                        println!(
                            "Minified {} bytes -> {} bytes ({:.3}%)",
                            s.len(),
                            minified.len(),
                            (((minified.len() as f64) - (s.len() as f64)) / s.len() as f64) * 100.
                        );
                        Ok::<_, anyhow::Error>(minified)
                    }?;
                    {
                        let mut f = File::create(&css_out_path)?;
                        f.write_all(minified_css.as_bytes())?;
                    }
                    new_styles.push(format!(
                        r#"
    <link rel="preload" href="/{0}" as="style" />
    <link rel="stylesheet" type="text/css" href="/{0}" />
    "#,
                        css_out_path
                            .strip_prefix(out_dir)
                            .unwrap_or(&css_out_path)
                            .to_str()
                            .unwrap_or("unknown")
                            .replace("\\", "/")
                    ));
                }
            }
            Ok::<_, std::io::Error>(new_styles)
        }?;
        let html = {
            println!("path = {}", prelude_html.to_str().unwrap_or("unknown"));
            let mut f = File::open(prelude_html)?;
            let mut s = String::new();
            f.read_to_string(&mut s)?;
            Ok::<_, std::io::Error>(s)
        }?
        .replace("@@@SLOT_STYLES@@@", &format!("\n{}\n", styles.join("\n")))
        .replace("@@@SLOT_CONTENT@@@", &html);

        // Minify HTML
        let minified = html_minifier::minify(&html)?;

        println!(
            "Minified {} bytes -> {} bytes ({:.3}%)",
            html.len(),
            minified.len(),
            (((minified.len() as f64) - (html.len() as f64)) / html.len() as f64) * 100.
        );

        // write only if file doesn't exist
        let needs_update = if let (Ok(out_metadata), Ok(in_metadata)) =
            (out_path.metadata(), filename.metadata())
        {
            in_metadata.modified()? >= out_metadata.modified()?
        } else {
            // failed to get metadata, or either path doesn't exist
            true
        };
        if !needs_update && !force {
            // nothing to do
            println!("fresh: {}", out_path.to_str().unwrap_or("unknown"));
        } else {
            // first, recursively create parents
            if let Some(p) = out_path.parent() {
                std::fs::create_dir_all(p)?;
            }

            if out_path.file_stem().unwrap().to_str().unwrap() == "_keep" {
                println!("special: _keep, no output");
            } else {
                let mut f = File::create(&out_path)?;
                f.write_all(minified.as_bytes())?;
                // println!("{}", html);
                println!("new: {}", out_path.to_str().unwrap_or("unknown"));
            }
        }

        self.finished.insert(input);

        Ok(())
    }
}
<commit_msg>Use VecDeque, allows dedup to work properly<commit_after>/*!
 * Processing the markdown and resolving references.
 */

use std::{
    collections::{HashSet, VecDeque},
    fs::File,
    io::{Read, Write},
    path::{Path, PathBuf},
};

use pulldown_cmark::{html, Event, LinkType, Parser, Tag};
use url::Url;

use crate::config::ResolvedConfig;

struct RenderAdapter<'a, 'b, 'c: 'a, I: Iterator<Item = Event<'b>>> {
    ctx: &'a mut ProcessorContext<'a, 'c>,
    iter: I,
}

impl<'a, 'b, 'c: 'a, I: Iterator<Item = Event<'b>>> Iterator for RenderAdapter<'a, 'b, 'c, I> {
    type Item = Event<'b>;

    fn next(&mut self) -> Option<Self::Item> {
        let item = self.iter.next()?;
        let styles = &mut self.ctx.styles;
        let render_stack = &mut *self.ctx.render_stack;
        let finished = self.ctx.finished;
        let out_dir = &self.ctx.config.roots.output;
        let base_dir = &self.ctx.config.roots.source;
        let filename = self.ctx.filename;
        Some(match item {
            Event::Start(tag) => match tag {
                i @ Tag::Image(..) => {
                    styles.insert("image");
                    Event::Start(i)
                }
                p @ Tag::Paragraph => {
                    styles.insert("paragraph");
                    Event::Start(p)
                }
                Tag::Link(ty, url, title) => {
                    styles.insert("link");
                    match ty {
                        LinkType::Inline => {
                            if let Ok(parsed) = Url::parse(&url) {
                                // check if scheme is hyperref, if so add to stack and rewrite url
                                if parsed.scheme() == "hyperref" {
                                    let parsed_path: &Path = parsed.path().as_ref();
                                    let fname: PathBuf = if parsed_path.is_absolute() {
                                        out_dir.join(parsed_path.strip_prefix("/").unwrap())
                                    } else {
                                        filename
                                            .parent()
                                            .unwrap_or("/".as_ref())
                                            .join(parsed.path())
                                    }
                                    .with_extension("md");
                                    #[cfg(target_os = "windows")]
                                    // replace with backslashes so that \\?\ isn't broken
                                    let fname: PathBuf =
                                        fname.to_str().unwrap().replace("/", "\\").into();
                                    if let Ok(fname) = fname.canonicalize() {
                                        let fname_for_url = fname.strip_prefix(&base_dir).unwrap();
                                        #[cfg(target_os = "windows")]
                                    // windows is dumb again
                                    let fname_for_url: PathBuf =
                                        fname_for_url.to_str().unwrap().replace("\\", "/").into();
                                        // figure out new location
                                        let new_location = format!(
                                            "/{}",
                                            fname_for_url.with_extension("html").to_str().unwrap(),
                                        );
                                        let input = RenderingInput::Other(fname);
                                        if !render_stack.contains(&input)
                                            && !finished.contains(&input)
                                        {
                                            match input {
                                                RenderingInput::Other(ref fname) => println!(
                                                    "walk: {}",
                                                    fname.to_str().unwrap_or("unknown")
                                                ),
                                                _ => {}
                                            }
                                            render_stack.push_back(input);
                                        }
                                        Event::Start(Tag::Link(ty, new_location.into(), title))
                                        // link.url = new_location.into_bytes();
                                    } else {
                                        println!("Couldn't resolve hyperref: {}", url);
                                        Event::Start(Tag::Link(ty, url, title))
                                    }
                                } else {
                                    Event::Start(Tag::Link(ty, url, title))
                                }
                            } else {
                                Event::Start(Tag::Link(ty, url, title))
                            }
                        }
                        _ => Event::Start(Tag::Link(ty, url, title)),
                    }
                }
                t => Event::Start(t),
            },
            ev => ev,
        })
    }
}

/// Processing context for a single file
pub struct ProcessorContext<'a, 'b: 'a> {
    styles: &'a mut HashSet<&'b str>,
    filename: &'a Path,
    config: &'a ResolvedConfig,
    finished: &'a HashSet<RenderingInput>,
    render_stack: &'a mut VecDeque<RenderingInput>,
}

/// Rendering input
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub enum RenderingInput {
    Index,
    Keep,
    Other(PathBuf),
}

/// Processes files
pub struct Processor {
    /// Stuff is derived from this
    config: ResolvedConfig,
    // next items to render
    render_stack: VecDeque<RenderingInput>,
    // items that have already been rendered
    finished: HashSet<RenderingInput>,
}

impl Processor {
    pub fn new(config: ResolvedConfig) -> Self {
        Self {
            config,
            render_stack: Default::default(),
            finished: Default::default(),
        }
    }

    pub fn render_toplevel(&mut self, force: bool) -> anyhow::Result<()> {
        self.render_stack.extend(std::array::IntoIter::new([
            RenderingInput::Index,
            RenderingInput::Keep,
        ]));
        self.render_all(force)?;
        Ok(())
    }

    fn render_all(&mut self, force: bool) -> anyhow::Result<()> {
        while let Some(input) = self.render_stack.pop_front() {
            println!("RENDER: {:?}", input);
            self.render(input, force)?;
        }
        Ok(())
    }

    pub fn render(&mut self, input: RenderingInput, force: bool) -> anyhow::Result<()> {
        let out_dir = &self.config.roots.output;
        let base_dir = &self.config.roots.source;
        let style_chunks_root = &self.config.lib.styles.chunks_root;
        let prelude_html = &self.config.lib.prelude_location;
        let filename = match input {
            RenderingInput::Index => &self.config.inputs.index,
            RenderingInput::Keep => &self.config.inputs.keep,
            RenderingInput::Other(ref o) => o,
        };

        if !filename.exists() {
            println!(
                "nonexistent: {}, nothing to do",
                filename.to_str().unwrap_or("unknown")
            );
            return Ok(());
        }

        // create out dir if doesn't exist
        if !out_dir.exists() {
            std::fs::create_dir_all(out_dir)?;
        }
        // // canonicalize paths
        // let (filename, base_dir, out_dir) = (
        //     filename.as_ref().canonicalize()?,
        //     base_dir.as_ref().canonicalize()?,
        //     out_dir.as_ref().canonicalize()?,
        // );
        // as-ref paths
        // let (filename, base_dir, out_dir) = (filename.as_ref(), base_dir.as_ref(), out_dir.as_ref());

        // NOTE: can't canonicalize here since the output path may not exist
        let out_path = out_dir
            .join(filename.strip_prefix(&base_dir)?)
            .with_extension("html");
        println!("out: {}", out_path.to_str().unwrap_or("unknown"));

        let buf = {
            let mut s = String::new();
            let mut f = File::open(&filename)?;
            f.read_to_string(&mut s)?;
            Ok::<_, std::io::Error>(s)
        }?;

        let mut styles = {
            let mut h = HashSet::new();
            h.insert("_global");
            h
        };

        let html = {
            let parser = Parser::new(&buf);
            let mut ctx = ProcessorContext {
                filename,
                styles: &mut styles,
                config: &self.config,
                finished: &self.finished,
                render_stack: &mut self.render_stack,
            };
            let adapter = RenderAdapter {
                ctx: &mut ctx,
                iter: parser,
            };

            let mut s = String::new();
            html::push_html(&mut s, adapter);
            s
        };

        let styles = {
            let mut new_styles = Vec::new();
            for sname in styles.into_iter() {
                let path = style_chunks_root.join(sname).with_extension("css");
                // skip missing files
                if let Ok(path) = AsRef::<Path>::as_ref(&path).canonicalize() {
                    let css_out_path = out_dir.join("css").join(sname).with_extension("css");
                    if let Some(p) = css_out_path.parent() {
                        std::fs::create_dir_all(p)?;
                    }
                    // Minify style first
                    let minified_css = {
                        let mut s = String::new();
                        let mut f = File::open(&path)?;
                        f.read_to_string(&mut s)?;
                        let minified = html_minifier::css::minify(&s)
                            .map_err(|_| anyhow::anyhow!("minify failed"))?;
                        println!(
                            "Minified {} bytes -> {} bytes ({:.3}%)",
                            s.len(),
                            minified.len(),
                            (((minified.len() as f64) - (s.len() as f64)) / s.len() as f64) * 100.
                        );
                        Ok::<_, anyhow::Error>(minified)
                    }?;
                    {
                        let mut f = File::create(&css_out_path)?;
                        f.write_all(minified_css.as_bytes())?;
                    }
                    new_styles.push(format!(
                        r#"
    <link rel="preload" href="/{0}" as="style" />
    <link rel="stylesheet" type="text/css" href="/{0}" />
    "#,
                        css_out_path
                            .strip_prefix(out_dir)
                            .unwrap_or(&css_out_path)
                            .to_str()
                            .unwrap_or("unknown")
                            .replace("\\", "/")
                    ));
                }
            }
            Ok::<_, std::io::Error>(new_styles)
        }?;
        let html = {
            println!("path = {}", prelude_html.to_str().unwrap_or("unknown"));
            let mut f = File::open(prelude_html)?;
            let mut s = String::new();
            f.read_to_string(&mut s)?;
            Ok::<_, std::io::Error>(s)
        }?
        .replace("@@@SLOT_STYLES@@@", &format!("\n{}\n", styles.join("\n")))
        .replace("@@@SLOT_CONTENT@@@", &html);

        // Minify HTML
        let minified = html_minifier::minify(&html)?;

        println!(
            "Minified {} bytes -> {} bytes ({:.3}%)",
            html.len(),
            minified.len(),
            (((minified.len() as f64) - (html.len() as f64)) / html.len() as f64) * 100.
        );

        // write only if file doesn't exist
        let needs_update = if let (Ok(out_metadata), Ok(in_metadata)) =
            (out_path.metadata(), filename.metadata())
        {
            in_metadata.modified()? >= out_metadata.modified()?
        } else {
            // failed to get metadata, or either path doesn't exist
            true
        };
        if !needs_update && !force {
            // nothing to do
            println!("fresh: {}", out_path.to_str().unwrap_or("unknown"));
        } else {
            // first, recursively create parents
            if let Some(p) = out_path.parent() {
                std::fs::create_dir_all(p)?;
            }

            if out_path.file_stem().unwrap().to_str().unwrap() == "_keep" {
                println!("special: _keep, no output");
            } else {
                let mut f = File::create(&out_path)?;
                f.write_all(minified.as_bytes())?;
                // println!("{}", html);
                println!("new: {}", out_path.to_str().unwrap_or("unknown"));
            }
        }

        self.finished.insert(input);

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::error;
use std::fmt;

use cairo;
use cssparser::BasicParseError;
use glib;
use glib::error::ErrorDomain;
use glib::translate::*;
use glib_sys;
use libc;

use attributes::Attribute;
use parsers::ParseError;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// The value could not be parsed
    Parse(ParseError),

    // The value could be parsed, but is invalid
    Value(String),
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    attr: Attribute,
    err: ValueErrorKind,
}

impl NodeError {
    pub fn parse_error(attr: Attribute, error: ParseError) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Parse(error),
        }
    }

    pub fn value_error(attr: Attribute, description: &str) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Value(description.to_string()),
        }
    }

    pub fn attribute_error(attr: Attribute, error: ValueErrorKind) -> NodeError {
        NodeError { attr, err: error }
    }
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.err {
            ValueErrorKind::Parse(ref n) => write!(
                f,
                "error parsing value for attribute \"{}\": {}",
                self.attr.to_string(),
                n.display
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value for attribute \"{}\": {}",
                self.attr.to_string(),
                s
            ),
        }
    }
}

impl From<ParseError> for ValueErrorKind {
    fn from(pe: ParseError) -> ValueErrorKind {
        ValueErrorKind::Parse(pe)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        ValueErrorKind::from(ParseError::from(e))
    }
}

#[derive(Clone)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

#[derive(Clone)]
pub enum LoadingError {
    // Could not parse data: URL
    BadDataUrl,
    Cairo(cairo::Status),
    EmptyData,
    Glib(glib::Error),
    Unknown,
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

extern "C" {
    fn rsvg_error_quark() -> glib_sys::GQuark;
}

pub fn set_gerror(err: *mut *mut glib_sys::GError, code: u32, msg: &str) {
    unsafe {
        // this is RSVG_ERROR_FAILED, the only error code available in RsvgError
        assert!(code == 0);

        glib_sys::g_set_error_literal(
            err,
            rsvg_error_quark(),
            code as libc::c_int,
            msg.to_glib_none().0,
        );
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}

/// Used as a generic error to translate to glib::Error
///
/// This type implements `glib::error::ErrorDomain`, so it can be used
/// to obtain the error code while calling `glib::Error::new()`.  Unfortunately
/// the public librsvg API does not have detailed error codes yet, so we use
/// this single value as the only possible error code to return.
#[derive(Copy, Clone)]
pub struct RsvgError;

// Keep in sync with rsvg.h:RsvgError
const RSVG_ERROR_FAILED: i32 = 0;

impl ErrorDomain for RsvgError {
    fn domain() -> glib::Quark {
        from_glib(unsafe { rsvg_error_quark() })
    }

    fn code(self) -> i32 {
        RSVG_ERROR_FAILED
    }

    fn from(code: i32) -> Option<Self> {
        match code {
            // We don't have enough information from glib error codes
            _ => Some(RsvgError),
        }
    }
}
<commit_msg>impl std::Error for LoadingError<commit_after>use std::error::{self, Error};
use std::fmt;

use cairo;
use cssparser::BasicParseError;
use glib;
use glib::error::ErrorDomain;
use glib::translate::*;
use glib_sys;
use libc;

use attributes::Attribute;
use parsers::ParseError;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// The value could not be parsed
    Parse(ParseError),

    // The value could be parsed, but is invalid
    Value(String),
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    attr: Attribute,
    err: ValueErrorKind,
}

impl NodeError {
    pub fn parse_error(attr: Attribute, error: ParseError) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Parse(error),
        }
    }

    pub fn value_error(attr: Attribute, description: &str) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Value(description.to_string()),
        }
    }

    pub fn attribute_error(attr: Attribute, error: ValueErrorKind) -> NodeError {
        NodeError { attr, err: error }
    }
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.err {
            ValueErrorKind::Parse(ref n) => write!(
                f,
                "error parsing value for attribute \"{}\": {}",
                self.attr.to_string(),
                n.display
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value for attribute \"{}\": {}",
                self.attr.to_string(),
                s
            ),
        }
    }
}

impl From<ParseError> for ValueErrorKind {
    fn from(pe: ParseError) -> ValueErrorKind {
        ValueErrorKind::Parse(pe)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        ValueErrorKind::from(ParseError::from(e))
    }
}

#[derive(Clone)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

#[derive(Debug, Clone)]
pub enum LoadingError {
    // Could not parse data: URL
    BadDataUrl,
    Cairo(cairo::Status),
    EmptyData,
    Glib(glib::Error),
    Unknown,
}

impl error::Error for LoadingError {
    fn description(&self) -> &str {
        match *self {
            LoadingError::BadDataUrl => "invalid data: URL",
            LoadingError::Cairo(_) => "cairo error",
            LoadingError::EmptyData => "empty data",
            LoadingError::Glib(ref e) => e.description(),
            LoadingError::Unknown => "unknown error",
        }
    }
}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::BadDataUrl
            | LoadingError::EmptyData
            | LoadingError::Glib(_)
            | LoadingError::Unknown => write!(f, "{}", self.description()),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

extern "C" {
    fn rsvg_error_quark() -> glib_sys::GQuark;
}

pub fn set_gerror(err: *mut *mut glib_sys::GError, code: u32, msg: &str) {
    unsafe {
        // this is RSVG_ERROR_FAILED, the only error code available in RsvgError
        assert!(code == 0);

        glib_sys::g_set_error_literal(
            err,
            rsvg_error_quark(),
            code as libc::c_int,
            msg.to_glib_none().0,
        );
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}

/// Used as a generic error to translate to glib::Error
///
/// This type implements `glib::error::ErrorDomain`, so it can be used
/// to obtain the error code while calling `glib::Error::new()`.  Unfortunately
/// the public librsvg API does not have detailed error codes yet, so we use
/// this single value as the only possible error code to return.
#[derive(Copy, Clone)]
pub struct RsvgError;

// Keep in sync with rsvg.h:RsvgError
const RSVG_ERROR_FAILED: i32 = 0;

impl ErrorDomain for RsvgError {
    fn domain() -> glib::Quark {
        from_glib(unsafe { rsvg_error_quark() })
    }

    fn code(self) -> i32 {
        RSVG_ERROR_FAILED
    }

    fn from(code: i32) -> Option<Self> {
        match code {
            // We don't have enough information from glib error codes
            _ => Some(RsvgError),
        }
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;

pub struct NormalizeDefault {
    pub has_element_before: bool,
    pub has_element_after: bool,
}

pub enum XmlSpaceNormalize {
    Default(NormalizeDefault),
    Preserve,
}

/// Implements `xml:space` handling per the SVG spec
///
/// Normalizes a string as it comes out of the XML parser's handler
/// for character data according to the SVG rules in
/// <https://www.w3.org/TR/SVG/text.html#WhiteSpace>
pub fn xml_space_normalize(mode: XmlSpaceNormalize, s: &str) -> String {
    match mode {
        XmlSpaceNormalize::Default(d) => normalize_default(d, s),
        XmlSpaceNormalize::Preserve => normalize_preserve(s),
    }
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="default", the SVG user agent will do the following
// using a copy of the original character data content. First, it will
// remove all newline characters. Then it will convert all tab
// characters into space characters. Then, it will strip off all
// leading and trailing space characters. Then, all contiguous space
// characters will be consolidated.
fn normalize_default(elements: NormalizeDefault, s: &str) -> String {
    let mut s = s;

    if !elements.has_element_before {
        s = s.trim_start();
    }

    if !elements.has_element_after {
        s = s.trim_end();
    }

    s.chars()
        .filter(|ch| *ch != '\n')
        .map(|ch| match ch {
            '\t' => ' ',
            c => c,
        })
        .coalesce(|current, next| match (current, next) {
            (' ', ' ') => Ok(' '),
            (_, _) => Err((current, next)),
        })
        .collect::<String>()
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="preserve", the SVG user agent will do the following
// using a copy of the original character data content. It will
// convert all newline and tab characters into space characters. Then,
// it will draw all space characters, including leading, trailing and
// multiple contiguous space characters. Thus, when drawn with
// xml:space="preserve", the string "a   b" (three spaces between "a"
// and "b") will produce a larger separation between "a" and "b" than
// "a b" (one space between "a" and "b").
fn normalize_preserve(s: &str) -> String {
    s.chars()
        .map(|ch| match ch {
            '\n' | '\t' => ' ',

            c => c,
        })
        .collect()
}

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

    #[test]
    fn xml_space_default() {
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\n    WS example\n    indented lines\n  "
            ),
            "WS example indented lines"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "
            ),
            "WS example indented lines"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\n  \t  \tWS \t\t\texample\n  \t  duplicate letters\t\t  \n  "
            ),
            "WS example duplicate letters"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\nWS example\nnon-indented lines\n  "
            ),
            "WS examplenon-indented lines"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\nWS example\tnon-indented lines\n  "
            ),
            "WS example non-indented lines"
        );
    }

    #[test]
    fn xml_space_default_with_elements() {
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: true,
                    has_element_after: false,
                }),
                " foo \n\t  bar "
            ),
            " foo bar"
        );

        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: true,
                }),
                " foo   \nbar "
            ),
            "foo bar "
        );
    }

    #[test]
    fn xml_space_preserve() {
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Preserve,
                "\n    WS example\n    indented lines\n  "
            ),
            "     WS example     indented lines   "
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Preserve,
                "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "
            ),
            "       WS    example      indented lines       "
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Preserve,
                "\n  \t  \tWS \t\t\texample\n  \t  duplicate letters\t\t  \n  "
            ),
            "       WS    example      duplicate letters       "
        );
    }
}
<commit_msg>space.rs: make function argument variable mutable<commit_after>use itertools::Itertools;

pub struct NormalizeDefault {
    pub has_element_before: bool,
    pub has_element_after: bool,
}

pub enum XmlSpaceNormalize {
    Default(NormalizeDefault),
    Preserve,
}

/// Implements `xml:space` handling per the SVG spec
///
/// Normalizes a string as it comes out of the XML parser's handler
/// for character data according to the SVG rules in
/// <https://www.w3.org/TR/SVG/text.html#WhiteSpace>
pub fn xml_space_normalize(mode: XmlSpaceNormalize, s: &str) -> String {
    match mode {
        XmlSpaceNormalize::Default(d) => normalize_default(d, s),
        XmlSpaceNormalize::Preserve => normalize_preserve(s),
    }
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="default", the SVG user agent will do the following
// using a copy of the original character data content. First, it will
// remove all newline characters. Then it will convert all tab
// characters into space characters. Then, it will strip off all
// leading and trailing space characters. Then, all contiguous space
// characters will be consolidated.
fn normalize_default(elements: NormalizeDefault, mut s: &str) -> String {
    if !elements.has_element_before {
        s = s.trim_start();
    }

    if !elements.has_element_after {
        s = s.trim_end();
    }

    s.chars()
        .filter(|ch| *ch != '\n')
        .map(|ch| match ch {
            '\t' => ' ',
            c => c,
        })
        .coalesce(|current, next| match (current, next) {
            (' ', ' ') => Ok(' '),
            (_, _) => Err((current, next)),
        })
        .collect::<String>()
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="preserve", the SVG user agent will do the following
// using a copy of the original character data content. It will
// convert all newline and tab characters into space characters. Then,
// it will draw all space characters, including leading, trailing and
// multiple contiguous space characters. Thus, when drawn with
// xml:space="preserve", the string "a   b" (three spaces between "a"
// and "b") will produce a larger separation between "a" and "b" than
// "a b" (one space between "a" and "b").
fn normalize_preserve(s: &str) -> String {
    s.chars()
        .map(|ch| match ch {
            '\n' | '\t' => ' ',

            c => c,
        })
        .collect()
}

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

    #[test]
    fn xml_space_default() {
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\n    WS example\n    indented lines\n  "
            ),
            "WS example indented lines"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "
            ),
            "WS example indented lines"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\n  \t  \tWS \t\t\texample\n  \t  duplicate letters\t\t  \n  "
            ),
            "WS example duplicate letters"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\nWS example\nnon-indented lines\n  "
            ),
            "WS examplenon-indented lines"
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: false,
                }),
                "\nWS example\tnon-indented lines\n  "
            ),
            "WS example non-indented lines"
        );
    }

    #[test]
    fn xml_space_default_with_elements() {
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: true,
                    has_element_after: false,
                }),
                " foo \n\t  bar "
            ),
            " foo bar"
        );

        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Default(NormalizeDefault {
                    has_element_before: false,
                    has_element_after: true,
                }),
                " foo   \nbar "
            ),
            "foo bar "
        );
    }

    #[test]
    fn xml_space_preserve() {
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Preserve,
                "\n    WS example\n    indented lines\n  "
            ),
            "     WS example     indented lines   "
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Preserve,
                "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "
            ),
            "       WS    example      indented lines       "
        );
        assert_eq!(
            xml_space_normalize(
                XmlSpaceNormalize::Preserve,
                "\n  \t  \tWS \t\t\texample\n  \t  duplicate letters\t\t  \n  "
            ),
            "       WS    example      duplicate letters       "
        );
    }
}
<|endoftext|>


<commit_before>#[derive(PartialEq, Debug, Clone)]
pub enum Token {
    Word(String),

    //characters
    LeftParenthesis,
    RightParenthesis,
    SemiColon,
    SingleQuote,
    Colon
}

pub struct Lexer {
    src: Vec<char>,
    string_expr: bool
}

impl Lexer {

    pub fn new(line: &str) -> Lexer {
        Lexer {
            src: line.chars().collect::<Vec<char>>(),
            string_expr: false
        }
    }

    pub fn next_lexem(&mut self) -> Option<Token> {
        self.remove_front_spaces();
        if self.src.is_empty() {
            None
        }
        else {
            Some(self.parse_lexem())
        }
    }

    fn parse_lexem(&mut self) -> Token {
        if self.has_lexem_in_begining() {
            match self.src.remove(0) {
                '(' => return Token::LeftParenthesis,
                ',' => return Token::Colon,
                ')' => return Token::RightParenthesis,
                ';' => return Token::SemiColon,
                _ => {}
            }
        }
        let mut i = 0;
        while i < self.src.len() {
            let s = self.src[i];
            match s {
                'a'...'z' |
                'A'...'Z' |
                '0'...'9' |
                '_' => i += 1,
                '\'' => {
                    if i == 0 && !self.string_expr {
                        self.src = self.src.split_off(1);
                        self.string_expr = !self.string_expr;
                        return Token::SingleQuote;
                    }
                    else if self.src.len() - i > 1
                            && self.src[i] == '\''
                            && self.src[i+1] == '\''
                            && self.string_expr {
                        self.src.remove(i);
                        i += 1;
                    }
                    else {
                        self.string_expr = !self.string_expr;
                        break;
                    }
                },
                ' ' | '\t' | '\n' => {
                    if i == 0 && !self.string_expr {
                        self.src.remove(0);
                    }
                    else if self.string_expr {
                        i += 1;
                    }
                    else {
                        break;
                    }
                },
                '-' => {
                    if i == 0 && self.src[1] == '-' {
                        let mut j = 2;
                        let mut c = self.src[j];
                        while c != '\n' {
                            j += 1;
                            c = self.src[j];
                        }
                        self.src = self.src.split_off(j);
                    }
                }
                _ => break,
            }
        }
        let mut v = self.src.clone();
        self.src = v.split_off(i);
        Token::Word(v.iter().map(|c| *c).collect::<String>().to_lowercase())
    }

    fn has_lexem_in_begining(&self) -> bool {
        match self.src[0] {
            '(' | ')' | ',' | ';' => true,
            _ => false,
        }
    }

    fn remove_front_spaces(&mut self) {
        while !self.src.is_empty()
                && !self.string_expr
                && (self.src[0] == ' '
                || self.src[0] == '\t'
                || self.src[0] == '\n') {
            self.src.remove(0);
        }
    }
}
<commit_msg>impl:chars inside strings value<commit_after>#[derive(PartialEq, Debug, Clone)]
pub enum Token {
    Word(String),

    //characters
    LeftParenthesis,
    RightParenthesis,
    SemiColon,
    SingleQuote,
    Colon
}

pub struct Lexer {
    src: Vec<char>,
    string_expr: bool
}

impl Lexer {

    pub fn new(line: &str) -> Lexer {
        Lexer {
            src: line.chars().collect::<Vec<char>>(),
            string_expr: false
        }
    }

    pub fn next_lexem(&mut self) -> Option<Token> {
        self.remove_front_spaces();
        if self.src.is_empty() {
            None
        }
        else {
            Some(self.parse_lexem())
        }
    }

    fn parse_lexem(&mut self) -> Token {
        if self.has_lexem_in_begining() {
            match self.src.remove(0) {
                '(' => return Token::LeftParenthesis,
                ',' => return Token::Colon,
                ')' => return Token::RightParenthesis,
                ';' => return Token::SemiColon,
                _ => {}
            }
        }
        let mut i = 0;
        while i < self.src.len() {
            let s = self.src[i];
            match s {
                'a'...'z' |
                'A'...'Z' |
                '0'...'9' |
                '_' => i += 1,
                '\'' => {
                    if i == 0 && !self.string_expr {
                        self.src = self.src.split_off(1);
                        self.string_expr = !self.string_expr;
                        return Token::SingleQuote;
                    }
                    else if self.src.len() - i > 1
                            && self.src[i] == '\''
                            && self.src[i+1] == '\''
                            && self.string_expr {
                        self.src.remove(i);
                        i += 1;
                    }
                    else {
                        self.string_expr = !self.string_expr;
                        break;
                    }
                },
                ' ' | '\t' | '\n' => {
                    if self.string_expr {
                        i += 1;
                    }
                    else if i == 0 && !self.string_expr {
                        self.src.remove(0);
                    }
                    else if self.string_expr {
                        i += 1;
                    }
                    else {
                        break;
                    }
                },
                '-' => {
                    if self.string_expr {
                        i += 1;
                    }
                    else if i == 0 && self.src[1] == '-' {
                        let mut j = 2;
                        let mut c = self.src[j];
                        while c != '\n' {
                            j += 1;
                            c = self.src[j];
                        }
                        self.src = self.src.split_off(j);
                    }
                }
                _ => if self.string_expr {
                        i += 1;
                    }
                    else {
                        break;
                    },
            }
        }
        let mut v = self.src.clone();
        self.src = v.split_off(i);
        Token::Word(v.iter().map(|c| *c).collect::<String>().to_lowercase())
    }

    fn has_lexem_in_begining(&self) -> bool {
        match self.src[0] {
            '(' | ')' | ',' | ';' => true,
            _ => false,
        }
    }

    fn remove_front_spaces(&mut self) {
        while !self.src.is_empty()
                && !self.string_expr
                && (self.src[0] == ' '
                || self.src[0] == '\t'
                || self.src[0] == '\n') {
            self.src.remove(0);
        }
    }
}
<|endoftext|>


<commit_before>//! Early memory stealing allocator
//!
//! During kernel bootup some memory may need to be allocated. This allocator
//! provides a way to allocate such memory under the assumption that it will
//! never be freed.
//!
//! It is assumed that any allocations can be fullfilled from memory that
//! resides in one of the early boot windows. The rest of the memory can
//! be iterated over to place into the final kernel window

use ::vspace::VSpaceWindow;
use ::core::marker::PhantomData;
use ::core::ops;
use ::core::mem::{size_of, forget, transmute};
use ::util;
use types::*;

/// Custom box for our returned alloccations
/// This does not implement drop as we do not support freeing these.
/// Has a reference to phantom data to ensure this allocation does not
/// live too long
#[allow(dead_code)]
pub struct StealBox<'a, T> {
    ptr: *const T,
    lifetime: &'a PhantomData<usize>,
}

pub struct StealBoxPlace<'a, T> {
    ptr: *mut T,
    lifetime: &'a PhantomData<usize>,
}

impl<'a, T> ops::Deref for StealBox<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe {&*self.ptr}
    }
}

impl<'a, T> ops::DerefMut for StealBox<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe {&mut *(self.ptr as *mut T)}
    }
}

impl<'a, T> ops::InPlace<T> for StealBoxPlace<'a, T> {
    type Owner = StealBox<'a, T>;
    unsafe fn finalize(self) -> Self::Owner {
        StealBox {ptr: self.ptr, lifetime: self.lifetime}
    }
}

impl<'a, T> ops::Placer<T> for StealBoxPlace<'a, T> {
    type Place = StealBoxPlace<'a, T>;
    /// This implementation makes no attempt to check that we can found a
    /// a valid place.
    fn make_place(self) -> Self {
        self
    }
}

impl<'a, T> ops::Place<T> for StealBoxPlace<'a, T> {
    fn pointer(&mut self) -> *mut T {
        self.ptr
    }
}

/// Abstract implementation of the memory stealing allocator.
///
/// Allocations can be performed by
///
/// ```
/// let steal_mem_impl = ...;
/// let obj = steal_mem_impl <- Obj::default()
/// ```
pub struct StealMem<'a, 'w, I, W>
        where I: Iterator<Item=(PAddr, PAddr)>, W: VSpaceWindow<'a> + 'w {
    iter: I,
    range: (PAddr, PAddr),
    window: &'w W,
    phantom: PhantomData<&'a usize>,
}

impl<'a, 'w, I, W> StealMem<'a, 'w, I, W>
        where I: Iterator<Item=(PAddr, PAddr)>, W:VSpaceWindow<'a> {
    /// Construct a new allocator. Expects to be passed an iterator that will
    /// yield (start, end) pairs of physical address. The return results
    /// should be ordered such that early pairs are (hopefully) valid in the
    /// supplied window such that any early allocations can succeed
    ///
    /// # Safety
    ///
    /// Enough of the early iterations must be valid in the supplied window
    /// for all in place allocations to succeed
    pub unsafe fn new(i: I, w: &'w W) -> StealMem<'a, 'w, I, W> {
        StealMem {iter: i, window: w, phantom: PhantomData, range: (PAddr(0), PAddr(0))}
    }
    /// Return an in place allocator to construct a variable out of
    ///
    /// # Safety
    ///
    /// Can only call alloc for as long as the original iterator returns
    /// memory within the vspace window
    pub unsafe fn alloc<T>(&mut self, align: usize) -> StealBoxPlace<'a, T> {
        /* Technically we should reserve a range here, and then complete
         * the allocation in finalize. But since this is a single threaded
         * allocated that panics if anything goes wrong (in a kernel that
         * panics if anything goes wrong) then it's fine to just alloc
         * directly. */
        let base = self.window.from_paddr(self.alloc_raw(size_of::<T>(), align));
        /* grab the raw range from the window */
        let slice: &'a [u8] = self.window.make_slice(base, size_of::<T>()).unwrap();
        /* pull out the pointer and forget about the slice */
        let pointer = slice.as_ptr();
        forget(slice);
        /* return the raw pointer to the start of the block, constructing
         * a fake lifetime that is equivalent to the original slice lifetime
         */
        StealBoxPlace { ptr: pointer as *mut T, lifetime: transmute(&PhantomData::<usize>) }
    }
    /// Internal function allocates a range with the given alignment
    fn alloc_raw(&mut self, size: usize, align: usize) -> PAddr {
        /* round the current range up by the alignment */
        let next_base = PAddr(util::round_up((self.range.0).0, align));
        /* see if this fits */
        if next_base.0 + size <= (self.range.1).0 {
            (self.range.0).0 = next_base.0 + size;
            return next_base;
        }
        /* look for another range */
        match self.iter.next() {
            Some(range) => self.range = range,
            None => panic!("Out of memory allocated {} bytes with {} align", size, align),
        }
        self.alloc_raw(size, align)
    }
}
<commit_msg>Return Option types from steal mem allocator<commit_after>//! Early memory stealing allocator
//!
//! During kernel bootup some memory may need to be allocated. This allocator
//! provides a way to allocate such memory under the assumption that it will
//! never be freed.
//!
//! It is assumed that any allocations can be fullfilled from memory that
//! resides in one of the early boot windows. The rest of the memory can
//! be iterated over to place into the final kernel window

use ::vspace::VSpaceWindow;
use ::core::marker::PhantomData;
use ::core::ops;
use ::core::mem::{size_of, forget, transmute};
use ::util;
use types::*;

/// Custom box for our returned alloccations
/// This does not implement drop as we do not support freeing these.
/// Has a reference to phantom data to ensure this allocation does not
/// live too long
#[allow(dead_code)]
pub struct StealBox<'a, T> {
    ptr: *const T,
    lifetime: &'a PhantomData<usize>,
}

pub struct StealBoxPlace<'a, T> {
    ptr: *mut T,
    lifetime: &'a PhantomData<usize>,
}

impl<'a, T> ops::Deref for StealBox<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe {&*self.ptr}
    }
}

impl<'a, T> ops::DerefMut for StealBox<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe {&mut *(self.ptr as *mut T)}
    }
}

impl<'a, T> ops::InPlace<T> for StealBoxPlace<'a, T> {
    type Owner = StealBox<'a, T>;
    unsafe fn finalize(self) -> Self::Owner {
        StealBox {ptr: self.ptr, lifetime: self.lifetime}
    }
}

impl<'a, T> ops::Placer<T> for StealBoxPlace<'a, T> {
    type Place = StealBoxPlace<'a, T>;
    /// This implementation makes no attempt to check that we can found a
    /// a valid place.
    fn make_place(self) -> Self {
        self
    }
}

impl<'a, T> ops::Place<T> for StealBoxPlace<'a, T> {
    fn pointer(&mut self) -> *mut T {
        self.ptr
    }
}

/// Abstract implementation of the memory stealing allocator.
///
/// Allocations can be performed by
///
/// ```
/// let steal_mem_impl = ...;
/// let obj = steal_mem_impl.alloc(align_of_obj).unwrap() <- Obj::default()
/// ```
pub struct StealMem<'a, 'w, I, W>
        where I: Iterator<Item=(PAddr, PAddr)>, W: VSpaceWindow<'a> + 'w {
    iter: I,
    range: (PAddr, PAddr),
    window: &'w W,
    phantom: PhantomData<&'a usize>,
}

impl<'a, 'w, I, W> StealMem<'a, 'w, I, W>
        where I: Iterator<Item=(PAddr, PAddr)>, W:VSpaceWindow<'a> {
    /// Construct a new allocator. Expects to be passed an iterator that will
    /// yield (start, end) pairs of physical address. The return results
    /// should be ordered such that early pairs are (hopefully) valid in the
    /// supplied window such that any early allocations can succeed
    ///
    /// # Safety
    ///
    /// Enough of the early iterations must be valid in the supplied window
    /// for all in place allocations to succeed
    pub unsafe fn new(i: I, w: &'w W) -> StealMem<'a, 'w, I, W> {
        StealMem {iter: i, window: w, phantom: PhantomData, range: (PAddr(0), PAddr(0))}
    }
    /// Return an in place allocator to construct a variable out of
    ///
    /// # Safety
    ///
    /// Can only call alloc for as long as the original iterator returns
    /// memory within the vspace window
    pub unsafe fn alloc<T>(&mut self, align: usize)
            -> Option<StealBoxPlace<'a, T>> {
        /* Technically we should reserve a range here, and then complete
         * the allocation in finalize. But since this is a single threaded
         * allocated that panics if anything goes wrong (in a kernel that
         * panics if anything goes wrong) then it's fine to just alloc
         * directly. */
        let paddr = match self.alloc_raw(size_of::<T>(), align) {
            Some(p) => p,
            None => return None,
        };
        let base = self.window.from_paddr(paddr);
        /* grab the raw range from the window */
        let slice: &'a [u8] = match self.window.make_slice(base, size_of::<T>()) {
            Some(s) => s,
            None => return None,
        };
        /* pull out the pointer and forget about the slice */
        let pointer = slice.as_ptr();
        forget(slice);
        /* return the raw pointer to the start of the block, constructing
         * a fake lifetime that is equivalent to the original slice lifetime
         */
        Some(StealBoxPlace { ptr: pointer as *mut T, lifetime: transmute(&PhantomData::<usize>) })
    }
    /// Internal function allocates a range with the given alignment
    fn alloc_raw(&mut self, size: usize, align: usize) -> Option<PAddr> {
        /* round the current range up by the alignment */
        let next_base = PAddr(util::round_up((self.range.0).0, align));
        /* see if this fits */
        if next_base.0 + size <= (self.range.1).0 {
            (self.range.0).0 = next_base.0 + size;
            return Some(next_base);
        }
        /* look for another range */
        match self.iter.next() {
            Some(range) =>
                {
                    self.range = range;
                    self.alloc_raw(size, align)
                },
            None => None,
        }
    }
}
<|endoftext|>


<commit_before>use super::SAME_ITEM_PUSH;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::path_to_local;
use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::symbol::sym;
use std::iter::Iterator;

/// Detects for loop pushing the same item into a Vec
pub(super) fn check<'tcx>(
    cx: &LateContext<'tcx>,
    pat: &'tcx Pat<'_>,
    _: &'tcx Expr<'_>,
    body: &'tcx Expr<'_>,
    _: &'tcx Expr<'_>,
) {
    fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
        let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
        let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");

        span_lint_and_help(
            cx,
            SAME_ITEM_PUSH,
            vec.span,
            "it looks like the same item is being pushed into this Vec",
            None,
            &format!(
                "try using vec![{}; SIZE] or {}.resize(NEW_SIZE, {})",
                item_str, vec_str, item_str
            ),
        );
    }

    if !matches!(pat.kind, PatKind::Wild) {
        return;
    }

    // Determine whether it is safe to lint the body
    let mut same_item_push_visitor = SameItemPushVisitor::new(cx);
    walk_expr(&mut same_item_push_visitor, body);
    if_chain! {
        if same_item_push_visitor.should_lint();
        if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push;
        let vec_ty = cx.typeck_results().expr_ty(vec);
        let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
        if cx
            .tcx
            .lang_items()
            .clone_trait()
            .map_or(false, |id| implements_trait(cx, ty, id, &[]));
        then {
            // Make sure that the push does not involve possibly mutating values
            match pushed_item.kind {
                ExprKind::Path(ref qpath) => {
                    match cx.qpath_res(qpath, pushed_item.hir_id) {
                        // immutable bindings that are initialized with literal or constant
                        Res::Local(hir_id) => {
                            let node = cx.tcx.hir().get(hir_id);
                            if_chain! {
                                if let Node::Binding(pat) = node;
                                if let PatKind::Binding(bind_ann, ..) = pat.kind;
                                if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
                                let parent_node = cx.tcx.hir().get_parent_node(hir_id);
                                if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
                                if let Some(init) = parent_let_expr.init;
                                then {
                                    match init.kind {
                                        // immutable bindings that are initialized with literal
                                        ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
                                        // immutable bindings that are initialized with constant
                                        ExprKind::Path(ref path) => {
                                            if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
                                                emit_lint(cx, vec, pushed_item);
                                            }
                                        }
                                        _ => {},
                                    }
                                }
                            }
                        },
                        // constant
                        Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
                        _ => {},
                    }
                },
                ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
                _ => {},
            }
        }
    }
}

// Scans the body of the for loop and determines whether lint should be given
struct SameItemPushVisitor<'a, 'tcx> {
    non_deterministic_expr: bool,
    multiple_pushes: bool,
    // this field holds the last vec push operation visited, which should be the only push seen
    vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
    cx: &'a LateContext<'tcx>,
    used_locals: FxHashSet<HirId>,
}

impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
    fn new(cx: &'a LateContext<'tcx>) -> Self {
        Self {
            non_deterministic_expr: false,
            multiple_pushes: false,
            vec_push: None,
            cx,
            used_locals: FxHashSet::default(),
        }
    }

    fn should_lint(&self) -> bool {
        if_chain! {
            if !self.non_deterministic_expr;
            if !self.multiple_pushes;
            if let Some((vec, _)) = self.vec_push;
            if let Some(hir_id) = path_to_local(vec);
            then {
                !self.used_locals.contains(&hir_id)
            } else {
                false
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        match &expr.kind {
            // Non-determinism may occur ... don't give a lint
            ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true,
            ExprKind::Block(block, _) => self.visit_block(block),
            _ => {
                if let Some(hir_id) = path_to_local(expr) {
                    self.used_locals.insert(hir_id);
                }
                walk_expr(self, expr);
            },
        }
    }

    fn visit_block(&mut self, b: &'tcx Block<'_>) {
        for stmt in b.stmts.iter() {
            self.visit_stmt(stmt);
        }
    }

    fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) {
        let vec_push_option = get_vec_push(self.cx, s);
        if vec_push_option.is_none() {
            // Current statement is not a push so visit inside
            match &s.kind {
                StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(expr),
                _ => {},
            }
        } else {
            // Current statement is a push ...check whether another
            // push had been previously done
            if self.vec_push.is_none() {
                self.vec_push = vec_push_option;
            } else {
                // There are multiple pushes ... don't lint
                self.multiple_pushes = true;
            }
        }
    }

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }
}

// Given some statement, determine if that statement is a push on a Vec. If it is, return
// the Vec being pushed into and the item being pushed
fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
    if_chain! {
            // Extract method being called
            if let StmtKind::Semi(semi_stmt) = &stmt.kind;
            if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind;
            // Figure out the parameters for the method call
            if let Some(self_expr) = args.get(0);
            if let Some(pushed_item) = args.get(1);
            // Check that the method being called is push() on a Vec
            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::vec_type);
            if path.ident.name.as_str() == "push";
            then {
                return Some((self_expr, pushed_item))
            }
    }
    None
}
<commit_msg>Fix `same_item_push.rs`<commit_after>use super::SAME_ITEM_PUSH;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::path_to_local;
use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::symbol::sym;
use std::iter::Iterator;

/// Detects for loop pushing the same item into a Vec
pub(super) fn check<'tcx>(
    cx: &LateContext<'tcx>,
    pat: &'tcx Pat<'_>,
    _: &'tcx Expr<'_>,
    body: &'tcx Expr<'_>,
    _: &'tcx Expr<'_>,
) {
    fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
        let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
        let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");

        span_lint_and_help(
            cx,
            SAME_ITEM_PUSH,
            vec.span,
            "it looks like the same item is being pushed into this Vec",
            None,
            &format!(
                "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
                item_str, vec_str, item_str
            ),
        );
    }

    if !matches!(pat.kind, PatKind::Wild) {
        return;
    }

    // Determine whether it is safe to lint the body
    let mut same_item_push_visitor = SameItemPushVisitor::new(cx);
    walk_expr(&mut same_item_push_visitor, body);
    if_chain! {
        if same_item_push_visitor.should_lint();
        if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push;
        let vec_ty = cx.typeck_results().expr_ty(vec);
        let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
        if cx
            .tcx
            .lang_items()
            .clone_trait()
            .map_or(false, |id| implements_trait(cx, ty, id, &[]));
        then {
            // Make sure that the push does not involve possibly mutating values
            match pushed_item.kind {
                ExprKind::Path(ref qpath) => {
                    match cx.qpath_res(qpath, pushed_item.hir_id) {
                        // immutable bindings that are initialized with literal or constant
                        Res::Local(hir_id) => {
                            let node = cx.tcx.hir().get(hir_id);
                            if_chain! {
                                if let Node::Binding(pat) = node;
                                if let PatKind::Binding(bind_ann, ..) = pat.kind;
                                if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
                                let parent_node = cx.tcx.hir().get_parent_node(hir_id);
                                if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
                                if let Some(init) = parent_let_expr.init;
                                then {
                                    match init.kind {
                                        // immutable bindings that are initialized with literal
                                        ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
                                        // immutable bindings that are initialized with constant
                                        ExprKind::Path(ref path) => {
                                            if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
                                                emit_lint(cx, vec, pushed_item);
                                            }
                                        }
                                        _ => {},
                                    }
                                }
                            }
                        },
                        // constant
                        Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
                        _ => {},
                    }
                },
                ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
                _ => {},
            }
        }
    }
}

// Scans the body of the for loop and determines whether lint should be given
struct SameItemPushVisitor<'a, 'tcx> {
    non_deterministic_expr: bool,
    multiple_pushes: bool,
    // this field holds the last vec push operation visited, which should be the only push seen
    vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
    cx: &'a LateContext<'tcx>,
    used_locals: FxHashSet<HirId>,
}

impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
    fn new(cx: &'a LateContext<'tcx>) -> Self {
        Self {
            non_deterministic_expr: false,
            multiple_pushes: false,
            vec_push: None,
            cx,
            used_locals: FxHashSet::default(),
        }
    }

    fn should_lint(&self) -> bool {
        if_chain! {
            if !self.non_deterministic_expr;
            if !self.multiple_pushes;
            if let Some((vec, _)) = self.vec_push;
            if let Some(hir_id) = path_to_local(vec);
            then {
                !self.used_locals.contains(&hir_id)
            } else {
                false
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        match &expr.kind {
            // Non-determinism may occur ... don't give a lint
            ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true,
            ExprKind::Block(block, _) => self.visit_block(block),
            _ => {
                if let Some(hir_id) = path_to_local(expr) {
                    self.used_locals.insert(hir_id);
                }
                walk_expr(self, expr);
            },
        }
    }

    fn visit_block(&mut self, b: &'tcx Block<'_>) {
        for stmt in b.stmts.iter() {
            self.visit_stmt(stmt);
        }
    }

    fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) {
        let vec_push_option = get_vec_push(self.cx, s);
        if vec_push_option.is_none() {
            // Current statement is not a push so visit inside
            match &s.kind {
                StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(expr),
                _ => {},
            }
        } else {
            // Current statement is a push ...check whether another
            // push had been previously done
            if self.vec_push.is_none() {
                self.vec_push = vec_push_option;
            } else {
                // There are multiple pushes ... don't lint
                self.multiple_pushes = true;
            }
        }
    }

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }
}

// Given some statement, determine if that statement is a push on a Vec. If it is, return
// the Vec being pushed into and the item being pushed
fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
    if_chain! {
            // Extract method being called
            if let StmtKind::Semi(semi_stmt) = &stmt.kind;
            if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind;
            // Figure out the parameters for the method call
            if let Some(self_expr) = args.get(0);
            if let Some(pushed_item) = args.get(1);
            // Check that the method being called is push() on a Vec
            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::vec_type);
            if path.ident.name.as_str() == "push";
            then {
                return Some((self_expr, pushed_item))
            }
    }
    None
}
<|endoftext|>