<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax::ast;
use syntax::codemap::{self, CodeMap, Span, BytePos};
use syntax::visit;

use strings::string_buffer::StringBuffer;

use utils;
use config::Config;
use rewrite::{Rewrite, RewriteContext};

pub struct FmtVisitor<'a> {
    pub codemap: &'a CodeMap,
    pub buffer: StringBuffer,
    pub last_pos: BytePos,
    // TODO RAII util for indenting
    pub block_indent: usize,
    pub config: &'a Config,
}

impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
    // FIXME: We'd rather not format expressions here, as we have little
    // context. How are we still reaching this?
    fn visit_expr(&mut self, ex: &'v ast::Expr) {
        debug!("visit_expr: {:?} {:?}",
               self.codemap.lookup_char_pos(ex.span.lo),
               self.codemap.lookup_char_pos(ex.span.hi));
        self.format_missing(ex.span.lo);

        let offset = self.buffer.cur_offset();
        let rewrite = ex.rewrite(&self.get_context(), self.config.max_width - offset, offset);

        if let Some(new_str) = rewrite {
            self.buffer.push_str(&new_str);
            self.last_pos = ex.span.hi;
        }
    }

    fn visit_stmt(&mut self, stmt: &'v ast::Stmt) {
        match stmt.node {
            ast::Stmt_::StmtDecl(ref decl, _) => {
                return match decl.node {
                    ast::Decl_::DeclLocal(ref local) => self.visit_let(local, stmt.span),
                    ast::Decl_::DeclItem(..) => visit::walk_stmt(self, stmt),
                };
            }
            ast::Stmt_::StmtExpr(ref ex, _) | ast::Stmt_::StmtSemi(ref ex, _) => {
                self.format_missing_with_indent(stmt.span.lo);
                let suffix = if let ast::Stmt_::StmtExpr(..) = stmt.node {
                    ""
                } else {
                    ";"
                };

                // 1 = trailing semicolon;
                let rewrite = ex.rewrite(&self.get_context(),
                                         self.config.max_width - self.block_indent - suffix.len(),
                                         self.block_indent);

                if let Some(new_str) = rewrite {
                    self.buffer.push_str(&new_str);
                    self.buffer.push_str(suffix);
                    self.last_pos = stmt.span.hi;
                }
            }
            ast::Stmt_::StmtMac(..) => {
                self.format_missing_with_indent(stmt.span.lo);
                visit::walk_stmt(self, stmt);
            }
        }
    }

    fn visit_block(&mut self, b: &'v ast::Block) {
        debug!("visit_block: {:?} {:?}",
               self.codemap.lookup_char_pos(b.span.lo),
               self.codemap.lookup_char_pos(b.span.hi));

        // Check if this block has braces.
        let snippet = self.snippet(b.span);
        let has_braces = snippet.chars().next().unwrap() == '{' || &snippet[..6] == "unsafe";
        let brace_compensation = if has_braces {
            BytePos(1)
        } else {
            BytePos(0)
        };

        self.last_pos = self.last_pos + brace_compensation;
        self.block_indent += self.config.tab_spaces;
        self.buffer.push_str("{");

        for stmt in &b.stmts {
            self.visit_stmt(&stmt)
        }

        match b.expr {
            Some(ref e) => {
                self.format_missing_with_indent(e.span.lo);
                self.visit_expr(e);
            }
            None => {}
        }

        self.block_indent -= self.config.tab_spaces;
        // TODO we should compress any newlines here to just one
        self.format_missing_with_indent(b.span.hi - brace_compensation);
        self.buffer.push_str("}");
        self.last_pos = b.span.hi;
    }

    // Note that this only gets called for function definitions. Required methods
    // on traits do not get handled here.
    fn visit_fn(&mut self,
                fk: visit::FnKind<'v>,
                fd: &'v ast::FnDecl,
                b: &'v ast::Block,
                s: Span,
                _: ast::NodeId) {
        self.format_missing_with_indent(s.lo);
        self.last_pos = s.lo;

        let indent = self.block_indent;
        match fk {
            visit::FkItemFn(ident,
                            ref generics,
                            ref unsafety,
                            ref constness,
                            ref abi,
                            vis) => {
                let new_fn = self.rewrite_fn(indent,
                                             ident,
                                             fd,
                                             None,
                                             generics,
                                             unsafety,
                                             constness,
                                             abi,
                                             vis,
                                             codemap::mk_sp(s.lo, b.span.lo));
                self.buffer.push_str(&new_fn);
            }
            visit::FkMethod(ident, ref sig, vis) => {
                let new_fn = self.rewrite_fn(indent,
                                             ident,
                                             fd,
                                             Some(&sig.explicit_self),
                                             &sig.generics,
                                             &sig.unsafety,
                                             &sig.constness,
                                             &sig.abi,
                                             vis.unwrap_or(ast::Visibility::Inherited),
                                             codemap::mk_sp(s.lo, b.span.lo));
                self.buffer.push_str(&new_fn);
            }
            visit::FkFnBlock(..) => {}
        }

        self.last_pos = b.span.lo;
        self.visit_block(b)
    }

    fn visit_item(&mut self, item: &'v ast::Item) {
        // Don't look at attributes for modules.
        // We want to avoid looking at attributes in another file, which the AST
        // doesn't distinguish. FIXME This is overly conservative and means we miss
        // attributes on inline modules.
        match item.node {
            ast::Item_::ItemMod(_) => {}
            _ => {
                if self.visit_attrs(&item.attrs) {
                    return;
                }
            }
        }

        match item.node {
            ast::Item_::ItemUse(ref vp) => {
                self.format_import(item.vis, vp, item.span);
            }
            ast::Item_::ItemImpl(..) |
            ast::Item_::ItemTrait(..) => {
                self.block_indent += self.config.tab_spaces;
                visit::walk_item(self, item);
                self.block_indent -= self.config.tab_spaces;
            }
            ast::Item_::ItemExternCrate(_) => {
                self.format_missing_with_indent(item.span.lo);
                let new_str = self.snippet(item.span);
                self.buffer.push_str(&new_str);
                self.last_pos = item.span.hi;
            }
            ast::Item_::ItemStruct(ref def, ref generics) => {
                self.format_missing_with_indent(item.span.lo);
                self.visit_struct(item.ident,
                                  item.vis,
                                  def,
                                  generics,
                                  item.span);
            }
            ast::Item_::ItemEnum(ref def, ref generics) => {
                self.format_missing_with_indent(item.span.lo);
                self.visit_enum(item.ident,
                                item.vis,
                                def,
                                generics,
                                item.span);
                self.last_pos = item.span.hi;
            }
            ast::Item_::ItemMod(ref module) => {
                self.format_missing_with_indent(item.span.lo);
                self.format_mod(module, item.span, item.ident);
            }
            _ => {
                visit::walk_item(self, item);
            }
        }
    }

    fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
        if self.visit_attrs(&ti.attrs) {
            return;
        }

        if let ast::TraitItem_::MethodTraitItem(ref sig, None) = ti.node {
            self.format_missing_with_indent(ti.span.lo);

            let indent = self.block_indent;
            let new_fn = self.rewrite_required_fn(indent,
                                                  ti.ident,
                                                  sig,
                                                  ti.span);

            self.buffer.push_str(&new_fn);
            self.last_pos = ti.span.hi;
        }
        // TODO format trait types

        visit::walk_trait_item(self, ti)
    }

    fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
        if self.visit_attrs(&ii.attrs) {
            return;
        }
        visit::walk_impl_item(self, ii)
    }

    fn visit_mac(&mut self, mac: &'v ast::Mac) {
        visit::walk_mac(self, mac)
    }
}

impl<'a> FmtVisitor<'a> {
    pub fn from_codemap<'b>(codemap: &'b CodeMap, config: &'b Config) -> FmtVisitor<'b> {
        FmtVisitor {
            codemap: codemap,
            buffer: StringBuffer::new(),
            last_pos: BytePos(0),
            block_indent: 0,
            config: config,
        }
    }

    pub fn snippet(&self, span: Span) -> String {
        match self.codemap.span_to_snippet(span) {
            Ok(s) => s,
            Err(_) => {
                println!("Couldn't make snippet for span {:?}->{:?}",
                         self.codemap.lookup_char_pos(span.lo),
                         self.codemap.lookup_char_pos(span.hi));
                "".to_owned()
            }
        }
    }

    // Returns true if we should skip the following item.
    pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
        if attrs.is_empty() {
            return false;
        }

        let first = &attrs[0];
        self.format_missing_with_indent(first.span.lo);

        if utils::contains_skip(attrs) {
            true
        } else {
            let rewrite = self.rewrite_attrs(attrs, self.block_indent);
            self.buffer.push_str(&rewrite);
            let last = attrs.last().unwrap();
            self.last_pos = last.span.hi;
            false
        }
    }

    pub fn rewrite_attrs(&self, attrs: &[ast::Attribute], indent: usize) -> String {
        let mut result = String::new();
        let indent = utils::make_indent(indent);

        for (i, a) in attrs.iter().enumerate() {
            let a_str = self.snippet(a.span);

            if i > 0 {
                let comment = self.snippet(codemap::mk_sp(attrs[i-1].span.hi, a.span.lo));
                // This particular horror show is to preserve line breaks in between doc
                // comments. An alternative would be to force such line breaks to start
                // with the usual doc comment token.
                let multi_line = a_str.starts_with("//") && comment.matches('\n').count() > 1;
                let comment = comment.trim();
                if !comment.is_empty() {
                    result.push_str(&indent);
                    result.push_str(comment);
                    result.push('\n');
                } else if multi_line {
                    result.push('\n');
                }
                result.push_str(&indent);
            }

            result.push_str(&a_str);

            if i < attrs.len() - 1 {
                result.push('\n');
            }
        }

        result
    }

    fn format_mod(&mut self, m: &ast::Mod, s: Span, ident: ast::Ident) {
        debug!("FmtVisitor::format_mod: ident: {:?}, span: {:?}", ident, s);

        // Decide whether this is an inline mod or an external mod.
        let local_file_name = self.codemap.span_to_filename(s);
        let is_internal = local_file_name == self.codemap.span_to_filename(m.inner);

        // TODO Should rewrite properly `mod X;`

        if is_internal {
            debug!("FmtVisitor::format_mod: internal mod");
            self.block_indent += self.config.tab_spaces;
            visit::walk_mod(self, m);
            debug!("... last_pos after: {:?}", self.last_pos);
            self.block_indent -= self.config.tab_spaces;
        }
    }

    pub fn format_separate_mod(&mut self, m: &ast::Mod, filename: &str) {
        let filemap = self.codemap.get_filemap(filename);
        self.last_pos = filemap.start_pos;
        self.block_indent = 0;
        visit::walk_mod(self, m);
        self.format_missing(filemap.end_pos);
    }

    fn format_import(&mut self, vis: ast::Visibility, vp: &ast::ViewPath, span: Span) {
        let vis = utils::format_visibility(vis);
        let offset = self.block_indent + vis.len() + "use ".len();
        let context = RewriteContext {
            codemap: self.codemap,
            config: self.config,
            block_indent: self.block_indent,
        };
        // 1 = ";"
        match vp.rewrite(&context, self.config.max_width - offset - 1, offset) {
            Some(ref s) if s.is_empty() => {
                // Format up to last newline
                let prev_span = codemap::mk_sp(self.last_pos, span.lo);
                let span_end = match self.snippet(prev_span).rfind('\n') {
                    Some(offset) => self.last_pos + BytePos(offset as u32),
                    None => span.lo,
                };
                self.format_missing(span_end);
                self.last_pos = span.hi;
            }
            Some(ref s) => {
                let s = format!("{}use {};", vis, s);
                self.format_missing_with_indent(span.lo);
                self.buffer.push_str(&s);
                self.last_pos = span.hi;
            }
            None => {
                self.format_missing_with_indent(span.lo);
                self.format_missing(span.hi);
            }
        }
    }

    pub fn get_context(&self) -> RewriteContext {
        RewriteContext {
            codemap: self.codemap,
            config: self.config,
            block_indent: self.block_indent,
        }
    }
}
<commit_msg>Fix build on nightly<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax::ast;
use syntax::codemap::{self, CodeMap, Span, BytePos};
use syntax::visit;

use strings::string_buffer::StringBuffer;

use utils;
use config::Config;
use rewrite::{Rewrite, RewriteContext};

pub struct FmtVisitor<'a> {
    pub codemap: &'a CodeMap,
    pub buffer: StringBuffer,
    pub last_pos: BytePos,
    // TODO RAII util for indenting
    pub block_indent: usize,
    pub config: &'a Config,
}

impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
    // FIXME: We'd rather not format expressions here, as we have little
    // context. How are we still reaching this?
    fn visit_expr(&mut self, ex: &'v ast::Expr) {
        debug!("visit_expr: {:?} {:?}",
               self.codemap.lookup_char_pos(ex.span.lo),
               self.codemap.lookup_char_pos(ex.span.hi));
        self.format_missing(ex.span.lo);

        let offset = self.buffer.cur_offset();
        let rewrite = ex.rewrite(&self.get_context(), self.config.max_width - offset, offset);

        if let Some(new_str) = rewrite {
            self.buffer.push_str(&new_str);
            self.last_pos = ex.span.hi;
        }
    }

    fn visit_stmt(&mut self, stmt: &'v ast::Stmt) {
        match stmt.node {
            ast::Stmt_::StmtDecl(ref decl, _) => {
                return match decl.node {
                    ast::Decl_::DeclLocal(ref local) => self.visit_let(local, stmt.span),
                    ast::Decl_::DeclItem(..) => visit::walk_stmt(self, stmt),
                };
            }
            ast::Stmt_::StmtExpr(ref ex, _) | ast::Stmt_::StmtSemi(ref ex, _) => {
                self.format_missing_with_indent(stmt.span.lo);
                let suffix = if let ast::Stmt_::StmtExpr(..) = stmt.node {
                    ""
                } else {
                    ";"
                };

                // 1 = trailing semicolon;
                let rewrite = ex.rewrite(&self.get_context(),
                                         self.config.max_width - self.block_indent - suffix.len(),
                                         self.block_indent);

                if let Some(new_str) = rewrite {
                    self.buffer.push_str(&new_str);
                    self.buffer.push_str(suffix);
                    self.last_pos = stmt.span.hi;
                }
            }
            ast::Stmt_::StmtMac(..) => {
                self.format_missing_with_indent(stmt.span.lo);
                visit::walk_stmt(self, stmt);
            }
        }
    }

    fn visit_block(&mut self, b: &'v ast::Block) {
        debug!("visit_block: {:?} {:?}",
               self.codemap.lookup_char_pos(b.span.lo),
               self.codemap.lookup_char_pos(b.span.hi));

        // Check if this block has braces.
        let snippet = self.snippet(b.span);
        let has_braces = snippet.chars().next().unwrap() == '{' || &snippet[..6] == "unsafe";
        let brace_compensation = if has_braces {
            BytePos(1)
        } else {
            BytePos(0)
        };

        self.last_pos = self.last_pos + brace_compensation;
        self.block_indent += self.config.tab_spaces;
        self.buffer.push_str("{");

        for stmt in &b.stmts {
            self.visit_stmt(&stmt)
        }

        match b.expr {
            Some(ref e) => {
                self.format_missing_with_indent(e.span.lo);
                self.visit_expr(e);
            }
            None => {}
        }

        self.block_indent -= self.config.tab_spaces;
        // TODO we should compress any newlines here to just one
        self.format_missing_with_indent(b.span.hi - brace_compensation);
        self.buffer.push_str("}");
        self.last_pos = b.span.hi;
    }

    // Note that this only gets called for function definitions. Required methods
    // on traits do not get handled here.
    fn visit_fn(&mut self,
                fk: visit::FnKind<'v>,
                fd: &'v ast::FnDecl,
                b: &'v ast::Block,
                s: Span,
                _: ast::NodeId) {
        self.format_missing_with_indent(s.lo);
        self.last_pos = s.lo;

        let indent = self.block_indent;
        match fk {
            visit::FkItemFn(ident,
                            ref generics,
                            ref unsafety,
                            ref constness,
                            ref abi,
                            vis) => {
                let new_fn = self.rewrite_fn(indent,
                                             ident,
                                             fd,
                                             None,
                                             generics,
                                             unsafety,
                                             constness,
                                             abi,
                                             vis,
                                             codemap::mk_sp(s.lo, b.span.lo));
                self.buffer.push_str(&new_fn);
            }
            visit::FkMethod(ident, ref sig, vis) => {
                let new_fn = self.rewrite_fn(indent,
                                             ident,
                                             fd,
                                             Some(&sig.explicit_self),
                                             &sig.generics,
                                             &sig.unsafety,
                                             &sig.constness,
                                             &sig.abi,
                                             vis.unwrap_or(ast::Visibility::Inherited),
                                             codemap::mk_sp(s.lo, b.span.lo));
                self.buffer.push_str(&new_fn);
            }
            visit::FkClosure(..) => {}
        }

        self.last_pos = b.span.lo;
        self.visit_block(b)
    }

    fn visit_item(&mut self, item: &'v ast::Item) {
        // Don't look at attributes for modules.
        // We want to avoid looking at attributes in another file, which the AST
        // doesn't distinguish. FIXME This is overly conservative and means we miss
        // attributes on inline modules.
        match item.node {
            ast::Item_::ItemMod(_) => {}
            _ => {
                if self.visit_attrs(&item.attrs) {
                    return;
                }
            }
        }

        match item.node {
            ast::Item_::ItemUse(ref vp) => {
                self.format_import(item.vis, vp, item.span);
            }
            ast::Item_::ItemImpl(..) |
            ast::Item_::ItemTrait(..) => {
                self.block_indent += self.config.tab_spaces;
                visit::walk_item(self, item);
                self.block_indent -= self.config.tab_spaces;
            }
            ast::Item_::ItemExternCrate(_) => {
                self.format_missing_with_indent(item.span.lo);
                let new_str = self.snippet(item.span);
                self.buffer.push_str(&new_str);
                self.last_pos = item.span.hi;
            }
            ast::Item_::ItemStruct(ref def, ref generics) => {
                self.format_missing_with_indent(item.span.lo);
                self.visit_struct(item.ident,
                                  item.vis,
                                  def,
                                  generics,
                                  item.span);
            }
            ast::Item_::ItemEnum(ref def, ref generics) => {
                self.format_missing_with_indent(item.span.lo);
                self.visit_enum(item.ident,
                                item.vis,
                                def,
                                generics,
                                item.span);
                self.last_pos = item.span.hi;
            }
            ast::Item_::ItemMod(ref module) => {
                self.format_missing_with_indent(item.span.lo);
                self.format_mod(module, item.span, item.ident);
            }
            _ => {
                visit::walk_item(self, item);
            }
        }
    }

    fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
        if self.visit_attrs(&ti.attrs) {
            return;
        }

        if let ast::TraitItem_::MethodTraitItem(ref sig, None) = ti.node {
            self.format_missing_with_indent(ti.span.lo);

            let indent = self.block_indent;
            let new_fn = self.rewrite_required_fn(indent,
                                                  ti.ident,
                                                  sig,
                                                  ti.span);

            self.buffer.push_str(&new_fn);
            self.last_pos = ti.span.hi;
        }
        // TODO format trait types

        visit::walk_trait_item(self, ti)
    }

    fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
        if self.visit_attrs(&ii.attrs) {
            return;
        }
        visit::walk_impl_item(self, ii)
    }

    fn visit_mac(&mut self, mac: &'v ast::Mac) {
        visit::walk_mac(self, mac)
    }
}

impl<'a> FmtVisitor<'a> {
    pub fn from_codemap<'b>(codemap: &'b CodeMap, config: &'b Config) -> FmtVisitor<'b> {
        FmtVisitor {
            codemap: codemap,
            buffer: StringBuffer::new(),
            last_pos: BytePos(0),
            block_indent: 0,
            config: config,
        }
    }

    pub fn snippet(&self, span: Span) -> String {
        match self.codemap.span_to_snippet(span) {
            Ok(s) => s,
            Err(_) => {
                println!("Couldn't make snippet for span {:?}->{:?}",
                         self.codemap.lookup_char_pos(span.lo),
                         self.codemap.lookup_char_pos(span.hi));
                "".to_owned()
            }
        }
    }

    // Returns true if we should skip the following item.
    pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
        if attrs.is_empty() {
            return false;
        }

        let first = &attrs[0];
        self.format_missing_with_indent(first.span.lo);

        if utils::contains_skip(attrs) {
            true
        } else {
            let rewrite = self.rewrite_attrs(attrs, self.block_indent);
            self.buffer.push_str(&rewrite);
            let last = attrs.last().unwrap();
            self.last_pos = last.span.hi;
            false
        }
    }

    pub fn rewrite_attrs(&self, attrs: &[ast::Attribute], indent: usize) -> String {
        let mut result = String::new();
        let indent = utils::make_indent(indent);

        for (i, a) in attrs.iter().enumerate() {
            let a_str = self.snippet(a.span);

            if i > 0 {
                let comment = self.snippet(codemap::mk_sp(attrs[i-1].span.hi, a.span.lo));
                // This particular horror show is to preserve line breaks in between doc
                // comments. An alternative would be to force such line breaks to start
                // with the usual doc comment token.
                let multi_line = a_str.starts_with("//") && comment.matches('\n').count() > 1;
                let comment = comment.trim();
                if !comment.is_empty() {
                    result.push_str(&indent);
                    result.push_str(comment);
                    result.push('\n');
                } else if multi_line {
                    result.push('\n');
                }
                result.push_str(&indent);
            }

            result.push_str(&a_str);

            if i < attrs.len() - 1 {
                result.push('\n');
            }
        }

        result
    }

    fn format_mod(&mut self, m: &ast::Mod, s: Span, ident: ast::Ident) {
        debug!("FmtVisitor::format_mod: ident: {:?}, span: {:?}", ident, s);

        // Decide whether this is an inline mod or an external mod.
        let local_file_name = self.codemap.span_to_filename(s);
        let is_internal = local_file_name == self.codemap.span_to_filename(m.inner);

        // TODO Should rewrite properly `mod X;`

        if is_internal {
            debug!("FmtVisitor::format_mod: internal mod");
            self.block_indent += self.config.tab_spaces;
            visit::walk_mod(self, m);
            debug!("... last_pos after: {:?}", self.last_pos);
            self.block_indent -= self.config.tab_spaces;
        }
    }

    pub fn format_separate_mod(&mut self, m: &ast::Mod, filename: &str) {
        let filemap = self.codemap.get_filemap(filename);
        self.last_pos = filemap.start_pos;
        self.block_indent = 0;
        visit::walk_mod(self, m);
        self.format_missing(filemap.end_pos);
    }

    fn format_import(&mut self, vis: ast::Visibility, vp: &ast::ViewPath, span: Span) {
        let vis = utils::format_visibility(vis);
        let offset = self.block_indent + vis.len() + "use ".len();
        let context = RewriteContext {
            codemap: self.codemap,
            config: self.config,
            block_indent: self.block_indent,
        };
        // 1 = ";"
        match vp.rewrite(&context, self.config.max_width - offset - 1, offset) {
            Some(ref s) if s.is_empty() => {
                // Format up to last newline
                let prev_span = codemap::mk_sp(self.last_pos, span.lo);
                let span_end = match self.snippet(prev_span).rfind('\n') {
                    Some(offset) => self.last_pos + BytePos(offset as u32),
                    None => span.lo,
                };
                self.format_missing(span_end);
                self.last_pos = span.hi;
            }
            Some(ref s) => {
                let s = format!("{}use {};", vis, s);
                self.format_missing_with_indent(span.lo);
                self.buffer.push_str(&s);
                self.last_pos = span.hi;
            }
            None => {
                self.format_missing_with_indent(span.lo);
                self.format_missing(span.hi);
            }
        }
    }

    pub fn get_context(&self) -> RewriteContext {
        RewriteContext {
            codemap: self.codemap,
            config: self.config,
            block_indent: self.block_indent,
        }
    }
}
<|endoftext|>


<commit_before>use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::ptr;
use winapi::shared::minwindef::DWORD;
use winapi::um::{
    errhandlingapi::GetLastError,
    libloaderapi::{
       GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
    }
};
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;

pub(crate) fn get_executable_path() -> Option<PathBuf> {
    fn get_executable_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let ret = GetModuleFileNameW(ptr::null_mut(), buf.as_mut_ptr(), len as DWORD) as usize;
            if ret == 0 {
                None
            } else if ret < len {
                // Success, we need to trim trailing null bytes from the vec.
                buf.set_len(ret);
                let s = OsString::from_wide(&buf);
                Some(s.into())
            } else {
                // The buffer might not be big enough so we need to check errno.
                let errno = GetLastError();
                if errno == ERROR_INSUFFICIENT_BUFFER {
                    get_executable_path(len * 2)
                } else {
                    None
                }
            }
        }
    }

    get_executable_path(256)
}

pub(crate) fn get_dylib_path() -> Option<PathBuf> {
    fn get_dylib_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let mut handle_module = core::ptr::null_mut();
            if GetModuleHandleExW(
                GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
                    | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                get_dylib_path as *const _,
                &mut handle_module,
            ) == 0 {
                None
            } else {
                let ret =
                    GetModuleFileNameW(handle_module, buf.as_mut_ptr(), len as DWORD) as usize;
                if ret == 0 {
                    None
                } else if ret < len {
                    // Success, we need to trim trailing null bytes from the vec.
                    buf.set_len(ret);
                    let s = OsString::from_wide(&buf);
                    Some(s.into())
                } else {
                    // The buffer might not be big enough so we need to check errno.
                    let errno = GetLastError();
                    if errno == ERROR_INSUFFICIENT_BUFFER {
                        get_dylib_path(len * 2)
                    } else {
                        None
                    }
                }
            }
        }
    }

    get_dylib_path(256)
}
<commit_msg>Start with MAX_PATH-sized buffers on Windows to avoid most reallocations<commit_after>use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::ptr;
use winapi::shared::minwindef::{DWORD, MAX_PATH};
use winapi::um::{
    errhandlingapi::GetLastError,
    libloaderapi::{
       GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
    }
};
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;

pub(crate) fn get_executable_path() -> Option<PathBuf> {
    fn get_executable_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let ret = GetModuleFileNameW(ptr::null_mut(), buf.as_mut_ptr(), len as DWORD) as usize;
            if ret == 0 {
                None
            } else if ret < len {
                // Success, we need to trim trailing null bytes from the vec.
                buf.set_len(ret);
                let s = OsString::from_wide(&buf);
                Some(s.into())
            } else {
                // The buffer might not be big enough so we need to check errno.
                let errno = GetLastError();
                if errno == ERROR_INSUFFICIENT_BUFFER {
                    get_executable_path(len * 2)
                } else {
                    None
                }
            }
        }
    }

    get_executable_path(MAX_PATH)
}

pub(crate) fn get_dylib_path() -> Option<PathBuf> {
    fn get_dylib_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let mut handle_module = core::ptr::null_mut();
            if GetModuleHandleExW(
                GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
                    | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                get_dylib_path as *const _,
                &mut handle_module,
            ) == 0 {
                None
            } else {
                let ret =
                    GetModuleFileNameW(handle_module, buf.as_mut_ptr(), len as DWORD) as usize;
                if ret == 0 {
                    None
                } else if ret < len {
                    // Success, we need to trim trailing null bytes from the vec.
                    buf.set_len(ret);
                    let s = OsString::from_wide(&buf);
                    Some(s.into())
                } else {
                    // The buffer might not be big enough so we need to check errno.
                    let errno = GetLastError();
                    if errno == ERROR_INSUFFICIENT_BUFFER {
                        get_dylib_path(len * 2)
                    } else {
                        None
                    }
                }
            }
        }
    }

    get_dylib_path(MAX_PATH)
}
<|endoftext|>


<commit_before>extern crate byteorder;

use std::io::Cursor;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Error;

use std::fs::File;

use byteorder::{BigEndian, ReadBytesExt};

#[derive(Debug)]
pub struct Strfile {
    /// Version of the header.
    pub version: u32,
    /// Number of strings stored in fortune file.
    pub number_of_strings: u32,
    /// Length of the longest quote.
    pub longest_length: u32,
    /// Length of the shortest quote.
    pub shortest_length: u32,
    /// Bit field for flags.
    pub flags: u32,
    /// Delimeter used for separating quotes.
    pub delim: u8,
    /// Byte offsets to beginnings of the strings.
    pub offsets: Vec<u32>,
}

pub enum Flags {
    /// Randomized pointers.
    Random = 0x1,
    /// Strings are ordered in alphabetical order.
    Ordered = 0x2,
    /// String are "encrypted" using ROT-13.
    Rotated = 0x4,
    /// String may have comments inside them.
    HasComments = 0x8,
}

fn rot13(c: char) -> char {
    let base = match c {
        'a'...'z' => 'a' as u8,
        'A'...'Z' => 'A' as u8,
        _ => return c,
    };

    let rotated = ((c as u8) - base + 13) % 26;
    (rotated + base) as char
}

#[test]
fn rot13_test() {
    let original_str = "Hello, world!".to_owned();
    let encrypted_str = "Uryyb, jbeyq!".to_owned();
    assert!(original_str == encrypted_str.chars().map(rot13).collect::<String>());
}

fn read_quote_from_file(reader: &mut BufReader<File>, delim: &u8) -> String {
    let mut quote = String::new();
    let mut buffer = String::new();
    let mut found = false;

    let bytes = vec![*delim, 10];
    let separator = String::from_utf8(bytes).unwrap();

    while !found {
        reader.read_line(&mut buffer).unwrap();
        if buffer.len() > 0 && buffer != separator {
            quote.push_str(&buffer);
            buffer.clear();
        } else {
            found = true;
        }
    }

    quote
}

impl Strfile {
    /// Check if flag is set.
    ///
    /// Examples:
    ///
    /// ```
    /// use strfile::{Strfile, Flags}
    ///
    /// pub fn main() {
    ///     let header = Strfile::new("fortune.dat");
    ///     println!("ROT13: {}", if header.is_flag_set(Flags::Rotated) { "yes" } else { "no" });
    /// }
    pub fn is_flag_set(&self, mask: Flags) -> bool {
        self.flags & (mask as u32) == 1
    }

    pub fn read_quotes(&self, filename: String) -> Result<Vec<String>, Error> {
        let mut quotes = Vec::new();
        let file = try!(File::open(filename));
        let mut reader = BufReader::new(file);

        for offset in &self.offsets {
            try!(reader.seek(SeekFrom::Start(*offset as u64)));
            let quote = read_quote_from_file(&mut reader, &self.delim);
            if self.is_flag_set(Flags::Rotated) {
                quotes.push(quote.chars().map(rot13).collect::<String>());
            } else {
                quotes.push(quote);
            }
        }
        Ok(quotes)
    }

    /// Read strfile header contents from a file.
    ///
    /// Example usage:
    ///
    /// ```
    /// use strfile::Strfile;
    ///
    /// pub fn main() {
    ///     let header = Strfile::new("fortune.dat");
    ///     println!("Header contents: {:d}", header);
    /// }
    /// ```
    pub fn parse(filename: String) -> Result<Strfile, Error> {
        let mut header_field = [0u8; 21];

        let handle = try!(File::open(filename.clone()));
        let mut file = BufReader::new(&handle);
        try!(file.read(&mut header_field));
        let mut buf = Cursor::new(&header_field[..]);

        let version = buf.read_u32::<BigEndian>().unwrap();
        let number_of_strings = buf.read_u32::<BigEndian>().unwrap();
        let longest_length = buf.read_u32::<BigEndian>().unwrap();
        let shortest_length = buf.read_u32::<BigEndian>().unwrap();
        let flags = buf.read_u32::<BigEndian>().unwrap();
        let delim = header_field[20];
        let mut offsets = Vec::new();

        try!(file.seek(SeekFrom::Current(3)));
        for _ in 1..number_of_strings + 1 {
            let mut raw_offset = [0u8; 4];
            try!(file.read(&mut raw_offset));
            let mut buf = Cursor::new(&raw_offset[..]);
            let offset = buf.read_u32::<BigEndian>().unwrap();
            offsets.push(offset);
        }

        let header = Strfile {
            version: version,
            number_of_strings: number_of_strings,
            longest_length: longest_length,
            shortest_length: shortest_length,
            flags: flags,
            delim: delim,
            offsets: offsets,
        };
        Ok(header)
    }
}
<commit_msg>Cleaned up code a bit.<commit_after>extern crate byteorder;

use std::io::Cursor;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Error;

use std::fs::File;

use byteorder::{BigEndian, ReadBytesExt};

#[derive(Debug)]
pub struct Strfile {
    /// Version of the header.
    pub version: u32,
    /// Number of strings stored in fortune file.
    pub number_of_strings: u32,
    /// Length of the longest quote.
    pub longest_length: u32,
    /// Length of the shortest quote.
    pub shortest_length: u32,
    /// Bit field for flags.
    pub flags: u32,
    /// Delimeter used for separating quotes.
    pub delim: u8,
    /// Byte offsets to beginnings of the strings.
    pub offsets: Vec<u32>,
}

pub enum Flags {
    /// Randomized pointers.
    Random = 0x1,
    /// Strings are ordered in alphabetical order.
    Ordered = 0x2,
    /// String are "encrypted" using ROT-13.
    Rotated = 0x4,
    /// String may have comments inside them.
    HasComments = 0x8,
}

fn rot13(c: char) -> char {
    let base = match c {
        'a'...'z' => b'a',
        'A'...'Z' => b'A',
        _ => return c,
    };

    let rotated = ((c as u8) - base + 13) % 26;
    (rotated + base) as char
}

#[test]
fn rot13_test() {
    let original_str = "Hello, world!".to_owned();
    let encrypted_str = "Uryyb, jbeyq!".to_owned();
    assert!(original_str == encrypted_str.chars().map(rot13).collect::<String>());
}

fn read_quote_from_file(reader: &mut BufReader<File>, delim: &u8) -> String {
    let mut quote = String::new();
    let mut buffer = String::new();
    let mut found = false;

    let bytes = vec![*delim, 10];
    let separator = String::from_utf8(bytes).unwrap();

    while !found {
        reader.read_line(&mut buffer).unwrap();
        if !buffer.is_empty() && buffer != separator {
            quote.push_str(&buffer);
            buffer.clear();
        } else {
            found = true;
        }
    }

    quote
}

impl Strfile {
    /// Check if flag is set.
    ///
    /// Examples:
    ///
    /// ```
    /// use strfile::{Strfile, Flags}
    ///
    /// pub fn main() {
    ///     let header = Strfile::new("fortune.dat");
    ///     println!("ROT13: {}", if header.is_flag_set(Flags::Rotated) { "yes" } else { "no" });
    /// }
    pub fn is_flag_set(&self, mask: Flags) -> bool {
        self.flags & (mask as u32) == 1
    }

    pub fn read_quotes(&self, filename: String) -> Result<Vec<String>, Error> {
        let mut quotes = Vec::new();
        let file = try!(File::open(filename));
        let mut reader = BufReader::new(file);

        for offset in &self.offsets {
            try!(reader.seek(SeekFrom::Start(*offset as u64)));
            let quote = read_quote_from_file(&mut reader, &self.delim);
            if self.is_flag_set(Flags::Rotated) {
                quotes.push(quote.chars().map(rot13).collect::<String>());
            } else {
                quotes.push(quote);
            }
        }
        Ok(quotes)
    }

    /// Read strfile header contents from a file.
    ///
    /// Example usage:
    ///
    /// ```
    /// use strfile::Strfile;
    ///
    /// pub fn main() {
    ///     let header = Strfile::new("fortune.dat");
    ///     println!("Header contents: {:d}", header);
    /// }
    /// ```
    pub fn parse(filename: String) -> Result<Strfile, Error> {
        let mut header_field = [0u8; 21];

        let handle = try!(File::open(filename.clone()));
        let mut file = BufReader::new(&handle);
        try!(file.read(&mut header_field));
        let mut buf = Cursor::new(&header_field[..]);

        let version = buf.read_u32::<BigEndian>().unwrap();
        let number_of_strings = buf.read_u32::<BigEndian>().unwrap();
        let longest_length = buf.read_u32::<BigEndian>().unwrap();
        let shortest_length = buf.read_u32::<BigEndian>().unwrap();
        let flags = buf.read_u32::<BigEndian>().unwrap();
        let delim = header_field[20];
        let mut offsets = Vec::new();

        try!(file.seek(SeekFrom::Current(3)));
        for _ in 1..number_of_strings + 1 {
            let mut raw_offset = [0u8; 4];
            try!(file.read(&mut raw_offset));
            let mut buf = Cursor::new(&raw_offset[..]);
            let offset = buf.read_u32::<BigEndian>().unwrap();
            offsets.push(offset);
        }

        let header = Strfile {
            version: version,
            number_of_strings: number_of_strings,
            longest_length: longest_length,
            shortest_length: shortest_length,
            flags: flags,
            delim: delim,
            offsets: offsets,
        };
        Ok(header)
    }
}
<|endoftext|>


<commit_before>extern crate num_cpus;

use std::path::Path;
use std::process::Command;
use std::io::Write;
use std::env;

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn run_command_or_fail(dir: &str, cmd: &str, args: &[&str]) {
    println_stderr!("Running command: \"{} {}\" in dir: {}", cmd, args.join(" "), dir);
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => { return },
        Ok((false, Some(c))) => { panic!("Command failed with error code {}", c) },
        Ok((false, None)) => { panic!("Command got killed") },
        Err(e) => { panic!("Command failed with error: {}", e) },
    }
}

fn main() {
    if !Path::new("librdkafka/LICENCE").exists() {
        println!("Setting up submodules");
        run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
    }

    let mut configure_flags = Vec::new();

    if env::var("CARGO_FEATURE_SASL").is_ok() {
        configure_flags.push("--enable-sasl");
    } else {
        configure_flags.push("--disable-sasl");
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl");
    } else {
        configure_flags.push("--disable-ssl");
    }

    configure_flags.push("--enable-static");

    println!("Configuring librdkafka");
    run_command_or_fail("librdkafka", "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    run_command_or_fail("librdkafka", "make", &["-j", &num_cpus::get().to_string()]);

    println!("cargo:rustc-link-search=native={}/librdkafka/src", env::current_dir().expect("Can't find current dir").display());
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<commit_msg>Fix file name for git submodule init<commit_after>extern crate num_cpus;

use std::path::Path;
use std::process::Command;
use std::io::Write;
use std::env;

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn run_command_or_fail(dir: &str, cmd: &str, args: &[&str]) {
    println_stderr!("Running command: \"{} {}\" in dir: {}", cmd, args.join(" "), dir);
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => { return },
        Ok((false, Some(c))) => { panic!("Command failed with error code {}", c) },
        Ok((false, None)) => { panic!("Command got killed") },
        Err(e) => { panic!("Command failed with error: {}", e) },
    }
}

fn main() {
    if !Path::new("librdkafka/LICENSE").exists() {
        println!("Setting up submodules");
        run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
    }

    let mut configure_flags = Vec::new();

    if env::var("CARGO_FEATURE_SASL").is_ok() {
        configure_flags.push("--enable-sasl");
    } else {
        configure_flags.push("--disable-sasl");
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl");
    } else {
        configure_flags.push("--disable-ssl");
    }

    configure_flags.push("--enable-static");

    println!("Configuring librdkafka");
    run_command_or_fail("librdkafka", "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    run_command_or_fail("librdkafka", "make", &["-j", &num_cpus::get().to_string()]);

    println!("cargo:rustc-link-search=native={}/librdkafka/src", env::current_dir().expect("Can't find current dir").display());
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<|endoftext|>


<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::arch::x86_64::{__cpuid, __cpuid_count};
use std::result;

use devices::{IrqChipCap, IrqChipX86_64};
use hypervisor::{HypervisorX86_64, VcpuX86_64};
use remain::sorted;
use thiserror::Error;

#[sorted]
#[derive(Error, Debug, PartialEq)]
pub enum Error {
    #[error("GetSupportedCpus ioctl failed: {0}")]
    GetSupportedCpusFailed(base::Error),
    #[error("SetSupportedCpus ioctl failed: {0}")]
    SetSupportedCpusFailed(base::Error),
}

pub type Result<T> = result::Result<T, Error>;

// CPUID bits in ebx, ecx, and edx.
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
const EBX_CPU_COUNT_SHIFT: u32 = 16; // Index of this CPU.
const EBX_CPUID_SHIFT: u32 = 24; // Index of this CPU.
const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
const ECX_X2APIC_SHIFT: u32 = 21; // APIC supports extended xAPIC (x2APIC) standard.
const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer.
const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.
const ECX_TOPO_TYPE_SHIFT: u32 = 8; // Topology Level type.
const ECX_TOPO_SMT_TYPE: u32 = 1; // SMT type.
const ECX_TOPO_CORE_TYPE: u32 = 2; // CORE type.
const EAX_CPU_CORES_SHIFT: u32 = 26; // Index of cpu cores in the same physical package.
const EDX_HYBRID_CPU_SHIFT: u32 = 15; // Hybrid. The processor is identified as a hybrid part.

fn filter_cpuid(
    vcpu_id: usize,
    cpu_count: usize,
    cpuid: &mut hypervisor::CpuId,
    irq_chip: &dyn IrqChipX86_64,
    no_smt: bool,
    host_cpu_topology: bool,
) {
    let entries = &mut cpuid.cpu_id_entries;

    for entry in entries {
        match entry.function {
            1 => {
                // X86 hypervisor feature
                if entry.index == 0 {
                    entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
                }
                if irq_chip.check_capability(IrqChipCap::X2Apic) {
                    entry.ecx |= 1 << ECX_X2APIC_SHIFT;
                } else {
                    entry.ecx &= !(1 << ECX_X2APIC_SHIFT);
                }
                if irq_chip.check_capability(IrqChipCap::TscDeadlineTimer) {
                    entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
                }

                if host_cpu_topology {
                    entry.ebx |= EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT;

                    // Expose HT flag to Guest.
                    let result = unsafe { __cpuid(entry.function) };
                    entry.edx |= result.edx & (1 << EDX_HTT_SHIFT);
                    continue;
                }

                entry.ebx = (vcpu_id << EBX_CPUID_SHIFT) as u32
                    | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
                if cpu_count > 1 && !no_smt {
                    // This field is only valid if CPUID.1.EDX.HTT[bit 28]= 1.
                    entry.ebx |= (cpu_count as u32) << EBX_CPU_COUNT_SHIFT;
                    entry.edx |= 1 << EDX_HTT_SHIFT;
                }
            }
            2 | 0x80000005 | 0x80000006 => unsafe {
                let result = __cpuid(entry.function);
                entry.eax = result.eax;
                entry.ebx = result.ebx;
                entry.ecx = result.ecx;
                entry.edx = result.edx;
            },
            4 => {
                unsafe {
                    let result = __cpuid_count(entry.function, entry.index);
                    entry.eax = result.eax;
                    entry.ebx = result.ebx;
                    entry.ecx = result.ecx;
                    entry.edx = result.edx;
                }

                if host_cpu_topology {
                    continue;
                }

                entry.eax &= !0xFC000000;
                if cpu_count > 1 {
                    let cpu_cores = if no_smt {
                        cpu_count as u32
                    } else if cpu_count % 2 == 0 {
                        (cpu_count >> 1) as u32
                    } else {
                        1
                    };
                    entry.eax |= (cpu_cores - 1) << EAX_CPU_CORES_SHIFT;
                }
            }
            6 => {
                // Clear X86 EPB feature.  No frequency selection in the hypervisor.
                entry.ecx &= !(1 << ECX_EPB_SHIFT);
            }
            7 => {
                if host_cpu_topology && entry.index == 0 {
                    // Safe because we pass 7 and 0 for this call and the host supports the
                    // `cpuid` instruction
                    let result = unsafe { __cpuid_count(entry.function, entry.index) };
                    entry.edx |= result.edx & (1 << EDX_HYBRID_CPU_SHIFT);
                }
            }
            0x1A => {
                // Hybrid information leaf.
                if host_cpu_topology {
                    // Safe because we pass 0x1A for this call and the host supports the
                    // `cpuid` instruction
                    let result = unsafe { __cpuid(entry.function) };
                    entry.eax = result.eax;
                    entry.ebx = result.ebx;
                    entry.ecx = result.ecx;
                    entry.edx = result.edx;
                }
            }
            0xB | 0x1F => {
                if host_cpu_topology {
                    continue;
                }
                // Extended topology enumeration / V2 Extended topology enumeration
                // NOTE: these will need to be split if any of the fields that differ between
                // the two versions are to be set.
                entry.edx = vcpu_id as u32; // x2APIC ID
                if entry.index == 0 {
                    if no_smt || (cpu_count == 1) {
                        // Make it so that all VCPUs appear as different,
                        // non-hyperthreaded cores on the same package.
                        entry.eax = 0; // Shift to get id of next level
                        entry.ebx = 1; // Number of logical cpus at this level
                    } else if cpu_count % 2 == 0 {
                        // Each core has 2 hyperthreads
                        entry.eax = 1; // Shift to get id of next level
                        entry.ebx = 2; // Number of logical cpus at this level
                    } else {
                        // One core contain all the cpu_count hyperthreads
                        let cpu_bits: u32 = 32 - ((cpu_count - 1) as u32).leading_zeros();
                        entry.eax = cpu_bits; // Shift to get id of next level
                        entry.ebx = cpu_count as u32; // Number of logical cpus at this level
                    }
                    entry.ecx = (ECX_TOPO_SMT_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
                } else if entry.index == 1 {
                    let cpu_bits: u32 = 32 - ((cpu_count - 1) as u32).leading_zeros();
                    entry.eax = cpu_bits;
                    entry.ebx = (cpu_count as u32) & 0xffff; // Number of logical cpus at this level
                    entry.ecx = (ECX_TOPO_CORE_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
                } else {
                    entry.eax = 0;
                    entry.ebx = 0;
                    entry.ecx = 0;
                }
            }
            _ => (),
        }
    }
}

/// Sets up the cpuid entries for the given vcpu.  Can fail if there are too many CPUs specified or
/// if an ioctl returns an error.
///
/// # Arguments
///
/// * `hypervisor` - `HypervisorX86_64` impl for getting supported CPU IDs.
/// * `vcpu` - `VcpuX86_64` for setting CPU ID.
/// * `vcpu_id` - The vcpu index of `vcpu`.
/// * `nrcpus` - The number of vcpus being used by this VM.
/// * `no_smt` - The flag indicates whether vCPUs supports SMT.
/// * `host_cpu_topology` - The flag indicates whether vCPUs use mirror CPU topology.
pub fn setup_cpuid(
    hypervisor: &dyn HypervisorX86_64,
    irq_chip: &dyn IrqChipX86_64,
    vcpu: &dyn VcpuX86_64,
    vcpu_id: usize,
    nrcpus: usize,
    no_smt: bool,
    host_cpu_topology: bool,
) -> Result<()> {
    let mut cpuid = hypervisor
        .get_supported_cpuid()
        .map_err(Error::GetSupportedCpusFailed)?;

    filter_cpuid(
        vcpu_id,
        nrcpus,
        &mut cpuid,
        irq_chip,
        no_smt,
        host_cpu_topology,
    );

    vcpu.set_cpuid(&cpuid)
        .map_err(Error::SetSupportedCpusFailed)
}

#[cfg(test)]
mod tests {
    use super::*;
    use hypervisor::{CpuIdEntry, ProtectionType};

    #[test]
    fn feature_and_vendor_name() {
        let mut cpuid = hypervisor::CpuId::new(2);
        let guest_mem =
            vm_memory::GuestMemory::new(&[(vm_memory::GuestAddress(0), 0x10000)]).unwrap();
        let kvm = hypervisor::kvm::Kvm::new().unwrap();
        let vm = hypervisor::kvm::KvmVm::new(&kvm, guest_mem, ProtectionType::Unprotected).unwrap();
        let irq_chip = devices::KvmKernelIrqChip::new(vm, 1).unwrap();

        let entries = &mut cpuid.cpu_id_entries;
        entries.push(CpuIdEntry {
            function: 0,
            ..Default::default()
        });
        entries.push(CpuIdEntry {
            function: 1,
            ecx: 0x10,
            edx: 0,
            ..Default::default()
        });
        filter_cpuid(1, 2, &mut cpuid, &irq_chip, false, false);

        let entries = &mut cpuid.cpu_id_entries;
        assert_eq!(entries[0].function, 0);
        assert_eq!(1, (entries[1].ebx >> EBX_CPUID_SHIFT) & 0x000000ff);
        assert_eq!(2, (entries[1].ebx >> EBX_CPU_COUNT_SHIFT) & 0x000000ff);
        assert_eq!(
            EBX_CLFLUSH_CACHELINE,
            (entries[1].ebx >> EBX_CLFLUSH_SIZE_SHIFT) & 0x000000ff
        );
        assert_ne!(0, entries[1].ecx & (1 << ECX_HYPERVISOR_SHIFT));
        assert_ne!(0, entries[1].edx & (1 << EDX_HTT_SHIFT));
    }
}
<commit_msg>x86: pass host's processor brand string to guests<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::arch::x86_64::{__cpuid, __cpuid_count};
use std::result;

use devices::{IrqChipCap, IrqChipX86_64};
use hypervisor::{HypervisorX86_64, VcpuX86_64};
use remain::sorted;
use thiserror::Error;

#[sorted]
#[derive(Error, Debug, PartialEq)]
pub enum Error {
    #[error("GetSupportedCpus ioctl failed: {0}")]
    GetSupportedCpusFailed(base::Error),
    #[error("SetSupportedCpus ioctl failed: {0}")]
    SetSupportedCpusFailed(base::Error),
}

pub type Result<T> = result::Result<T, Error>;

// CPUID bits in ebx, ecx, and edx.
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
const EBX_CPU_COUNT_SHIFT: u32 = 16; // Index of this CPU.
const EBX_CPUID_SHIFT: u32 = 24; // Index of this CPU.
const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
const ECX_X2APIC_SHIFT: u32 = 21; // APIC supports extended xAPIC (x2APIC) standard.
const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer.
const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.
const ECX_TOPO_TYPE_SHIFT: u32 = 8; // Topology Level type.
const ECX_TOPO_SMT_TYPE: u32 = 1; // SMT type.
const ECX_TOPO_CORE_TYPE: u32 = 2; // CORE type.
const EAX_CPU_CORES_SHIFT: u32 = 26; // Index of cpu cores in the same physical package.
const EDX_HYBRID_CPU_SHIFT: u32 = 15; // Hybrid. The processor is identified as a hybrid part.

fn filter_cpuid(
    vcpu_id: usize,
    cpu_count: usize,
    cpuid: &mut hypervisor::CpuId,
    irq_chip: &dyn IrqChipX86_64,
    no_smt: bool,
    host_cpu_topology: bool,
) {
    let entries = &mut cpuid.cpu_id_entries;

    for entry in entries {
        match entry.function {
            1 => {
                // X86 hypervisor feature
                if entry.index == 0 {
                    entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
                }
                if irq_chip.check_capability(IrqChipCap::X2Apic) {
                    entry.ecx |= 1 << ECX_X2APIC_SHIFT;
                } else {
                    entry.ecx &= !(1 << ECX_X2APIC_SHIFT);
                }
                if irq_chip.check_capability(IrqChipCap::TscDeadlineTimer) {
                    entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
                }

                if host_cpu_topology {
                    entry.ebx |= EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT;

                    // Expose HT flag to Guest.
                    let result = unsafe { __cpuid(entry.function) };
                    entry.edx |= result.edx & (1 << EDX_HTT_SHIFT);
                    continue;
                }

                entry.ebx = (vcpu_id << EBX_CPUID_SHIFT) as u32
                    | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
                if cpu_count > 1 && !no_smt {
                    // This field is only valid if CPUID.1.EDX.HTT[bit 28]= 1.
                    entry.ebx |= (cpu_count as u32) << EBX_CPU_COUNT_SHIFT;
                    entry.edx |= 1 << EDX_HTT_SHIFT;
                }
            }
            2 | // Cache and TLB Descriptor information
            0x80000002 | 0x80000003 | 0x80000004 | // Processor Brand String
            0x80000005 | 0x80000006 // L1 and L2 cache information
              => unsafe {
                let result = __cpuid(entry.function);
                entry.eax = result.eax;
                entry.ebx = result.ebx;
                entry.ecx = result.ecx;
                entry.edx = result.edx;
            },
            4 => {
                unsafe {
                    let result = __cpuid_count(entry.function, entry.index);
                    entry.eax = result.eax;
                    entry.ebx = result.ebx;
                    entry.ecx = result.ecx;
                    entry.edx = result.edx;
                }

                if host_cpu_topology {
                    continue;
                }

                entry.eax &= !0xFC000000;
                if cpu_count > 1 {
                    let cpu_cores = if no_smt {
                        cpu_count as u32
                    } else if cpu_count % 2 == 0 {
                        (cpu_count >> 1) as u32
                    } else {
                        1
                    };
                    entry.eax |= (cpu_cores - 1) << EAX_CPU_CORES_SHIFT;
                }
            }
            6 => {
                // Clear X86 EPB feature.  No frequency selection in the hypervisor.
                entry.ecx &= !(1 << ECX_EPB_SHIFT);
            }
            7 => {
                if host_cpu_topology && entry.index == 0 {
                    // Safe because we pass 7 and 0 for this call and the host supports the
                    // `cpuid` instruction
                    let result = unsafe { __cpuid_count(entry.function, entry.index) };
                    entry.edx |= result.edx & (1 << EDX_HYBRID_CPU_SHIFT);
                }
            }
            0x1A => {
                // Hybrid information leaf.
                if host_cpu_topology {
                    // Safe because we pass 0x1A for this call and the host supports the
                    // `cpuid` instruction
                    let result = unsafe { __cpuid(entry.function) };
                    entry.eax = result.eax;
                    entry.ebx = result.ebx;
                    entry.ecx = result.ecx;
                    entry.edx = result.edx;
                }
            }
            0xB | 0x1F => {
                if host_cpu_topology {
                    continue;
                }
                // Extended topology enumeration / V2 Extended topology enumeration
                // NOTE: these will need to be split if any of the fields that differ between
                // the two versions are to be set.
                entry.edx = vcpu_id as u32; // x2APIC ID
                if entry.index == 0 {
                    if no_smt || (cpu_count == 1) {
                        // Make it so that all VCPUs appear as different,
                        // non-hyperthreaded cores on the same package.
                        entry.eax = 0; // Shift to get id of next level
                        entry.ebx = 1; // Number of logical cpus at this level
                    } else if cpu_count % 2 == 0 {
                        // Each core has 2 hyperthreads
                        entry.eax = 1; // Shift to get id of next level
                        entry.ebx = 2; // Number of logical cpus at this level
                    } else {
                        // One core contain all the cpu_count hyperthreads
                        let cpu_bits: u32 = 32 - ((cpu_count - 1) as u32).leading_zeros();
                        entry.eax = cpu_bits; // Shift to get id of next level
                        entry.ebx = cpu_count as u32; // Number of logical cpus at this level
                    }
                    entry.ecx = (ECX_TOPO_SMT_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
                } else if entry.index == 1 {
                    let cpu_bits: u32 = 32 - ((cpu_count - 1) as u32).leading_zeros();
                    entry.eax = cpu_bits;
                    entry.ebx = (cpu_count as u32) & 0xffff; // Number of logical cpus at this level
                    entry.ecx = (ECX_TOPO_CORE_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
                } else {
                    entry.eax = 0;
                    entry.ebx = 0;
                    entry.ecx = 0;
                }
            }
            _ => (),
        }
    }
}

/// Sets up the cpuid entries for the given vcpu.  Can fail if there are too many CPUs specified or
/// if an ioctl returns an error.
///
/// # Arguments
///
/// * `hypervisor` - `HypervisorX86_64` impl for getting supported CPU IDs.
/// * `vcpu` - `VcpuX86_64` for setting CPU ID.
/// * `vcpu_id` - The vcpu index of `vcpu`.
/// * `nrcpus` - The number of vcpus being used by this VM.
/// * `no_smt` - The flag indicates whether vCPUs supports SMT.
/// * `host_cpu_topology` - The flag indicates whether vCPUs use mirror CPU topology.
pub fn setup_cpuid(
    hypervisor: &dyn HypervisorX86_64,
    irq_chip: &dyn IrqChipX86_64,
    vcpu: &dyn VcpuX86_64,
    vcpu_id: usize,
    nrcpus: usize,
    no_smt: bool,
    host_cpu_topology: bool,
) -> Result<()> {
    let mut cpuid = hypervisor
        .get_supported_cpuid()
        .map_err(Error::GetSupportedCpusFailed)?;

    filter_cpuid(
        vcpu_id,
        nrcpus,
        &mut cpuid,
        irq_chip,
        no_smt,
        host_cpu_topology,
    );

    vcpu.set_cpuid(&cpuid)
        .map_err(Error::SetSupportedCpusFailed)
}

#[cfg(test)]
mod tests {
    use super::*;
    use hypervisor::{CpuIdEntry, ProtectionType};

    #[test]
    fn feature_and_vendor_name() {
        let mut cpuid = hypervisor::CpuId::new(2);
        let guest_mem =
            vm_memory::GuestMemory::new(&[(vm_memory::GuestAddress(0), 0x10000)]).unwrap();
        let kvm = hypervisor::kvm::Kvm::new().unwrap();
        let vm = hypervisor::kvm::KvmVm::new(&kvm, guest_mem, ProtectionType::Unprotected).unwrap();
        let irq_chip = devices::KvmKernelIrqChip::new(vm, 1).unwrap();

        let entries = &mut cpuid.cpu_id_entries;
        entries.push(CpuIdEntry {
            function: 0,
            ..Default::default()
        });
        entries.push(CpuIdEntry {
            function: 1,
            ecx: 0x10,
            edx: 0,
            ..Default::default()
        });
        filter_cpuid(1, 2, &mut cpuid, &irq_chip, false, false);

        let entries = &mut cpuid.cpu_id_entries;
        assert_eq!(entries[0].function, 0);
        assert_eq!(1, (entries[1].ebx >> EBX_CPUID_SHIFT) & 0x000000ff);
        assert_eq!(2, (entries[1].ebx >> EBX_CPU_COUNT_SHIFT) & 0x000000ff);
        assert_eq!(
            EBX_CLFLUSH_CACHELINE,
            (entries[1].ebx >> EBX_CLFLUSH_SIZE_SHIFT) & 0x000000ff
        );
        assert_ne!(0, entries[1].ecx & (1 << ECX_HYPERVISOR_SHIFT));
        assert_ne!(0, entries[1].edx & (1 << EDX_HTT_SHIFT));
    }
}
<|endoftext|>


<commit_before>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::Editor;

use crate::core::view::LayoutEnv;

use crate::core::codec::text::u32_to_char;
use crate::core::codec::text::utf8;
use crate::core::codec::text::TextCodec;

use crate::core::codepointinfo::TextStyle;

use crate::core::view::View;

//
static COLOR_DEFAULT: (u8, u8, u8) = (192, 192, 192);
static COLOR_RED: (u8, u8, u8) = (195, 75, 0);
static COLOR_GREEN: (u8, u8, u8) = (85, 170, 127);
static COLOR_ORANGE: (u8, u8, u8) = (247, 104, 38);
static COLOR_CYAN: (u8, u8, u8) = (86, 182, 185);
static COLOR_BLUE: (u8, u8, u8) = (35, 168, 242);
static COLOR_BRACE: (u8, u8, u8) = (0, 185, 163);
static COLOR_NUMBER: (u8, u8, u8) = (111, 100, 80);

#[derive(Debug, Clone, Copy, PartialEq)]
enum TokenType {
    Unknown,
    InvalidUnicode,
    Blank,            // ' ' | '\n' | '\t' : TODO(ceg): specific END_OF_LINE ?
    Identifier,       // _a-zA-Z unicode // default ?
    ParenOpen,        // (
    ParenClose,       // )
    BraceOpen,        // {
    BraceClose,       // }
    BracketOpen,      // [
    BracketClose,     // ]
    SingleQuote,      // '
    DoubleQuote,      // "
    Comma,            // ,
    Colon,            // :
    Semicolon,        // ;
    Ampersand,        // &
    VerticalBar,      // |
    Tilde,            // ~
    CircumflexAccent, // ^
    Dot,              // .
    ExclamationPoint, // !
    Equal,
    Plus,
    Minus,
    Mul,
    Div,
    Mod,
    LowerThan,
    GreaterThan,
}

pub struct HighlightFilter {
    token_io: Vec<FilterIo>,
    prev_token_type: TokenType,
    utf8_token: Vec<u8>,
    new_color: (u8, u8, u8),
    utf8_codec: Box<dyn TextCodec>, // internal token representation is utf8
    skip_filter: bool,
    max_token_size: usize,
}

impl HighlightFilter {
    pub fn new() -> Self {
        HighlightFilter {
            token_io: Vec::new(),
            prev_token_type: TokenType::Unknown,
            utf8_token: Vec::new(),
            new_color: TextStyle::default_color(),
            utf8_codec: Box::new(utf8::Utf8Codec::new()),
            skip_filter: false,
            max_token_size: 1024,
        }
    }

    fn colorize_token(&mut self) {
        // build token utf8 string
        for tok in self.token_io.iter() {
            match tok {
                &FilterIo {
                    data: FilterData::TextInfo { real_cp, .. },
                    ..
                } => {
                    let mut utf8_out: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
                    let nr_bytes = self.utf8_codec.encode(real_cp, &mut utf8_out);
                    for b in utf8_out.iter().take(nr_bytes) {
                        self.utf8_token.push(*b);
                    }
                }
                _ => {
                    panic!();
                }
            }
        }

        self.new_color = match self.prev_token_type {
            TokenType::Unknown => COLOR_DEFAULT,
            TokenType::InvalidUnicode => COLOR_DEFAULT,
            TokenType::Blank => COLOR_DEFAULT, // ' ' | '\n' | '\t' : TODO(ceg): specific END_OF_LINE ?
            TokenType::ParenOpen => COLOR_GREEN, // (
            TokenType::ParenClose => COLOR_GREEN, // )
            TokenType::BraceOpen => COLOR_BRACE, // {
            TokenType::BraceClose => COLOR_BRACE, // }
            TokenType::BracketOpen => COLOR_BRACE, // [
            TokenType::BracketClose => COLOR_BRACE, // ]
            TokenType::SingleQuote => COLOR_ORANGE, // '
            TokenType::DoubleQuote => COLOR_ORANGE, // "
            TokenType::Comma => COLOR_GREEN,   // ,
            TokenType::Colon => COLOR_GREEN,   // :
            TokenType::Semicolon => COLOR_GREEN, // ;
            TokenType::Ampersand => COLOR_CYAN, // &
            TokenType::VerticalBar => COLOR_CYAN, // |
            TokenType::Tilde => COLOR_CYAN,    // ~
            TokenType::CircumflexAccent => COLOR_CYAN, // ^
            TokenType::Dot => COLOR_GREEN,     // .
            TokenType::ExclamationPoint => COLOR_GREEN, // !
            TokenType::Equal => COLOR_GREEN,
            TokenType::Plus => COLOR_GREEN,
            TokenType::Minus => COLOR_GREEN,
            TokenType::Mul => COLOR_GREEN,
            TokenType::Div => COLOR_GREEN,
            TokenType::Mod => COLOR_GREEN,
            TokenType::LowerThan => COLOR_GREEN,
            TokenType::GreaterThan => COLOR_GREEN,
            TokenType::Identifier => {
                self.set_indentifier_color();
                self.new_color
            }
        };
    }

    fn set_indentifier_color(&mut self) {
        // select color
        let token_str = if let Ok(s) = String::from_utf8(self.utf8_token.clone()) {
            s
        } else {
            "�".to_string()
        };

        // dbg_println!("TOKEN_STR = '{}'", token_str);
        self.new_color = match token_str.as_ref() {
            // some Rust keywords
            "use" | "crate" | "pub" => COLOR_RED,

            // some Rust keywords
            "let" | "ref" | "mut" | "fn" | "impl" | "trait" | "type" => (0, 128, 128),
            "Option" | "Some" | "None" | "Result" => (0, 128, 128),

            "unsafe" | "panic" => COLOR_RED,

            "borrow" | "unwrap" => (0, 128, 128),

            "str" | "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64"
            | "i128" | "f32" | "f64" => (0, 128, 128),

            // some C preprocessor tokens
            "#include" | "#if" | "#ifdef" | "#ifndef" | "#endif" | "#else" | "#undef"
            | "#define" | "#pragma" => COLOR_RED,

            // some C keywords
            "break" | "case" | "char" | "const" | "continue" | "default" | "do" | "double"
            | "enum" | "extern" | "float" | "for" | "int" | "long" | "register" | "short"
            | "signed" | "sizeof" | "static" | "struct" | "switch" | "typedef" | "union"
            | "unsigned" | "void" | "volatile" | "while" | "inline" => (0, 128, 128),

            "if" | "then" | "else" => COLOR_BRACE,

            // some C++ keywords
            "bool" | "class" | "template" | "namespace" | "auto" => (0, 128, 128),

            //
            "return" | "goto" | "true" | "false" => COLOR_BLUE,

            _ => {
                let mut non_alnum = 0;
                let mut digit_count = 0;

                let skip_n = if self.utf8_token.len() >= 2
                    && self.utf8_token[0] == b'0'
                    && self.utf8_token[1] == b'x'
                {
                    2
                } else {
                    0
                };

                for c in self.utf8_token.iter().skip(skip_n) {
                    if *c == b'_' {
                        continue;
                    }

                    if *c >= b'0' && *c <= b'9' {
                        digit_count += 1;
                        continue;
                    }

                    if *c >= b'a' && *c <= b'f' {
                        continue;
                    }

                    if *c >= b'A' && *c <= b'F' {
                        continue;
                    }

                    non_alnum += 1;
                    break;
                }

                if non_alnum == 0 && digit_count != 0 {
                    COLOR_NUMBER
                } else {
                    self.new_color
                }
            }
        };
    }
}

fn get_token_type(c: char) -> TokenType {
    match c {
        '�' => TokenType::InvalidUnicode,
        ' ' | '\n' | '\t' => TokenType::Blank,
        '(' => TokenType::ParenOpen,
        ')' => TokenType::ParenClose,
        '{' => TokenType::BraceOpen,
        '}' => TokenType::BraceClose,
        '[' => TokenType::BracketOpen,
        ']' => TokenType::BracketClose,
        '\'' => TokenType::SingleQuote,
        '"' => TokenType::DoubleQuote,
        '=' => TokenType::Equal,
        '*' => TokenType::Mul,
        '+' => TokenType::Plus,
        '-' => TokenType::Minus,
        '/' => TokenType::Div,
        '<' => TokenType::LowerThan,
        '>' => TokenType::GreaterThan,
        ',' => TokenType::Comma,
        ':' => TokenType::Colon,
        ';' => TokenType::Semicolon,
        '&' => TokenType::Ampersand,
        '%' => TokenType::Mod,
        '|' => TokenType::VerticalBar,
        '~' => TokenType::Tilde,
        '^' => TokenType::CircumflexAccent,
        '.' => TokenType::Dot,
        '!' => TokenType::ExclamationPoint,

        // '0'...'9' => TokenType::NUM,
        _ => TokenType::Identifier,
    }
}

// TODO(ceg): monitor env.quit
// to flush
impl ContentFilter<'_> for HighlightFilter {
    fn name(&self) -> &'static str {
        &"HighlightFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor<'static>,
        env: &mut LayoutEnv,
        _view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.token_io = Vec::new();
        self.prev_token_type = TokenType::Unknown;
        self.utf8_token = Vec::new();
        self.new_color = TextStyle::default_color();
        // self.utf8_codec =  Box::new(utf8::Utf8Codec::new());

        self.skip_filter = false;

        let p_input = crate::core::event::pending_input_event_count();
        if p_input > 255 {
            //dbg_println!("*** SKIP HIGHLIGHT *** p_input {}", p_input);
            self.skip_filter = true;
        }
        if env.screen.is_off_screen {
            self.skip_filter = true;
        }

        self.max_token_size = env.screen.width() * env.screen.height(); //
    }

    fn run(
        &mut self,
        _view: &View,
        _env: &mut LayoutEnv,
        filter_in: &[FilterIo],
        filter_out: &mut Vec<FilterIo>,
    ) {
        if self.skip_filter {
            // return NOP hand let the caller skip swap
            *filter_out = filter_in.to_vec();
            return;
        }

        // flush too big token
        if self.token_io.len() > self.max_token_size {
            for mut io in self.token_io.iter_mut() {
                io.style.color = self.new_color;
            }
            filter_out.append(&mut self.token_io);
        }

        for io in filter_in {
            match &*io {
                FilterIo {
                    data: FilterData::TextInfo { real_cp, .. },
                    ..
                } => {
                    let c = u32_to_char(*real_cp);

                    // dbg_println!("-----------");
                    // dbg_println!("parsing char : '{}'", c);
                    let token_type = get_token_type(c);

                    if token_type == TokenType::Identifier
                        && self.prev_token_type == TokenType::Identifier
                    {
                        self.token_io.push(io.clone());
                        self.prev_token_type = token_type;
                        continue;
                    }

                    // flush token: set color
                    self.colorize_token();
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }
                    filter_out.append(&mut self.token_io);
                    // reset state
                    self.utf8_token.clear();
                    self.new_color = TextStyle::default_color();

                    // prepare next token
                    self.prev_token_type = token_type;
                    self.token_io.push(io.clone());
                }

                FilterIo {
                    data: FilterData::EndOfStream | FilterData::CustomLimitReached,
                    ..
                } => {
                    // flush pending token: set color
                    self.colorize_token();
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }

                    filter_out.append(&mut self.token_io);

                    // forward tag
                    filter_out.push(io.clone());
                }

                _ => {
                    dbg_println!("unexpected {:?}", io);
                    panic!("");
                }
            }
        }
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {
        // default
        if !self.token_io.is_empty() {
            // The parsing is incomplete
            // panic!("");
        }
    }
}
<commit_msg>highlight '$', '≠'<commit_after>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::Editor;

use crate::core::view::LayoutEnv;

use crate::core::codec::text::u32_to_char;
use crate::core::codec::text::utf8;
use crate::core::codec::text::TextCodec;

use crate::core::codepointinfo::TextStyle;

use crate::core::view::View;

//
static COLOR_DEFAULT: (u8, u8, u8) = (192, 192, 192);
static COLOR_RED: (u8, u8, u8) = (195, 75, 0);
static COLOR_GREEN: (u8, u8, u8) = (85, 170, 127);
static COLOR_ORANGE: (u8, u8, u8) = (247, 104, 38);
static COLOR_CYAN: (u8, u8, u8) = (86, 182, 185);
static COLOR_BLUE: (u8, u8, u8) = (35, 168, 242);
static COLOR_BRACE: (u8, u8, u8) = (0, 185, 163);
static COLOR_NUMBER: (u8, u8, u8) = (111, 100, 80);

#[derive(Debug, Clone, Copy, PartialEq)]
enum TokenType {
    Unknown,
    InvalidUnicode,
    Blank,            // ' ' | '\n' | '\t' : TODO(ceg): specific END_OF_LINE ?
    Identifier,       // _a-zA-Z unicode // default ?
    ParenOpen,        // (
    ParenClose,       // )
    BraceOpen,        // {
    BraceClose,       // }
    BracketOpen,      // [
    BracketClose,     // ]
    SingleQuote,      // '
    DoubleQuote,      // "
    Comma,            // ,
    Colon,            // :
    Semicolon,        // ;
    Ampersand,        // &
    VerticalBar,      // |
    Tilde,            // ~
    CircumflexAccent, // ^
    Dot,              // .
    ExclamationPoint, // !
    Equal,
    Different, // ≠
    Plus,
    Minus,
    Mul,
    Div,
    Mod,
    LowerThan,
    GreaterThan,
    Dollar, // $
}

pub struct HighlightFilter {
    token_io: Vec<FilterIo>,
    prev_token_type: TokenType,
    utf8_token: Vec<u8>,
    new_color: (u8, u8, u8),
    utf8_codec: Box<dyn TextCodec>, // internal token representation is utf8
    skip_filter: bool,
    max_token_size: usize,
}

impl HighlightFilter {
    pub fn new() -> Self {
        HighlightFilter {
            token_io: Vec::new(),
            prev_token_type: TokenType::Unknown,
            utf8_token: Vec::new(),
            new_color: TextStyle::default_color(),
            utf8_codec: Box::new(utf8::Utf8Codec::new()),
            skip_filter: false,
            max_token_size: 1024,
        }
    }

    fn colorize_token(&mut self) {
        // build token utf8 string
        for tok in self.token_io.iter() {
            match tok {
                &FilterIo {
                    data: FilterData::TextInfo { real_cp, .. },
                    ..
                } => {
                    let mut utf8_out: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
                    let nr_bytes = self.utf8_codec.encode(real_cp, &mut utf8_out);
                    for b in utf8_out.iter().take(nr_bytes) {
                        self.utf8_token.push(*b);
                    }
                }
                _ => {
                    panic!();
                }
            }
        }

        self.new_color = match self.prev_token_type {
            TokenType::Unknown => COLOR_DEFAULT,
            TokenType::InvalidUnicode => COLOR_DEFAULT,
            TokenType::Blank => COLOR_DEFAULT, // ' ' | '\n' | '\t' : TODO(ceg): specific END_OF_LINE ?
            TokenType::ParenOpen => COLOR_GREEN, // (
            TokenType::ParenClose => COLOR_GREEN, // )
            TokenType::BraceOpen => COLOR_BRACE, // {
            TokenType::BraceClose => COLOR_BRACE, // }
            TokenType::BracketOpen => COLOR_BRACE, // [
            TokenType::BracketClose => COLOR_BRACE, // ]
            TokenType::SingleQuote => COLOR_ORANGE, // '
            TokenType::DoubleQuote => COLOR_ORANGE, // "
            TokenType::Comma => COLOR_GREEN,   // ,
            TokenType::Colon => COLOR_GREEN,   // :
            TokenType::Semicolon => COLOR_GREEN, // ;
            TokenType::Ampersand => COLOR_CYAN, // &
            TokenType::Dollar => COLOR_BLUE,   // $
            TokenType::VerticalBar => COLOR_CYAN, // |
            TokenType::Tilde => COLOR_CYAN,    // ~
            TokenType::CircumflexAccent => COLOR_CYAN, // ^
            TokenType::Dot => COLOR_GREEN,     // .
            TokenType::ExclamationPoint => COLOR_GREEN, // !
            TokenType::Equal => COLOR_GREEN,
            TokenType::Different => COLOR_GREEN,
            TokenType::Plus => COLOR_GREEN,
            TokenType::Minus => COLOR_GREEN,
            TokenType::Mul => COLOR_GREEN,
            TokenType::Div => COLOR_GREEN,
            TokenType::Mod => COLOR_GREEN,
            TokenType::LowerThan => COLOR_GREEN,
            TokenType::GreaterThan => COLOR_GREEN,
            TokenType::Identifier => {
                self.set_indentifier_color();
                self.new_color
            }
        };
    }

    fn set_indentifier_color(&mut self) {
        // select color
        let token_str = if let Ok(s) = String::from_utf8(self.utf8_token.clone()) {
            s
        } else {
            "�".to_string()
        };

        // dbg_println!("TOKEN_STR = '{}'", token_str);
        self.new_color = match token_str.as_ref() {
            // some Rust keywords
            "use" | "crate" | "pub" => COLOR_RED,

            // some Rust keywords
            "let" | "ref" | "mut" | "fn" | "impl" | "trait" | "type" => (0, 128, 128),
            "Option" | "Some" | "None" | "Result" => (0, 128, 128),

            "unsafe" | "panic" => COLOR_RED,

            "borrow" | "unwrap" => (0, 128, 128),

            "str" | "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64"
            | "i128" | "f32" | "f64" => (0, 128, 128),

            // some C preprocessor tokens
            "#include" | "#if" | "#ifdef" | "#ifndef" | "#endif" | "#else" | "#undef"
            | "#define" | "#pragma" => COLOR_RED,

            // some C keywords
            "break" | "case" | "char" | "const" | "continue" | "default" | "do" | "double"
            | "enum" | "extern" | "float" | "for" | "int" | "long" | "register" | "short"
            | "signed" | "sizeof" | "static" | "struct" | "switch" | "typedef" | "union"
            | "unsigned" | "void" | "volatile" | "while" | "inline" => (0, 128, 128),

            "if" | "then" | "else" => COLOR_BRACE,

            // some C++ keywords
            "bool" | "class" | "template" | "namespace" | "auto" => (0, 128, 128),

            //
            "return" | "goto" | "true" | "false" => COLOR_BLUE,

            _ => {
                let mut non_alnum = 0;
                let mut digit_count = 0;

                let skip_n = if self.utf8_token.len() >= 2
                    && self.utf8_token[0] == b'0'
                    && self.utf8_token[1] == b'x'
                {
                    2
                } else {
                    0
                };

                for c in self.utf8_token.iter().skip(skip_n) {
                    if *c == b'_' {
                        continue;
                    }

                    if *c >= b'0' && *c <= b'9' {
                        digit_count += 1;
                        continue;
                    }

                    if *c >= b'a' && *c <= b'f' {
                        continue;
                    }

                    if *c >= b'A' && *c <= b'F' {
                        continue;
                    }

                    non_alnum += 1;
                    break;
                }

                if non_alnum == 0 && digit_count != 0 {
                    COLOR_NUMBER
                } else {
                    self.new_color
                }
            }
        };
    }
}

fn get_token_type(c: char) -> TokenType {
    match c {
        '�' => TokenType::InvalidUnicode,
        ' ' | '\n' | '\t' => TokenType::Blank,
        '(' => TokenType::ParenOpen,
        ')' => TokenType::ParenClose,
        '{' => TokenType::BraceOpen,
        '}' => TokenType::BraceClose,
        '[' => TokenType::BracketOpen,
        ']' => TokenType::BracketClose,
        '\'' => TokenType::SingleQuote,
        '"' => TokenType::DoubleQuote,
        '=' => TokenType::Equal,
        '≠' => TokenType::Different,
        '*' => TokenType::Mul,
        '+' => TokenType::Plus,
        '-' => TokenType::Minus,
        '/' => TokenType::Div,
        '<' => TokenType::LowerThan,
        '>' => TokenType::GreaterThan,
        ',' => TokenType::Comma,
        ':' => TokenType::Colon,
        ';' => TokenType::Semicolon,
        '&' => TokenType::Ampersand,
        '%' => TokenType::Mod,
        '|' => TokenType::VerticalBar,
        '~' => TokenType::Tilde,
        '^' => TokenType::CircumflexAccent,
        '.' => TokenType::Dot,
        '!' => TokenType::ExclamationPoint,
        '$' => TokenType::Dollar,

        // '0'...'9' => TokenType::NUM,
        _ => TokenType::Identifier,
    }
}

// TODO(ceg): monitor env.quit
// to flush
impl ContentFilter<'_> for HighlightFilter {
    fn name(&self) -> &'static str {
        &"HighlightFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor<'static>,
        env: &mut LayoutEnv,
        _view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.token_io = Vec::new();
        self.prev_token_type = TokenType::Unknown;
        self.utf8_token = Vec::new();
        self.new_color = TextStyle::default_color();
        // self.utf8_codec =  Box::new(utf8::Utf8Codec::new());

        self.skip_filter = false;

        let p_input = crate::core::event::pending_input_event_count();
        if p_input > 255 {
            //dbg_println!("*** SKIP HIGHLIGHT *** p_input {}", p_input);
            self.skip_filter = true;
        }
        if env.screen.is_off_screen {
            self.skip_filter = true;
        }

        self.max_token_size = env.screen.width() * env.screen.height(); //
    }

    fn run(
        &mut self,
        _view: &View,
        _env: &mut LayoutEnv,
        filter_in: &[FilterIo],
        filter_out: &mut Vec<FilterIo>,
    ) {
        if self.skip_filter {
            // return NOP hand let the caller skip swap
            *filter_out = filter_in.to_vec();
            return;
        }

        // flush too big token
        if self.token_io.len() > self.max_token_size {
            for mut io in self.token_io.iter_mut() {
                io.style.color = self.new_color;
            }
            filter_out.append(&mut self.token_io);
        }

        for io in filter_in {
            match &*io {
                FilterIo {
                    data: FilterData::TextInfo { real_cp, .. },
                    ..
                } => {
                    let c = u32_to_char(*real_cp);

                    // dbg_println!("-----------");
                    // dbg_println!("parsing char : '{}'", c);
                    let token_type = get_token_type(c);

                    if token_type == TokenType::Identifier
                        && self.prev_token_type == TokenType::Identifier
                    {
                        self.token_io.push(io.clone());
                        self.prev_token_type = token_type;
                        continue;
                    }

                    // flush token: set color
                    self.colorize_token();
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }
                    filter_out.append(&mut self.token_io);
                    // reset state
                    self.utf8_token.clear();
                    self.new_color = TextStyle::default_color();

                    // prepare next token
                    self.prev_token_type = token_type;
                    self.token_io.push(io.clone());
                }

                FilterIo {
                    data: FilterData::EndOfStream | FilterData::CustomLimitReached,
                    ..
                } => {
                    // flush pending token: set color
                    self.colorize_token();
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }

                    filter_out.append(&mut self.token_io);

                    // forward tag
                    filter_out.push(io.clone());
                }

                _ => {
                    dbg_println!("unexpected {:?}", io);
                    panic!("");
                }
            }
        }
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {
        // default
        if !self.token_io.is_empty() {
            // The parsing is incomplete
            // panic!("");
        }
    }
}
<|endoftext|>