<commit_before>// These are taken from http://en.wikipedia.org/wiki/List_of_HTTP_Status_Codes.
// Last updated on 2013-07-04
// Entries from third-party vendors not standardised upon are not included.
// If not specified otherwise, they are defined in RFC 2616.

// Yes, this is ugly code.
// No, I don't mind.
// That was easy. :-)

use std::ascii::StrAsciiExt;
use std::hashmap::HashSet;
use std::either::{Either, Left, Right};
use std::vec;
use super::get_writer;

type HeadingOrStatus = Either<&'static str, Status>;

struct Status {
    code: uint,
    reason: &'static str,
    comment: Option<&'static str>,
}

/// Status with comment
fn StatusC(code: uint, reason: &'static str, comment: &'static str) -> HeadingOrStatus {
    Right(Status { code: code, reason: reason, comment: Some(comment) })
}

/// Status without comment
fn StatusN(code: uint, reason: &'static str) -> HeadingOrStatus {
    Right(Status { code: code, reason: reason, comment: None })
}

impl Status {
    fn ident(&self) -> ~str {
        camel_case(self.reason)
    }

    fn padded_ident(&self) -> ~str {
        self.ident() + " ".repeat(unsafe { longest_ident } - self.ident().len())
    }

    fn reason_padding_spaces(&self) -> ~str {
        " ".repeat(unsafe { longest_reason } - self.reason.len())
    }
}

/// >>> camel_case("I'm a Tea-pot")
/// "ImATeaPot"
fn camel_case(msg: &str) -> ~str {
    let msg = msg.replace("-", " ").replace("'", "");
    let mut result: ~[Ascii] = vec::with_capacity(msg.len());
    let mut capitalise = true;
    for c in msg.iter() {
        let c = match capitalise {
            true => c.to_ascii().to_upper(),
            false => c.to_ascii().to_lower(),
        };
        // For a space, capitalise the next char
        capitalise = c.to_byte() == (' ' as u8);
        if !capitalise {  // And also, for a space, don't keep it
            result.push(c);
        }
    }
    result.to_str_ascii()
}

static mut longest_ident: uint = 0;
static mut longest_reason: uint = 0;

pub fn generate(output_dir: &Path) {
    let out = get_writer(output_dir, "status.rs");
    let entries = [
        Left("1xx Informational"),
        StatusN(100, "Continue"),
        StatusN(101, "Switching Protocols"),
        StatusC(102, "Processing", "WebDAV; RFC 2518"),

        Left("2xx Success"),
        StatusN(200, "OK"),
        StatusN(201, "Created"),
        StatusN(202, "Accepted"),
        StatusC(203, "Non-Authoritative Information", "since HTTP/1.1"),
        StatusN(204, "No Content"),
        StatusN(205, "Reset Content"),
        StatusN(206, "Partial Content"),
        StatusC(207, "Multi-Status", "WebDAV; RFC 4918"),
        StatusC(208, "Already Reported", "WebDAV; RFC 5842"),
        StatusC(226, "IM Used", "RFC 3229"),

        Left("3xx Redirection"),
        StatusN(300, "Multiple Choices"),
        StatusN(301, "Moved Permanently"),
        StatusN(302, "Found"),
        StatusC(303, "See Other", "since HTTP/1.1"),
        StatusN(304, "Not Modified"),
        StatusC(305, "Use Proxy", "since HTTP/1.1"),
        StatusN(306, "Switch Proxy"),
        StatusC(307, "Temporary Redirect", "since HTTP/1.1"),
        StatusC(308, "Permanent Redirect", "approved as experimental RFC: http://tools.ietf.org/html/draft-reschke-http-status-308"),

        Left("4xx Client Error"),
        StatusN(400, "Bad Request"),
        StatusN(401, "Unauthorized"),
        StatusN(402, "Payment Required"),
        StatusN(403, "Forbidden"),
        StatusN(404, "Not Found"),
        StatusN(405, "Method Not Allowed"),
        StatusN(406, "Not Acceptable"),
        StatusN(407, "Proxy Authentication Required"),
        StatusN(408, "Request Timeout"),
        StatusN(409, "Conflict"),
        StatusN(410, "Gone"),
        StatusN(411, "Length Required"),
        StatusN(412, "Precondition Failed"),
        StatusN(413, "Request Entity Too Large"),
        StatusN(414, "Request-URI Too Long"),
        StatusN(415, "Unsupported Media Type"),
        StatusN(416, "Requested Range Not Satisfiable"),
        StatusN(417, "Expectation Failed"),
        StatusC(418, "I'm a teapot", "RFC 2324"),
        StatusN(419, "Authentication Timeout"),
        StatusC(422, "Unprocessable Entity", "WebDAV; RFC 4918"),
        StatusC(423, "Locked", "WebDAV; RFC 4918"),
        StatusC(424, "Failed Dependency", "WebDAV; RFC 4918"),
        StatusC(424, "Method Failure", "WebDAV"),
        StatusC(425, "Unordered Collection", "Internet draft"),
        StatusC(426, "Upgrade Required", "RFC 2817"),
        StatusC(428, "Precondition Required", "RFC 6585"),
        StatusC(429, "Too Many Requests", "RFC 6585"),
        StatusC(431, "Request Header Fields Too Large", "RFC 6585"),
        StatusC(451, "Unavailable For Legal Reasons", "Internet draft"),

        Left("5xx Server Error"),
        StatusN(500, "Internal Server Error"),
        StatusN(501, "Not Implemented"),
        StatusN(502, "Bad Gateway"),
        StatusN(503, "Service Unavailable"),
        StatusN(504, "Gateway Timeout"),
        StatusN(505, "HTTP Version Not Supported"),
        StatusC(506, "Variant Also Negotiates", "RFC 2295"),
        StatusC(507, "Insufficient Storage", "WebDAV; RFC 4918"),
        StatusC(508, "Loop Detected", "WebDAV; RFC 5842"),
        StatusC(510, "Not Extended", "RFC 2774"),
        StatusC(511, "Network Authentication Required", "RFC 6585"),
        ];
    unsafe {
        longest_ident = entries.iter().map(|&e| match e {
            Left(_heading) => 0,
            Right(status) => status.ident().len(),
        }).max_by(|&i| i).unwrap();
        longest_reason = entries.iter().map(|&e| match e {
            Left(_heading) => 0,
            Right(status) => status.reason.len(),
        }).max_by(|&i| i).unwrap();
    }
    out.write_str("// This file is automatically generated file is used as http::status.

use std::fmt;
use std::num::IntConvertible;
use std::ascii::StrAsciiExt;

/// HTTP status code
#[deriving(Eq)]
pub enum Status {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n    // {}\n", heading)),
            Right(status) => match status.comment {
                None => out.write_str(format!("    {},\n", status.ident())),
                Some(comment) => out.write_str(format!("    {},  // {}\n", status.ident(), comment)),
            },
        }
    }

    out.write_str("
    UnregisteredStatus(u16, ~str),
}

impl Status {

    /// Get the status code
    pub fn code(&self) -> u16 {
        match *self {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => out.write_str(format!("            {} => {},\n",
                                                status.padded_ident(), status.code)),
        }
    }
    out.write_str("
            UnregisteredStatus(code, _)   => code,
        }
    }

    /// Get the reason phrase
    pub fn reason(&self) -> ~str {
        match *self {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => out.write_str(format!("            {} => ~\"{}\",\n",
                                                status.padded_ident(), status.reason))
        }
    }
    out.write_str("
            UnregisteredStatus(_, ref reason) => (*reason).clone(),
        }
    }

    /// Get a status from the code and reason
    pub fn from_code_and_reason(status: u16, reason: ~str) -> Status {
        let reason_lower = reason.to_ascii_lower();
        match (status, reason_lower.as_slice()) {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => out.write_str(format!("            ({}, \"{}\"){} => {},\n",
                                                status.code,
                                                status.reason.to_ascii_lower(),
                                                status.reason_padding_spaces(),
                                                status.ident())),
        }
    }
    out.write_str("
            (_, _) => UnregisteredStatus(status, reason),
        }
    }
}

impl ToStr for Status {
    /// Produce the HTTP status message incorporating both code and message,
    /// e.g. `ImATeapot.to_str() == \"418 I'm a teapot\"`
	fn to_str(&self) -> ~str {
		format!(\"{} {}\", self.code().to_str(), self.reason())
	}
}

impl fmt::Default for Status {
    fn fmt(s: &Status, f: &mut fmt::Formatter) {
        write!(f.buf, \"{} {}\", s.code(), s.reason());
    }
}

impl fmt::Unsigned for Status {
    fn fmt(s: &Status, f: &mut fmt::Formatter) {
        fmt::Unsigned::fmt(&s.code(), f);
    }
}

impl IntConvertible for Status {

    /// Equivalent to `self.code()`
    fn to_int(&self) -> int {
        self.code() as int
    }

    /// Get a *registered* status code from the number of its status code.
    ///
    /// This will fail if an unknown code is passed.
    ///
    /// For example, `from_int(200)` will return `OK`.
    fn from_int(n: int) -> Status {
        match n {
");
    let mut matched_numbers = HashSet::new();
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => {
                if !matched_numbers.contains(&status.code) {
                    // Purpose: FailedDependency and MethodFailure both use 424,
                    // but clearly they mustn't both go in here
                    out.write_str(format!("            {:u} => {},\n", status.code, status.ident()));
                    matched_numbers.insert(status.code);
                }
            },
        }
    }
    out.write_str("
            _   => { fail!(format!(\"No registered HTTP status code {}\", n)); }
        }
    }
}");
}
<commit_msg>Replace IntConvertible with {From,To}Primitive.<commit_after>// These are taken from http://en.wikipedia.org/wiki/List_of_HTTP_Status_Codes.
// Last updated on 2013-07-04
// Entries from third-party vendors not standardised upon are not included.
// If not specified otherwise, they are defined in RFC 2616.

// Yes, this is ugly code.
// No, I don't mind.
// That was easy. :-)

use std::ascii::StrAsciiExt;
use std::hashmap::HashSet;
use std::either::{Either, Left, Right};
use std::vec;
use super::get_writer;

type HeadingOrStatus = Either<&'static str, Status>;

struct Status {
    code: uint,
    reason: &'static str,
    comment: Option<&'static str>,
}

/// Status with comment
fn StatusC(code: uint, reason: &'static str, comment: &'static str) -> HeadingOrStatus {
    Right(Status { code: code, reason: reason, comment: Some(comment) })
}

/// Status without comment
fn StatusN(code: uint, reason: &'static str) -> HeadingOrStatus {
    Right(Status { code: code, reason: reason, comment: None })
}

impl Status {
    fn ident(&self) -> ~str {
        camel_case(self.reason)
    }

    fn padded_ident(&self) -> ~str {
        self.ident() + " ".repeat(unsafe { longest_ident } - self.ident().len())
    }

    fn reason_padding_spaces(&self) -> ~str {
        " ".repeat(unsafe { longest_reason } - self.reason.len())
    }
}

/// >>> camel_case("I'm a Tea-pot")
/// "ImATeaPot"
fn camel_case(msg: &str) -> ~str {
    let msg = msg.replace("-", " ").replace("'", "");
    let mut result: ~[Ascii] = vec::with_capacity(msg.len());
    let mut capitalise = true;
    for c in msg.iter() {
        let c = match capitalise {
            true => c.to_ascii().to_upper(),
            false => c.to_ascii().to_lower(),
        };
        // For a space, capitalise the next char
        capitalise = c.to_byte() == (' ' as u8);
        if !capitalise {  // And also, for a space, don't keep it
            result.push(c);
        }
    }
    result.to_str_ascii()
}

static mut longest_ident: uint = 0;
static mut longest_reason: uint = 0;

pub fn generate(output_dir: &Path) {
    let out = get_writer(output_dir, "status.rs");
    let entries = [
        Left("1xx Informational"),
        StatusN(100, "Continue"),
        StatusN(101, "Switching Protocols"),
        StatusC(102, "Processing", "WebDAV; RFC 2518"),

        Left("2xx Success"),
        StatusN(200, "OK"),
        StatusN(201, "Created"),
        StatusN(202, "Accepted"),
        StatusC(203, "Non-Authoritative Information", "since HTTP/1.1"),
        StatusN(204, "No Content"),
        StatusN(205, "Reset Content"),
        StatusN(206, "Partial Content"),
        StatusC(207, "Multi-Status", "WebDAV; RFC 4918"),
        StatusC(208, "Already Reported", "WebDAV; RFC 5842"),
        StatusC(226, "IM Used", "RFC 3229"),

        Left("3xx Redirection"),
        StatusN(300, "Multiple Choices"),
        StatusN(301, "Moved Permanently"),
        StatusN(302, "Found"),
        StatusC(303, "See Other", "since HTTP/1.1"),
        StatusN(304, "Not Modified"),
        StatusC(305, "Use Proxy", "since HTTP/1.1"),
        StatusN(306, "Switch Proxy"),
        StatusC(307, "Temporary Redirect", "since HTTP/1.1"),
        StatusC(308, "Permanent Redirect", "approved as experimental RFC: http://tools.ietf.org/html/draft-reschke-http-status-308"),

        Left("4xx Client Error"),
        StatusN(400, "Bad Request"),
        StatusN(401, "Unauthorized"),
        StatusN(402, "Payment Required"),
        StatusN(403, "Forbidden"),
        StatusN(404, "Not Found"),
        StatusN(405, "Method Not Allowed"),
        StatusN(406, "Not Acceptable"),
        StatusN(407, "Proxy Authentication Required"),
        StatusN(408, "Request Timeout"),
        StatusN(409, "Conflict"),
        StatusN(410, "Gone"),
        StatusN(411, "Length Required"),
        StatusN(412, "Precondition Failed"),
        StatusN(413, "Request Entity Too Large"),
        StatusN(414, "Request-URI Too Long"),
        StatusN(415, "Unsupported Media Type"),
        StatusN(416, "Requested Range Not Satisfiable"),
        StatusN(417, "Expectation Failed"),
        StatusC(418, "I'm a teapot", "RFC 2324"),
        StatusN(419, "Authentication Timeout"),
        StatusC(422, "Unprocessable Entity", "WebDAV; RFC 4918"),
        StatusC(423, "Locked", "WebDAV; RFC 4918"),
        StatusC(424, "Failed Dependency", "WebDAV; RFC 4918"),
        StatusC(424, "Method Failure", "WebDAV"),
        StatusC(425, "Unordered Collection", "Internet draft"),
        StatusC(426, "Upgrade Required", "RFC 2817"),
        StatusC(428, "Precondition Required", "RFC 6585"),
        StatusC(429, "Too Many Requests", "RFC 6585"),
        StatusC(431, "Request Header Fields Too Large", "RFC 6585"),
        StatusC(451, "Unavailable For Legal Reasons", "Internet draft"),

        Left("5xx Server Error"),
        StatusN(500, "Internal Server Error"),
        StatusN(501, "Not Implemented"),
        StatusN(502, "Bad Gateway"),
        StatusN(503, "Service Unavailable"),
        StatusN(504, "Gateway Timeout"),
        StatusN(505, "HTTP Version Not Supported"),
        StatusC(506, "Variant Also Negotiates", "RFC 2295"),
        StatusC(507, "Insufficient Storage", "WebDAV; RFC 4918"),
        StatusC(508, "Loop Detected", "WebDAV; RFC 5842"),
        StatusC(510, "Not Extended", "RFC 2774"),
        StatusC(511, "Network Authentication Required", "RFC 6585"),
        ];
    unsafe {
        longest_ident = entries.iter().map(|&e| match e {
            Left(_heading) => 0,
            Right(status) => status.ident().len(),
        }).max_by(|&i| i).unwrap();
        longest_reason = entries.iter().map(|&e| match e {
            Left(_heading) => 0,
            Right(status) => status.reason.len(),
        }).max_by(|&i| i).unwrap();
    }
    out.write_str("// This file is automatically generated file is used as http::status.

use std::fmt;
use std::ascii::StrAsciiExt;

/// HTTP status code
#[deriving(Eq)]
pub enum Status {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n    // {}\n", heading)),
            Right(status) => match status.comment {
                None => out.write_str(format!("    {},\n", status.ident())),
                Some(comment) => out.write_str(format!("    {},  // {}\n", status.ident(), comment)),
            },
        }
    }

    out.write_str("
    UnregisteredStatus(u16, ~str),
}

impl Status {

    /// Get the status code
    pub fn code(&self) -> u16 {
        match *self {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => out.write_str(format!("            {} => {},\n",
                                                status.padded_ident(), status.code)),
        }
    }
    out.write_str("
            UnregisteredStatus(code, _)   => code,
        }
    }

    /// Get the reason phrase
    pub fn reason(&self) -> ~str {
        match *self {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => out.write_str(format!("            {} => ~\"{}\",\n",
                                                status.padded_ident(), status.reason))
        }
    }
    out.write_str("
            UnregisteredStatus(_, ref reason) => (*reason).clone(),
        }
    }

    /// Get a status from the code and reason
    pub fn from_code_and_reason(status: u16, reason: ~str) -> Status {
        let reason_lower = reason.to_ascii_lower();
        match (status, reason_lower.as_slice()) {
");
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => out.write_str(format!("            ({}, \"{}\"){} => {},\n",
                                                status.code,
                                                status.reason.to_ascii_lower(),
                                                status.reason_padding_spaces(),
                                                status.ident())),
        }
    }
    out.write_str("
            (_, _) => UnregisteredStatus(status, reason),
        }
    }
}

impl ToStr for Status {
    /// Produce the HTTP status message incorporating both code and message,
    /// e.g. `ImATeapot.to_str() == \"418 I'm a teapot\"`
	fn to_str(&self) -> ~str {
		format!(\"{} {}\", self.code().to_str(), self.reason())
	}
}

impl fmt::Default for Status {
    fn fmt(s: &Status, f: &mut fmt::Formatter) {
        write!(f.buf, \"{} {}\", s.code(), s.reason());
    }
}

impl fmt::Unsigned for Status {
    fn fmt(s: &Status, f: &mut fmt::Formatter) {
        fmt::Unsigned::fmt(&s.code(), f);
    }
}

impl ToPrimitive for Status {

    /// Equivalent to `Some(self.code() as i64)`
    fn to_i64(&self) -> Option<i64> {
        Some(self.code() as i64)
    }

    /// Equivalent to `Some(self.code() as u64)`
    fn to_u64(&self) -> Option<u64> {
        Some(self.code() as u64)
    }
}

impl FromPrimitive for Status {
    /// Get a *registered* status code from the number of its status code.
    ///
    /// This will return None if an unknown (or negative, which are invalid) code is passed.
    ///
    /// For example, `from_i64(200)` will return `OK`.
    fn from_i64(n: i64) -> Option<Status> {
        if n < 0 {
            None
        } else {
            FromPrimitive::from_u64(n as u64)
        }
    }

    /// Get a *registered* status code from the number of its status code.
    ///
    /// This will return None if an unknown code is passed.
    ///
    /// For example, `from_u64(200)` will return `OK`.
    fn from_u64(n: u64) -> Option<Status> {
        Some(match n {
");
    let mut matched_numbers = HashSet::new();
    for &entry in entries.iter() {
        match entry {
            Left(heading) => out.write_str(format!("\n            // {}\n", heading)),
            Right(status) => {
                if !matched_numbers.contains(&status.code) {
                    // Purpose: FailedDependency and MethodFailure both use 424,
                    // but clearly they mustn't both go in here
                    out.write_str(format!("            {:u} => {},\n", status.code, status.ident()));
                    matched_numbers.insert(status.code);
                }
            },
        }
    }
    out.write_str("
            _   => { return None }
        })
    }
}");
}
<|endoftext|>


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

//! Enforces the Rust effect system. Currently there is just one effect,
/// `unsafe`.
use self::UnsafeContext::*;

use middle::def;
use middle::ty::{self, Ty};
use middle::ty::MethodCall;
use util::ppaux;

use syntax::ast;
use syntax::ast_util::PostExpansionMethod;
use syntax::codemap::Span;
use syntax::visit;
use syntax::visit::Visitor;

#[derive(Copy, PartialEq)]
enum UnsafeContext {
    SafeContext,
    UnsafeFn,
    UnsafeBlock(ast::NodeId),
}

fn type_is_unsafe_function(ty: Ty) -> bool {
    match ty.sty {
        ty::ty_bare_fn(_, ref f) => f.unsafety == ast::Unsafety::Unsafe,
        _ => false,
    }
}

struct EffectCheckVisitor<'a, 'tcx: 'a> {
    tcx: &'a ty::ctxt<'tcx>,

    /// Whether we're in an unsafe context.
    unsafe_context: UnsafeContext,
}

impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
    fn require_unsafe(&mut self, span: Span, description: &str) {
        match self.unsafe_context {
            SafeContext => {
                // Report an error.
                span_err!(self.tcx.sess, span, E0133,
                          "{} requires unsafe function or block",
                          description);
            }
            UnsafeBlock(block_id) => {
                // OK, but record this.
                debug!("effect: recording unsafe block as used: {}", block_id);
                self.tcx.used_unsafe.borrow_mut().insert(block_id);
            }
            UnsafeFn => {}
        }
    }

    fn check_str_index(&mut self, e: &ast::Expr) {
        let base_type = match e.node {
            ast::ExprIndex(ref base, _) => ty::node_id_to_type(self.tcx, base.id),
            _ => return
        };
        debug!("effect: checking index with base type {}",
                ppaux::ty_to_string(self.tcx, base_type));
        match base_type.sty {
            ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => if ty::ty_str == ty.sty {
                span_err!(self.tcx.sess, e.span, E0134,
                          "modification of string types is not allowed");
            },
            ty::ty_str => {
                span_err!(self.tcx.sess, e.span, E0135,
                          "modification of string types is not allowed");
            }
            _ => {}
        }
    }
}

impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
    fn visit_fn(&mut self, fn_kind: visit::FnKind<'v>, fn_decl: &'v ast::FnDecl,
                block: &'v ast::Block, span: Span, _: ast::NodeId) {

        let (is_item_fn, is_unsafe_fn) = match fn_kind {
            visit::FkItemFn(_, _, fn_style, _) =>
                (true, fn_style == ast::Unsafety::Unsafe),
            visit::FkMethod(_, _, method) =>
                (true, method.pe_unsafety() == ast::Unsafety::Unsafe),
            _ => (false, false),
        };

        let old_unsafe_context = self.unsafe_context;
        if is_unsafe_fn {
            self.unsafe_context = UnsafeFn
        } else if is_item_fn {
            self.unsafe_context = SafeContext
        }

        visit::walk_fn(self, fn_kind, fn_decl, block, span);

        self.unsafe_context = old_unsafe_context
    }

    fn visit_block(&mut self, block: &ast::Block) {
        let old_unsafe_context = self.unsafe_context;
        match block.rules {
            ast::DefaultBlock => {}
            ast::UnsafeBlock(source) => {
                // By default only the outermost `unsafe` block is
                // "used" and so nested unsafe blocks are pointless
                // (the inner ones are unnecessary and we actually
                // warn about them). As such, there are two cases when
                // we need to create a new context, when we're
                // - outside `unsafe` and found a `unsafe` block
                //   (normal case)
                // - inside `unsafe`, found an `unsafe` block
                //   created internally to the compiler
                //
                // The second case is necessary to ensure that the
                // compiler `unsafe` blocks don't accidentally "use"
                // external blocks (e.g. `unsafe { println("") }`,
                // expands to `unsafe { ... unsafe { ... } }` where
                // the inner one is compiler generated).
                if self.unsafe_context == SafeContext || source == ast::CompilerGenerated {
                    self.unsafe_context = UnsafeBlock(block.id)
                }
            }
        }

        visit::walk_block(self, block);

        self.unsafe_context = old_unsafe_context
    }

    fn visit_expr(&mut self, expr: &ast::Expr) {
        match expr.node {
            ast::ExprMethodCall(_, _, _) => {
                let method_call = MethodCall::expr(expr.id);
                let base_type = (*self.tcx.method_map.borrow())[method_call].ty;
                debug!("effect: method call case, base type is {}",
                       ppaux::ty_to_string(self.tcx, base_type));
                if type_is_unsafe_function(base_type) {
                    self.require_unsafe(expr.span,
                                        "invocation of unsafe method")
                }
            }
            ast::ExprCall(ref base, _) => {
                let base_type = ty::node_id_to_type(self.tcx, base.id);
                debug!("effect: call case, base type is {}",
                       ppaux::ty_to_string(self.tcx, base_type));
                if type_is_unsafe_function(base_type) {
                    self.require_unsafe(expr.span, "call to unsafe function")
                }
            }
            ast::ExprUnary(ast::UnDeref, ref base) => {
                let base_type = ty::node_id_to_type(self.tcx, base.id);
                debug!("effect: unary case, base type is {}",
                       ppaux::ty_to_string(self.tcx, base_type));
                if let ty::ty_ptr(_) = base_type.sty {
                    self.require_unsafe(expr.span, "dereference of unsafe pointer")
                }
            }
            ast::ExprAssign(ref base, _) | ast::ExprAssignOp(_, ref base, _) => {
                self.check_str_index(&**base);
            }
            ast::ExprAddrOf(ast::MutMutable, ref base) => {
                self.check_str_index(&**base);
            }
            ast::ExprInlineAsm(..) => {
                self.require_unsafe(expr.span, "use of inline assembly");
            }
            ast::ExprPath(_) | ast::ExprQPath(_) => {
                if let def::DefStatic(_, true) = ty::resolve_expr(self.tcx, expr) {
                    self.require_unsafe(expr.span, "use of mutable static");
                }
            }
            _ => {}
        }

        visit::walk_expr(self, expr);
    }
}

pub fn check_crate(tcx: &ty::ctxt) {
    let mut visitor = EffectCheckVisitor {
        tcx: tcx,
        unsafe_context: SafeContext,
    };

    visit::walk_crate(&mut visitor, tcx.map.krate());
}
<commit_msg>Rollup merge of #22562 - dckc:patch-4, r=Aatch<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Enforces the Rust effect system. Currently there is just one effect,
//! `unsafe`.
use self::UnsafeContext::*;

use middle::def;
use middle::ty::{self, Ty};
use middle::ty::MethodCall;
use util::ppaux;

use syntax::ast;
use syntax::ast_util::PostExpansionMethod;
use syntax::codemap::Span;
use syntax::visit;
use syntax::visit::Visitor;

#[derive(Copy, PartialEq)]
enum UnsafeContext {
    SafeContext,
    UnsafeFn,
    UnsafeBlock(ast::NodeId),
}

fn type_is_unsafe_function(ty: Ty) -> bool {
    match ty.sty {
        ty::ty_bare_fn(_, ref f) => f.unsafety == ast::Unsafety::Unsafe,
        _ => false,
    }
}

struct EffectCheckVisitor<'a, 'tcx: 'a> {
    tcx: &'a ty::ctxt<'tcx>,

    /// Whether we're in an unsafe context.
    unsafe_context: UnsafeContext,
}

impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
    fn require_unsafe(&mut self, span: Span, description: &str) {
        match self.unsafe_context {
            SafeContext => {
                // Report an error.
                span_err!(self.tcx.sess, span, E0133,
                          "{} requires unsafe function or block",
                          description);
            }
            UnsafeBlock(block_id) => {
                // OK, but record this.
                debug!("effect: recording unsafe block as used: {}", block_id);
                self.tcx.used_unsafe.borrow_mut().insert(block_id);
            }
            UnsafeFn => {}
        }
    }

    fn check_str_index(&mut self, e: &ast::Expr) {
        let base_type = match e.node {
            ast::ExprIndex(ref base, _) => ty::node_id_to_type(self.tcx, base.id),
            _ => return
        };
        debug!("effect: checking index with base type {}",
                ppaux::ty_to_string(self.tcx, base_type));
        match base_type.sty {
            ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => if ty::ty_str == ty.sty {
                span_err!(self.tcx.sess, e.span, E0134,
                          "modification of string types is not allowed");
            },
            ty::ty_str => {
                span_err!(self.tcx.sess, e.span, E0135,
                          "modification of string types is not allowed");
            }
            _ => {}
        }
    }
}

impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
    fn visit_fn(&mut self, fn_kind: visit::FnKind<'v>, fn_decl: &'v ast::FnDecl,
                block: &'v ast::Block, span: Span, _: ast::NodeId) {

        let (is_item_fn, is_unsafe_fn) = match fn_kind {
            visit::FkItemFn(_, _, fn_style, _) =>
                (true, fn_style == ast::Unsafety::Unsafe),
            visit::FkMethod(_, _, method) =>
                (true, method.pe_unsafety() == ast::Unsafety::Unsafe),
            _ => (false, false),
        };

        let old_unsafe_context = self.unsafe_context;
        if is_unsafe_fn {
            self.unsafe_context = UnsafeFn
        } else if is_item_fn {
            self.unsafe_context = SafeContext
        }

        visit::walk_fn(self, fn_kind, fn_decl, block, span);

        self.unsafe_context = old_unsafe_context
    }

    fn visit_block(&mut self, block: &ast::Block) {
        let old_unsafe_context = self.unsafe_context;
        match block.rules {
            ast::DefaultBlock => {}
            ast::UnsafeBlock(source) => {
                // By default only the outermost `unsafe` block is
                // "used" and so nested unsafe blocks are pointless
                // (the inner ones are unnecessary and we actually
                // warn about them). As such, there are two cases when
                // we need to create a new context, when we're
                // - outside `unsafe` and found a `unsafe` block
                //   (normal case)
                // - inside `unsafe`, found an `unsafe` block
                //   created internally to the compiler
                //
                // The second case is necessary to ensure that the
                // compiler `unsafe` blocks don't accidentally "use"
                // external blocks (e.g. `unsafe { println("") }`,
                // expands to `unsafe { ... unsafe { ... } }` where
                // the inner one is compiler generated).
                if self.unsafe_context == SafeContext || source == ast::CompilerGenerated {
                    self.unsafe_context = UnsafeBlock(block.id)
                }
            }
        }

        visit::walk_block(self, block);

        self.unsafe_context = old_unsafe_context
    }

    fn visit_expr(&mut self, expr: &ast::Expr) {
        match expr.node {
            ast::ExprMethodCall(_, _, _) => {
                let method_call = MethodCall::expr(expr.id);
                let base_type = (*self.tcx.method_map.borrow())[method_call].ty;
                debug!("effect: method call case, base type is {}",
                       ppaux::ty_to_string(self.tcx, base_type));
                if type_is_unsafe_function(base_type) {
                    self.require_unsafe(expr.span,
                                        "invocation of unsafe method")
                }
            }
            ast::ExprCall(ref base, _) => {
                let base_type = ty::node_id_to_type(self.tcx, base.id);
                debug!("effect: call case, base type is {}",
                       ppaux::ty_to_string(self.tcx, base_type));
                if type_is_unsafe_function(base_type) {
                    self.require_unsafe(expr.span, "call to unsafe function")
                }
            }
            ast::ExprUnary(ast::UnDeref, ref base) => {
                let base_type = ty::node_id_to_type(self.tcx, base.id);
                debug!("effect: unary case, base type is {}",
                       ppaux::ty_to_string(self.tcx, base_type));
                if let ty::ty_ptr(_) = base_type.sty {
                    self.require_unsafe(expr.span, "dereference of unsafe pointer")
                }
            }
            ast::ExprAssign(ref base, _) | ast::ExprAssignOp(_, ref base, _) => {
                self.check_str_index(&**base);
            }
            ast::ExprAddrOf(ast::MutMutable, ref base) => {
                self.check_str_index(&**base);
            }
            ast::ExprInlineAsm(..) => {
                self.require_unsafe(expr.span, "use of inline assembly");
            }
            ast::ExprPath(_) | ast::ExprQPath(_) => {
                if let def::DefStatic(_, true) = ty::resolve_expr(self.tcx, expr) {
                    self.require_unsafe(expr.span, "use of mutable static");
                }
            }
            _ => {}
        }

        visit::walk_expr(self, expr);
    }
}

pub fn check_crate(tcx: &ty::ctxt) {
    let mut visitor = EffectCheckVisitor {
        tcx: tcx,
        unsafe_context: SafeContext,
    };

    visit::walk_crate(&mut visitor, tcx.map.krate());
}
<|endoftext|>


<commit_before>#![allow(missing_docs, nonstandard_style)]

use crate::ffi::{OsStr, OsString};
use crate::io::ErrorKind;
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
use crate::path::PathBuf;
use crate::ptr;
use crate::time::Duration;

pub use self::rand::hashmap_random_keys;
pub use libc::strlen;

#[macro_use]
pub mod compat;

pub mod alloc;
pub mod args;
pub mod c;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fast_thread_local;
pub mod fs;
pub mod handle;
pub mod io;
pub mod memchr;
pub mod mutex;
pub mod net;
pub mod os;
pub mod os_str;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod rwlock;
pub mod thread;
pub mod thread_local;
pub mod time;
cfg_if::cfg_if! {
    if #[cfg(not(target_vendor = "uwp"))] {
        pub mod stdio;
        pub mod stack_overflow;
    } else {
        pub mod stdio_uwp;
        pub mod stack_overflow_uwp;
        pub use self::stdio_uwp as stdio;
        pub use self::stack_overflow_uwp as stack_overflow;
    }
}

#[cfg(not(test))]
pub fn init() {}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as c::DWORD {
        c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied,
        c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe,
        c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_NO_DATA => return ErrorKind::BrokenPipe,
        c::ERROR_SEM_TIMEOUT
        | c::WAIT_TIMEOUT
        | c::ERROR_DRIVER_CANCEL_TIMEOUT
        | c::ERROR_OPERATION_ABORTED
        | c::ERROR_SERVICE_REQUEST_TIMEOUT
        | c::ERROR_COUNTER_TIMEOUT
        | c::ERROR_TIMEOUT
        | c::ERROR_RESOURCE_CALL_TIMED_OUT
        | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT
        | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT
        | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT
        | c::ERROR_DS_TIMELIMIT_EXCEEDED
        | c::DNS_ERROR_RECORD_TIMED_OUT
        | c::ERROR_IPSEC_IKE_TIMED_OUT
        | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
        | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
        _ => {}
    }

    match errno {
        c::WSAEACCES => ErrorKind::PermissionDenied,
        c::WSAEADDRINUSE => ErrorKind::AddrInUse,
        c::WSAEADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
        c::WSAECONNABORTED => ErrorKind::ConnectionAborted,
        c::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
        c::WSAECONNRESET => ErrorKind::ConnectionReset,
        c::WSAEINVAL => ErrorKind::InvalidInput,
        c::WSAENOTCONN => ErrorKind::NotConnected,
        c::WSAEWOULDBLOCK => ErrorKind::WouldBlock,
        c::WSAETIMEDOUT => ErrorKind::TimedOut,

        _ => ErrorKind::Other,
    }
}

pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
    let ptr = haystack.as_ptr();
    let mut len = haystack.len();
    let mut start = &haystack[..];

    // For performance reasons unfold the loop eight times.
    while len >= 8 {
        if start[0] == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2);
        }
        if start[1] == needle {
            return Some((start[1..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[2] == needle {
            return Some((start[2..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[3] == needle {
            return Some((start[3..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[4] == needle {
            return Some((start[4..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[5] == needle {
            return Some((start[5..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[6] == needle {
            return Some((start[6..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[7] == needle {
            return Some((start[7..].as_ptr() as usize - ptr as usize) / 2);
        }

        start = &start[8..];
        len -= 8;
    }

    for (i, c) in start.iter().enumerate() {
        if *c == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2 + i);
        }
    }
    None
}

pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
    fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
        let mut maybe_result: Vec<u16> = s.encode_wide().collect();
        if unrolled_find_u16s(0, &maybe_result).is_some() {
            return Err(crate::io::Error::new(
                ErrorKind::InvalidInput,
                "strings passed to WinAPI cannot contain NULs",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }
    inner(s.as_ref())
}

// Many Windows APIs follow a pattern of where we hand a buffer and then they
// will report back to us how large the buffer should be or how many bytes
// currently reside in the buffer. This function is an abstraction over these
// functions by making them easier to call.
//
// The first callback, `f1`, is yielded a (pointer, len) pair which can be
// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case).
// The closure is expected to return what the syscall returns which will be
// interpreted by this function to determine if the syscall needs to be invoked
// again (with more buffer space).
//
// Once the syscall has completed (errors bail out early) the second closure is
// yielded the data which has been read from the syscall. The return value
// from this closure is then the return value of the function.
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T>
where
    F1: FnMut(*mut u16, c::DWORD) -> c::DWORD,
    F2: FnOnce(&[u16]) -> T,
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();
        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            c::SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as c::DWORD) {
                0 if c::GetLastError() == 0 => 0,
                0 => return Err(crate::io::Error::last_os_error()),
                n => n,
            } as usize;
            if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]));
            }
        }
    }
}

fn os2path(s: &[u16]) -> PathBuf {
    PathBuf::from(OsString::from_wide(s))
}

#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename()
fn wide_char_to_multi_byte(
    code_page: u32,
    flags: u32,
    s: &[u16],
    no_default_char: bool,
) -> crate::io::Result<Vec<i8>> {
    unsafe {
        let mut size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            ptr::null_mut(),
            0,
            ptr::null(),
            ptr::null_mut(),
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }

        let mut buf = Vec::with_capacity(size as usize);
        buf.set_len(size as usize);

        let mut used_default_char = c::FALSE;
        size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            buf.as_mut_ptr(),
            buf.len() as i32,
            ptr::null(),
            if no_default_char { &mut used_default_char } else { ptr::null_mut() },
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }
        if no_default_char && used_default_char == c::TRUE {
            return Err(crate::io::Error::new(
                crate::io::ErrorKind::InvalidData,
                "string cannot be converted to requested code page",
            ));
        }

        buf.set_len(size as usize);

        Ok(buf)
    }
}

pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] {
    match unrolled_find_u16s(0, v) {
        // don't include the 0
        Some(i) => &v[..i],
        None => v,
    }
}

pub trait IsZero {
    fn is_zero(&self) -> bool;
}

macro_rules! impl_is_zero {
    ($($t:ident)*) => ($(impl IsZero for $t {
        fn is_zero(&self) -> bool {
            *self == 0
        }
    })*)
}

impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }

pub fn cvt<I: IsZero>(i: I) -> crate::io::Result<I> {
    if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) }
}

pub fn dur2timeout(dur: Duration) -> c::DWORD {
    // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
    // timeouts in windows APIs are typically u32 milliseconds. To translate, we
    // have two pieces to take care of:
    //
    // * Nanosecond precision is rounded up
    // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
    //   (never time out).
    dur.as_secs()
        .checked_mul(1000)
        .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
        .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 }))
        .map(|ms| if ms > <c::DWORD>::MAX as u64 { c::INFINITE } else { ms as c::DWORD })
        .unwrap_or(c::INFINITE)
}

// On Windows, use the processor-specific __fastfail mechanism.  In Windows 8
// and later, this will terminate the process immediately without running any
// in-process exception handlers.  In earlier versions of Windows, this
// sequence of instructions will be treated as an access violation,
// terminating the process but without necessarily bypassing all exception
// handlers.
//
// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail
#[allow(unreachable_code)]
pub fn abort_internal() -> ! {
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    unsafe {
        llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT
        crate::intrinsics::unreachable();
    }
    crate::intrinsics::abort();
}
<commit_msg>Map ERROR_INVALID_PARAMETER to InvalidInput<commit_after>#![allow(missing_docs, nonstandard_style)]

use crate::ffi::{OsStr, OsString};
use crate::io::ErrorKind;
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
use crate::path::PathBuf;
use crate::ptr;
use crate::time::Duration;

pub use self::rand::hashmap_random_keys;
pub use libc::strlen;

#[macro_use]
pub mod compat;

pub mod alloc;
pub mod args;
pub mod c;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fast_thread_local;
pub mod fs;
pub mod handle;
pub mod io;
pub mod memchr;
pub mod mutex;
pub mod net;
pub mod os;
pub mod os_str;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod rwlock;
pub mod thread;
pub mod thread_local;
pub mod time;
cfg_if::cfg_if! {
    if #[cfg(not(target_vendor = "uwp"))] {
        pub mod stdio;
        pub mod stack_overflow;
    } else {
        pub mod stdio_uwp;
        pub mod stack_overflow_uwp;
        pub use self::stdio_uwp as stdio;
        pub use self::stack_overflow_uwp as stack_overflow;
    }
}

#[cfg(not(test))]
pub fn init() {}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as c::DWORD {
        c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied,
        c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe,
        c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_NO_DATA => return ErrorKind::BrokenPipe,
        c::ERROR_INVALID_PARAMETER => return ErrorKind::InvalidInput,
        c::ERROR_SEM_TIMEOUT
        | c::WAIT_TIMEOUT
        | c::ERROR_DRIVER_CANCEL_TIMEOUT
        | c::ERROR_OPERATION_ABORTED
        | c::ERROR_SERVICE_REQUEST_TIMEOUT
        | c::ERROR_COUNTER_TIMEOUT
        | c::ERROR_TIMEOUT
        | c::ERROR_RESOURCE_CALL_TIMED_OUT
        | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT
        | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT
        | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT
        | c::ERROR_DS_TIMELIMIT_EXCEEDED
        | c::DNS_ERROR_RECORD_TIMED_OUT
        | c::ERROR_IPSEC_IKE_TIMED_OUT
        | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
        | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
        _ => {}
    }

    match errno {
        c::WSAEACCES => ErrorKind::PermissionDenied,
        c::WSAEADDRINUSE => ErrorKind::AddrInUse,
        c::WSAEADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
        c::WSAECONNABORTED => ErrorKind::ConnectionAborted,
        c::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
        c::WSAECONNRESET => ErrorKind::ConnectionReset,
        c::WSAEINVAL => ErrorKind::InvalidInput,
        c::WSAENOTCONN => ErrorKind::NotConnected,
        c::WSAEWOULDBLOCK => ErrorKind::WouldBlock,
        c::WSAETIMEDOUT => ErrorKind::TimedOut,

        _ => ErrorKind::Other,
    }
}

pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
    let ptr = haystack.as_ptr();
    let mut len = haystack.len();
    let mut start = &haystack[..];

    // For performance reasons unfold the loop eight times.
    while len >= 8 {
        if start[0] == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2);
        }
        if start[1] == needle {
            return Some((start[1..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[2] == needle {
            return Some((start[2..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[3] == needle {
            return Some((start[3..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[4] == needle {
            return Some((start[4..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[5] == needle {
            return Some((start[5..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[6] == needle {
            return Some((start[6..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[7] == needle {
            return Some((start[7..].as_ptr() as usize - ptr as usize) / 2);
        }

        start = &start[8..];
        len -= 8;
    }

    for (i, c) in start.iter().enumerate() {
        if *c == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2 + i);
        }
    }
    None
}

pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
    fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
        let mut maybe_result: Vec<u16> = s.encode_wide().collect();
        if unrolled_find_u16s(0, &maybe_result).is_some() {
            return Err(crate::io::Error::new(
                ErrorKind::InvalidInput,
                "strings passed to WinAPI cannot contain NULs",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }
    inner(s.as_ref())
}

// Many Windows APIs follow a pattern of where we hand a buffer and then they
// will report back to us how large the buffer should be or how many bytes
// currently reside in the buffer. This function is an abstraction over these
// functions by making them easier to call.
//
// The first callback, `f1`, is yielded a (pointer, len) pair which can be
// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case).
// The closure is expected to return what the syscall returns which will be
// interpreted by this function to determine if the syscall needs to be invoked
// again (with more buffer space).
//
// Once the syscall has completed (errors bail out early) the second closure is
// yielded the data which has been read from the syscall. The return value
// from this closure is then the return value of the function.
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T>
where
    F1: FnMut(*mut u16, c::DWORD) -> c::DWORD,
    F2: FnOnce(&[u16]) -> T,
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();
        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            c::SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as c::DWORD) {
                0 if c::GetLastError() == 0 => 0,
                0 => return Err(crate::io::Error::last_os_error()),
                n => n,
            } as usize;
            if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]));
            }
        }
    }
}

fn os2path(s: &[u16]) -> PathBuf {
    PathBuf::from(OsString::from_wide(s))
}

#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename()
fn wide_char_to_multi_byte(
    code_page: u32,
    flags: u32,
    s: &[u16],
    no_default_char: bool,
) -> crate::io::Result<Vec<i8>> {
    unsafe {
        let mut size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            ptr::null_mut(),
            0,
            ptr::null(),
            ptr::null_mut(),
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }

        let mut buf = Vec::with_capacity(size as usize);
        buf.set_len(size as usize);

        let mut used_default_char = c::FALSE;
        size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            buf.as_mut_ptr(),
            buf.len() as i32,
            ptr::null(),
            if no_default_char { &mut used_default_char } else { ptr::null_mut() },
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }
        if no_default_char && used_default_char == c::TRUE {
            return Err(crate::io::Error::new(
                crate::io::ErrorKind::InvalidData,
                "string cannot be converted to requested code page",
            ));
        }

        buf.set_len(size as usize);

        Ok(buf)
    }
}

pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] {
    match unrolled_find_u16s(0, v) {
        // don't include the 0
        Some(i) => &v[..i],
        None => v,
    }
}

pub trait IsZero {
    fn is_zero(&self) -> bool;
}

macro_rules! impl_is_zero {
    ($($t:ident)*) => ($(impl IsZero for $t {
        fn is_zero(&self) -> bool {
            *self == 0
        }
    })*)
}

impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }

pub fn cvt<I: IsZero>(i: I) -> crate::io::Result<I> {
    if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) }
}

pub fn dur2timeout(dur: Duration) -> c::DWORD {
    // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
    // timeouts in windows APIs are typically u32 milliseconds. To translate, we
    // have two pieces to take care of:
    //
    // * Nanosecond precision is rounded up
    // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
    //   (never time out).
    dur.as_secs()
        .checked_mul(1000)
        .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
        .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 }))
        .map(|ms| if ms > <c::DWORD>::MAX as u64 { c::INFINITE } else { ms as c::DWORD })
        .unwrap_or(c::INFINITE)
}

// On Windows, use the processor-specific __fastfail mechanism.  In Windows 8
// and later, this will terminate the process immediately without running any
// in-process exception handlers.  In earlier versions of Windows, this
// sequence of instructions will be treated as an access violation,
// terminating the process but without necessarily bypassing all exception
// handlers.
//
// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail
#[allow(unreachable_code)]
pub fn abort_internal() -> ! {
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    unsafe {
        llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT
        crate::intrinsics::unreachable();
    }
    crate::intrinsics::abort();
}
<|endoftext|>


<commit_before>use std::fs;
use std::path::{ Path, PathBuf };

use find_package;

fn uninstall(name: &str, path: &Path) -> Result<bool, String> {
    let package = find_package::select_package(name, path)?;

    let mut installation_path = PathBuf::new();
            installation_path.push(path);
            installation_path.push(format!("install/{}", package.name));

    if installation_path.exists() {
        match fs::remove_dir_all(&path) {
            Ok(x) => x,
            Err(why) => return Err(format!("{}", why))
        }
        return Ok(true)
    } else {
        return Ok(false)
    }
}

fn full_uninstall(name: &str, path: &Path) -> Result<bool, String> {
    let package = find_package::select_package(name, path)?;
    uninstall(name, path)?;

    let mut download_path = PathBuf::new();
            download_path.push(path);
            download_path.push(format!("download/{}", package.name));
    match fs::remove_file(download_path) {
        Ok(x) => x,
        Err(why) => return Err(format!("{}", why))
    };

    let mut tar_path = PathBuf::new();
            tar_path.push(path);
            tar_path.push(format!("download/{}.tar", package.name));
    match fs::remove_file(tar_path) {
        Ok(x) => x,
        Err(why) => return Err(format!("{}", why))
    };

    return Ok(true)
}
<commit_msg>Oops, forgot to make them public.<commit_after>use std::fs;
use std::path::{ Path, PathBuf };

use find_package;

pub fn uninstall(name: &str, path: &Path) -> Result<bool, String> {
    let package = find_package::select_package(name, path)?;

    let mut installation_path = PathBuf::new();
            installation_path.push(path);
            installation_path.push(format!("install/{}", package.name));

    if installation_path.exists() {
        match fs::remove_dir_all(&path) {
            Ok(x) => x,
            Err(why) => return Err(format!("{}", why))
        }
        return Ok(true)
    } else {
        return Ok(false)
    }
}

pub fn full_uninstall(name: &str, path: &Path) -> Result<bool, String> {
    let package = find_package::select_package(name, path)?;
    uninstall(name, path)?;

    let mut download_path = PathBuf::new();
            download_path.push(path);
            download_path.push(format!("download/{}", package.name));
    match fs::remove_file(download_path) {
        Ok(x) => x,
        Err(why) => return Err(format!("{}", why))
    };

    let mut tar_path = PathBuf::new();
            tar_path.push(path);
            tar_path.push(format!("download/{}.tar", package.name));
    match fs::remove_file(tar_path) {
        Ok(x) => x,
        Err(why) => return Err(format!("{}", why))
    };

    return Ok(true)
}
<|endoftext|>


<commit_before>
mod common;

use std::env::{self, VarError};
use std::mem::size_of;
use std::process::{exit, Child, Command, ExitStatus};
use std::thread;
use std::time::Duration;

use rand::Rng;

use sled::Config;

use common::cleanup;

const TEST_ENV_VAR: &str = "SLED_CRASH_TEST";
const N_TESTS: usize = 100;
const CYCLE: usize = 256;
const BATCH_SIZE: u32 = 8;

// test names, also used as dir names
const RECOVERY_NO_SNAPSHOT: &str = "crash_recovery_no_runtime_snapshot";
const BATCHES_NO_SNAPSHOT: &str = "crash_batches_no_runtime_snapshot";

fn main() {
    match env::var(TEST_ENV_VAR) {
        Err(VarError::NotPresent) => {
            test_crash_recovery();

            // TODO this is currently being ignored until it is fixed
            // with a refactor of recovery logic.
            // test_crash_batches();
        }

        Ok(ref s) if s == RECOVERY_NO_SNAPSHOT => run_without_snapshot(s),
        Ok(ref s) if s == BATCHES_NO_SNAPSHOT => run_batches(s),

        Ok(_) | Err(_) => panic!("invalid crash test case"),
    }
}

/// Verifies that the keys in the tree are correctly recovered.
/// Panics if they are incorrect.
/// Returns the key that should be resumed at, and the current cycle value.
fn verify(tree: &sled::Tree) -> (u32, u32) {
    // key 0 should always be the highest value, as that's where we increment
    // at some point, it might go down by one
    // it should never return, or go down again after that
    let mut iter = tree.iter();
    let highest = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return (0, 0),
    };

    let highest_vec = u32_to_vec(highest);

    // find how far we got
    let mut contiguous: u32 = 0;
    let mut lowest = 0;
    for res in iter {
        let (_k, v) = res.unwrap();
        if &v[..4] == &highest_vec[..4] {
            contiguous += 1;
        } else {
            let expected = if highest == 0 {
                CYCLE as u32 - 1
            } else {
                (highest - 1) % CYCLE as u32
            };
            let actual = slice_to_u32(&*v);
            assert_eq!(expected, actual);
            lowest = actual;
            break;
        }
    }

    // ensure nothing changes after this point
    let low_beginning = u32_to_vec(contiguous + 1);

    for res in tree.range(&*low_beginning..) {
        let (k, v): (sled::IVec, _) = res.unwrap();
        assert_eq!(
            slice_to_u32(&*v),
            lowest,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            slice_to_u32(&*k),
            lowest,
            slice_to_u32(&*v),
            tree
        );
    }

    (contiguous, highest)
}

fn u32_to_vec(u: u32) -> Vec<u8> {
    let buf: [u8; size_of::<u32>()] = u.to_be_bytes();
    buf.to_vec()
}

fn slice_to_u32(b: &[u8]) -> u32 {
    let mut buf = [0u8; size_of::<u32>()];
    buf.copy_from_slice(&b[..size_of::<u32>()]);

    u32::from_be_bytes(buf)
}

fn spawn_killah() {
    thread::spawn(|| {
        let runtime = rand::thread_rng().gen_range(0, 200);
        thread::sleep(Duration::from_millis(runtime));
        exit(9);
    });
}

fn run_inner(config: Config) {
    common::setup_logger();

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let tree = config.open().unwrap();

    if !crash_during_initialization {
        spawn_killah();
    }

    let (key, highest) = verify(&tree);

    let mut hu = ((highest as usize) * CYCLE) + key as usize;
    assert_eq!(hu % CYCLE, key as usize);
    assert_eq!(hu / CYCLE, highest as usize);

    loop {
        hu += 1;

        if hu / CYCLE >= CYCLE {
            hu = 0;
        }

        let key = u32_to_vec((hu % CYCLE) as u32);

        let mut value = u32_to_vec((hu / CYCLE) as u32);
        let additional_len = rand::thread_rng().gen_range(0, 1000);
        value.append(&mut vec![0u8; additional_len]);

        tree.insert(&key, value).unwrap();
    }
}

/// Verifies that the keys in the tree are correctly recovered (i.e., equal).
/// Panics if they are incorrect.
fn verify_batches(tree: &sled::Tree) -> u32 {
    let mut iter = tree.iter();
    let first_value = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return 0,
    };
    for key in 0..BATCH_SIZE {
        let res = tree.get(u32_to_vec(key));
        let option = res.unwrap();
        let v = match option {
            Some(v) => v,
            None => panic!(
                "expected key {} to have a value, instead it was missing in db: {:?}",
                key,
                tree
            ),
        };
        let value = slice_to_u32(&*v);
        assert_eq!(
            first_value,
            value,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            key,
            first_value,
            value,
            tree
        );
    }

    first_value
}

fn run_batches_inner(config: Config) {
    fn do_batch(i: u32, tree: &sled::Tree) {
        let mut rng = rand::thread_rng();
        let base_value = u32_to_vec(i);

        let mut batch = sled::Batch::default();
        if rng.gen_bool(0.1) {
            for key in 0..BATCH_SIZE {
                batch.remove(u32_to_vec(key));
            }
        } else {
            for key in 0..BATCH_SIZE {
                let mut value = base_value.clone();
                let additional_len = rng.gen_range(0, 1000);
                value.append(&mut vec![0u8; additional_len]);

                batch.insert(u32_to_vec(key), value);
            }
        }
        tree.apply_batch(batch).unwrap();
    }

    common::setup_logger();

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    let tree = config.open().unwrap();

    let mut i = verify_batches(&tree);
    i += 1;
    do_batch(i, &tree);

    if !crash_during_initialization {
        spawn_killah();
    }

    loop {
        i += 1;
        do_batch(i, &tree);
    }
}

fn run_without_snapshot(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .segment_size(1024);

    match thread::spawn(|| run_inner(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_batches(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .segment_size(1024);

    match thread::spawn(|| run_batches_inner(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_child_process(test_name: &str) -> Child {
    let bin = env::current_exe().expect("could not get test binary path");

    env::set_var(TEST_ENV_VAR, test_name);

    Command::new(bin).env(TEST_ENV_VAR, test_name).spawn().expect(&format!(
        "could not spawn child process for {} test",
        test_name
    ))
}

fn handle_child_exit_status(dir: &str, status: ExitStatus) {
    let code = status.code();

    if code.is_none() || code.unwrap() != 9 {
        cleanup(dir);
        panic!("{} test child exited abnormally", dir);
    }
}

fn handle_child_wait_err(dir: &str, e: std::io::Error) {
    cleanup(dir);

    panic!("error waiting for {} test child: {}", dir, e);
}

fn test_crash_recovery() {
    let dir = RECOVERY_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}

#[allow(dead_code)]
fn test_crash_batches() {
    let dir = BATCHES_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(BATCHES_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}<commit_msg>properly crash on initialization in batch test<commit_after>mod common;

use std::env::{self, VarError};
use std::mem::size_of;
use std::process::{exit, Child, Command, ExitStatus};
use std::thread;
use std::time::Duration;

use rand::Rng;

use sled::Config;

use common::cleanup;

const TEST_ENV_VAR: &str = "SLED_CRASH_TEST";
const N_TESTS: usize = 100;
const CYCLE: usize = 256;
const BATCH_SIZE: u32 = 8;

// test names, also used as dir names
const RECOVERY_NO_SNAPSHOT: &str = "crash_recovery_no_runtime_snapshot";
const BATCHES_NO_SNAPSHOT: &str = "crash_batches_no_runtime_snapshot";

fn main() {
    match env::var(TEST_ENV_VAR) {
        Err(VarError::NotPresent) => {
            test_crash_recovery();

            // TODO this is currently being ignored until it is fixed
            // with a refactor of recovery logic.
            // test_crash_batches();
        }

        Ok(ref s) if s == RECOVERY_NO_SNAPSHOT => run_without_snapshot(s),
        Ok(ref s) if s == BATCHES_NO_SNAPSHOT => run_batches(s),

        Ok(_) | Err(_) => panic!("invalid crash test case"),
    }
}

/// Verifies that the keys in the tree are correctly recovered.
/// Panics if they are incorrect.
/// Returns the key that should be resumed at, and the current cycle value.
fn verify(tree: &sled::Tree) -> (u32, u32) {
    // key 0 should always be the highest value, as that's where we increment
    // at some point, it might go down by one
    // it should never return, or go down again after that
    let mut iter = tree.iter();
    let highest = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return (0, 0),
    };

    let highest_vec = u32_to_vec(highest);

    // find how far we got
    let mut contiguous: u32 = 0;
    let mut lowest = 0;
    for res in iter {
        let (_k, v) = res.unwrap();
        if &v[..4] == &highest_vec[..4] {
            contiguous += 1;
        } else {
            let expected = if highest == 0 {
                CYCLE as u32 - 1
            } else {
                (highest - 1) % CYCLE as u32
            };
            let actual = slice_to_u32(&*v);
            assert_eq!(expected, actual);
            lowest = actual;
            break;
        }
    }

    // ensure nothing changes after this point
    let low_beginning = u32_to_vec(contiguous + 1);

    for res in tree.range(&*low_beginning..) {
        let (k, v): (sled::IVec, _) = res.unwrap();
        assert_eq!(
            slice_to_u32(&*v),
            lowest,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            slice_to_u32(&*k),
            lowest,
            slice_to_u32(&*v),
            tree
        );
    }

    (contiguous, highest)
}

fn u32_to_vec(u: u32) -> Vec<u8> {
    let buf: [u8; size_of::<u32>()] = u.to_be_bytes();
    buf.to_vec()
}

fn slice_to_u32(b: &[u8]) -> u32 {
    let mut buf = [0u8; size_of::<u32>()];
    buf.copy_from_slice(&b[..size_of::<u32>()]);

    u32::from_be_bytes(buf)
}

fn spawn_killah() {
    thread::spawn(|| {
        let runtime = rand::thread_rng().gen_range(0, 200);
        thread::sleep(Duration::from_millis(runtime));
        exit(9);
    });
}

fn run_inner(config: Config) {
    common::setup_logger();

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let tree = config.open().unwrap();

    if !crash_during_initialization {
        spawn_killah();
    }

    let (key, highest) = verify(&tree);

    let mut hu = ((highest as usize) * CYCLE) + key as usize;
    assert_eq!(hu % CYCLE, key as usize);
    assert_eq!(hu / CYCLE, highest as usize);

    loop {
        hu += 1;

        if hu / CYCLE >= CYCLE {
            hu = 0;
        }

        let key = u32_to_vec((hu % CYCLE) as u32);

        let mut value = u32_to_vec((hu / CYCLE) as u32);
        let additional_len = rand::thread_rng().gen_range(0, 1000);
        value.append(&mut vec![0u8; additional_len]);

        tree.insert(&key, value).unwrap();
    }
}

/// Verifies that the keys in the tree are correctly recovered (i.e., equal).
/// Panics if they are incorrect.
fn verify_batches(tree: &sled::Tree) -> u32 {
    let mut iter = tree.iter();
    let first_value = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return 0,
    };
    for key in 0..BATCH_SIZE {
        let res = tree.get(u32_to_vec(key));
        let option = res.unwrap();
        let v = match option {
            Some(v) => v,
            None => panic!(
                "expected key {} to have a value, instead it was missing in db: {:?}",
                key,
                tree
            ),
        };
        let value = slice_to_u32(&*v);
        assert_eq!(
            first_value,
            value,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            key,
            first_value,
            value,
            tree
        );
    }

    first_value
}

fn run_batches_inner(config: Config) {
    fn do_batch(i: u32, tree: &sled::Tree) {
        let mut rng = rand::thread_rng();
        let base_value = u32_to_vec(i);

        let mut batch = sled::Batch::default();
        if rng.gen_bool(0.1) {
            for key in 0..BATCH_SIZE {
                batch.remove(u32_to_vec(key));
            }
        } else {
            for key in 0..BATCH_SIZE {
                let mut value = base_value.clone();
                let additional_len = rng.gen_range(0, 1000);
                value.append(&mut vec![0u8; additional_len]);

                batch.insert(u32_to_vec(key), value);
            }
        }
        tree.apply_batch(batch).unwrap();
    }

    common::setup_logger();

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let tree = config.open().unwrap();

    let mut i = verify_batches(&tree);
    i += 1;
    do_batch(i, &tree);

    if !crash_during_initialization {
        spawn_killah();
    }

    loop {
        i += 1;
        do_batch(i, &tree);
    }
}

fn run_without_snapshot(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .segment_size(1024);

    match thread::spawn(|| run_inner(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_batches(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .segment_size(1024);

    match thread::spawn(|| run_batches_inner(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_child_process(test_name: &str) -> Child {
    let bin = env::current_exe().expect("could not get test binary path");

    env::set_var(TEST_ENV_VAR, test_name);

    Command::new(bin).env(TEST_ENV_VAR, test_name).spawn().expect(&format!(
        "could not spawn child process for {} test",
        test_name
    ))
}

fn handle_child_exit_status(dir: &str, status: ExitStatus) {
    let code = status.code();

    if code.is_none() || code.unwrap() != 9 {
        cleanup(dir);
        panic!("{} test child exited abnormally", dir);
    }
}

fn handle_child_wait_err(dir: &str, e: std::io::Error) {
    cleanup(dir);

    panic!("error waiting for {} test child: {}", dir, e);
}

fn test_crash_recovery() {
    let dir = RECOVERY_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}

#[allow(dead_code)]
fn test_crash_batches() {
    let dir = BATCHES_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(BATCHES_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate cc;
extern crate cmake;
extern crate pkg_config;

use std::path::Path;
use std::{env, fs, io};
use std::env::VarError;

use cmake::Config;
use cc::Build;
use pkg_config::Config as PkgConfig;

const GRPC_VERSION: &'static str = "1.6.1";

fn link_grpc(cc: &mut Build, library: &str) {
    match PkgConfig::new()
        .atleast_version(GRPC_VERSION)
        .probe(library)
    {
        Ok(lib) => for inc_path in lib.include_paths {
            cc.include(inc_path);
        },
        Err(e) => panic!("can't find library {} via pkg-config: {:?}", library, e),
    }
}

fn prepare_grpc() {
    let mut modules = vec![
        "grpc",
        "grpc/third_party/zlib",
        "grpc/third_party/cares/cares",
    ];

    if cfg!(feature = "secure") {
        modules.push("grpc/third_party/boringssl");
    }

    for module in modules {
        if is_directory_empty(module).unwrap_or(true) {
            panic!(
                "Can't find module {}. You need to run `git submodule \
                 update --init --recursive` first to build the project.",
                module
            );
        }
    }
}

fn is_directory_empty<P: AsRef<Path>>(p: P) -> Result<bool, io::Error> {
    let mut entries = fs::read_dir(p)?;
    Ok(entries.next().is_none())
}

fn build_grpc(cc: &mut Build, library: &str) {
    prepare_grpc();

    let dst = {
        let mut config = Config::new("grpc");
        if cfg!(target_os = "macos") {
            config.cxxflag("-stdlib=libc++");
        }
        config.build_target(library).uses_cxx11().build()
    };

    let mut zlib = "z";
    let build_dir = format!("{}/build", dst.display());
    let third_party = vec![
        "cares/cares/lib",
        "zlib",
        "boringssl/ssl",
        "boringssl/crypto",
    ];
    if cfg!(target_os = "windows") {
        let profile = match &*env::var("PROFILE").unwrap_or("debug".to_owned()) {
            "bench" | "release" => {
                zlib = "zlibstatic";
                "Release"
            }
            _ => {
                zlib = "zlibstaticd";
                "Debug"
            }
        };
        println!("cargo:rustc-link-search=native={}/{}", build_dir, profile);
        for path in third_party {
            println!(
                "cargo:rustc-link-search=native={}/third_party/{}/{}",
                build_dir, path, profile
            );
        }
    } else {
        println!("cargo:rustc-link-search=native={}", build_dir);
        for path in third_party {
            println!(
                "cargo:rustc-link-search=native={}/third_party/{}",
                build_dir, path,
            );
        }
    }

    println!("cargo:rustc-link-lib=static={}", zlib);
    println!("cargo:rustc-link-lib=static=cares");
    println!("cargo:rustc-link-lib=static=gpr");
    println!("cargo:rustc-link-lib=static={}", library);

    if cfg!(feature = "secure") {
        println!("cargo:rustc-link-lib=static=ssl");
        println!("cargo:rustc-link-lib=static=crypto");
    }

    cc.include("grpc/include");
}

fn get_env(name: &str) -> Option<String> {
    println!("cargo:rerun-if-env-changed={}", name);
    match env::var(name) {
        Ok(s) => Some(s),
        Err(VarError::NotPresent) => None,
        Err(VarError::NotUnicode(s)) => {
            panic!("unrecognize env var of {}: {:?}", name, s.to_string_lossy());
        }
    }
}

fn main() {
    let mut cc = Build::new();

    println!("cargo:rerun-if-changed=grpc_wrap.c");
    println!("cargo:rerun-if-changed=grpc");

    let library = if cfg!(feature = "secure") {
        cc.define("GRPC_SYS_SECURE", None);
        "grpc"
    } else {
        "grpc_unsecure"
    };

    if get_env("GRPCIO_SYS_USE_PKG_CONFIG").map_or(false, |s| s == "1") {
        link_grpc(&mut cc, library);
    } else {
        build_grpc(&mut cc, library);
    }

    cc.file("grpc_wrap.c");

    if cfg!(target_os = "windows") {
        // At lease win7
        cc.define("_WIN32_WINNT", Some("0x0700"));
    }

    cc.warnings_into_errors(true).compile("libgrpc_wrap.a");
}
<commit_msg>Fix grpcio-sys building under musl (#195)<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate cc;
extern crate cmake;
extern crate pkg_config;

use std::path::Path;
use std::{env, fs, io};
use std::env::VarError;

use cmake::Config;
use cc::Build;
use pkg_config::Config as PkgConfig;

const GRPC_VERSION: &'static str = "1.6.1";

fn link_grpc(cc: &mut Build, library: &str) {
    match PkgConfig::new()
        .atleast_version(GRPC_VERSION)
        .probe(library)
    {
        Ok(lib) => for inc_path in lib.include_paths {
            cc.include(inc_path);
        },
        Err(e) => panic!("can't find library {} via pkg-config: {:?}", library, e),
    }
}

fn prepare_grpc() {
    let mut modules = vec![
        "grpc",
        "grpc/third_party/zlib",
        "grpc/third_party/cares/cares",
    ];

    if cfg!(feature = "secure") {
        modules.push("grpc/third_party/boringssl");
    }

    for module in modules {
        if is_directory_empty(module).unwrap_or(true) {
            panic!(
                "Can't find module {}. You need to run `git submodule \
                 update --init --recursive` first to build the project.",
                module
            );
        }
    }
}

fn is_directory_empty<P: AsRef<Path>>(p: P) -> Result<bool, io::Error> {
    let mut entries = fs::read_dir(p)?;
    Ok(entries.next().is_none())
}

fn build_grpc(cc: &mut Build, library: &str) {
    prepare_grpc();

    let dst = {
        let mut config = Config::new("grpc");
        if cfg!(target_os = "macos") {
            config.cxxflag("-stdlib=libc++");
        }
        if env::var("CARGO_CFG_TARGET_ENV").unwrap_or("".to_owned()) == "musl" {
            config.define("CMAKE_CXX_COMPILER", "g++");
        }
        config.build_target(library).uses_cxx11().build()
    };

    let mut zlib = "z";
    let build_dir = format!("{}/build", dst.display());
    let third_party = vec![
        "cares/cares/lib",
        "zlib",
        "boringssl/ssl",
        "boringssl/crypto",
    ];
    if cfg!(target_os = "windows") {
        let profile = match &*env::var("PROFILE").unwrap_or("debug".to_owned()) {
            "bench" | "release" => {
                zlib = "zlibstatic";
                "Release"
            }
            _ => {
                zlib = "zlibstaticd";
                "Debug"
            }
        };
        println!("cargo:rustc-link-search=native={}/{}", build_dir, profile);
        for path in third_party {
            println!(
                "cargo:rustc-link-search=native={}/third_party/{}/{}",
                build_dir, path, profile
            );
        }
    } else {
        println!("cargo:rustc-link-search=native={}", build_dir);
        for path in third_party {
            println!(
                "cargo:rustc-link-search=native={}/third_party/{}",
                build_dir, path,
            );
        }
    }

    println!("cargo:rustc-link-lib=static={}", zlib);
    println!("cargo:rustc-link-lib=static=cares");
    println!("cargo:rustc-link-lib=static=gpr");
    println!("cargo:rustc-link-lib=static={}", library);

    if cfg!(feature = "secure") {
        println!("cargo:rustc-link-lib=static=ssl");
        println!("cargo:rustc-link-lib=static=crypto");
    }

    cc.include("grpc/include");
}

fn get_env(name: &str) -> Option<String> {
    println!("cargo:rerun-if-env-changed={}", name);
    match env::var(name) {
        Ok(s) => Some(s),
        Err(VarError::NotPresent) => None,
        Err(VarError::NotUnicode(s)) => {
            panic!("unrecognize env var of {}: {:?}", name, s.to_string_lossy());
        }
    }
}

fn main() {
    let mut cc = Build::new();

    println!("cargo:rerun-if-changed=grpc_wrap.c");
    println!("cargo:rerun-if-changed=grpc");

    let library = if cfg!(feature = "secure") {
        cc.define("GRPC_SYS_SECURE", None);
        "grpc"
    } else {
        "grpc_unsecure"
    };

    if get_env("GRPCIO_SYS_USE_PKG_CONFIG").map_or(false, |s| s == "1") {
        link_grpc(&mut cc, library);
    } else {
        build_grpc(&mut cc, library);
    }

    cc.file("grpc_wrap.c");

    if cfg!(target_os = "windows") {
        // At lease win7
        cc.define("_WIN32_WINNT", Some("0x0700"));
    }

    cc.warnings_into_errors(true).compile("libgrpc_wrap.a");
}
<|endoftext|>