<commit_before>use conduit::Request;
use curl::easy::Easy;
use flate2::read::GzDecoder;
use krate::Crate;
use s3;
use semver;
use tar;
use util::{human, internal, CargoResult, ChainError};
use util::{hash, LimitErrorReader, read_le_u32};

use app::{App, RequestApp};
use std::sync::Arc;
use std::fs::{self, File};
use std::env;
use std::io::{Read, Write};

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with a proxy.
    S3 {
        bucket: s3::Bucket,
        proxy: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,

    /// For one-off scripts where creating a Config is needed, but uploading is not.
    NoOp,
}

impl Uploader {
    pub fn proxy(&self) -> Option<&str> {
        match *self {
            Uploader::S3 { ref proxy, .. } => proxy.as_ref().map(String::as_str),
            Uploader::Local | Uploader::NoOp => None,
        }
    }

    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    /// It returns `None` if the current `Uploader` is `NoOp`.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> Option<String> {
        match *self {
            Uploader::S3 { ref bucket, .. } => Some(format!(
                "https://{}/{}",
                bucket.host(),
                Uploader::crate_path(crate_name, version)
            )),
            Uploader::Local => Some(format!("/{}", Uploader::crate_path(crate_name, version))),
            Uploader::NoOp => None,
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    /// It returns `None` if the current `Uploader` is `NoOp`.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> Option<String> {
        match *self {
            Uploader::S3 { ref bucket, .. } => Some(format!(
                "https://{}/{}",
                bucket.host(),
                Uploader::readme_path(crate_name, version)
            )),
            Uploader::Local => Some(format!("/{}", Uploader::readme_path(crate_name, version))),
            Uploader::NoOp => None,
        }
    }

    /// Returns the interna path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        // No slash in front so we can use join
        format!("crates/{}/{}-{}.crate", name, name, version)
    }

    /// Returns the interna path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{}/{}-{}.html", name, name, version)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local` or `NoOp`).
    ///
    /// It returns a a tuple containing the path of the uploaded file
    /// and its checksum.
    pub fn upload(
        &self,
        mut handle: Easy,
        path: &str,
        body: &[u8],
        content_type: &str,
        content_length: u64,
    ) -> CargoResult<(Option<String>, Vec<u8>)> {
        let hash = hash(body);
        match *self {
            Uploader::S3 { ref bucket, .. } => {
                let (response, cksum) = {
                    let mut response = Vec::new();
                    {
                        let mut s3req =
                            bucket.put(&mut handle, path, body, content_type, content_length);
                        s3req
                            .write_function(|data| {
                                response.extend(data);
                                Ok(data.len())
                            })
                            .unwrap();
                        s3req.perform().chain_error(|| {
                            internal(&format_args!("failed to upload to S3: `{}`", path))
                        })?;
                    }
                    (response, hash)
                };
                if handle.response_code().unwrap() != 200 {
                    let response = String::from_utf8_lossy(&response);
                    return Err(internal(&format_args!(
                        "failed to get a 200 response from S3: {}",
                        response
                    )));
                }
                Ok((Some(String::from(path)), cksum))
            }
            Uploader::Local => {
                let filename = env::current_dir().unwrap().join("local_uploads").join(path);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                file.write_all(body)?;
                Ok((filename.to_str().map(String::from), hash))
            }
            Uploader::NoOp => Ok((None, vec![])),
        }
    }

    /// Uploads a crate and its readme. Returns the checksum of the uploaded crate
    /// file, and bombs for the uploaded crate and the uploaded readme.
    pub fn upload_crate(
        &self,
        req: &mut Request,
        krate: &Crate,
        readme: Option<String>,
        max: u64,
        max_unpack: u64,
        vers: &semver::Version,
    ) -> CargoResult<(Vec<u8>, Bomb, Bomb)> {
        let app = Arc::clone(req.app());
        let (crate_path, checksum) = {
            let path = Uploader::crate_path(&krate.name, &vers.to_string());
            let length = read_le_u32(req.body())?;
            let mut body = Vec::new();
            LimitErrorReader::new(req.body(), max).read_to_end(&mut body)?;
            verify_tarball(krate, vers, &body, max_unpack)?;
            self.upload(
                app.handle(),
                &path,
                &body,
                "application/x-tar",
                u64::from(length),
            )?
        };
        // We create the bomb for the crate file before uploading the readme so that if the
        // readme upload fails, the uploaded crate file is automatically deleted.
        let crate_bomb = Bomb {
            app: Arc::clone(&app),
            path: crate_path,
        };
        let (readme_path, _) = if let Some(rendered) = readme {
            let path = Uploader::readme_path(&krate.name, &vers.to_string());
            let length = rendered.len();
            self.upload(
                app.handle(),
                &path,
                rendered.as_bytes(),
                "text/html",
                length as u64,
            )?
        } else {
            (None, vec![])
        };
        Ok((
            checksum,
            crate_bomb,
            Bomb {
                app: Arc::clone(&app),
                path: readme_path,
            },
        ))
    }

    /// Deletes an uploaded file.
    fn delete(&self, app: Arc<App>, path: &str) -> CargoResult<()> {
        match *self {
            Uploader::S3 { ref bucket, .. } => {
                let mut handle = app.handle();
                bucket.delete(&mut handle, path).perform()?;
                Ok(())
            }
            Uploader::Local => {
                fs::remove_file(path)?;
                Ok(())
            }
            Uploader::NoOp => Ok(()),
        }
    }
}

// Can't derive Debug because of App.
#[allow(missing_debug_implementations)]
pub struct Bomb {
    app: Arc<App>,
    pub path: Option<String>,
}

impl Drop for Bomb {
    fn drop(&mut self) {
        if let Some(ref path) = self.path {
            if let Err(e) = self.app.config.uploader.delete(Arc::clone(&self.app), path) {
                println!("unable to delete {}, {:?}", path, e);
            }
        }
    }
}

fn verify_tarball(
    krate: &Crate,
    vers: &semver::Version,
    tarball: &[u8],
    max_unpack: u64
) -> CargoResult<()> {
    // All our data is currently encoded with gzip
    let decoder = GzDecoder::new(tarball)?;

    // Don't let gzip decompression go into the weeeds, apply a fixed cap after
    // which point we say the decompressed source is "too large".
    let decoder = LimitErrorReader::new(decoder, max_unpack);

    // Use this I/O object now to take a peek inside
    let mut archive = tar::Archive::new(decoder);
    let prefix = format!("{}-{}", krate.name, vers);
    for entry in archive.entries()? {
        let entry = entry.chain_error(|| {
            human("uploaded tarball is malformed or too large when decompressed")
        })?;

        // Verify that all entries actually start with `$name-$vers/`.
        // Historically Cargo didn't verify this on extraction so you could
        // upload a tarball that contains both `foo-0.1.0/` source code as well
        // as `bar-0.1.0/` source code, and this could overwrite other crates in
        // the registry!
        if !entry.path()?.starts_with(&prefix) {
            return Err(human("invalid tarball uploaded"));
        }
    }
    Ok(())
}
<commit_msg>oops rustfmt again<commit_after>use conduit::Request;
use curl::easy::Easy;
use flate2::read::GzDecoder;
use krate::Crate;
use s3;
use semver;
use tar;
use util::{human, internal, CargoResult, ChainError};
use util::{hash, LimitErrorReader, read_le_u32};

use app::{App, RequestApp};
use std::sync::Arc;
use std::fs::{self, File};
use std::env;
use std::io::{Read, Write};

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with a proxy.
    S3 {
        bucket: s3::Bucket,
        proxy: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,

    /// For one-off scripts where creating a Config is needed, but uploading is not.
    NoOp,
}

impl Uploader {
    pub fn proxy(&self) -> Option<&str> {
        match *self {
            Uploader::S3 { ref proxy, .. } => proxy.as_ref().map(String::as_str),
            Uploader::Local | Uploader::NoOp => None,
        }
    }

    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    /// It returns `None` if the current `Uploader` is `NoOp`.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> Option<String> {
        match *self {
            Uploader::S3 { ref bucket, .. } => Some(format!(
                "https://{}/{}",
                bucket.host(),
                Uploader::crate_path(crate_name, version)
            )),
            Uploader::Local => Some(format!("/{}", Uploader::crate_path(crate_name, version))),
            Uploader::NoOp => None,
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    /// It returns `None` if the current `Uploader` is `NoOp`.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> Option<String> {
        match *self {
            Uploader::S3 { ref bucket, .. } => Some(format!(
                "https://{}/{}",
                bucket.host(),
                Uploader::readme_path(crate_name, version)
            )),
            Uploader::Local => Some(format!("/{}", Uploader::readme_path(crate_name, version))),
            Uploader::NoOp => None,
        }
    }

    /// Returns the interna path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        // No slash in front so we can use join
        format!("crates/{}/{}-{}.crate", name, name, version)
    }

    /// Returns the interna path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{}/{}-{}.html", name, name, version)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local` or `NoOp`).
    ///
    /// It returns a a tuple containing the path of the uploaded file
    /// and its checksum.
    pub fn upload(
        &self,
        mut handle: Easy,
        path: &str,
        body: &[u8],
        content_type: &str,
        content_length: u64,
    ) -> CargoResult<(Option<String>, Vec<u8>)> {
        let hash = hash(body);
        match *self {
            Uploader::S3 { ref bucket, .. } => {
                let (response, cksum) = {
                    let mut response = Vec::new();
                    {
                        let mut s3req =
                            bucket.put(&mut handle, path, body, content_type, content_length);
                        s3req
                            .write_function(|data| {
                                response.extend(data);
                                Ok(data.len())
                            })
                            .unwrap();
                        s3req.perform().chain_error(|| {
                            internal(&format_args!("failed to upload to S3: `{}`", path))
                        })?;
                    }
                    (response, hash)
                };
                if handle.response_code().unwrap() != 200 {
                    let response = String::from_utf8_lossy(&response);
                    return Err(internal(&format_args!(
                        "failed to get a 200 response from S3: {}",
                        response
                    )));
                }
                Ok((Some(String::from(path)), cksum))
            }
            Uploader::Local => {
                let filename = env::current_dir().unwrap().join("local_uploads").join(path);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                file.write_all(body)?;
                Ok((filename.to_str().map(String::from), hash))
            }
            Uploader::NoOp => Ok((None, vec![])),
        }
    }

    /// Uploads a crate and its readme. Returns the checksum of the uploaded crate
    /// file, and bombs for the uploaded crate and the uploaded readme.
    pub fn upload_crate(
        &self,
        req: &mut Request,
        krate: &Crate,
        readme: Option<String>,
        max: u64,
        max_unpack: u64,
        vers: &semver::Version,
    ) -> CargoResult<(Vec<u8>, Bomb, Bomb)> {
        let app = Arc::clone(req.app());
        let (crate_path, checksum) = {
            let path = Uploader::crate_path(&krate.name, &vers.to_string());
            let length = read_le_u32(req.body())?;
            let mut body = Vec::new();
            LimitErrorReader::new(req.body(), max).read_to_end(&mut body)?;
            verify_tarball(krate, vers, &body, max_unpack)?;
            self.upload(
                app.handle(),
                &path,
                &body,
                "application/x-tar",
                u64::from(length),
            )?
        };
        // We create the bomb for the crate file before uploading the readme so that if the
        // readme upload fails, the uploaded crate file is automatically deleted.
        let crate_bomb = Bomb {
            app: Arc::clone(&app),
            path: crate_path,
        };
        let (readme_path, _) = if let Some(rendered) = readme {
            let path = Uploader::readme_path(&krate.name, &vers.to_string());
            let length = rendered.len();
            self.upload(
                app.handle(),
                &path,
                rendered.as_bytes(),
                "text/html",
                length as u64,
            )?
        } else {
            (None, vec![])
        };
        Ok((
            checksum,
            crate_bomb,
            Bomb {
                app: Arc::clone(&app),
                path: readme_path,
            },
        ))
    }

    /// Deletes an uploaded file.
    fn delete(&self, app: Arc<App>, path: &str) -> CargoResult<()> {
        match *self {
            Uploader::S3 { ref bucket, .. } => {
                let mut handle = app.handle();
                bucket.delete(&mut handle, path).perform()?;
                Ok(())
            }
            Uploader::Local => {
                fs::remove_file(path)?;
                Ok(())
            }
            Uploader::NoOp => Ok(()),
        }
    }
}

// Can't derive Debug because of App.
#[allow(missing_debug_implementations)]
pub struct Bomb {
    app: Arc<App>,
    pub path: Option<String>,
}

impl Drop for Bomb {
    fn drop(&mut self) {
        if let Some(ref path) = self.path {
            if let Err(e) = self.app.config.uploader.delete(Arc::clone(&self.app), path) {
                println!("unable to delete {}, {:?}", path, e);
            }
        }
    }
}

fn verify_tarball(
    krate: &Crate,
    vers: &semver::Version,
    tarball: &[u8],
    max_unpack: u64,
) -> CargoResult<()> {
    // All our data is currently encoded with gzip
    let decoder = GzDecoder::new(tarball)?;

    // Don't let gzip decompression go into the weeeds, apply a fixed cap after
    // which point we say the decompressed source is "too large".
    let decoder = LimitErrorReader::new(decoder, max_unpack);

    // Use this I/O object now to take a peek inside
    let mut archive = tar::Archive::new(decoder);
    let prefix = format!("{}-{}", krate.name, vers);
    for entry in archive.entries()? {
        let entry = entry.chain_error(|| {
            human("uploaded tarball is malformed or too large when decompressed")
        })?;

        // Verify that all entries actually start with `$name-$vers/`.
        // Historically Cargo didn't verify this on extraction so you could
        // upload a tarball that contains both `foo-0.1.0/` source code as well
        // as `bar-0.1.0/` source code, and this could overwrite other crates in
        // the registry!
        if !entry.path()?.starts_with(&prefix) {
            return Err(human("invalid tarball uploaded"));
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::path::PathBuf;

use serde_json::Value;

use crate::protocol::{
    read_message, write_error_response, write_notification, write_success_response,
    write_success_result, ErrorCodes, Message, NotificationMessage, RequestMessage, ResponseError,
};

use crate::definition;
use crate::import::{check_imports, ImportedFiles};
use crate::mojomast::MojomAst;
use crate::{Error, Result};

#[derive(PartialEq)]
enum State {
    Initialized,
    ShuttingDown,
}

struct ServerContext {
    state: State,
    // Workspace root path.
    root_path: std::path::PathBuf,
    // True when the previous text document has errors.
    has_error: bool,
    // Contains the current document text and ast. None when the text is an
    // invalid mojom.
    ast: Option<MojomAst>,
    // Parsed mojom files that are imported from the current document.
    imported_files: Option<ImportedFiles>,
    // Set when `exit` notification is received.
    exit_code: Option<i32>,
}

impl ServerContext {
    fn new(root_path: PathBuf) -> ServerContext {
        ServerContext {
            state: State::Initialized,
            root_path: root_path,
            has_error: false,
            ast: None,
            imported_files: None,
            exit_code: None,
        }
    }
}

fn create_server_capabilities() -> lsp_types::ServerCapabilities {
    let options = lsp_types::TextDocumentSyncOptions {
        open_close: Some(true),
        change: Some(lsp_types::TextDocumentSyncKind::Full),
        will_save: None,
        will_save_wait_until: None,
        save: None,
    };

    let text_document_sync = lsp_types::TextDocumentSyncCapability::Options(options);

    lsp_types::ServerCapabilities {
        text_document_sync: Some(text_document_sync),
        hover_provider: None,
        completion_provider: None,
        signature_help_provider: None,
        definition_provider: Some(true),
        type_definition_provider: None,
        implementation_provider: None,
        references_provider: None,
        document_highlight_provider: None,
        document_symbol_provider: None,
        workspace_symbol_provider: None,
        code_action_provider: None,
        code_lens_provider: None,
        document_formatting_provider: None,
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: None,
        rename_provider: None,
        color_provider: None,
        folding_range_provider: None,
        execute_command_provider: None,
        workspace: None,
    }
}

// Requests

fn get_request_params<P: serde::de::DeserializeOwned>(
    params: Value,
) -> std::result::Result<P, ResponseError> {
    serde_json::from_value::<P>(params)
        .map_err(|err| ResponseError::new(ErrorCodes::InvalidRequest, err.to_string()))
}

fn handle_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: RequestMessage,
) -> Result<()> {
    let id = msg.id;
    let method = msg.method.as_str();

    use lsp_types::request::*;
    let res = match method {
        Initialize::METHOD => initialize_request(),
        Shutdown::METHOD => shutdown_request(ctx),
        GotoDefinition::METHOD => get_request_params(msg.params)
            .and_then(|params| goto_definition_request(writer, ctx, params)),
        _ => {
            eprintln!("Received unimplemented request: {:?}", msg);
            unimplemented!();
        }
    };
    match res {
        Ok(res) => write_success_response(writer, id, res)?,
        Err(error) => write_error_response(writer, id, error)?,
    }
    Ok(())
}

type RequestResult = std::result::Result<Value, ResponseError>;

fn initialize_request() -> RequestResult {
    // The server has been initialized already.
    let error_message = "Unexpected initialize message".to_owned();
    Err(ResponseError::new(
        ErrorCodes::ServerNotInitialized,
        error_message,
    ))
}

fn shutdown_request(ctx: &mut ServerContext) -> RequestResult {
    ctx.state = State::ShuttingDown;
    Ok(Value::Null)
}

fn _get_offset_from_position(text: &str, pos: lsp_types::Position) -> usize {
    let pos_line = pos.line as usize;
    let pos_col = pos.character as usize;
    let mut offset = 0;
    for (i, line) in text.lines().enumerate() {
        if i == pos_line {
            break;
        }
        offset += line.len() + 1;
    }
    offset + pos_col
}

#[inline(always)]
fn is_identifier_char(ch: char) -> bool {
    ch.is_ascii_alphanumeric() || ch == '_' || ch == '.'
}

fn get_identifier(text: &str, pos: lsp_types::Position) -> &str {
    // TODO: The current implementation isn't accurate.

    let offset = _get_offset_from_position(text, pos);
    let mut s = offset;
    for ch in text[..offset].chars().rev() {
        if !is_identifier_char(ch) {
            break;
        }
        s -= 1;
    }
    let mut e = offset;
    for ch in text[offset..].chars() {
        if !is_identifier_char(ch) {
            break;
        }
        e += 1;
    }
    &text[s..e]
}

fn find_definition_in_doc(ast: &MojomAst, ident: &str) -> Option<lsp_types::Location> {
    definition::find_definition(ident, ast)
}

fn find_definition_in_imports(ctx: &ServerContext, ident: &str) -> Option<lsp_types::Location> {
    ctx.imported_files
        .as_ref()
        .and_then(|ref imported_files| imported_files.find_definition(ident))
}

fn goto_definition_request(
    _writer: &mut Write,
    ctx: &mut ServerContext,
    params: lsp_types::TextDocumentPositionParams,
) -> RequestResult {
    if let Some(ref ast) = &ctx.ast {
        let ident = get_identifier(&ast.text, params.position);
        let loc = find_definition_in_doc(ast, ident).or(find_definition_in_imports(ctx, ident));
        let res = match loc {
            Some(loc) => serde_json::to_value(loc).unwrap(),
            None => Value::Null,
        };
        Ok(res)
    } else {
        Ok(Value::Null)
    }
}

// Notifications

fn get_params<P: serde::de::DeserializeOwned>(params: Value) -> Result<P> {
    serde_json::from_value::<P>(params).map_err(|err| Error::ProtocolError(err.to_string()))
}

fn do_nothing(msg: &NotificationMessage) -> Result<()> {
    eprintln!("Received `{}` but do nothing.", msg.method.as_str());
    Ok(())
}

fn handle_notification(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: NotificationMessage,
) -> Result<()> {
    let method = msg.method.as_str();
    eprintln!("Got notification: {}", method);

    use lsp_types::notification::*;
    match msg.method.as_str() {
        Exit::METHOD => exit_notification(ctx),
        DidOpenTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_open_text_document(writer, ctx, params))
        }
        DidChangeTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_change_text_document(writer, ctx, params))
        }
        // Accept following notifications but do nothing.
        DidChangeConfiguration::METHOD => do_nothing(&msg),
        WillSaveTextDocument::METHOD => do_nothing(&msg),
        DidSaveTextDocument::METHOD => do_nothing(&msg),
        _ => {
            eprintln!("Received unimplemented notification: {:#?}", msg);
            Ok(())
        }
    }
}

fn publish_diagnotics(
    writer: &mut impl Write,
    params: lsp_types::PublishDiagnosticsParams,
) -> Result<()> {
    // TODO: Don't use unwrap().
    let params = serde_json::to_value(&params).unwrap();
    write_notification(writer, "textDocument/publishDiagnostics", params)
}

fn convert_error_position(line_col: &mojom_parser::LineColLocation) -> lsp_types::Range {
    let (start, end) = match line_col {
        mojom_parser::LineColLocation::Pos((line, col)) => {
            let start = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            // ???
            let end = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            (start, end)
        }
        mojom_parser::LineColLocation::Span(start, end) => {
            // `start` and `end` are tuples like (line, col).
            let start = lsp_types::Position {
                line: start.0 as u64 - 1,
                character: start.1 as u64 - 1,
            };
            let end = lsp_types::Position {
                line: end.0 as u64 - 1,
                character: end.1 as u64 - 1,
            };
            (start, end)
        }
    };
    lsp_types::Range {
        start: start,
        end: end,
    }
}

fn _check_syntax(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    text: String,
    uri: lsp_types::Url,
) -> Result<()> {
    match MojomAst::new(uri.clone(), text) {
        Ok(ast) => {
            ctx.ast = Some(ast);
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = false;
        }
        Err(err) => {
            let range = convert_error_position(&err.line_col);
            let diagnostic = lsp_types::Diagnostic {
                range: range,
                severity: Some(lsp_types::DiagnosticSeverity::Error),
                code: Some(lsp_types::NumberOrString::String("mojom".to_owned())),
                source: Some("mojom-lsp".to_owned()),
                message: err.to_string(),
                related_information: None,
            };
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![diagnostic],
            };
            publish_diagnotics(writer, params)?;
            ctx.ast = None;
            ctx.has_error = true;
        }
    }
    // TODO: Tentative
    if let Some(ast) = &ctx.ast {
        let imported_files = check_imports(&ctx.root_path, ast);
        ctx.imported_files = Some(imported_files);
    }
    Ok(())
}

fn exit_notification(ctx: &mut ServerContext) -> Result<()> {
    // https://microsoft.github.io/language-server-protocol/specification#exit
    if ctx.state == State::ShuttingDown {
        ctx.exit_code = Some(0);
    } else {
        ctx.exit_code = Some(1);
    }
    Ok(())
}

fn did_open_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidOpenTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    _check_syntax(writer, ctx, params.text_document.text, uri)
}

fn did_change_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidChangeTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    let content = params
        .content_changes
        .iter()
        .map(|i| i.text.to_owned())
        .collect::<Vec<_>>();
    let text = content.join("");
    _check_syntax(writer, ctx, text, uri)
}

// Initialization

fn initialize(
    reader: &mut impl BufRead,
    writer: &mut impl Write,
) -> Result<lsp_types::InitializeParams> {
    let message = read_message(reader)?;

    let (id, params) = match message {
        Message::Request(req) => req.cast::<lsp_types::request::Initialize>()?,
        _ => {
            // TODO: Gracefully handle `exit` and `shutdown` messages.
            let error_message = format!("Expected initialize message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    let capabilities = create_server_capabilities();
    let res = lsp_types::InitializeResult {
        capabilities: capabilities,
    };
    write_success_result(writer, id, res)?;

    let message = read_message(reader)?;
    match message {
        Message::Notofication(notif) => notif.cast::<lsp_types::notification::Initialized>()?,
        _ => {
            let error_message = format!("Expected initialized message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    Ok(params)
}

fn get_root_path(params: &lsp_types::InitializeParams) -> PathBuf {
    if let Some(ref uri) = params.root_uri.as_ref() {
        if uri.scheme() == "file" {
            if let Ok(path) = uri.to_file_path() {
                return path;
            }
        }
    }
    if let Some(ref path) = params.root_path.as_ref() {
        return PathBuf::from(path);
    }
    PathBuf::new()
}

// Returns exit code.
pub fn start() -> Result<i32> {
    let mut reader = BufReader::new(io::stdin());
    let mut writer = BufWriter::new(io::stdout());

    let params = initialize(&mut reader, &mut writer)?;

    let root_path = get_root_path(&params);
    eprintln!("root_path: {:?}", root_path);

    let mut ctx = ServerContext::new(root_path);

    loop {
        eprintln!("Reading message...");
        let message = read_message(&mut reader)?;
        match message {
            Message::Request(request) => handle_request(&mut writer, &mut ctx, request)?,
            Message::Notofication(notification) => {
                handle_notification(&mut writer, &mut ctx, notification)?
            }
            _ => unimplemented!(),
        };

        if let Some(exit_code) = ctx.exit_code {
            return Ok(exit_code);
        }
    }
}
<commit_msg>server: Invalidate the current document when a request has different URI<commit_after>use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::path::PathBuf;

use serde_json::Value;

use crate::protocol::{
    read_message, write_error_response, write_notification, write_success_response,
    write_success_result, ErrorCodes, Message, NotificationMessage, RequestMessage, ResponseError,
};

use crate::definition;
use crate::import::{check_imports, ImportedFiles};
use crate::mojomast::MojomAst;
use crate::{Error, Result};

#[derive(PartialEq)]
enum State {
    Initialized,
    ShuttingDown,
}

struct ServerContext {
    state: State,
    // Workspace root path.
    root_path: std::path::PathBuf,
    // True when the previous text document has errors.
    has_error: bool,
    // Contains the current document text and ast. None when the text is an
    // invalid mojom.
    ast: Option<MojomAst>,
    // Parsed mojom files that are imported from the current document.
    imported_files: Option<ImportedFiles>,
    // Set when `exit` notification is received.
    exit_code: Option<i32>,
}

impl ServerContext {
    fn new(root_path: PathBuf) -> ServerContext {
        ServerContext {
            state: State::Initialized,
            root_path: root_path,
            has_error: false,
            ast: None,
            imported_files: None,
            exit_code: None,
        }
    }
}

fn create_server_capabilities() -> lsp_types::ServerCapabilities {
    let options = lsp_types::TextDocumentSyncOptions {
        open_close: Some(true),
        change: Some(lsp_types::TextDocumentSyncKind::Full),
        will_save: None,
        will_save_wait_until: None,
        save: None,
    };

    let text_document_sync = lsp_types::TextDocumentSyncCapability::Options(options);

    lsp_types::ServerCapabilities {
        text_document_sync: Some(text_document_sync),
        hover_provider: None,
        completion_provider: None,
        signature_help_provider: None,
        definition_provider: Some(true),
        type_definition_provider: None,
        implementation_provider: None,
        references_provider: None,
        document_highlight_provider: None,
        document_symbol_provider: None,
        workspace_symbol_provider: None,
        code_action_provider: None,
        code_lens_provider: None,
        document_formatting_provider: None,
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: None,
        rename_provider: None,
        color_provider: None,
        folding_range_provider: None,
        execute_command_provider: None,
        workspace: None,
    }
}

// Requests

fn get_request_params<P: serde::de::DeserializeOwned>(
    params: Value,
) -> std::result::Result<P, ResponseError> {
    serde_json::from_value::<P>(params)
        .map_err(|err| ResponseError::new(ErrorCodes::InvalidRequest, err.to_string()))
}

fn handle_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: RequestMessage,
) -> Result<()> {
    let id = msg.id;
    let method = msg.method.as_str();

    use lsp_types::request::*;
    let res = match method {
        Initialize::METHOD => initialize_request(),
        Shutdown::METHOD => shutdown_request(ctx),
        GotoDefinition::METHOD => get_request_params(msg.params)
            .and_then(|params| goto_definition_request(writer, ctx, params)),
        _ => {
            eprintln!("Received unimplemented request: {:?}", msg);
            unimplemented!();
        }
    };
    match res {
        Ok(res) => write_success_response(writer, id, res)?,
        Err(error) => write_error_response(writer, id, error)?,
    }
    Ok(())
}

type RequestResult = std::result::Result<Value, ResponseError>;

fn initialize_request() -> RequestResult {
    // The server has been initialized already.
    let error_message = "Unexpected initialize message".to_owned();
    Err(ResponseError::new(
        ErrorCodes::ServerNotInitialized,
        error_message,
    ))
}

fn shutdown_request(ctx: &mut ServerContext) -> RequestResult {
    ctx.state = State::ShuttingDown;
    Ok(Value::Null)
}

fn _get_offset_from_position(text: &str, pos: lsp_types::Position) -> usize {
    let pos_line = pos.line as usize;
    let pos_col = pos.character as usize;
    let mut offset = 0;
    for (i, line) in text.lines().enumerate() {
        if i == pos_line {
            break;
        }
        offset += line.len() + 1;
    }
    offset + pos_col
}

#[inline(always)]
fn is_identifier_char(ch: char) -> bool {
    ch.is_ascii_alphanumeric() || ch == '_' || ch == '.'
}

fn get_identifier(text: &str, pos: lsp_types::Position) -> &str {
    // TODO: The current implementation isn't accurate.

    let offset = _get_offset_from_position(text, pos);
    let mut s = offset;
    for ch in text[..offset].chars().rev() {
        if !is_identifier_char(ch) {
            break;
        }
        s -= 1;
    }
    let mut e = offset;
    for ch in text[offset..].chars() {
        if !is_identifier_char(ch) {
            break;
        }
        e += 1;
    }
    &text[s..e]
}

fn find_definition_in_doc(ast: &MojomAst, ident: &str) -> Option<lsp_types::Location> {
    definition::find_definition(ident, ast)
}

fn find_definition_in_imports(ctx: &ServerContext, ident: &str) -> Option<lsp_types::Location> {
    ctx.imported_files
        .as_ref()
        .and_then(|ref imported_files| imported_files.find_definition(ident))
}

fn goto_definition_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::TextDocumentPositionParams,
) -> RequestResult {
    let is_same_uri = if let Some(ref ast) = ctx.ast {
        params.text_document.uri == ast.uri
    } else {
        false
    };
    if !is_same_uri {
        // Invalidate the current document.
        use std::io::Read;
        let uri = params.text_document.uri.clone();
        let path = uri.to_file_path().unwrap();
        let mut text = String::new();
        std::fs::File::open(path)
            .unwrap()
            .read_to_string(&mut text)
            .unwrap();

        match _check_syntax(writer, ctx, text, uri) {
            Ok(_) => (),
            Err(err) => {
                // TODO: Should return an Err that indicates the file specified
                // by `uri` isn't a vailid mojom file.
                eprintln!("{:#?}", err);
                unimplemented!();
            }
        }
    }

    if let Some(ref ast) = &ctx.ast {
        let ident = get_identifier(&ast.text, params.position);
        let loc = find_definition_in_doc(ast, ident).or(find_definition_in_imports(ctx, ident));
        let res = match loc {
            Some(loc) => serde_json::to_value(loc).unwrap(),
            None => Value::Null,
        };
        Ok(res)
    } else {
        Ok(Value::Null)
    }
}

// Notifications

fn get_params<P: serde::de::DeserializeOwned>(params: Value) -> Result<P> {
    serde_json::from_value::<P>(params).map_err(|err| Error::ProtocolError(err.to_string()))
}

fn do_nothing(msg: &NotificationMessage) -> Result<()> {
    eprintln!("Received `{}` but do nothing.", msg.method.as_str());
    Ok(())
}

fn handle_notification(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: NotificationMessage,
) -> Result<()> {
    let method = msg.method.as_str();
    eprintln!("Got notification: {}", method);

    use lsp_types::notification::*;
    match msg.method.as_str() {
        Exit::METHOD => exit_notification(ctx),
        DidOpenTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_open_text_document(writer, ctx, params))
        }
        DidChangeTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_change_text_document(writer, ctx, params))
        }
        // Accept following notifications but do nothing.
        DidChangeConfiguration::METHOD => do_nothing(&msg),
        WillSaveTextDocument::METHOD => do_nothing(&msg),
        DidSaveTextDocument::METHOD => do_nothing(&msg),
        _ => {
            eprintln!("Received unimplemented notification: {:#?}", msg);
            Ok(())
        }
    }
}

fn publish_diagnotics(
    writer: &mut impl Write,
    params: lsp_types::PublishDiagnosticsParams,
) -> Result<()> {
    // TODO: Don't use unwrap().
    let params = serde_json::to_value(&params).unwrap();
    write_notification(writer, "textDocument/publishDiagnostics", params)
}

fn convert_error_position(line_col: &mojom_parser::LineColLocation) -> lsp_types::Range {
    let (start, end) = match line_col {
        mojom_parser::LineColLocation::Pos((line, col)) => {
            let start = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            // ???
            let end = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            (start, end)
        }
        mojom_parser::LineColLocation::Span(start, end) => {
            // `start` and `end` are tuples like (line, col).
            let start = lsp_types::Position {
                line: start.0 as u64 - 1,
                character: start.1 as u64 - 1,
            };
            let end = lsp_types::Position {
                line: end.0 as u64 - 1,
                character: end.1 as u64 - 1,
            };
            (start, end)
        }
    };
    lsp_types::Range {
        start: start,
        end: end,
    }
}

fn _check_syntax(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    text: String,
    uri: lsp_types::Url,
) -> Result<()> {
    match MojomAst::new(uri.clone(), text) {
        Ok(ast) => {
            ctx.ast = Some(ast);
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = false;
        }
        Err(err) => {
            let range = convert_error_position(&err.line_col);
            let diagnostic = lsp_types::Diagnostic {
                range: range,
                severity: Some(lsp_types::DiagnosticSeverity::Error),
                code: Some(lsp_types::NumberOrString::String("mojom".to_owned())),
                source: Some("mojom-lsp".to_owned()),
                message: err.to_string(),
                related_information: None,
            };
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![diagnostic],
            };
            publish_diagnotics(writer, params)?;
            ctx.ast = None;
            ctx.has_error = true;
        }
    }
    // TODO: Tentative
    if let Some(ast) = &ctx.ast {
        let imported_files = check_imports(&ctx.root_path, ast);
        ctx.imported_files = Some(imported_files);
    }
    Ok(())
}

fn exit_notification(ctx: &mut ServerContext) -> Result<()> {
    // https://microsoft.github.io/language-server-protocol/specification#exit
    if ctx.state == State::ShuttingDown {
        ctx.exit_code = Some(0);
    } else {
        ctx.exit_code = Some(1);
    }
    Ok(())
}

fn did_open_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidOpenTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    _check_syntax(writer, ctx, params.text_document.text, uri)
}

fn did_change_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidChangeTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    let content = params
        .content_changes
        .iter()
        .map(|i| i.text.to_owned())
        .collect::<Vec<_>>();
    let text = content.join("");
    _check_syntax(writer, ctx, text, uri)
}

// Initialization

fn initialize(
    reader: &mut impl BufRead,
    writer: &mut impl Write,
) -> Result<lsp_types::InitializeParams> {
    let message = read_message(reader)?;

    let (id, params) = match message {
        Message::Request(req) => req.cast::<lsp_types::request::Initialize>()?,
        _ => {
            // TODO: Gracefully handle `exit` and `shutdown` messages.
            let error_message = format!("Expected initialize message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    let capabilities = create_server_capabilities();
    let res = lsp_types::InitializeResult {
        capabilities: capabilities,
    };
    write_success_result(writer, id, res)?;

    let message = read_message(reader)?;
    match message {
        Message::Notofication(notif) => notif.cast::<lsp_types::notification::Initialized>()?,
        _ => {
            let error_message = format!("Expected initialized message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    Ok(params)
}

fn get_root_path(params: &lsp_types::InitializeParams) -> PathBuf {
    if let Some(ref uri) = params.root_uri.as_ref() {
        if uri.scheme() == "file" {
            if let Ok(path) = uri.to_file_path() {
                return path;
            }
        }
    }
    if let Some(ref path) = params.root_path.as_ref() {
        return PathBuf::from(path);
    }
    PathBuf::new()
}

// Returns exit code.
pub fn start() -> Result<i32> {
    let mut reader = BufReader::new(io::stdin());
    let mut writer = BufWriter::new(io::stdout());

    let params = initialize(&mut reader, &mut writer)?;

    let root_path = get_root_path(&params);
    eprintln!("root_path: {:?}", root_path);

    let mut ctx = ServerContext::new(root_path);

    loop {
        eprintln!("Reading message...");
        let message = read_message(&mut reader)?;
        match message {
            Message::Request(request) => handle_request(&mut writer, &mut ctx, request)?,
            Message::Notofication(notification) => {
                handle_notification(&mut writer, &mut ctx, notification)?
            }
            _ => unimplemented!(),
        };

        if let Some(exit_code) = ctx.exit_code {
            return Ok(exit_code);
        }
    }
}
<|endoftext|>


<commit_before>use std::{
    cell::RefCell,
    rc::Rc,
    sync::{Arc, Mutex},
};

use rand;

use smithay::{
    reexports::wayland_server::{
        protocol::{wl_buffer, wl_pointer::ButtonState, wl_shell_surface, wl_surface},
        Display,
    },
    utils::Rectangle,
    wayland::{
        compositor::{compositor_init, CompositorToken, RegionAttributes, SurfaceAttributes, SurfaceEvent},
        data_device::DnDIconRole,
        seat::{AxisFrame, CursorImageRole, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
        shell::{
            legacy::{
                wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind, ShellSurfaceRole,
            },
            xdg::{
                xdg_shell_init, PopupConfigure, ShellState as XdgShellState, ToplevelConfigure, XdgRequest,
                XdgSurfaceRole,
            },
        },
        SERIAL_COUNTER as SCOUNTER,
    },
};

use crate::window_map::{Kind as SurfaceKind, WindowMap};

define_roles!(Roles =>
    [ XdgSurface, XdgSurfaceRole ]
    [ ShellSurface, ShellSurfaceRole]
    [ DnDIcon, DnDIconRole ]
    [ CursorImage, CursorImageRole ]
);

pub type MyWindowMap = WindowMap<
    Roles,
    fn(&SurfaceAttributes) -> Option<(i32, i32)>,
    fn(&SurfaceAttributes, (f64, f64)) -> bool,
>;

pub type MyCompositorToken = CompositorToken<Roles>;

struct MoveSurfaceGrab {
    start_data: GrabStartData,
    window_map: Rc<RefCell<MyWindowMap>>,
    toplevel: SurfaceKind<Roles>,
    initial_window_location: (i32, i32),
}

impl PointerGrab for MoveSurfaceGrab {
    fn motion(
        &mut self,
        _handle: &mut PointerInnerHandle<'_>,
        location: (f64, f64),
        _focus: Option<(wl_surface::WlSurface, (f64, f64))>,
        _serial: u32,
        _time: u32,
    ) {
        let dx = location.0 - self.start_data.location.0;
        let dy = location.1 - self.start_data.location.1;
        let new_window_x = (self.initial_window_location.0 as f64 + dx) as i32;
        let new_window_y = (self.initial_window_location.1 as f64 + dy) as i32;

        self.window_map
            .borrow_mut()
            .set_location(&self.toplevel, (new_window_x, new_window_y));
    }

    fn button(
        &mut self,
        handle: &mut PointerInnerHandle<'_>,
        button: u32,
        state: ButtonState,
        serial: u32,
        time: u32,
    ) {
        handle.button(button, state, serial, time);
        if handle.current_pressed().is_empty() {
            // No more buttons are pressed, release the grab.
            handle.unset_grab(serial, time);
        }
    }

    fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame) {
        handle.axis(details)
    }

    fn start_data(&self) -> &GrabStartData {
        &self.start_data
    }
}

pub fn init_shell(
    display: &mut Display,
    log: ::slog::Logger,
) -> (
    CompositorToken<Roles>,
    Arc<Mutex<XdgShellState<Roles>>>,
    Arc<Mutex<WlShellState<Roles>>>,
    Rc<RefCell<MyWindowMap>>,
) {
    // Create the compositor
    let (compositor_token, _, _) = compositor_init(
        display,
        move |request, surface, ctoken| match request {
            SurfaceEvent::Commit => surface_commit(&surface, ctoken),
            SurfaceEvent::Frame { callback } => callback
                .implement_closure(|_, _| unreachable!(), None::<fn(_)>, ())
                .done(0),
        },
        log.clone(),
    );

    // Init a window map, to track the location of our windows
    let window_map = Rc::new(RefCell::new(WindowMap::new(
        compositor_token,
        get_size as _,
        contains_point as _,
    )));

    // init the xdg_shell
    let xdg_window_map = window_map.clone();
    let (xdg_shell_state, _, _) = xdg_shell_init(
        display,
        compositor_token,
        move |shell_event| match shell_event {
            XdgRequest::NewToplevel { surface } => {
                // place the window at a random location in the [0;800]x[0;800] square
                use rand::distributions::{Distribution, Uniform};
                let range = Uniform::new(0, 800);
                let mut rng = rand::thread_rng();
                let x = range.sample(&mut rng);
                let y = range.sample(&mut rng);
                surface.send_configure(ToplevelConfigure {
                    size: None,
                    states: vec![],
                    serial: 42,
                });
                xdg_window_map
                    .borrow_mut()
                    .insert(SurfaceKind::Xdg(surface), (x, y));
            }
            XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure {
                size: (10, 10),
                position: (10, 10),
                serial: 42,
            }),
            XdgRequest::Move {
                surface,
                seat,
                serial,
            } => {
                let seat = Seat::from_resource(&seat).unwrap();
                // TODO: touch move.
                let pointer = seat.get_pointer().unwrap();

                // Check that this surface has a click grab.
                if !pointer.has_grab(serial) {
                    return;
                }

                let start_data = pointer.grab_start_data().unwrap();

                let toplevel = SurfaceKind::Xdg(surface);
                let initial_window_location = xdg_window_map.borrow().location(&toplevel).unwrap();

                let grab = MoveSurfaceGrab {
                    start_data,
                    window_map: xdg_window_map.clone(),
                    toplevel,
                    initial_window_location,
                };

                pointer.set_grab(grab, serial);
            }
            _ => (),
        },
        log.clone(),
    );

    // init the wl_shell
    let shell_window_map = window_map.clone();
    let (wl_shell_state, _) = wl_shell_init(
        display,
        compositor_token,
        move |req: ShellRequest<_>| {
            match req {
                ShellRequest::SetKind {
                    surface,
                    kind: ShellSurfaceKind::Toplevel,
                } => {
                    // place the window at a random location in the [0;800]x[0;800] square
                    use rand::distributions::{Distribution, Uniform};
                    let range = Uniform::new(0, 800);
                    let mut rng = rand::thread_rng();
                    let x = range.sample(&mut rng);
                    let y = range.sample(&mut rng);
                    surface.send_configure((0, 0), wl_shell_surface::Resize::None);
                    shell_window_map
                        .borrow_mut()
                        .insert(SurfaceKind::Wl(surface), (x, y));
                }
                ShellRequest::Move {
                    surface,
                    seat,
                    serial,
                } => {
                    let seat = Seat::from_resource(&seat).unwrap();
                    // TODO: touch move.
                    let pointer = seat.get_pointer().unwrap();

                    // Check that this surface has a click grab.
                    if !pointer.has_grab(serial) {
                        return;
                    }

                    let start_data = pointer.grab_start_data().unwrap();

                    let toplevel = SurfaceKind::Wl(surface);
                    let initial_window_location = shell_window_map.borrow().location(&toplevel).unwrap();

                    let grab = MoveSurfaceGrab {
                        start_data,
                        window_map: shell_window_map.clone(),
                        toplevel,
                        initial_window_location,
                    };

                    pointer.set_grab(grab, serial);
                }
                _ => (),
            }
        },
        log.clone(),
    );

    (compositor_token, xdg_shell_state, wl_shell_state, window_map)
}

#[derive(Default)]
pub struct SurfaceData {
    pub buffer: Option<wl_buffer::WlBuffer>,
    pub texture: Option<crate::glium_drawer::TextureMetadata>,
    pub input_region: Option<RegionAttributes>,
}

fn surface_commit(surface: &wl_surface::WlSurface, token: CompositorToken<Roles>) {
    token.with_surface_data(surface, |attributes| {
        attributes.user_data.insert_if_missing(SurfaceData::default);
        let data = attributes.user_data.get_mut::<SurfaceData>().unwrap();

        data.input_region = attributes.input_region.clone();

        // we retrieve the contents of the associated buffer and copy it
        match attributes.buffer.take() {
            Some(Some((buffer, (_x, _y)))) => {
                // new contents
                // TODO: handle hotspot coordinates
                if let Some(old_buffer) = data.buffer.replace(buffer) {
                    old_buffer.release();
                }
                data.texture = None;
            }
            Some(None) => {
                // erase the contents
                if let Some(old_buffer) = data.buffer.take() {
                    old_buffer.release();
                }
                data.texture = None;
            }
            None => {}
        }
    });
}

fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> {
    attrs.user_data.get::<SurfaceData>().and_then(|data| {
        data.texture
            .as_ref()
            .map(|ref meta| meta.dimensions)
            .map(|(x, y)| (x as i32, y as i32))
    })
}

fn contains_point(attrs: &SurfaceAttributes, point: (f64, f64)) -> bool {
    let (w, h) = match get_size(attrs) {
        None => return false, // If the surface has no size, it can't have an input region.
        Some(wh) => wh,
    };

    let rect = Rectangle {
        x: 0,
        y: 0,
        width: w,
        height: h,
    };

    let point = (point.0 as i32, point.1 as i32);

    // The input region is always within the surface itself, so if the surface itself doesn't contain the
    // point we can return false.
    if !rect.contains(point) {
        return false;
    }

    let input_region = &attrs.user_data.get::<SurfaceData>().unwrap().input_region;

    // If there's no input region, we're done.
    if input_region.is_none() {
        return true;
    }

    input_region.as_ref().unwrap().contains(point)
}
<commit_msg>anvil.shell: check that grab is correct in Move<commit_after>use std::{
    cell::RefCell,
    rc::Rc,
    sync::{Arc, Mutex},
};

use rand;

use smithay::{
    reexports::wayland_server::{
        protocol::{wl_buffer, wl_pointer::ButtonState, wl_shell_surface, wl_surface},
        Display,
    },
    utils::Rectangle,
    wayland::{
        compositor::{compositor_init, CompositorToken, RegionAttributes, SurfaceAttributes, SurfaceEvent},
        data_device::DnDIconRole,
        seat::{AxisFrame, CursorImageRole, GrabStartData, PointerGrab, PointerInnerHandle, Seat},
        shell::{
            legacy::{
                wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind, ShellSurfaceRole,
            },
            xdg::{
                xdg_shell_init, PopupConfigure, ShellState as XdgShellState, ToplevelConfigure, XdgRequest,
                XdgSurfaceRole,
            },
        },
        SERIAL_COUNTER as SCOUNTER,
    },
};

use crate::window_map::{Kind as SurfaceKind, WindowMap};

define_roles!(Roles =>
    [ XdgSurface, XdgSurfaceRole ]
    [ ShellSurface, ShellSurfaceRole]
    [ DnDIcon, DnDIconRole ]
    [ CursorImage, CursorImageRole ]
);

pub type MyWindowMap = WindowMap<
    Roles,
    fn(&SurfaceAttributes) -> Option<(i32, i32)>,
    fn(&SurfaceAttributes, (f64, f64)) -> bool,
>;

pub type MyCompositorToken = CompositorToken<Roles>;

struct MoveSurfaceGrab {
    start_data: GrabStartData,
    window_map: Rc<RefCell<MyWindowMap>>,
    toplevel: SurfaceKind<Roles>,
    initial_window_location: (i32, i32),
}

impl PointerGrab for MoveSurfaceGrab {
    fn motion(
        &mut self,
        _handle: &mut PointerInnerHandle<'_>,
        location: (f64, f64),
        _focus: Option<(wl_surface::WlSurface, (f64, f64))>,
        _serial: u32,
        _time: u32,
    ) {
        let dx = location.0 - self.start_data.location.0;
        let dy = location.1 - self.start_data.location.1;
        let new_window_x = (self.initial_window_location.0 as f64 + dx) as i32;
        let new_window_y = (self.initial_window_location.1 as f64 + dy) as i32;

        self.window_map
            .borrow_mut()
            .set_location(&self.toplevel, (new_window_x, new_window_y));
    }

    fn button(
        &mut self,
        handle: &mut PointerInnerHandle<'_>,
        button: u32,
        state: ButtonState,
        serial: u32,
        time: u32,
    ) {
        handle.button(button, state, serial, time);
        if handle.current_pressed().is_empty() {
            // No more buttons are pressed, release the grab.
            handle.unset_grab(serial, time);
        }
    }

    fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame) {
        handle.axis(details)
    }

    fn start_data(&self) -> &GrabStartData {
        &self.start_data
    }
}

pub fn init_shell(
    display: &mut Display,
    log: ::slog::Logger,
) -> (
    CompositorToken<Roles>,
    Arc<Mutex<XdgShellState<Roles>>>,
    Arc<Mutex<WlShellState<Roles>>>,
    Rc<RefCell<MyWindowMap>>,
) {
    // Create the compositor
    let (compositor_token, _, _) = compositor_init(
        display,
        move |request, surface, ctoken| match request {
            SurfaceEvent::Commit => surface_commit(&surface, ctoken),
            SurfaceEvent::Frame { callback } => callback
                .implement_closure(|_, _| unreachable!(), None::<fn(_)>, ())
                .done(0),
        },
        log.clone(),
    );

    // Init a window map, to track the location of our windows
    let window_map = Rc::new(RefCell::new(WindowMap::new(
        compositor_token,
        get_size as _,
        contains_point as _,
    )));

    // init the xdg_shell
    let xdg_window_map = window_map.clone();
    let (xdg_shell_state, _, _) = xdg_shell_init(
        display,
        compositor_token,
        move |shell_event| match shell_event {
            XdgRequest::NewToplevel { surface } => {
                // place the window at a random location in the [0;800]x[0;800] square
                use rand::distributions::{Distribution, Uniform};
                let range = Uniform::new(0, 800);
                let mut rng = rand::thread_rng();
                let x = range.sample(&mut rng);
                let y = range.sample(&mut rng);
                surface.send_configure(ToplevelConfigure {
                    size: None,
                    states: vec![],
                    serial: 42,
                });
                xdg_window_map
                    .borrow_mut()
                    .insert(SurfaceKind::Xdg(surface), (x, y));
            }
            XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure {
                size: (10, 10),
                position: (10, 10),
                serial: 42,
            }),
            XdgRequest::Move {
                surface,
                seat,
                serial,
            } => {
                let seat = Seat::from_resource(&seat).unwrap();
                // TODO: touch move.
                let pointer = seat.get_pointer().unwrap();

                // Check that this surface has a click grab.
                if !pointer.has_grab(serial) {
                    return;
                }

                let start_data = pointer.grab_start_data().unwrap();

                // If the focus was for a different surface, ignore the request.
                if start_data.focus.is_none()
                    || !start_data
                        .focus
                        .as_ref()
                        .unwrap()
                        .0
                        .as_ref()
                        .same_client_as(surface.get_surface().unwrap().as_ref())
                {
                    return;
                }

                let toplevel = SurfaceKind::Xdg(surface);
                let initial_window_location = xdg_window_map.borrow().location(&toplevel).unwrap();

                let grab = MoveSurfaceGrab {
                    start_data,
                    window_map: xdg_window_map.clone(),
                    toplevel,
                    initial_window_location,
                };

                pointer.set_grab(grab, serial);
            }
            _ => (),
        },
        log.clone(),
    );

    // init the wl_shell
    let shell_window_map = window_map.clone();
    let (wl_shell_state, _) = wl_shell_init(
        display,
        compositor_token,
        move |req: ShellRequest<_>| {
            match req {
                ShellRequest::SetKind {
                    surface,
                    kind: ShellSurfaceKind::Toplevel,
                } => {
                    // place the window at a random location in the [0;800]x[0;800] square
                    use rand::distributions::{Distribution, Uniform};
                    let range = Uniform::new(0, 800);
                    let mut rng = rand::thread_rng();
                    let x = range.sample(&mut rng);
                    let y = range.sample(&mut rng);
                    surface.send_configure((0, 0), wl_shell_surface::Resize::None);
                    shell_window_map
                        .borrow_mut()
                        .insert(SurfaceKind::Wl(surface), (x, y));
                }
                ShellRequest::Move {
                    surface,
                    seat,
                    serial,
                } => {
                    let seat = Seat::from_resource(&seat).unwrap();
                    // TODO: touch move.
                    let pointer = seat.get_pointer().unwrap();

                    // Check that this surface has a click grab.
                    if !pointer.has_grab(serial) {
                        return;
                    }

                    let start_data = pointer.grab_start_data().unwrap();

                    // If the focus was for a different surface, ignore the request.
                    if start_data.focus.is_none()
                        || !start_data
                            .focus
                            .as_ref()
                            .unwrap()
                            .0
                            .as_ref()
                            .same_client_as(surface.get_surface().unwrap().as_ref())
                    {
                        return;
                    }

                    let toplevel = SurfaceKind::Wl(surface);
                    let initial_window_location = shell_window_map.borrow().location(&toplevel).unwrap();

                    let grab = MoveSurfaceGrab {
                        start_data,
                        window_map: shell_window_map.clone(),
                        toplevel,
                        initial_window_location,
                    };

                    pointer.set_grab(grab, serial);
                }
                _ => (),
            }
        },
        log.clone(),
    );

    (compositor_token, xdg_shell_state, wl_shell_state, window_map)
}

#[derive(Default)]
pub struct SurfaceData {
    pub buffer: Option<wl_buffer::WlBuffer>,
    pub texture: Option<crate::glium_drawer::TextureMetadata>,
    pub input_region: Option<RegionAttributes>,
}

fn surface_commit(surface: &wl_surface::WlSurface, token: CompositorToken<Roles>) {
    token.with_surface_data(surface, |attributes| {
        attributes.user_data.insert_if_missing(SurfaceData::default);
        let data = attributes.user_data.get_mut::<SurfaceData>().unwrap();

        data.input_region = attributes.input_region.clone();

        // we retrieve the contents of the associated buffer and copy it
        match attributes.buffer.take() {
            Some(Some((buffer, (_x, _y)))) => {
                // new contents
                // TODO: handle hotspot coordinates
                if let Some(old_buffer) = data.buffer.replace(buffer) {
                    old_buffer.release();
                }
                data.texture = None;
            }
            Some(None) => {
                // erase the contents
                if let Some(old_buffer) = data.buffer.take() {
                    old_buffer.release();
                }
                data.texture = None;
            }
            None => {}
        }
    });
}

fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> {
    attrs.user_data.get::<SurfaceData>().and_then(|data| {
        data.texture
            .as_ref()
            .map(|ref meta| meta.dimensions)
            .map(|(x, y)| (x as i32, y as i32))
    })
}

fn contains_point(attrs: &SurfaceAttributes, point: (f64, f64)) -> bool {
    let (w, h) = match get_size(attrs) {
        None => return false, // If the surface has no size, it can't have an input region.
        Some(wh) => wh,
    };

    let rect = Rectangle {
        x: 0,
        y: 0,
        width: w,
        height: h,
    };

    let point = (point.0 as i32, point.1 as i32);

    // The input region is always within the surface itself, so if the surface itself doesn't contain the
    // point we can return false.
    if !rect.contains(point) {
        return false;
    }

    let input_region = &attrs.user_data.get::<SurfaceData>().unwrap().input_region;

    // If there's no input region, we're done.
    if input_region.is_none() {
        return true;
    }

    input_region.as_ref().unwrap().contains(point)
}
<|endoftext|>


<commit_before>#![feature(test)]
#![cfg(feature = "rand")]

extern crate test;
extern crate num_bigint;
extern crate num_traits;
extern crate num_integer;
extern crate rand;

use std::mem::replace;
use test::Bencher;
use num_bigint::{BigInt, BigUint, RandBigInt};
use num_traits::{Zero, One, FromPrimitive, Num};
use rand::{SeedableRng, StdRng};

fn get_rng() -> StdRng {
    let mut seed = [0; 32];
    for i in 1..32 {
        seed[usize::from(i)] = i;
    }
    SeedableRng::from_seed(seed)
}

fn multiply_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x * &y);
}

fn divide_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x / &y);
}

fn factorial(n: usize) -> BigUint {
    let mut f: BigUint = One::one();
    for i in 1..(n+1) {
        let bu: BigUint = FromPrimitive::from_usize(i).unwrap();
        f = f * bu;
    }
    f
}

/// Compute Fibonacci numbers
fn fib(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        let f2 = f0 + &f1;
        f0 = replace(&mut f1, f2);
    }
    f0
}

/// Compute Fibonacci numbers with two ops per iteration
/// (add and subtract, like issue #200)
fn fib2(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        f1 = f1 + &f0;
        f0 = &f1 - f0;
    }
    f0
}

#[bench]
fn multiply_0(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 8);
}

#[bench]
fn multiply_1(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 16);
}

#[bench]
fn multiply_2(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 16);
}

#[bench]
fn multiply_3(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 17);
}

#[bench]
fn divide_0(b: &mut Bencher) {
    divide_bench(b, 1 << 8, 1 << 6);
}

#[bench]
fn divide_1(b: &mut Bencher) {
    divide_bench(b, 1 << 12, 1 << 8);
}

#[bench]
fn divide_2(b: &mut Bencher) {
    divide_bench(b, 1 << 16, 1 << 12);
}

#[bench]
fn factorial_100(b: &mut Bencher) {
    b.iter(|| factorial(100));
}

#[bench]
fn fib_100(b: &mut Bencher) {
    b.iter(|| fib(100));
}

#[bench]
fn fib_1000(b: &mut Bencher) {
    b.iter(|| fib(1000));
}

#[bench]
fn fib_10000(b: &mut Bencher) {
    b.iter(|| fib(10000));
}

#[bench]
fn fib2_100(b: &mut Bencher) {
    b.iter(|| fib2(100));
}

#[bench]
fn fib2_1000(b: &mut Bencher) {
    b.iter(|| fib2(1000));
}

#[bench]
fn fib2_10000(b: &mut Bencher) {
    b.iter(|| fib2(10000));
}

#[bench]
fn fac_to_string(b: &mut Bencher) {
    let fac = factorial(100);
    b.iter(|| fac.to_string());
}

#[bench]
fn fib_to_string(b: &mut Bencher) {
    let fib = fib(100);
    b.iter(|| fib.to_string());
}

fn to_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    b.iter(|| x.to_str_radix(radix));
}

#[bench]
fn to_str_radix_02(b: &mut Bencher) {
    to_str_radix_bench(b, 2);
}

#[bench]
fn to_str_radix_08(b: &mut Bencher) {
    to_str_radix_bench(b, 8);
}

#[bench]
fn to_str_radix_10(b: &mut Bencher) {
    to_str_radix_bench(b, 10);
}

#[bench]
fn to_str_radix_16(b: &mut Bencher) {
    to_str_radix_bench(b, 16);
}

#[bench]
fn to_str_radix_36(b: &mut Bencher) {
    to_str_radix_bench(b, 36);
}

fn from_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    let s = x.to_str_radix(radix);
    assert_eq!(x, BigInt::from_str_radix(&s, radix).unwrap());
    b.iter(|| BigInt::from_str_radix(&s, radix));
}

#[bench]
fn from_str_radix_02(b: &mut Bencher) {
    from_str_radix_bench(b, 2);
}

#[bench]
fn from_str_radix_08(b: &mut Bencher) {
    from_str_radix_bench(b, 8);
}

#[bench]
fn from_str_radix_10(b: &mut Bencher) {
    from_str_radix_bench(b, 10);
}

#[bench]
fn from_str_radix_16(b: &mut Bencher) {
    from_str_radix_bench(b, 16);
}

#[bench]
fn from_str_radix_36(b: &mut Bencher) {
    from_str_radix_bench(b, 36);
}

fn rand_bench(b: &mut Bencher, bits: usize) {
    let mut rng = get_rng();

    b.iter(|| rng.gen_bigint(bits));
}

#[bench]
fn rand_64(b: &mut Bencher) {
    rand_bench(b, 1 << 6);
}

#[bench]
fn rand_256(b: &mut Bencher) {
    rand_bench(b, 1 << 8);
}

#[bench]
fn rand_1009(b: &mut Bencher) {
    rand_bench(b, 1009);
}

#[bench]
fn rand_2048(b: &mut Bencher) {
    rand_bench(b, 1 << 11);
}

#[bench]
fn rand_4096(b: &mut Bencher) {
    rand_bench(b, 1 << 12);
}

#[bench]
fn rand_8192(b: &mut Bencher) {
    rand_bench(b, 1 << 13);
}

#[bench]
fn rand_65536(b: &mut Bencher) {
    rand_bench(b, 1 << 16);
}

#[bench]
fn rand_131072(b: &mut Bencher) {
    rand_bench(b, 1 << 17);
}

#[bench]
fn shl(b: &mut Bencher) {
    let n = BigUint::one() << 1000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m << i;
        }
    })
}

#[bench]
fn shr(b: &mut Bencher) {
    let n = BigUint::one() << 2000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m >> i;
        }
    })
}

#[bench]
fn hash(b: &mut Bencher) {
    use std::collections::HashSet;
    let mut rng = get_rng();
    let v: Vec<BigInt> = (1000..2000).map(|bits| rng.gen_bigint(bits)).collect();
    b.iter(|| {
        let h: HashSet<&BigInt> = v.iter().collect();
        assert_eq!(h.len(), v.len());
    });
}

#[bench]
fn pow_bench(b: &mut Bencher) {
    b.iter(|| {
        let upper = 100_usize;
        for i in 2..upper + 1 {
            for j in 2..upper + 1 {
                let i_big = BigUint::from_usize(i).unwrap();
                i_big.pow(j);
            }
        }
    });
}

/// This modulus is the prime from the 2048-bit MODP DH group:
/// https://tools.ietf.org/html/rfc3526#section-3
const RFC3526_2048BIT_MODP_GROUP: &'static str = "\
    FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
    29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
    EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
    E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
    EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
    C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
    83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
    670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
    E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
    DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
    15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF";

#[bench]
fn modpow(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap();

    b.iter(|| base.modpow(&e, &m));
}

#[bench]
fn modpow_even(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    // Make the modulus even, so monty (base-2^32) doesn't apply.
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap() - 1u32;

    b.iter(|| base.modpow(&e, &m));
}

#[bench]
fn roots_sqrt(b: &mut Bencher) {
    let mut rng = get_rng();
    let x = rng.gen_biguint(2048);

    b.iter(|| x.sqrt());
}

#[bench]
fn roots_cbrt(b: &mut Bencher) {
    let mut rng = get_rng();
    let x = rng.gen_biguint(2048);

    b.iter(|| x.cbrt());
}

#[bench]
fn roots_nth_100(b: &mut Bencher) {
    let mut rng = get_rng();
    let x = rng.gen_biguint(2048);

    b.iter(|| x.nth_root(100));
}
<commit_msg>Fix benchmark<commit_after>#![feature(test)]
#![cfg(feature = "rand")]

extern crate test;
extern crate num_bigint;
extern crate num_traits;
extern crate num_integer;
extern crate rand;

use std::mem::replace;
use test::Bencher;
use num_bigint::{BigInt, BigUint, RandBigInt};
use num_traits::{Zero, One, FromPrimitive, Num, Pow};
use rand::{SeedableRng, StdRng};

fn get_rng() -> StdRng {
    let mut seed = [0; 32];
    for i in 1..32 {
        seed[usize::from(i)] = i;
    }
    SeedableRng::from_seed(seed)
}

fn multiply_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x * &y);
}

fn divide_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x / &y);
}

fn factorial(n: usize) -> BigUint {
    let mut f: BigUint = One::one();
    for i in 1..(n+1) {
        let bu: BigUint = FromPrimitive::from_usize(i).unwrap();
        f = f * bu;
    }
    f
}

/// Compute Fibonacci numbers
fn fib(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        let f2 = f0 + &f1;
        f0 = replace(&mut f1, f2);
    }
    f0
}

/// Compute Fibonacci numbers with two ops per iteration
/// (add and subtract, like issue #200)
fn fib2(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        f1 = f1 + &f0;
        f0 = &f1 - f0;
    }
    f0
}

#[bench]
fn multiply_0(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 8);
}

#[bench]
fn multiply_1(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 16);
}

#[bench]
fn multiply_2(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 16);
}

#[bench]
fn multiply_3(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 17);
}

#[bench]
fn divide_0(b: &mut Bencher) {
    divide_bench(b, 1 << 8, 1 << 6);
}

#[bench]
fn divide_1(b: &mut Bencher) {
    divide_bench(b, 1 << 12, 1 << 8);
}

#[bench]
fn divide_2(b: &mut Bencher) {
    divide_bench(b, 1 << 16, 1 << 12);
}

#[bench]
fn factorial_100(b: &mut Bencher) {
    b.iter(|| factorial(100));
}

#[bench]
fn fib_100(b: &mut Bencher) {
    b.iter(|| fib(100));
}

#[bench]
fn fib_1000(b: &mut Bencher) {
    b.iter(|| fib(1000));
}

#[bench]
fn fib_10000(b: &mut Bencher) {
    b.iter(|| fib(10000));
}

#[bench]
fn fib2_100(b: &mut Bencher) {
    b.iter(|| fib2(100));
}

#[bench]
fn fib2_1000(b: &mut Bencher) {
    b.iter(|| fib2(1000));
}

#[bench]
fn fib2_10000(b: &mut Bencher) {
    b.iter(|| fib2(10000));
}

#[bench]
fn fac_to_string(b: &mut Bencher) {
    let fac = factorial(100);
    b.iter(|| fac.to_string());
}

#[bench]
fn fib_to_string(b: &mut Bencher) {
    let fib = fib(100);
    b.iter(|| fib.to_string());
}

fn to_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    b.iter(|| x.to_str_radix(radix));
}

#[bench]
fn to_str_radix_02(b: &mut Bencher) {
    to_str_radix_bench(b, 2);
}

#[bench]
fn to_str_radix_08(b: &mut Bencher) {
    to_str_radix_bench(b, 8);
}

#[bench]
fn to_str_radix_10(b: &mut Bencher) {
    to_str_radix_bench(b, 10);
}

#[bench]
fn to_str_radix_16(b: &mut Bencher) {
    to_str_radix_bench(b, 16);
}

#[bench]
fn to_str_radix_36(b: &mut Bencher) {
    to_str_radix_bench(b, 36);
}

fn from_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    let s = x.to_str_radix(radix);
    assert_eq!(x, BigInt::from_str_radix(&s, radix).unwrap());
    b.iter(|| BigInt::from_str_radix(&s, radix));
}

#[bench]
fn from_str_radix_02(b: &mut Bencher) {
    from_str_radix_bench(b, 2);
}

#[bench]
fn from_str_radix_08(b: &mut Bencher) {
    from_str_radix_bench(b, 8);
}

#[bench]
fn from_str_radix_10(b: &mut Bencher) {
    from_str_radix_bench(b, 10);
}

#[bench]
fn from_str_radix_16(b: &mut Bencher) {
    from_str_radix_bench(b, 16);
}

#[bench]
fn from_str_radix_36(b: &mut Bencher) {
    from_str_radix_bench(b, 36);
}

fn rand_bench(b: &mut Bencher, bits: usize) {
    let mut rng = get_rng();

    b.iter(|| rng.gen_bigint(bits));
}

#[bench]
fn rand_64(b: &mut Bencher) {
    rand_bench(b, 1 << 6);
}

#[bench]
fn rand_256(b: &mut Bencher) {
    rand_bench(b, 1 << 8);
}

#[bench]
fn rand_1009(b: &mut Bencher) {
    rand_bench(b, 1009);
}

#[bench]
fn rand_2048(b: &mut Bencher) {
    rand_bench(b, 1 << 11);
}

#[bench]
fn rand_4096(b: &mut Bencher) {
    rand_bench(b, 1 << 12);
}

#[bench]
fn rand_8192(b: &mut Bencher) {
    rand_bench(b, 1 << 13);
}

#[bench]
fn rand_65536(b: &mut Bencher) {
    rand_bench(b, 1 << 16);
}

#[bench]
fn rand_131072(b: &mut Bencher) {
    rand_bench(b, 1 << 17);
}

#[bench]
fn shl(b: &mut Bencher) {
    let n = BigUint::one() << 1000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m << i;
        }
    })
}

#[bench]
fn shr(b: &mut Bencher) {
    let n = BigUint::one() << 2000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m >> i;
        }
    })
}

#[bench]
fn hash(b: &mut Bencher) {
    use std::collections::HashSet;
    let mut rng = get_rng();
    let v: Vec<BigInt> = (1000..2000).map(|bits| rng.gen_bigint(bits)).collect();
    b.iter(|| {
        let h: HashSet<&BigInt> = v.iter().collect();
        assert_eq!(h.len(), v.len());
    });
}

#[bench]
fn pow_bench(b: &mut Bencher) {
    b.iter(|| {
        let upper = 100_usize;
        for i in 2..upper + 1 {
            for j in 2..upper + 1 {
                let i_big = BigUint::from_usize(i).unwrap();
                i_big.pow(j);
            }
        }
    });
}

/// This modulus is the prime from the 2048-bit MODP DH group:
/// https://tools.ietf.org/html/rfc3526#section-3
const RFC3526_2048BIT_MODP_GROUP: &'static str = "\
    FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
    29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
    EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
    E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
    EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
    C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
    83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
    670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
    E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
    DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
    15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF";

#[bench]
fn modpow(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap();

    b.iter(|| base.modpow(&e, &m));
}

#[bench]
fn modpow_even(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    // Make the modulus even, so monty (base-2^32) doesn't apply.
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap() - 1u32;

    b.iter(|| base.modpow(&e, &m));
}

#[bench]
fn roots_sqrt(b: &mut Bencher) {
    let mut rng = get_rng();
    let x = rng.gen_biguint(2048);

    b.iter(|| x.sqrt());
}

#[bench]
fn roots_cbrt(b: &mut Bencher) {
    let mut rng = get_rng();
    let x = rng.gen_biguint(2048);

    b.iter(|| x.cbrt());
}

#[bench]
fn roots_nth_100(b: &mut Bencher) {
    let mut rng = get_rng();
    let x = rng.gen_biguint(2048);

    b.iter(|| x.nth_root(100));
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate env_logger;
extern crate futuremio;
extern crate ssl;

#[macro_use]
extern crate cfg_if;

use std::io::Error;
use std::net::ToSocketAddrs;

use futures::Future;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
    })
}

cfg_if! {
    if #[cfg(any(feature = "force-openssl",
                 all(not(target_os = "macos"),
                     not(target_os = "windows"))))] {
        extern crate openssl;

        use openssl::ssl as ossl;

        fn get(err: &Error) -> &[ossl::error::OpenSslError] {
            let err = err.get_ref().unwrap();
            match *err.downcast_ref::<ossl::error::Error>().unwrap() {
                ossl::Error::Ssl(ref v) => v,
                ref e => panic!("not an ssl eror: {:?}", e),
            }
        }

        fn verify_failed(err: &Error) {
            assert!(get(err).iter().any(|e| {
                e.reason() == "certificate verify failed"
            }), "bad errors: {:?}", err);
        }

        use verify_failed as assert_expired_error;
        use verify_failed as assert_wrong_host;
        use verify_failed as assert_self_signed;
        use verify_failed as assert_untrusted_root;

        fn assert_dh_too_small(err: &Error) {
            assert!(get(err).iter().any(|e| {
                e.reason() == "dh key too small"
            }), "bad errors: {:?}", err);
        }
    } else if #[cfg(target_os = "macos")] {
        extern crate security_framework;

        use security_framework::base::Error as SfError;

        fn assert_invalid_cert_chain(err: &Error) {
            let err = err.get_ref().unwrap();
            let err = err.downcast_ref::<SfError>().unwrap();
            assert_eq!(err.message().unwrap(), "invalid certificate chain");
        }

        use assert_invalid_cert_chain as assert_expired_error;
        use assert_invalid_cert_chain as assert_wrong_host;
        use assert_invalid_cert_chain as assert_self_signed;
        use assert_invalid_cert_chain as assert_untrusted_root;

        fn assert_dh_too_small(err: &Error) {
            let err = err.get_ref().unwrap();
            let err = err.downcast_ref::<SfError>().unwrap();
            assert_eq!(err.message().unwrap(), "handshake failure");
        }
    } else {
        extern crate winapi;

        fn assert_expired_error(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_CN_NO_MATCH as usize);
        }

        fn assert_wrong_host(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_CN_NO_MATCH as usize);
        }

        fn assert_self_signed(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_CN_NO_MATCH as usize);
        }

        fn assert_untrusted_root(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_CN_NO_MATCH as usize);
        }

        fn assert_dh_too_small(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_CN_NO_MATCH as usize);
        }
    }
}

fn get_host(host: &'static str) -> Error {
    drop(env_logger::init());

    let addr = format!("{}:443", host);
    let addr = t!(addr.to_socket_addrs()).next().unwrap();

    let mut l = t!(futuremio::Loop::new());
    let client = l.handle().tcp_connect(&addr);
    let data = client.and_then(move |socket| {
        t!(ssl::ClientContext::new()).handshake(host, socket)
    });

    let res = l.run(data);
    assert!(res.is_err());
    res.err().unwrap()
}

#[test]
fn expired() {
    assert_expired_error(&get_host("expired.badssl.com"))
}

#[test]
fn wrong_host() {
    assert_wrong_host(&get_host("wrong.host.badssl.com"))
}

#[test]
fn self_signed() {
    assert_self_signed(&get_host("self-signed.badssl.com"))
}

#[test]
fn untrusted_root() {
    assert_untrusted_root(&get_host("untrusted-root.badssl.com"))
}

#[test]
fn dh_too_small() {
    assert_dh_too_small(&get_host("dh480.badssl.com"))
}
<commit_msg>Fix bad SSL tests on Windows<commit_after>extern crate futures;
extern crate env_logger;
extern crate futuremio;
extern crate ssl;

#[macro_use]
extern crate cfg_if;

use std::io::Error;
use std::net::ToSocketAddrs;

use futures::Future;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
    })
}

cfg_if! {
    if #[cfg(any(feature = "force-openssl",
                 all(not(target_os = "macos"),
                     not(target_os = "windows"))))] {
        extern crate openssl;

        use openssl::ssl as ossl;

        fn get(err: &Error) -> &[ossl::error::OpenSslError] {
            let err = err.get_ref().unwrap();
            match *err.downcast_ref::<ossl::error::Error>().unwrap() {
                ossl::Error::Ssl(ref v) => v,
                ref e => panic!("not an ssl eror: {:?}", e),
            }
        }

        fn verify_failed(err: &Error) {
            assert!(get(err).iter().any(|e| {
                e.reason() == "certificate verify failed"
            }), "bad errors: {:?}", err);
        }

        use verify_failed as assert_expired_error;
        use verify_failed as assert_wrong_host;
        use verify_failed as assert_self_signed;
        use verify_failed as assert_untrusted_root;

        fn assert_dh_too_small(err: &Error) {
            assert!(get(err).iter().any(|e| {
                e.reason() == "dh key too small"
            }), "bad errors: {:?}", err);
        }
    } else if #[cfg(target_os = "macos")] {
        extern crate security_framework;

        use security_framework::base::Error as SfError;

        fn assert_invalid_cert_chain(err: &Error) {
            let err = err.get_ref().unwrap();
            let err = err.downcast_ref::<SfError>().unwrap();
            assert_eq!(err.message().unwrap(), "invalid certificate chain");
        }

        use assert_invalid_cert_chain as assert_expired_error;
        use assert_invalid_cert_chain as assert_wrong_host;
        use assert_invalid_cert_chain as assert_self_signed;
        use assert_invalid_cert_chain as assert_untrusted_root;

        fn assert_dh_too_small(err: &Error) {
            let err = err.get_ref().unwrap();
            let err = err.downcast_ref::<SfError>().unwrap();
            assert_eq!(err.message().unwrap(), "handshake failure");
        }
    } else {
        extern crate winapi;

        fn assert_expired_error(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_EXPIRED as usize);
        }

        fn assert_wrong_host(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_CN_NO_MATCH as usize);
        }

        fn assert_self_signed(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_UNTRUSTEDROOT as usize);
        }

        fn assert_untrusted_root(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::CERT_E_UNTRUSTEDROOT as usize);
        }

        fn assert_dh_too_small(err: &Error) {
            let code = err.raw_os_error().unwrap();
            assert_eq!(code as usize, winapi::SEC_E_INTERNAL_ERROR as usize);
        }
    }
}

fn get_host(host: &'static str) -> Error {
    drop(env_logger::init());

    let addr = format!("{}:443", host);
    let addr = t!(addr.to_socket_addrs()).next().unwrap();

    let mut l = t!(futuremio::Loop::new());
    let client = l.handle().tcp_connect(&addr);
    let data = client.and_then(move |socket| {
        t!(ssl::ClientContext::new()).handshake(host, socket)
    });

    let res = l.run(data);
    assert!(res.is_err());
    res.err().unwrap()
}

#[test]
fn expired() {
    assert_expired_error(&get_host("expired.badssl.com"))
}

#[test]
fn wrong_host() {
    assert_wrong_host(&get_host("wrong.host.badssl.com"))
}

#[test]
fn self_signed() {
    assert_self_signed(&get_host("self-signed.badssl.com"))
}

#[test]
fn untrusted_root() {
    assert_untrusted_root(&get_host("untrusted-root.badssl.com"))
}

#[test]
fn dh_too_small() {
    assert_dh_too_small(&get_host("dh480.badssl.com"))
}
<|endoftext|>