<commit_before>//! See `AssistContext`

use algo::find_covering_element;
use hir::Semantics;
use ra_db::{FileId, FileRange};
use ra_fmt::{leading_indent, reindent};
use ra_ide_db::{
    source_change::{SourceChange, SourceFileEdit},
    RootDatabase,
};
use ra_syntax::{
    algo::{self, find_node_at_offset, SyntaxRewriter},
    AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
    TokenAtOffset,
};
use ra_text_edit::TextEditBuilder;

use crate::{
    assist_config::{AssistConfig, SnippetCap},
    Assist, AssistId, GroupLabel, ResolvedAssist,
};
use rustc_hash::FxHashMap;

/// `AssistContext` allows to apply an assist or check if it could be applied.
///
/// Assists use a somewhat over-engineered approach, given the current needs.
/// The assists workflow consists of two phases. In the first phase, a user asks
/// for the list of available assists. In the second phase, the user picks a
/// particular assist and it gets applied.
///
/// There are two peculiarities here:
///
/// * first, we ideally avoid computing more things then necessary to answer "is
///   assist applicable" in the first phase.
/// * second, when we are applying assist, we don't have a guarantee that there
///   weren't any changes between the point when user asked for assists and when
///   they applied a particular assist. So, when applying assist, we need to do
///   all the checks from scratch.
///
/// To avoid repeating the same code twice for both "check" and "apply"
/// functions, we use an approach reminiscent of that of Django's function based
/// views dealing with forms. Each assist receives a runtime parameter,
/// `resolve`. It first check if an edit is applicable (potentially computing
/// info required to compute the actual edit). If it is applicable, and
/// `resolve` is `true`, it then computes the actual edit.
///
/// So, to implement the original assists workflow, we can first apply each edit
/// with `resolve = false`, and then applying the selected edit again, with
/// `resolve = true` this time.
///
/// Note, however, that we don't actually use such two-phase logic at the
/// moment, because the LSP API is pretty awkward in this place, and it's much
/// easier to just compute the edit eagerly :-)
pub(crate) struct AssistContext<'a> {
    pub(crate) config: &'a AssistConfig,
    pub(crate) sema: Semantics<'a, RootDatabase>,
    pub(crate) db: &'a RootDatabase,
    pub(crate) frange: FileRange,
    source_file: SourceFile,
}

impl<'a> AssistContext<'a> {
    pub(crate) fn new(
        sema: Semantics<'a, RootDatabase>,
        config: &'a AssistConfig,
        frange: FileRange,
    ) -> AssistContext<'a> {
        let source_file = sema.parse(frange.file_id);
        let db = sema.db;
        AssistContext { config, sema, db, frange, source_file }
    }

    // NB, this ignores active selection.
    pub(crate) fn offset(&self) -> TextSize {
        self.frange.range.start()
    }

    pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
        self.source_file.syntax().token_at_offset(self.offset())
    }
    pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
        self.token_at_offset().find(|it| it.kind() == kind)
    }
    pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
        find_node_at_offset(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
        self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn covering_element(&self) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), self.frange.range)
    }
    // FIXME: remove
    pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), range)
    }
}

pub(crate) struct Assists {
    resolve: bool,
    file: FileId,
    buf: Vec<(Assist, Option<SourceChange>)>,
}

impl Assists {
    pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
        Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() }
    }
    pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
        Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() }
    }

    pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
        assert!(!self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| {
                assert!(edit.is_none());
                label
            })
            .collect()
    }

    pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
        assert!(self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() })
            .collect()
    }

    pub(crate) fn add(
        &mut self,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        let label = Assist::new(id, label.into(), None, target);
        self.add_impl(label, f)
    }
    pub(crate) fn add_in_multiple_files(
        &mut self,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistDirector),
    ) -> Option<()> {
        let label = Assist::new(id, label.into(), None, target);
        self.add_impl_multiple_files(label, f)
    }
    pub(crate) fn add_group(
        &mut self,
        group: &GroupLabel,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        let label = Assist::new(id, label.into(), Some(group.clone()), target);
        self.add_impl(label, f)
    }
    fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
        let source_change = if self.resolve {
            let mut builder = AssistBuilder::new(self.file);
            f(&mut builder);
            Some(builder.finish())
        } else {
            None
        };

        self.buf.push((label, source_change));
        Some(())
    }

    fn add_impl_multiple_files(
        &mut self,
        label: Assist,
        f: impl FnOnce(&mut AssistDirector),
    ) -> Option<()> {
        let change_label = label.label.clone();
        if !self.resolve {
            return None;
        }
        let mut director = AssistDirector::new(change_label.clone());
        f(&mut director);
        let changes = director.finish();
        let file_edits: Vec<SourceFileEdit> =
            changes.into_iter().map(|mut change| change.source_file_edits.pop().unwrap()).collect();

        let source_change = SourceChange {
            source_file_edits: file_edits,
            file_system_edits: vec![],
            is_snippet: false,
        };

        self.buf.push((label, Some(source_change)));
        Some(())
    }

    fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> {
        self.buf.sort_by_key(|(label, _edit)| label.target.len());
        self.buf
    }
}

pub(crate) struct AssistBuilder {
    edit: TextEditBuilder,
    file: FileId,
    is_snippet: bool,
}

impl AssistBuilder {
    pub(crate) fn new(file: FileId) -> AssistBuilder {
        AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false }
    }

    /// Remove specified `range` of text.
    pub(crate) fn delete(&mut self, range: TextRange) {
        self.edit.delete(range)
    }
    /// Append specified `text` at the given `offset`
    pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
        self.edit.insert(offset, text.into())
    }
    /// Append specified `snippet` at the given `offset`
    pub(crate) fn insert_snippet(
        &mut self,
        _cap: SnippetCap,
        offset: TextSize,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.insert(offset, snippet);
    }
    /// Replaces specified `range` of text with a given string.
    pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
        self.edit.replace(range, replace_with.into())
    }
    /// Replaces specified `range` of text with a given `snippet`.
    pub(crate) fn replace_snippet(
        &mut self,
        _cap: SnippetCap,
        range: TextRange,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.replace(range, snippet);
    }
    pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
        algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
    }
    /// Replaces specified `node` of text with a given string, reindenting the
    /// string to maintain `node`'s existing indent.
    // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
    pub(crate) fn replace_node_and_indent(
        &mut self,
        node: &SyntaxNode,
        replace_with: impl Into<String>,
    ) {
        let mut replace_with = replace_with.into();
        if let Some(indent) = leading_indent(node) {
            replace_with = reindent(&replace_with, &indent)
        }
        self.replace(node.text_range(), replace_with)
    }
    pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
        let node = rewriter.rewrite_root().unwrap();
        let new = rewriter.rewrite(&node);
        algo::diff(&node, &new).into_text_edit(&mut self.edit)
    }

    // FIXME: better API
    pub(crate) fn set_file(&mut self, assist_file: FileId) {
        self.file = assist_file;
    }

    // FIXME: kill this API
    /// Get access to the raw `TextEditBuilder`.
    pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
        &mut self.edit
    }

    fn finish(self) -> SourceChange {
        let edit = self.edit.finish();
        let source_file_edit = SourceFileEdit { file_id: self.file, edit };
        let mut res: SourceChange = source_file_edit.into();
        if self.is_snippet {
            res.is_snippet = true;
        }
        res
    }
}

pub(crate) struct AssistDirector {
    source_changes: Vec<SourceChange>,
    builders: FxHashMap<FileId, AssistBuilder>,
    change_label: String,
}

impl AssistDirector {
    fn new(change_label: String) -> AssistDirector {
        AssistDirector { source_changes: vec![], builders: FxHashMap::default(), change_label }
    }

    pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) {
        let mut builder = self.builders.entry(file_id).or_insert(AssistBuilder::new(file_id));
        f(&mut builder);
    }

    fn finish(mut self) -> Vec<SourceChange> {
        for (file_id, builder) in
            self.builders.into_iter().collect::<Vec<(FileId, AssistBuilder)>>()
        {
            self.source_changes.push(builder.finish());
        }
        self.source_changes
    }
}
<commit_msg>Refactor AssistDirector<commit_after>//! See `AssistContext`

use algo::find_covering_element;
use hir::Semantics;
use ra_db::{FileId, FileRange};
use ra_fmt::{leading_indent, reindent};
use ra_ide_db::{
    source_change::{SourceChange, SourceFileEdit},
    RootDatabase,
};
use ra_syntax::{
    algo::{self, find_node_at_offset, SyntaxRewriter},
    AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
    TokenAtOffset,
};
use ra_text_edit::TextEditBuilder;

use crate::{
    assist_config::{AssistConfig, SnippetCap},
    Assist, AssistId, GroupLabel, ResolvedAssist,
};
use rustc_hash::FxHashMap;

/// `AssistContext` allows to apply an assist or check if it could be applied.
///
/// Assists use a somewhat over-engineered approach, given the current needs.
/// The assists workflow consists of two phases. In the first phase, a user asks
/// for the list of available assists. In the second phase, the user picks a
/// particular assist and it gets applied.
///
/// There are two peculiarities here:
///
/// * first, we ideally avoid computing more things then necessary to answer "is
///   assist applicable" in the first phase.
/// * second, when we are applying assist, we don't have a guarantee that there
///   weren't any changes between the point when user asked for assists and when
///   they applied a particular assist. So, when applying assist, we need to do
///   all the checks from scratch.
///
/// To avoid repeating the same code twice for both "check" and "apply"
/// functions, we use an approach reminiscent of that of Django's function based
/// views dealing with forms. Each assist receives a runtime parameter,
/// `resolve`. It first check if an edit is applicable (potentially computing
/// info required to compute the actual edit). If it is applicable, and
/// `resolve` is `true`, it then computes the actual edit.
///
/// So, to implement the original assists workflow, we can first apply each edit
/// with `resolve = false`, and then applying the selected edit again, with
/// `resolve = true` this time.
///
/// Note, however, that we don't actually use such two-phase logic at the
/// moment, because the LSP API is pretty awkward in this place, and it's much
/// easier to just compute the edit eagerly :-)
pub(crate) struct AssistContext<'a> {
    pub(crate) config: &'a AssistConfig,
    pub(crate) sema: Semantics<'a, RootDatabase>,
    pub(crate) db: &'a RootDatabase,
    pub(crate) frange: FileRange,
    source_file: SourceFile,
}

impl<'a> AssistContext<'a> {
    pub(crate) fn new(
        sema: Semantics<'a, RootDatabase>,
        config: &'a AssistConfig,
        frange: FileRange,
    ) -> AssistContext<'a> {
        let source_file = sema.parse(frange.file_id);
        let db = sema.db;
        AssistContext { config, sema, db, frange, source_file }
    }

    // NB, this ignores active selection.
    pub(crate) fn offset(&self) -> TextSize {
        self.frange.range.start()
    }

    pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
        self.source_file.syntax().token_at_offset(self.offset())
    }
    pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
        self.token_at_offset().find(|it| it.kind() == kind)
    }
    pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
        find_node_at_offset(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
        self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
    }
    pub(crate) fn covering_element(&self) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), self.frange.range)
    }
    // FIXME: remove
    pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
        find_covering_element(self.source_file.syntax(), range)
    }
}

pub(crate) struct Assists {
    resolve: bool,
    file: FileId,
    buf: Vec<(Assist, Option<SourceChange>)>,
}

impl Assists {
    pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
        Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() }
    }
    pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
        Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() }
    }

    pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
        assert!(!self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| {
                assert!(edit.is_none());
                label
            })
            .collect()
    }

    pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
        assert!(self.resolve);
        self.finish()
            .into_iter()
            .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() })
            .collect()
    }

    pub(crate) fn add(
        &mut self,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        let label = Assist::new(id, label.into(), None, target);
        self.add_impl(label, f)
    }
    pub(crate) fn add_in_multiple_files(
        &mut self,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistDirector),
    ) -> Option<()> {
        let label = Assist::new(id, label.into(), None, target);
        self.add_impl_multiple_files(label, f)
    }
    pub(crate) fn add_group(
        &mut self,
        group: &GroupLabel,
        id: AssistId,
        label: impl Into<String>,
        target: TextRange,
        f: impl FnOnce(&mut AssistBuilder),
    ) -> Option<()> {
        let label = Assist::new(id, label.into(), Some(group.clone()), target);
        self.add_impl(label, f)
    }
    fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
        let source_change = if self.resolve {
            let mut builder = AssistBuilder::new(self.file);
            f(&mut builder);
            Some(builder.finish())
        } else {
            None
        };

        self.buf.push((label, source_change));
        Some(())
    }

    fn add_impl_multiple_files(
        &mut self,
        label: Assist,
        f: impl FnOnce(&mut AssistDirector),
    ) -> Option<()> {
        if !self.resolve {
            return None;
        }
        let mut director = AssistDirector::new();
        f(&mut director);
        let changes = director.finish();
        let file_edits: Vec<SourceFileEdit> =
            changes.into_iter().map(|mut change| change.source_file_edits.pop().unwrap()).collect();

        let source_change = SourceChange {
            source_file_edits: file_edits,
            file_system_edits: vec![],
            is_snippet: false,
        };

        self.buf.push((label, Some(source_change)));
        Some(())
    }

    fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> {
        self.buf.sort_by_key(|(label, _edit)| label.target.len());
        self.buf
    }
}

pub(crate) struct AssistBuilder {
    edit: TextEditBuilder,
    file: FileId,
    is_snippet: bool,
}

impl AssistBuilder {
    pub(crate) fn new(file: FileId) -> AssistBuilder {
        AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false }
    }

    /// Remove specified `range` of text.
    pub(crate) fn delete(&mut self, range: TextRange) {
        self.edit.delete(range)
    }
    /// Append specified `text` at the given `offset`
    pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
        self.edit.insert(offset, text.into())
    }
    /// Append specified `snippet` at the given `offset`
    pub(crate) fn insert_snippet(
        &mut self,
        _cap: SnippetCap,
        offset: TextSize,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.insert(offset, snippet);
    }
    /// Replaces specified `range` of text with a given string.
    pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
        self.edit.replace(range, replace_with.into())
    }
    /// Replaces specified `range` of text with a given `snippet`.
    pub(crate) fn replace_snippet(
        &mut self,
        _cap: SnippetCap,
        range: TextRange,
        snippet: impl Into<String>,
    ) {
        self.is_snippet = true;
        self.replace(range, snippet);
    }
    pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
        algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
    }
    /// Replaces specified `node` of text with a given string, reindenting the
    /// string to maintain `node`'s existing indent.
    // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
    pub(crate) fn replace_node_and_indent(
        &mut self,
        node: &SyntaxNode,
        replace_with: impl Into<String>,
    ) {
        let mut replace_with = replace_with.into();
        if let Some(indent) = leading_indent(node) {
            replace_with = reindent(&replace_with, &indent)
        }
        self.replace(node.text_range(), replace_with)
    }
    pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
        let node = rewriter.rewrite_root().unwrap();
        let new = rewriter.rewrite(&node);
        algo::diff(&node, &new).into_text_edit(&mut self.edit)
    }

    // FIXME: better API
    pub(crate) fn set_file(&mut self, assist_file: FileId) {
        self.file = assist_file;
    }

    // FIXME: kill this API
    /// Get access to the raw `TextEditBuilder`.
    pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
        &mut self.edit
    }

    fn finish(self) -> SourceChange {
        let edit = self.edit.finish();
        let source_file_edit = SourceFileEdit { file_id: self.file, edit };
        let mut res: SourceChange = source_file_edit.into();
        if self.is_snippet {
            res.is_snippet = true;
        }
        res
    }
}

pub(crate) struct AssistDirector {
    source_changes: Vec<SourceChange>,
    builders: FxHashMap<FileId, AssistBuilder>,
}

impl AssistDirector {
    fn new() -> AssistDirector {
        AssistDirector { source_changes: vec![], builders: FxHashMap::default() }
    }

    pub(crate) fn perform(&mut self, file_id: FileId, f: impl FnOnce(&mut AssistBuilder)) {
        let mut builder = self.builders.entry(file_id).or_insert(AssistBuilder::new(file_id));
        f(&mut builder);
    }

    fn finish(mut self) -> Vec<SourceChange> {
        for (_, builder) in
            self.builders.into_iter().collect::<Vec<(FileId, AssistBuilder)>>()
        {
            self.source_changes.push(builder.finish());
        }
        self.source_changes
    }
}
<|endoftext|>


<commit_before>use parser::lexer_error::LexerError;
use parser::token::Token;

pub struct Lexer {
  text: String,
}

impl Lexer {
  pub fn new(text: String) -> Lexer {
    Lexer {
      text: text,
    }
  }

  fn advance(&mut self, n: usize) {
    let text = self.text.clone();
    let (_, t) = text.split_at(n);
    self.text = t.to_string();
  }

  fn lex_integer(&mut self) -> Result<Token, LexerError> {
    let int_str: String = self.text
      .chars()
      .take_while(|c| c.is_digit(10))
      .collect();

    let n = try!(int_str.parse::<isize>());

    self.advance(int_str.len());
    Ok(Token::Int(n))
  }

  fn lex_keyword(&mut self) -> Result<Token, LexerError> {
    let keyword: String = self.text
      .chars()
      .take_while(|c| c.is_alphabetic() || *c == '_')
      .collect();

    self.advance(keyword.len());

    let token = match keyword.as_ref() {
      "true" => Token::Bool(true),
      "false" => Token::Bool(false),
      "fn" => Token::FnDecl,
      "let" => Token::Let,
      "var" => Token::VarDecl,
      "if" => Token::If,
      "else" => Token::Else,
      "while" => Token::While,
      s if s.len() > 0 => Token::Var(s.to_string()),
      s => return Err(LexerError::InvalidKeyword(format!("invalid keyword {:?}", s)))
    };

    Ok(token)
  }

  fn skip_whitespace(&mut self) {
    let spaces_str: String = self.text
      .chars()
      .take_while(|c| c.is_whitespace())
      .collect();

    self.advance(spaces_str.len());
  }

  fn skip_inline_comment(&mut self) {
    let comment_str: String = self.text
      .chars()
      .take_while(|c| !(c == &'\n'))
      .collect();

    self.advance(comment_str.len());
  }

  fn skip_block_comment(&mut self) {
    // TODO: remove everything through the next '*/'
  }

  fn peek_next(&mut self) -> Option<char> {
    self.text.chars().next()
  }

  pub fn get_next_token(&mut self) -> Result<Token, LexerError> {
    while self.peek_next() != None {
      debug!("get_next_token: {}", self.text);

      let token = match self.peek_next() {
        Some('/') if self.text.starts_with("//") => {
          self.advance(2);
          self.skip_inline_comment();
          continue;
        },
        Some('/') if self.text.starts_with("/*") => {
          self.advance(2);
          self.skip_block_comment();
          continue;
        },
        Some('+') => {
          self.advance(1);
          Token::Plus
        },
        Some('-') => {
          self.advance(1);
          Token::Minus
        },
        Some('*') => {
          self.advance(1);
          Token::Times
        },
        Some('/') => {
          self.advance(1);
          Token::Div
        },
        Some('%') => {
          self.advance(1);
          Token::Mod
        },
        Some('(') => {
          self.advance(1);
          Token::LParen
        },
        Some(')') => {
          self.advance(1);
          Token::RParen
        },
        Some('&') if self.text.starts_with("&&") => {
          self.advance(2);
          Token::And
        },
        Some('|') if self.text.starts_with("||") => {
          self.advance(2);
          Token::Or
        },
        Some('=') if self.text.starts_with("==") => {
          self.advance(2);
          Token::Eq
        },
        Some('=') => {
          self.advance(1);
          Token::Assign
        },
        Some('!') if self.text.starts_with("!=") => {
          self.advance(2);
          Token::Ne
        },
        Some('!') => {
          self.advance(1);
          Token::Not
        },
        Some('>') if self.text.starts_with(">=") => {
          self.advance(2);
          Token::Geq
        },
        Some('>') => {
          self.advance(1);
          Token::Gt
        },
        Some('<') if self.text.starts_with("<=") => {
          self.advance(2);
          Token::Leq
        },
        Some('<') => {
          self.advance(1);
          Token::Lt
        },
        Some(';') => {
          self.advance(1);
          Token::Seq
        },
        Some('?') => {
          self.advance(1);
          Token::Ternary
        },
        Some(':') => {
          self.advance(1);
          Token::Else
        },
        Some('{') => {
          self.advance(1);
          Token::LBracket
        },
        Some('}') => {
          self.advance(1);
          Token::RBracket
        },
        Some(',') => {
          self.advance(1);
          Token::Comma
        },
        Some(c) if c.is_alphabetic() => return self.lex_keyword(),
        Some(c) if c.is_digit(10) => return self.lex_integer(),
        Some(c) if c.is_whitespace() => {
          self.skip_whitespace();
          continue;
        },
        Some(c) => return Err(LexerError::UnexpectedCharacter(format!("unexpected char {:?}", c))),
        None => Token::EOF
      };

      return Ok(token)
    }

    Ok(Token::EOF)
  }
}
<commit_msg>remove try from lexer<commit_after>use parser::lexer_error::LexerError;
use parser::token::Token;

pub struct Lexer {
  text: String,
}

impl Lexer {
  pub fn new(text: String) -> Lexer {
    Lexer {
      text: text,
    }
  }

  fn advance(&mut self, n: usize) {
    let text = self.text.clone();
    let (_, t) = text.split_at(n);
    self.text = t.to_string();
  }

  fn lex_integer(&mut self) -> Result<Token, LexerError> {
    let int_str: String = self.text
      .chars()
      .take_while(|c| c.is_digit(10))
      .collect();

    let n = int_str.parse::<isize>()?;

    self.advance(int_str.len());
    Ok(Token::Int(n))
  }

  fn lex_keyword(&mut self) -> Result<Token, LexerError> {
    let keyword: String = self.text
      .chars()
      .take_while(|c| c.is_alphabetic() || *c == '_')
      .collect();

    self.advance(keyword.len());

    let token = match keyword.as_ref() {
      "true" => Token::Bool(true),
      "false" => Token::Bool(false),
      "fn" => Token::FnDecl,
      "let" => Token::Let,
      "var" => Token::VarDecl,
      "if" => Token::If,
      "else" => Token::Else,
      "while" => Token::While,
      s if s.len() > 0 => Token::Var(s.to_string()),
      s => return Err(LexerError::InvalidKeyword(format!("invalid keyword {:?}", s)))
    };

    Ok(token)
  }

  fn skip_whitespace(&mut self) {
    let spaces_str: String = self.text
      .chars()
      .take_while(|c| c.is_whitespace())
      .collect();

    self.advance(spaces_str.len());
  }

  fn skip_inline_comment(&mut self) {
    let comment_str: String = self.text
      .chars()
      .take_while(|c| !(c == &'\n'))
      .collect();

    self.advance(comment_str.len());
  }

  fn skip_block_comment(&mut self) {
    // TODO: remove everything through the next '*/'
  }

  fn peek_next(&mut self) -> Option<char> {
    self.text.chars().next()
  }

  pub fn get_next_token(&mut self) -> Result<Token, LexerError> {
    while self.peek_next() != None {
      debug!("get_next_token: {}", self.text);

      let token = match self.peek_next() {
        Some('/') if self.text.starts_with("//") => {
          self.advance(2);
          self.skip_inline_comment();
          continue;
        },
        Some('/') if self.text.starts_with("/*") => {
          self.advance(2);
          self.skip_block_comment();
          continue;
        },
        Some('+') => {
          self.advance(1);
          Token::Plus
        },
        Some('-') => {
          self.advance(1);
          Token::Minus
        },
        Some('*') => {
          self.advance(1);
          Token::Times
        },
        Some('/') => {
          self.advance(1);
          Token::Div
        },
        Some('%') => {
          self.advance(1);
          Token::Mod
        },
        Some('(') => {
          self.advance(1);
          Token::LParen
        },
        Some(')') => {
          self.advance(1);
          Token::RParen
        },
        Some('&') if self.text.starts_with("&&") => {
          self.advance(2);
          Token::And
        },
        Some('|') if self.text.starts_with("||") => {
          self.advance(2);
          Token::Or
        },
        Some('=') if self.text.starts_with("==") => {
          self.advance(2);
          Token::Eq
        },
        Some('=') => {
          self.advance(1);
          Token::Assign
        },
        Some('!') if self.text.starts_with("!=") => {
          self.advance(2);
          Token::Ne
        },
        Some('!') => {
          self.advance(1);
          Token::Not
        },
        Some('>') if self.text.starts_with(">=") => {
          self.advance(2);
          Token::Geq
        },
        Some('>') => {
          self.advance(1);
          Token::Gt
        },
        Some('<') if self.text.starts_with("<=") => {
          self.advance(2);
          Token::Leq
        },
        Some('<') => {
          self.advance(1);
          Token::Lt
        },
        Some(';') => {
          self.advance(1);
          Token::Seq
        },
        Some('?') => {
          self.advance(1);
          Token::Ternary
        },
        Some(':') => {
          self.advance(1);
          Token::Else
        },
        Some('{') => {
          self.advance(1);
          Token::LBracket
        },
        Some('}') => {
          self.advance(1);
          Token::RBracket
        },
        Some(',') => {
          self.advance(1);
          Token::Comma
        },
        Some(c) if c.is_alphabetic() => return self.lex_keyword(),
        Some(c) if c.is_digit(10) => return self.lex_integer(),
        Some(c) if c.is_whitespace() => {
          self.skip_whitespace();
          continue;
        },
        Some(c) => return Err(LexerError::UnexpectedCharacter(format!("unexpected char {:?}", c))),
        None => Token::EOF
      };

      return Ok(token)
    }

    Ok(Token::EOF)
  }
}
<|endoftext|>


<commit_before>//! An extension trait that provides the ability to strip IRC colors from a string
use std::borrow::Cow;

enum ParserState {
    Text,
    ColorCode,
    Foreground1(char),
    Foreground2,
    Comma,
    Background1(char),
}
struct Parser {
    state: ParserState,
}

/// An extension trait giving strings a function to strip IRC colors
pub trait FormattedStringExt<'a> {

    /// Returns true if the string contains color, bold, underline or italics
    fn is_formatted(&self) -> bool;

    /// Returns the string with all color, bold, underline and italics stripped
    fn strip_formatting(self) -> Cow<'a, str>;

}

const FORMAT_CHARACTERS: &[char] = &[
    '\x02', // bold
    '\x1F', // underline
    '\x16', // reverse
    '\x0F', // normal
    '\x03', // color
];

impl<'a> FormattedStringExt<'a> for &'a str {
    fn is_formatted(&self) -> bool {
        self.contains(FORMAT_CHARACTERS)
    }

    fn strip_formatting(self) -> Cow<'a, str> {
        if !self.is_formatted() {
            return Cow::Borrowed(self);
        }
        let mut s = String::from(self);
        strip_formatting(&mut s);
        Cow::Owned(s)
    }
}

fn strip_formatting(buf: &mut String) {
    let mut parser = Parser::new();
    buf.retain(|cur| parser.next(cur));
}

impl Parser {
    fn new() -> Self {
        Parser  {
            state: ParserState::Text,
        }
    }

    fn next(&mut self, cur: char) -> bool {
        use self::ParserState::*;
        match self.state {
            Text | Foreground1(_) | Foreground2 if cur == '\x03' => {
                self.state = ColorCode;
                false
            }
            Text => {
                !FORMAT_CHARACTERS.contains(&cur)
            }
            ColorCode if cur.is_digit(10) => {
                self.state = Foreground1(cur);
                false
            }
            Foreground1('1') if cur.is_digit(6) => {
                // can only consume another digit if previous char was 1.
                self.state = Foreground2;
                false
            }
            Foreground1(_) if cur.is_digit(6) => {
                self.state = Text;
                true
            }
            Foreground1(_) if cur == ',' => {
                self.state = Comma;
                false
            }
            Foreground2 if cur == ',' => {
                self.state = Comma;
                false
            }
            Comma if (cur.is_digit(10)) => {
                self.state = Background1(cur);
                false
            }
            Background1(prev) if cur.is_digit(6) => {
                // can only consume another digit if previous char was 1.
                self.state = Text;
                prev != '1'
            }
            _ => {
                self.state = Text;
                true
            }
        }
    }
}

impl FormattedStringExt<'static> for String {
    fn is_formatted(&self) -> bool {
        self.as_str().is_formatted()
    }
    fn strip_formatting(mut self) -> Cow<'static, str> {
        if !self.is_formatted() {
            return Cow::Owned(self);
        }
        strip_formatting(&mut self);
        Cow::Owned(self)
    }
}

#[cfg(test)]
mod test {
    use std::borrow::Cow;
    use proto::colors::FormattedStringExt;

    macro_rules! test_formatted_string_ext {
        { $( $name:ident ( $($line:tt)* ), )* } => {
            $(
            mod $name {
                use super::*;
                test_formatted_string_ext!(@ $($line)*);
            }
            )*
        };
        (@ $text:expr, should stripped into $expected:expr) => {
            #[test]
            fn test_formatted() {
                assert!($text.is_formatted());
            }
            #[test]
            fn test_strip() {
                assert_eq!($text.strip_formatting(), $expected);
            }
        };
        (@ $text:expr, is not formatted) => {
            #[test]
            fn test_formatted() {
                assert!(!$text.is_formatted());
            }
            #[test]
            fn test_strip() {
                assert_eq!($text.strip_formatting(), $text);
            }
        }
    }

    test_formatted_string_ext! {
        blank("", is not formatted),
        blank2("    ", is not formatted),
        blank3("\t\r\n", is not formatted),
        bold("l\x02ol", should stripped into "lol"),
        bold_from_string(String::from("l\x02ol"), should stripped into "lol"),
        bold_hangul("우왕\x02굳", should stripped into "우왕굳"),
        fg_color("l\x033ol", should stripped into "lol"),
        fg_color2("l\x0312ol", should stripped into "lol"),
        fg_bg_11("l\x031,2ol", should stripped into "lol"),
        fg_bg_21("l\x0312,3ol", should stripped into "lol"),
        fg_bg_12("l\x031,12ol", should stripped into "lol"),
        fg_bg_22("l\x0312,13ol", should stripped into "lol"),
        string_with_multiple_colors("hoo\x034r\x033a\x0312y", should stripped into "hooray"),
        string_with_digit_after_color("\x0344\x0355\x0366", should stripped into "456"),
        string_with_multiple_2digit_colors("hoo\x0310r\x0311a\x0312y", should stripped into "hooray"),
        string_with_digit_after_2digit_color("\x031212\x031111\x031010", should stripped into "121110"),
        thinking("🤔...", is not formatted),
        unformatted("a plain text", is not formatted),
    }

    #[test]
    fn test_strip_no_allocation_for_unformatted_text() {
        if let Cow::Borrowed(formatted) = "plain text".strip_formatting() {
            assert_eq!(formatted, "plain text");
        } else {
            panic!("allocation detected");
        }
    }
}
<commit_msg>Removed the old colors module (now in irc_proto).<commit_after><|endoftext|>


<commit_before>use bytes::{Buf, Bytes};
use h2::{RecvStream, SendStream};
use http::header::{
    HeaderName, CONNECTION, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, TE, TRAILER,
    TRANSFER_ENCODING, UPGRADE,
};
use http::HeaderMap;
use pin_project_lite::pin_project;
use std::error::Error as StdError;
use std::io::{self, Cursor, IoSlice};
use std::mem;
use std::task::Context;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

use crate::body::HttpBody;
use crate::common::{task, Future, Pin, Poll};
use crate::proto::h2::ping::Recorder;

pub(crate) mod ping;

cfg_client! {
    pub(crate) mod client;
    pub(crate) use self::client::ClientTask;
}

cfg_server! {
    pub(crate) mod server;
    pub(crate) use self::server::Server;
}

/// Default initial stream window size defined in HTTP2 spec.
pub(crate) const SPEC_WINDOW_SIZE: u32 = 65_535;

fn strip_connection_headers(headers: &mut HeaderMap, is_request: bool) {
    // List of connection headers from:
    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
    //
    // TE headers are allowed in HTTP/2 requests as long as the value is "trailers", so they're
    // tested separately.
    let connection_headers = [
        HeaderName::from_lowercase(b"keep-alive").unwrap(),
        HeaderName::from_lowercase(b"proxy-connection").unwrap(),
        PROXY_AUTHENTICATE,
        PROXY_AUTHORIZATION,
        TRAILER,
        TRANSFER_ENCODING,
        UPGRADE,
    ];

    for header in connection_headers.iter() {
        if headers.remove(header).is_some() {
            warn!("Connection header illegal in HTTP/2: {}", header.as_str());
        }
    }

    if is_request {
        if headers
            .get(TE)
            .map(|te_header| te_header != "trailers")
            .unwrap_or(false)
        {
            warn!("TE headers not set to \"trailers\" are illegal in HTTP/2 requests");
            headers.remove(TE);
        }
    } else if headers.remove(TE).is_some() {
        warn!("TE headers illegal in HTTP/2 responses");
    }

    if let Some(header) = headers.remove(CONNECTION) {
        warn!(
            "Connection header illegal in HTTP/2: {}",
            CONNECTION.as_str()
        );
        let header_contents = header.to_str().unwrap();

        // A `Connection` header may have a comma-separated list of names of other headers that
        // are meant for only this specific connection.
        //
        // Iterate these names and remove them as headers. Connection-specific headers are
        // forbidden in HTTP2, as that information has been moved into frame types of the h2
        // protocol.
        for name in header_contents.split(',') {
            let name = name.trim();
            headers.remove(name);
        }
    }
}

// body adapters used by both Client and Server

pin_project! {
    struct PipeToSendStream<S>
    where
        S: HttpBody,
    {
        body_tx: SendStream<SendBuf<S::Data>>,
        data_done: bool,
        #[pin]
        stream: S,
    }
}

impl<S> PipeToSendStream<S>
where
    S: HttpBody,
{
    fn new(stream: S, tx: SendStream<SendBuf<S::Data>>) -> PipeToSendStream<S> {
        PipeToSendStream {
            body_tx: tx,
            data_done: false,
            stream,
        }
    }
}

impl<S> Future for PipeToSendStream<S>
where
    S: HttpBody,
    S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
    type Output = crate::Result<()>;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let mut me = self.project();
        loop {
            if !*me.data_done {
                // we don't have the next chunk of data yet, so just reserve 1 byte to make
                // sure there's some capacity available. h2 will handle the capacity management
                // for the actual body chunk.
                me.body_tx.reserve_capacity(1);

                if me.body_tx.capacity() == 0 {
                    loop {
                        match ready!(me.body_tx.poll_capacity(cx)) {
                            Some(Ok(0)) => {}
                            Some(Ok(_)) => break,
                            Some(Err(e)) => {
                                return Poll::Ready(Err(crate::Error::new_body_write(e)))
                            }
                            None => {
                                // None means the stream is no longer in a
                                // streaming state, we either finished it
                                // somehow, or the remote reset us.
                                return Poll::Ready(Err(crate::Error::new_body_write(
                                    "send stream capacity unexpectedly closed",
                                )));
                            }
                        }
                    }
                } else if let Poll::Ready(reason) = me
                    .body_tx
                    .poll_reset(cx)
                    .map_err(crate::Error::new_body_write)?
                {
                    debug!("stream received RST_STREAM: {:?}", reason);
                    return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
                        reason,
                    ))));
                }

                match ready!(me.stream.as_mut().poll_data(cx)) {
                    Some(Ok(chunk)) => {
                        let is_eos = me.stream.is_end_stream();
                        trace!(
                            "send body chunk: {} bytes, eos={}",
                            chunk.remaining(),
                            is_eos,
                        );

                        let buf = SendBuf::Buf(chunk);
                        me.body_tx
                            .send_data(buf, is_eos)
                            .map_err(crate::Error::new_body_write)?;

                        if is_eos {
                            return Poll::Ready(Ok(()));
                        }
                    }
                    Some(Err(e)) => return Poll::Ready(Err(me.body_tx.on_user_err(e))),
                    None => {
                        me.body_tx.reserve_capacity(0);
                        let is_eos = me.stream.is_end_stream();
                        if is_eos {
                            return Poll::Ready(me.body_tx.send_eos_frame());
                        } else {
                            *me.data_done = true;
                            // loop again to poll_trailers
                        }
                    }
                }
            } else {
                if let Poll::Ready(reason) = me
                    .body_tx
                    .poll_reset(cx)
                    .map_err(crate::Error::new_body_write)?
                {
                    debug!("stream received RST_STREAM: {:?}", reason);
                    return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
                        reason,
                    ))));
                }

                match ready!(me.stream.poll_trailers(cx)) {
                    Ok(Some(trailers)) => {
                        me.body_tx
                            .send_trailers(trailers)
                            .map_err(crate::Error::new_body_write)?;
                        return Poll::Ready(Ok(()));
                    }
                    Ok(None) => {
                        // There were no trailers, so send an empty DATA frame...
                        return Poll::Ready(me.body_tx.send_eos_frame());
                    }
                    Err(e) => return Poll::Ready(Err(me.body_tx.on_user_err(e))),
                }
            }
        }
    }
}

trait SendStreamExt {
    fn on_user_err<E>(&mut self, err: E) -> crate::Error
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>;
    fn send_eos_frame(&mut self) -> crate::Result<()>;
}

impl<B: Buf> SendStreamExt for SendStream<SendBuf<B>> {
    fn on_user_err<E>(&mut self, err: E) -> crate::Error
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>,
    {
        let err = crate::Error::new_user_body(err);
        debug!("send body user stream error: {}", err);
        self.send_reset(err.h2_reason());
        err
    }

    fn send_eos_frame(&mut self) -> crate::Result<()> {
        trace!("send body eos");
        self.send_data(SendBuf::None, true)
            .map_err(crate::Error::new_body_write)
    }
}

#[repr(usize)]
enum SendBuf<B> {
    Buf(B),
    Cursor(Cursor<Box<[u8]>>),
    None,
}

impl<B: Buf> Buf for SendBuf<B> {
    #[inline]
    fn remaining(&self) -> usize {
        match *self {
            Self::Buf(ref b) => b.remaining(),
            Self::Cursor(ref c) => c.remaining(),
            Self::None => 0,
        }
    }

    #[inline]
    fn chunk(&self) -> &[u8] {
        match *self {
            Self::Buf(ref b) => b.chunk(),
            Self::Cursor(ref c) => c.chunk(),
            Self::None => &[],
        }
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        match *self {
            Self::Buf(ref mut b) => b.advance(cnt),
            Self::Cursor(ref mut c) => c.advance(cnt),
            Self::None => {}
        }
    }

    fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
        match *self {
            Self::Buf(ref b) => b.chunks_vectored(dst),
            Self::Cursor(ref c) => c.chunks_vectored(dst),
            Self::None => 0,
        }
    }
}

struct H2Upgraded<B>
where
    B: Buf,
{
    ping: Recorder,
    send_stream: UpgradedSendStream<B>,
    recv_stream: RecvStream,
    buf: Bytes,
}

impl<B> AsyncRead for H2Upgraded<B>
where
    B: Buf,
{
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        read_buf: &mut ReadBuf<'_>,
    ) -> Poll<Result<(), io::Error>> {
        if self.buf.is_empty() {
            self.buf = loop {
                match ready!(self.recv_stream.poll_data(cx)) {
                    None => return Poll::Ready(Ok(())),
                    Some(Ok(buf)) if buf.is_empty() && !self.recv_stream.is_end_stream() => {
                        continue
                    }
                    Some(Ok(buf)) => {
                        self.ping.record_data(buf.len());
                        break buf;
                    }
                    Some(Err(e)) => {
                        return Poll::Ready(Err(h2_to_io_error(e)));
                    }
                }
            };
        }
        let cnt = std::cmp::min(self.buf.len(), read_buf.remaining());
        read_buf.put_slice(&self.buf[..cnt]);
        self.buf.advance(cnt);
        let _ = self.recv_stream.flow_control().release_capacity(cnt);
        Poll::Ready(Ok(()))
    }
}

impl<B> AsyncWrite for H2Upgraded<B>
where
    B: Buf,
{
    fn poll_write(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<Result<usize, io::Error>> {
        if let Poll::Ready(reset) = self.send_stream.poll_reset(cx) {
            return Poll::Ready(Err(h2_to_io_error(match reset {
                Ok(reason) => reason.into(),
                Err(e) => e,
            })));
        }
        if buf.is_empty() {
            return Poll::Ready(Ok(0));
        }
        self.send_stream.reserve_capacity(buf.len());
        Poll::Ready(match ready!(self.send_stream.poll_capacity(cx)) {
            None => Ok(0),
            Some(Ok(cnt)) => self.send_stream.write(&buf[..cnt], false).map(|()| cnt),
            Some(Err(e)) => Err(h2_to_io_error(e)),
        })
    }

    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
        Poll::Ready(Ok(()))
    }

    fn poll_shutdown(
        mut self: Pin<&mut Self>,
        _cx: &mut Context<'_>,
    ) -> Poll<Result<(), io::Error>> {
        Poll::Ready(self.send_stream.write(&[], true))
    }
}

fn h2_to_io_error(e: h2::Error) -> io::Error {
    if e.is_io() {
        e.into_io().unwrap()
    } else {
        io::Error::new(io::ErrorKind::Other, e)
    }
}

struct UpgradedSendStream<B>(SendStream<SendBuf<Neutered<B>>>);

impl<B> UpgradedSendStream<B>
where
    B: Buf,
{
    unsafe fn new(inner: SendStream<SendBuf<B>>) -> Self {
        assert_eq!(mem::size_of::<B>(), mem::size_of::<Neutered<B>>());
        Self(mem::transmute(inner))
    }

    fn reserve_capacity(&mut self, cnt: usize) {
        unsafe { self.as_inner_unchecked().reserve_capacity(cnt) }
    }

    fn poll_capacity(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<usize, h2::Error>>> {
        unsafe { self.as_inner_unchecked().poll_capacity(cx) }
    }

    fn poll_reset(&mut self, cx: &mut Context<'_>) -> Poll<Result<h2::Reason, h2::Error>> {
        unsafe { self.as_inner_unchecked().poll_reset(cx) }
    }

    fn write(&mut self, buf: &[u8], end_of_stream: bool) -> Result<(), io::Error> {
        let send_buf = SendBuf::Cursor(Cursor::new(buf.into()));
        unsafe {
            self.as_inner_unchecked()
                .send_data(send_buf, end_of_stream)
                .map_err(h2_to_io_error)
        }
    }

    unsafe fn as_inner_unchecked(&mut self) -> &mut SendStream<SendBuf<B>> {
        &mut *(&mut self.0 as *mut _ as *mut _)
    }
}

#[repr(transparent)]
struct Neutered<B> {
    _inner: B,
    impossible: Impossible,
}

enum Impossible {}

unsafe impl<B> Send for Neutered<B> {}

impl<B> Buf for Neutered<B> {
    fn remaining(&self) -> usize {
        match self.impossible {}
    }

    fn chunk(&self) -> &[u8] {
        match self.impossible {}
    }

    fn advance(&mut self, _cnt: usize) {
        match self.impossible {}
    }
}
<commit_msg>refactor(http2): fix unstable name clash of Cursor::remaining<commit_after>use bytes::{Buf, Bytes};
use h2::{RecvStream, SendStream};
use http::header::{
    HeaderName, CONNECTION, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, TE, TRAILER,
    TRANSFER_ENCODING, UPGRADE,
};
use http::HeaderMap;
use pin_project_lite::pin_project;
use std::error::Error as StdError;
use std::io::{self, Cursor, IoSlice};
use std::mem;
use std::task::Context;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

use crate::body::HttpBody;
use crate::common::{task, Future, Pin, Poll};
use crate::proto::h2::ping::Recorder;

pub(crate) mod ping;

cfg_client! {
    pub(crate) mod client;
    pub(crate) use self::client::ClientTask;
}

cfg_server! {
    pub(crate) mod server;
    pub(crate) use self::server::Server;
}

/// Default initial stream window size defined in HTTP2 spec.
pub(crate) const SPEC_WINDOW_SIZE: u32 = 65_535;

fn strip_connection_headers(headers: &mut HeaderMap, is_request: bool) {
    // List of connection headers from:
    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
    //
    // TE headers are allowed in HTTP/2 requests as long as the value is "trailers", so they're
    // tested separately.
    let connection_headers = [
        HeaderName::from_lowercase(b"keep-alive").unwrap(),
        HeaderName::from_lowercase(b"proxy-connection").unwrap(),
        PROXY_AUTHENTICATE,
        PROXY_AUTHORIZATION,
        TRAILER,
        TRANSFER_ENCODING,
        UPGRADE,
    ];

    for header in connection_headers.iter() {
        if headers.remove(header).is_some() {
            warn!("Connection header illegal in HTTP/2: {}", header.as_str());
        }
    }

    if is_request {
        if headers
            .get(TE)
            .map(|te_header| te_header != "trailers")
            .unwrap_or(false)
        {
            warn!("TE headers not set to \"trailers\" are illegal in HTTP/2 requests");
            headers.remove(TE);
        }
    } else if headers.remove(TE).is_some() {
        warn!("TE headers illegal in HTTP/2 responses");
    }

    if let Some(header) = headers.remove(CONNECTION) {
        warn!(
            "Connection header illegal in HTTP/2: {}",
            CONNECTION.as_str()
        );
        let header_contents = header.to_str().unwrap();

        // A `Connection` header may have a comma-separated list of names of other headers that
        // are meant for only this specific connection.
        //
        // Iterate these names and remove them as headers. Connection-specific headers are
        // forbidden in HTTP2, as that information has been moved into frame types of the h2
        // protocol.
        for name in header_contents.split(',') {
            let name = name.trim();
            headers.remove(name);
        }
    }
}

// body adapters used by both Client and Server

pin_project! {
    struct PipeToSendStream<S>
    where
        S: HttpBody,
    {
        body_tx: SendStream<SendBuf<S::Data>>,
        data_done: bool,
        #[pin]
        stream: S,
    }
}

impl<S> PipeToSendStream<S>
where
    S: HttpBody,
{
    fn new(stream: S, tx: SendStream<SendBuf<S::Data>>) -> PipeToSendStream<S> {
        PipeToSendStream {
            body_tx: tx,
            data_done: false,
            stream,
        }
    }
}

impl<S> Future for PipeToSendStream<S>
where
    S: HttpBody,
    S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
    type Output = crate::Result<()>;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let mut me = self.project();
        loop {
            if !*me.data_done {
                // we don't have the next chunk of data yet, so just reserve 1 byte to make
                // sure there's some capacity available. h2 will handle the capacity management
                // for the actual body chunk.
                me.body_tx.reserve_capacity(1);

                if me.body_tx.capacity() == 0 {
                    loop {
                        match ready!(me.body_tx.poll_capacity(cx)) {
                            Some(Ok(0)) => {}
                            Some(Ok(_)) => break,
                            Some(Err(e)) => {
                                return Poll::Ready(Err(crate::Error::new_body_write(e)))
                            }
                            None => {
                                // None means the stream is no longer in a
                                // streaming state, we either finished it
                                // somehow, or the remote reset us.
                                return Poll::Ready(Err(crate::Error::new_body_write(
                                    "send stream capacity unexpectedly closed",
                                )));
                            }
                        }
                    }
                } else if let Poll::Ready(reason) = me
                    .body_tx
                    .poll_reset(cx)
                    .map_err(crate::Error::new_body_write)?
                {
                    debug!("stream received RST_STREAM: {:?}", reason);
                    return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
                        reason,
                    ))));
                }

                match ready!(me.stream.as_mut().poll_data(cx)) {
                    Some(Ok(chunk)) => {
                        let is_eos = me.stream.is_end_stream();
                        trace!(
                            "send body chunk: {} bytes, eos={}",
                            chunk.remaining(),
                            is_eos,
                        );

                        let buf = SendBuf::Buf(chunk);
                        me.body_tx
                            .send_data(buf, is_eos)
                            .map_err(crate::Error::new_body_write)?;

                        if is_eos {
                            return Poll::Ready(Ok(()));
                        }
                    }
                    Some(Err(e)) => return Poll::Ready(Err(me.body_tx.on_user_err(e))),
                    None => {
                        me.body_tx.reserve_capacity(0);
                        let is_eos = me.stream.is_end_stream();
                        if is_eos {
                            return Poll::Ready(me.body_tx.send_eos_frame());
                        } else {
                            *me.data_done = true;
                            // loop again to poll_trailers
                        }
                    }
                }
            } else {
                if let Poll::Ready(reason) = me
                    .body_tx
                    .poll_reset(cx)
                    .map_err(crate::Error::new_body_write)?
                {
                    debug!("stream received RST_STREAM: {:?}", reason);
                    return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
                        reason,
                    ))));
                }

                match ready!(me.stream.poll_trailers(cx)) {
                    Ok(Some(trailers)) => {
                        me.body_tx
                            .send_trailers(trailers)
                            .map_err(crate::Error::new_body_write)?;
                        return Poll::Ready(Ok(()));
                    }
                    Ok(None) => {
                        // There were no trailers, so send an empty DATA frame...
                        return Poll::Ready(me.body_tx.send_eos_frame());
                    }
                    Err(e) => return Poll::Ready(Err(me.body_tx.on_user_err(e))),
                }
            }
        }
    }
}

trait SendStreamExt {
    fn on_user_err<E>(&mut self, err: E) -> crate::Error
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>;
    fn send_eos_frame(&mut self) -> crate::Result<()>;
}

impl<B: Buf> SendStreamExt for SendStream<SendBuf<B>> {
    fn on_user_err<E>(&mut self, err: E) -> crate::Error
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>,
    {
        let err = crate::Error::new_user_body(err);
        debug!("send body user stream error: {}", err);
        self.send_reset(err.h2_reason());
        err
    }

    fn send_eos_frame(&mut self) -> crate::Result<()> {
        trace!("send body eos");
        self.send_data(SendBuf::None, true)
            .map_err(crate::Error::new_body_write)
    }
}

#[repr(usize)]
enum SendBuf<B> {
    Buf(B),
    Cursor(Cursor<Box<[u8]>>),
    None,
}

impl<B: Buf> Buf for SendBuf<B> {
    #[inline]
    fn remaining(&self) -> usize {
        match *self {
            Self::Buf(ref b) => b.remaining(),
            Self::Cursor(ref c) => Buf::remaining(c),
            Self::None => 0,
        }
    }

    #[inline]
    fn chunk(&self) -> &[u8] {
        match *self {
            Self::Buf(ref b) => b.chunk(),
            Self::Cursor(ref c) => c.chunk(),
            Self::None => &[],
        }
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        match *self {
            Self::Buf(ref mut b) => b.advance(cnt),
            Self::Cursor(ref mut c) => c.advance(cnt),
            Self::None => {}
        }
    }

    fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
        match *self {
            Self::Buf(ref b) => b.chunks_vectored(dst),
            Self::Cursor(ref c) => c.chunks_vectored(dst),
            Self::None => 0,
        }
    }
}

struct H2Upgraded<B>
where
    B: Buf,
{
    ping: Recorder,
    send_stream: UpgradedSendStream<B>,
    recv_stream: RecvStream,
    buf: Bytes,
}

impl<B> AsyncRead for H2Upgraded<B>
where
    B: Buf,
{
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        read_buf: &mut ReadBuf<'_>,
    ) -> Poll<Result<(), io::Error>> {
        if self.buf.is_empty() {
            self.buf = loop {
                match ready!(self.recv_stream.poll_data(cx)) {
                    None => return Poll::Ready(Ok(())),
                    Some(Ok(buf)) if buf.is_empty() && !self.recv_stream.is_end_stream() => {
                        continue
                    }
                    Some(Ok(buf)) => {
                        self.ping.record_data(buf.len());
                        break buf;
                    }
                    Some(Err(e)) => {
                        return Poll::Ready(Err(h2_to_io_error(e)));
                    }
                }
            };
        }
        let cnt = std::cmp::min(self.buf.len(), read_buf.remaining());
        read_buf.put_slice(&self.buf[..cnt]);
        self.buf.advance(cnt);
        let _ = self.recv_stream.flow_control().release_capacity(cnt);
        Poll::Ready(Ok(()))
    }
}

impl<B> AsyncWrite for H2Upgraded<B>
where
    B: Buf,
{
    fn poll_write(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<Result<usize, io::Error>> {
        if let Poll::Ready(reset) = self.send_stream.poll_reset(cx) {
            return Poll::Ready(Err(h2_to_io_error(match reset {
                Ok(reason) => reason.into(),
                Err(e) => e,
            })));
        }
        if buf.is_empty() {
            return Poll::Ready(Ok(0));
        }
        self.send_stream.reserve_capacity(buf.len());
        Poll::Ready(match ready!(self.send_stream.poll_capacity(cx)) {
            None => Ok(0),
            Some(Ok(cnt)) => self.send_stream.write(&buf[..cnt], false).map(|()| cnt),
            Some(Err(e)) => Err(h2_to_io_error(e)),
        })
    }

    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
        Poll::Ready(Ok(()))
    }

    fn poll_shutdown(
        mut self: Pin<&mut Self>,
        _cx: &mut Context<'_>,
    ) -> Poll<Result<(), io::Error>> {
        Poll::Ready(self.send_stream.write(&[], true))
    }
}

fn h2_to_io_error(e: h2::Error) -> io::Error {
    if e.is_io() {
        e.into_io().unwrap()
    } else {
        io::Error::new(io::ErrorKind::Other, e)
    }
}

struct UpgradedSendStream<B>(SendStream<SendBuf<Neutered<B>>>);

impl<B> UpgradedSendStream<B>
where
    B: Buf,
{
    unsafe fn new(inner: SendStream<SendBuf<B>>) -> Self {
        assert_eq!(mem::size_of::<B>(), mem::size_of::<Neutered<B>>());
        Self(mem::transmute(inner))
    }

    fn reserve_capacity(&mut self, cnt: usize) {
        unsafe { self.as_inner_unchecked().reserve_capacity(cnt) }
    }

    fn poll_capacity(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<usize, h2::Error>>> {
        unsafe { self.as_inner_unchecked().poll_capacity(cx) }
    }

    fn poll_reset(&mut self, cx: &mut Context<'_>) -> Poll<Result<h2::Reason, h2::Error>> {
        unsafe { self.as_inner_unchecked().poll_reset(cx) }
    }

    fn write(&mut self, buf: &[u8], end_of_stream: bool) -> Result<(), io::Error> {
        let send_buf = SendBuf::Cursor(Cursor::new(buf.into()));
        unsafe {
            self.as_inner_unchecked()
                .send_data(send_buf, end_of_stream)
                .map_err(h2_to_io_error)
        }
    }

    unsafe fn as_inner_unchecked(&mut self) -> &mut SendStream<SendBuf<B>> {
        &mut *(&mut self.0 as *mut _ as *mut _)
    }
}

#[repr(transparent)]
struct Neutered<B> {
    _inner: B,
    impossible: Impossible,
}

enum Impossible {}

unsafe impl<B> Send for Neutered<B> {}

impl<B> Buf for Neutered<B> {
    fn remaining(&self) -> usize {
        match self.impossible {}
    }

    fn chunk(&self) -> &[u8] {
        match self.impossible {}
    }

    fn advance(&mut self, _cnt: usize) {
        match self.impossible {}
    }
}
<|endoftext|>


<commit_before>#![allow(
    clippy::boxed_local,
    clippy::ptr_arg,
    clippy::trivially_copy_pass_by_ref
)]

pub mod extra;
pub mod module;

use cxx::{CxxString, CxxVector, UniquePtr};
use std::fmt::{self, Display};

mod other {
    use cxx::kind::{Opaque, Trivial};
    use cxx::{type_id, CxxString, ExternType};

    #[repr(C)]
    pub struct D {
        pub d: u64,
    }

    #[repr(C)]
    pub struct E {
        e_str: CxxString,
        e: u64,
    }

    unsafe impl ExternType for D {
        type Id = type_id!("tests::D");
        type Kind = Trivial;
    }

    unsafe impl ExternType for E {
        type Id = type_id!("tests::E");
        type Kind = Opaque;
    }
}

#[cxx::bridge(namespace = tests)]
pub mod ffi {
    #[derive(Clone)]
    struct Shared {
        z: usize,
    }

    enum Enum {
        AVal,
        BVal = 2020,
        CVal,
    }

    extern "C" {
        include!("tests/ffi/tests.h");

        type C;

        fn c_return_primitive() -> usize;
        fn c_return_shared() -> Shared;
        fn c_return_box() -> Box<R>;
        fn c_return_unique_ptr() -> UniquePtr<C>;
        fn c_return_ref(shared: &Shared) -> &usize;
        fn c_return_mut(shared: &mut Shared) -> &mut usize;
        fn c_return_str(shared: &Shared) -> &str;
        fn c_return_sliceu8(shared: &Shared) -> &[u8];
        fn c_return_rust_string() -> String;
        fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
        fn c_return_unique_ptr_vector_f64() -> UniquePtr<CxxVector<f64>>;
        fn c_return_unique_ptr_vector_string() -> UniquePtr<CxxVector<CxxString>>;
        fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
        fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
        fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
        fn c_return_mut_vector(c: &mut C) -> &mut CxxVector<u8>;
        fn c_return_rust_vec() -> Vec<u8>;
        fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
        fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec<u8>;
        fn c_return_rust_vec_string() -> Vec<String>;
        fn c_return_identity(_: usize) -> usize;
        fn c_return_sum(_: usize, _: usize) -> usize;
        fn c_return_enum(n: u16) -> Enum;

        fn c_take_primitive(n: usize);
        fn c_take_shared(shared: Shared);
        fn c_take_box(r: Box<R>);
        fn c_take_ref_r(r: &R);
        fn c_take_ref_c(c: &C);
        fn c_take_str(s: &str);
        fn c_take_sliceu8(s: &[u8]);
        fn c_take_rust_string(s: String);
        fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
        fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);
        fn c_take_unique_ptr_vector_string(v: UniquePtr<CxxVector<CxxString>>);
        fn c_take_unique_ptr_vector_shared(v: UniquePtr<CxxVector<Shared>>);
        fn c_take_ref_vector(v: &CxxVector<u8>);
        fn c_take_rust_vec(v: Vec<u8>);
        fn c_take_rust_vec_shared(v: Vec<Shared>);
        fn c_take_rust_vec_string(v: Vec<String>);
        fn c_take_rust_vec_index(v: Vec<u8>);
        fn c_take_rust_vec_shared_index(v: Vec<Shared>);
        fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
        fn c_take_ref_rust_vec(v: &Vec<u8>);
        fn c_take_ref_rust_vec_string(v: &Vec<String>);
        fn c_take_ref_rust_vec_index(v: &Vec<u8>);
        fn c_take_ref_rust_vec_copy(v: &Vec<u8>);
        /*
        // https://github.com/dtolnay/cxx/issues/232
        fn c_take_callback(callback: fn(String) -> usize);
        */
        fn c_take_enum(e: Enum);

        fn c_try_return_void() -> Result<()>;
        fn c_try_return_primitive() -> Result<usize>;
        fn c_fail_return_primitive() -> Result<usize>;
        fn c_try_return_box() -> Result<Box<R>>;
        fn c_try_return_ref(s: &String) -> Result<&String>;
        fn c_try_return_str(s: &str) -> Result<&str>;
        fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
        fn c_try_return_rust_string() -> Result<String>;
        fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
        fn c_try_return_rust_vec() -> Result<Vec<u8>>;
        fn c_try_return_rust_vec_string() -> Result<Vec<String>>;
        fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;

        fn get(self: &C) -> usize;
        fn set(self: &mut C, n: usize) -> usize;
        fn get2(&self) -> usize;
        fn set2(&mut self, n: usize) -> usize;
        fn set_succeed(&mut self, n: usize) -> Result<usize>;
        fn get_fail(&mut self) -> Result<usize>;

        #[rust_name = "i32_overloaded_method"]
        fn cOverloadedMethod(&self, x: i32) -> String;
        #[rust_name = "str_overloaded_method"]
        fn cOverloadedMethod(&self, x: &str) -> String;
        #[rust_name = "i32_overloaded_function"]
        fn cOverloadedFunction(x: i32) -> String;
        #[rust_name = "str_overloaded_function"]
        fn cOverloadedFunction(x: &str) -> String;
    }

    extern "C" {
        type COwnedEnum;
    }

    #[repr(u32)]
    enum COwnedEnum {
        CVal1,
        CVal2,
    }

    extern "Rust" {
        type R;
        type R2;

        fn r_return_primitive() -> usize;
        fn r_return_shared() -> Shared;
        fn r_return_box() -> Box<R>;
        fn r_return_unique_ptr() -> UniquePtr<C>;
        fn r_return_ref(shared: &Shared) -> &usize;
        fn r_return_mut(shared: &mut Shared) -> &mut usize;
        fn r_return_str(shared: &Shared) -> &str;
        fn r_return_rust_string() -> String;
        fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn r_return_rust_vec() -> Vec<u8>;
        fn r_return_rust_vec_string() -> Vec<String>;
        fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
        fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec<u8>;
        fn r_return_identity(_: usize) -> usize;
        fn r_return_sum(_: usize, _: usize) -> usize;
        fn r_return_enum(n: u32) -> Enum;

        fn r_take_primitive(n: usize);
        fn r_take_shared(shared: Shared);
        fn r_take_box(r: Box<R>);
        fn r_take_unique_ptr(c: UniquePtr<C>);
        fn r_take_ref_r(r: &R);
        fn r_take_ref_c(c: &C);
        fn r_take_str(s: &str);
        fn r_take_sliceu8(s: &[u8]);
        fn r_take_rust_string(s: String);
        fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn r_take_ref_vector(v: &CxxVector<u8>);
        fn r_take_ref_empty_vector(v: &CxxVector<u64>);
        fn r_take_rust_vec(v: Vec<u8>);
        fn r_take_rust_vec_string(v: Vec<String>);
        fn r_take_ref_rust_vec(v: &Vec<u8>);
        fn r_take_ref_rust_vec_string(v: &Vec<String>);
        fn r_take_enum(e: Enum);

        fn r_try_return_void() -> Result<()>;
        fn r_try_return_primitive() -> Result<usize>;
        fn r_try_return_box() -> Result<Box<R>>;
        fn r_fail_return_primitive() -> Result<usize>;

        fn r_return_r2(n: usize) -> Box<R2>;
        fn get(self: &R2) -> usize;
        fn set(self: &mut R2, n: usize) -> usize;

        #[cxx_name = "rAliasedFunction"]
        fn r_aliased_function(x: i32) -> String;
    }
}

pub type R = usize;

pub struct R2(usize);

impl R2 {
    fn get(&self) -> usize {
        self.0
    }

    fn set(&mut self, n: usize) -> usize {
        self.0 = n;
        n
    }
}

#[derive(Debug)]
struct Error;

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("rust error")
    }
}

fn r_return_primitive() -> usize {
    2020
}

fn r_return_shared() -> ffi::Shared {
    ffi::Shared { z: 2020 }
}

fn r_return_box() -> Box<R> {
    Box::new(2020)
}

fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr() -> *mut ffi::C;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
}

fn r_return_ref(shared: &ffi::Shared) -> &usize {
    &shared.z
}

fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize {
    &mut shared.z
}

fn r_return_str(shared: &ffi::Shared) -> &str {
    let _ = shared;
    "2020"
}

fn r_return_rust_string() -> String {
    "2020".to_owned()
}

fn r_return_unique_ptr_string() -> UniquePtr<CxxString> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr_string() -> *mut CxxString;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr_string()) }
}

fn r_return_rust_vec() -> Vec<u8> {
    Vec::new()
}

fn r_return_rust_vec_string() -> Vec<String> {
    Vec::new()
}

fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
    let _ = shared;
    unimplemented!()
}

fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec<u8> {
    let _ = shared;
    unimplemented!()
}

fn r_return_identity(n: usize) -> usize {
    n
}

fn r_return_sum(n1: usize, n2: usize) -> usize {
    n1 + n2
}

fn r_return_enum(n: u32) -> ffi::Enum {
    if n == 0 {
        ffi::Enum::AVal
    } else if n <= 2020 {
        ffi::Enum::BVal
    } else {
        ffi::Enum::CVal
    }
}

fn r_take_primitive(n: usize) {
    assert_eq!(n, 2020);
}

fn r_take_shared(shared: ffi::Shared) {
    assert_eq!(shared.z, 2020);
}

fn r_take_box(r: Box<R>) {
    let _ = r;
}

fn r_take_unique_ptr(c: UniquePtr<ffi::C>) {
    let _ = c;
}

fn r_take_ref_r(r: &R) {
    let _ = r;
}

fn r_take_ref_c(c: &ffi::C) {
    let _ = c;
}

fn r_take_str(s: &str) {
    assert_eq!(s, "2020");
}

fn r_take_rust_string(s: String) {
    assert_eq!(s, "2020");
}

fn r_take_sliceu8(s: &[u8]) {
    assert_eq!(s.len(), 5);
    assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0");
}

fn r_take_unique_ptr_string(s: UniquePtr<CxxString>) {
    assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020");
}

fn r_take_ref_vector(v: &CxxVector<u8>) {
    let slice = v.as_slice();
    assert_eq!(slice, [20, 2, 0]);
}

fn r_take_ref_empty_vector(v: &CxxVector<u64>) {
    assert!(v.as_slice().is_empty());
    assert!(v.is_empty());
}

fn r_take_rust_vec(v: Vec<u8>) {
    let _ = v;
}

fn r_take_rust_vec_string(v: Vec<String>) {
    let _ = v;
}

fn r_take_ref_rust_vec(v: &Vec<u8>) {
    let _ = v;
}

fn r_take_ref_rust_vec_string(v: &Vec<String>) {
    let _ = v;
}

fn r_take_enum(e: ffi::Enum) {
    let _ = e;
}

fn r_try_return_void() -> Result<(), Error> {
    Ok(())
}

fn r_try_return_primitive() -> Result<usize, Error> {
    Ok(2020)
}

fn r_try_return_box() -> Result<Box<R>, Error> {
    Ok(Box::new(2020))
}

fn r_fail_return_primitive() -> Result<usize, Error> {
    Err(Error)
}

fn r_return_r2(n: usize) -> Box<R2> {
    Box::new(R2(n))
}

fn r_aliased_function(x: i32) -> String {
    x.to_string()
}
<commit_msg>Match layout of C++ tests::E and struct E in Rust<commit_after>#![allow(
    clippy::boxed_local,
    clippy::ptr_arg,
    clippy::trivially_copy_pass_by_ref
)]

pub mod extra;
pub mod module;

use cxx::{CxxString, CxxVector, UniquePtr};
use std::fmt::{self, Display};

mod other {
    use cxx::kind::{Opaque, Trivial};
    use cxx::{type_id, CxxString, ExternType};

    #[repr(C)]
    pub struct D {
        pub d: u64,
    }

    #[repr(C)]
    pub struct E {
        e: u64,
        e_str: CxxString,
    }

    unsafe impl ExternType for D {
        type Id = type_id!("tests::D");
        type Kind = Trivial;
    }

    unsafe impl ExternType for E {
        type Id = type_id!("tests::E");
        type Kind = Opaque;
    }
}

#[cxx::bridge(namespace = tests)]
pub mod ffi {
    #[derive(Clone)]
    struct Shared {
        z: usize,
    }

    enum Enum {
        AVal,
        BVal = 2020,
        CVal,
    }

    extern "C" {
        include!("tests/ffi/tests.h");

        type C;

        fn c_return_primitive() -> usize;
        fn c_return_shared() -> Shared;
        fn c_return_box() -> Box<R>;
        fn c_return_unique_ptr() -> UniquePtr<C>;
        fn c_return_ref(shared: &Shared) -> &usize;
        fn c_return_mut(shared: &mut Shared) -> &mut usize;
        fn c_return_str(shared: &Shared) -> &str;
        fn c_return_sliceu8(shared: &Shared) -> &[u8];
        fn c_return_rust_string() -> String;
        fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
        fn c_return_unique_ptr_vector_f64() -> UniquePtr<CxxVector<f64>>;
        fn c_return_unique_ptr_vector_string() -> UniquePtr<CxxVector<CxxString>>;
        fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
        fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
        fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
        fn c_return_mut_vector(c: &mut C) -> &mut CxxVector<u8>;
        fn c_return_rust_vec() -> Vec<u8>;
        fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
        fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec<u8>;
        fn c_return_rust_vec_string() -> Vec<String>;
        fn c_return_identity(_: usize) -> usize;
        fn c_return_sum(_: usize, _: usize) -> usize;
        fn c_return_enum(n: u16) -> Enum;

        fn c_take_primitive(n: usize);
        fn c_take_shared(shared: Shared);
        fn c_take_box(r: Box<R>);
        fn c_take_ref_r(r: &R);
        fn c_take_ref_c(c: &C);
        fn c_take_str(s: &str);
        fn c_take_sliceu8(s: &[u8]);
        fn c_take_rust_string(s: String);
        fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
        fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);
        fn c_take_unique_ptr_vector_string(v: UniquePtr<CxxVector<CxxString>>);
        fn c_take_unique_ptr_vector_shared(v: UniquePtr<CxxVector<Shared>>);
        fn c_take_ref_vector(v: &CxxVector<u8>);
        fn c_take_rust_vec(v: Vec<u8>);
        fn c_take_rust_vec_shared(v: Vec<Shared>);
        fn c_take_rust_vec_string(v: Vec<String>);
        fn c_take_rust_vec_index(v: Vec<u8>);
        fn c_take_rust_vec_shared_index(v: Vec<Shared>);
        fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
        fn c_take_ref_rust_vec(v: &Vec<u8>);
        fn c_take_ref_rust_vec_string(v: &Vec<String>);
        fn c_take_ref_rust_vec_index(v: &Vec<u8>);
        fn c_take_ref_rust_vec_copy(v: &Vec<u8>);
        /*
        // https://github.com/dtolnay/cxx/issues/232
        fn c_take_callback(callback: fn(String) -> usize);
        */
        fn c_take_enum(e: Enum);

        fn c_try_return_void() -> Result<()>;
        fn c_try_return_primitive() -> Result<usize>;
        fn c_fail_return_primitive() -> Result<usize>;
        fn c_try_return_box() -> Result<Box<R>>;
        fn c_try_return_ref(s: &String) -> Result<&String>;
        fn c_try_return_str(s: &str) -> Result<&str>;
        fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
        fn c_try_return_rust_string() -> Result<String>;
        fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
        fn c_try_return_rust_vec() -> Result<Vec<u8>>;
        fn c_try_return_rust_vec_string() -> Result<Vec<String>>;
        fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;

        fn get(self: &C) -> usize;
        fn set(self: &mut C, n: usize) -> usize;
        fn get2(&self) -> usize;
        fn set2(&mut self, n: usize) -> usize;
        fn set_succeed(&mut self, n: usize) -> Result<usize>;
        fn get_fail(&mut self) -> Result<usize>;

        #[rust_name = "i32_overloaded_method"]
        fn cOverloadedMethod(&self, x: i32) -> String;
        #[rust_name = "str_overloaded_method"]
        fn cOverloadedMethod(&self, x: &str) -> String;
        #[rust_name = "i32_overloaded_function"]
        fn cOverloadedFunction(x: i32) -> String;
        #[rust_name = "str_overloaded_function"]
        fn cOverloadedFunction(x: &str) -> String;
    }

    extern "C" {
        type COwnedEnum;
    }

    #[repr(u32)]
    enum COwnedEnum {
        CVal1,
        CVal2,
    }

    extern "Rust" {
        type R;
        type R2;

        fn r_return_primitive() -> usize;
        fn r_return_shared() -> Shared;
        fn r_return_box() -> Box<R>;
        fn r_return_unique_ptr() -> UniquePtr<C>;
        fn r_return_ref(shared: &Shared) -> &usize;
        fn r_return_mut(shared: &mut Shared) -> &mut usize;
        fn r_return_str(shared: &Shared) -> &str;
        fn r_return_rust_string() -> String;
        fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
        fn r_return_rust_vec() -> Vec<u8>;
        fn r_return_rust_vec_string() -> Vec<String>;
        fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
        fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec<u8>;
        fn r_return_identity(_: usize) -> usize;
        fn r_return_sum(_: usize, _: usize) -> usize;
        fn r_return_enum(n: u32) -> Enum;

        fn r_take_primitive(n: usize);
        fn r_take_shared(shared: Shared);
        fn r_take_box(r: Box<R>);
        fn r_take_unique_ptr(c: UniquePtr<C>);
        fn r_take_ref_r(r: &R);
        fn r_take_ref_c(c: &C);
        fn r_take_str(s: &str);
        fn r_take_sliceu8(s: &[u8]);
        fn r_take_rust_string(s: String);
        fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
        fn r_take_ref_vector(v: &CxxVector<u8>);
        fn r_take_ref_empty_vector(v: &CxxVector<u64>);
        fn r_take_rust_vec(v: Vec<u8>);
        fn r_take_rust_vec_string(v: Vec<String>);
        fn r_take_ref_rust_vec(v: &Vec<u8>);
        fn r_take_ref_rust_vec_string(v: &Vec<String>);
        fn r_take_enum(e: Enum);

        fn r_try_return_void() -> Result<()>;
        fn r_try_return_primitive() -> Result<usize>;
        fn r_try_return_box() -> Result<Box<R>>;
        fn r_fail_return_primitive() -> Result<usize>;

        fn r_return_r2(n: usize) -> Box<R2>;
        fn get(self: &R2) -> usize;
        fn set(self: &mut R2, n: usize) -> usize;

        #[cxx_name = "rAliasedFunction"]
        fn r_aliased_function(x: i32) -> String;
    }
}

pub type R = usize;

pub struct R2(usize);

impl R2 {
    fn get(&self) -> usize {
        self.0
    }

    fn set(&mut self, n: usize) -> usize {
        self.0 = n;
        n
    }
}

#[derive(Debug)]
struct Error;

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("rust error")
    }
}

fn r_return_primitive() -> usize {
    2020
}

fn r_return_shared() -> ffi::Shared {
    ffi::Shared { z: 2020 }
}

fn r_return_box() -> Box<R> {
    Box::new(2020)
}

fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr() -> *mut ffi::C;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
}

fn r_return_ref(shared: &ffi::Shared) -> &usize {
    &shared.z
}

fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize {
    &mut shared.z
}

fn r_return_str(shared: &ffi::Shared) -> &str {
    let _ = shared;
    "2020"
}

fn r_return_rust_string() -> String {
    "2020".to_owned()
}

fn r_return_unique_ptr_string() -> UniquePtr<CxxString> {
    extern "C" {
        fn cxx_test_suite_get_unique_ptr_string() -> *mut CxxString;
    }
    unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr_string()) }
}

fn r_return_rust_vec() -> Vec<u8> {
    Vec::new()
}

fn r_return_rust_vec_string() -> Vec<String> {
    Vec::new()
}

fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
    let _ = shared;
    unimplemented!()
}

fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec<u8> {
    let _ = shared;
    unimplemented!()
}

fn r_return_identity(n: usize) -> usize {
    n
}

fn r_return_sum(n1: usize, n2: usize) -> usize {
    n1 + n2
}

fn r_return_enum(n: u32) -> ffi::Enum {
    if n == 0 {
        ffi::Enum::AVal
    } else if n <= 2020 {
        ffi::Enum::BVal
    } else {
        ffi::Enum::CVal
    }
}

fn r_take_primitive(n: usize) {
    assert_eq!(n, 2020);
}

fn r_take_shared(shared: ffi::Shared) {
    assert_eq!(shared.z, 2020);
}

fn r_take_box(r: Box<R>) {
    let _ = r;
}

fn r_take_unique_ptr(c: UniquePtr<ffi::C>) {
    let _ = c;
}

fn r_take_ref_r(r: &R) {
    let _ = r;
}

fn r_take_ref_c(c: &ffi::C) {
    let _ = c;
}

fn r_take_str(s: &str) {
    assert_eq!(s, "2020");
}

fn r_take_rust_string(s: String) {
    assert_eq!(s, "2020");
}

fn r_take_sliceu8(s: &[u8]) {
    assert_eq!(s.len(), 5);
    assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0");
}

fn r_take_unique_ptr_string(s: UniquePtr<CxxString>) {
    assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020");
}

fn r_take_ref_vector(v: &CxxVector<u8>) {
    let slice = v.as_slice();
    assert_eq!(slice, [20, 2, 0]);
}

fn r_take_ref_empty_vector(v: &CxxVector<u64>) {
    assert!(v.as_slice().is_empty());
    assert!(v.is_empty());
}

fn r_take_rust_vec(v: Vec<u8>) {
    let _ = v;
}

fn r_take_rust_vec_string(v: Vec<String>) {
    let _ = v;
}

fn r_take_ref_rust_vec(v: &Vec<u8>) {
    let _ = v;
}

fn r_take_ref_rust_vec_string(v: &Vec<String>) {
    let _ = v;
}

fn r_take_enum(e: ffi::Enum) {
    let _ = e;
}

fn r_try_return_void() -> Result<(), Error> {
    Ok(())
}

fn r_try_return_primitive() -> Result<usize, Error> {
    Ok(2020)
}

fn r_try_return_box() -> Result<Box<R>, Error> {
    Ok(Box::new(2020))
}

fn r_fail_return_primitive() -> Result<usize, Error> {
    Err(Error)
}

fn r_return_r2(n: usize) -> Box<R2> {
    Box::new(R2(n))
}

fn r_aliased_function(x: i32) -> String {
    x.to_string()
}
<|endoftext|>


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

use std::collections::RingBuf;

use gc_object::GCObjectRef;
use line_allocator::LineAllocator;

pub struct RCCollector {
    old_root_buffer: RingBuf<GCObjectRef>,
    decrement_buffer: RingBuf<GCObjectRef>,
    modified_buffer: RingBuf<GCObjectRef>,
}

impl RCCollector {
    pub fn new() -> RCCollector {
        return RCCollector {
            old_root_buffer: RingBuf::new(),
            decrement_buffer: RingBuf::new(),
            modified_buffer: RingBuf::new(),
        };
    }

    pub fn collect(&mut self, line_allocator: &mut LineAllocator,
                   roots: &[GCObjectRef]) {
        debug!("Start RC collection");
        self.process_old_roots();
        self.process_current_roots(line_allocator, roots);
        self.process_mod_buffer(line_allocator);
        self.process_decrement_buffer(line_allocator);
        debug!("Complete collection");
    }

    pub fn write_barrier(&mut self, object: GCObjectRef) {
        debug!("Write barrier on object {}", object);
        self.modified(object);
        for child in unsafe{ (*object).children() }.into_iter() {
            self.decrement(child);
        }
        unsafe{ (*object).set_logged(true); }
    }
}

impl RCCollector {
    fn modified(&mut self, object: GCObjectRef) {
        debug!("Push object {} into mod buffer", object);
        self.modified_buffer.push_back(object);
    }

    fn decrement(&mut self, object: GCObjectRef) {
        debug!("Push object {} into dec buffer", object);
        self.decrement_buffer.push_back(object);
    }

    fn increment(&mut self, line_allocator: &mut LineAllocator, object: GCObjectRef) {
        debug!("Increment object {}", object);
        if unsafe{ (*object).increment() } {
            line_allocator.increment_lines(object);
            self.modified(object);
        }
    }

    fn process_old_roots(&mut self) {
        debug!("Process old roots (size {})", self.old_root_buffer.len());
        self.decrement_buffer.extend(self.old_root_buffer.drain());
    }

    fn process_current_roots(&mut self, line_allocator: &mut LineAllocator,
                             roots: &[GCObjectRef]) {
        debug!("Process current roots (size {})", roots.len());
        for root in roots.iter().map(|o| *o) {
            debug!("Process root object: {}", root);
            self.increment(line_allocator, root);
            self.old_root_buffer.push_back(root);
        }
    }

    fn process_mod_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process mod buffer (size {})", self.modified_buffer.len());
        while let Some(object) = self.modified_buffer.pop_front() {
            debug!("Process object {} in mod buffer", object);
            unsafe { (*object).set_logged(false); }
            for child in unsafe{ (*object).children() }.into_iter() {
                self.increment(line_allocator, child);
            }
        }
    }

    fn process_decrement_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process dec buffer (size {})", self.decrement_buffer.len());
        while let Some(object) =  self.decrement_buffer.pop_front() {
            debug!("Process object {} in dec buffer", object);
            if unsafe{ (*object).decrement() } {
                line_allocator.unset_gc_object(object);
                line_allocator.decrement_lines(object);
                for child in unsafe{ (*object).children() }.into_iter() {
                    self.decrement(child);
                }
                valgrind_freelike!(object);
            }
        }
    }
}
<commit_msg>Implement opportunistic proactive defragmentation in RCColletor<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

use std::collections::RingBuf;

use gc_object::GCObjectRef;
use line_allocator::LineAllocator;

pub struct RCCollector {
    old_root_buffer: RingBuf<GCObjectRef>,
    decrement_buffer: RingBuf<GCObjectRef>,
    modified_buffer: RingBuf<GCObjectRef>,
}

impl RCCollector {
    pub fn new() -> RCCollector {
        return RCCollector {
            old_root_buffer: RingBuf::new(),
            decrement_buffer: RingBuf::new(),
            modified_buffer: RingBuf::new(),
        };
    }

    pub fn collect(&mut self, line_allocator: &mut LineAllocator,
                   roots: &[GCObjectRef]) {
        debug!("Start RC collection");
        self.process_old_roots();
        self.process_current_roots(line_allocator, roots);
        self.process_mod_buffer(line_allocator);
        self.process_decrement_buffer(line_allocator);
        debug!("Complete collection");
    }

    pub fn write_barrier(&mut self, object: GCObjectRef) {
        debug!("Write barrier on object {}", object);
        self.modified(object);
        for child in unsafe{ (*object).children() }.into_iter() {
            self.decrement(child);
        }
        unsafe{ (*object).set_logged(true); }
    }
}

impl RCCollector {
    fn modified(&mut self, object: GCObjectRef) {
        debug!("Push object {} into mod buffer", object);
        self.modified_buffer.push_back(object);
    }

    fn decrement(&mut self, object: GCObjectRef) {
        debug!("Push object {} into dec buffer", object);
        self.decrement_buffer.push_back(object);
    }

    fn increment(&mut self, line_allocator: &mut LineAllocator,
                 object: GCObjectRef, try_evacuate: bool) -> Option<GCObjectRef> {
        debug!("Increment object {}", object);
        if unsafe{ (*object).increment() } {
            if try_evacuate {
                if let Some(new_object) = line_allocator.maybe_evacuate(object) {
                    debug!("Evacuated object {} to {}", object, new_object);
                    line_allocator.decrement_lines(object);
                    line_allocator.increment_lines(new_object);
                    self.modified(new_object);
                    return Some(new_object);
                }
            }
            line_allocator.increment_lines(object);
            self.modified(object);
        }
        return None;
    }

    fn process_old_roots(&mut self) {
        debug!("Process old roots (size {})", self.old_root_buffer.len());
        self.decrement_buffer.extend(self.old_root_buffer.drain());
    }

    fn process_current_roots(&mut self, line_allocator: &mut LineAllocator,
                             roots: &[GCObjectRef]) {
        debug!("Process current roots (size {})", roots.len());
        for root in roots.iter().map(|o| *o) {
            debug!("Process root object: {}", root);
            self.increment(line_allocator, root, false);
            self.old_root_buffer.push_back(root);
        }
    }

    fn process_mod_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process mod buffer (size {})", self.modified_buffer.len());
        while let Some(object) = self.modified_buffer.pop_front() {
            debug!("Process object {} in mod buffer", object);
            unsafe { (*object).set_logged(false); }
            let children = unsafe{ (*object).children() };
            for (num, child) in children.into_iter().enumerate() {
                if let Some(new_child) = unsafe{ (*child).is_forwarded() } {
                    debug!("Child {} is forwarded to {}", child, new_child);
                    unsafe{ (*object).set_child(num, new_child); }
                    self.increment(line_allocator, child, false);
                } else {
                    if let Some(new_child) = self.increment(line_allocator,
                                                            child, true) {
                        unsafe{ (*object).set_child(num, new_child); }
                    }
                }
            }
        }
    }

    fn process_decrement_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process dec buffer (size {})", self.decrement_buffer.len());
        while let Some(object) =  self.decrement_buffer.pop_front() {
            debug!("Process object {} in dec buffer", object);
            if unsafe{ (*object).decrement() } {
                line_allocator.unset_gc_object(object);
                line_allocator.decrement_lines(object);
                for child in unsafe{ (*object).children() }.into_iter() {
                    self.decrement(child);
                }
                valgrind_freelike!(object);
            }
        }
    }
}
<|endoftext|>