<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{SourceLocation, ParseError as CssParseError, Token, BasicParseError};
use cssparser::CowRcStr;
use selectors::parser::SelectorParseError;
use std::ptr;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::stylesheets::UrlExtraData;
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError, ValueParseError};

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               url: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = RefPtr::from_ptr_ref(&url);
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url.mBaseURI.raw::<nsIURI>()))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CowRcStr<'a>),
    Ident(CowRcStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> String {
        match self {
            ErrorString::Snippet(s) => s.as_ref().to_owned(),
            ErrorString::Ident(i) => escape_css_ident(&i),
            ErrorString::UnexpectedToken(t) => token_to_str(t),
        }
    }
}

// This is identical to the behaviour of cssparser::serialize_identifier, except that
// it uses numerical escapes for a larger set of characters.
fn escape_css_ident(ident: &str) -> String {
    // The relevant parts of the CSS grammar are:
    //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
    //   nmstart  [_a-z]|{nonascii}|{escape}
    //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
    //   nonascii [^\0-\177]
    //   escape   {unicode}|\\[^\n\r\f0-9a-f]
    //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
    // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
    // modified for idents by
    // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
    // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
    if ident.is_empty() {
        return ident.into()
    }

    let mut escaped = String::with_capacity(ident.len());

    // A leading dash does not need to be escaped as long as it is not the
    // *only* character in the identifier.
    let mut iter = ident.chars().peekable();
    if iter.peek() == Some(&'-') {
        if ident.len() == 1 {
            return "\\-".into();
        }

        escaped.push('-');
        // Skip the first character.
        let _ = iter.next();
    }

    // Escape a digit at the start (including after a dash),
    // numerically.  If we didn't escape it numerically, it would get
    // interpreted as a numeric escape for the wrong character.
    if iter.peek().map_or(false, |&c| '0' <= c && c <= '9') {
        let ch = iter.next().unwrap();
        escaped.push_str(&format!("\\{:x} ", ch as u32));
    }

    while let Some(ch) = iter.next() {
        if ch == '\0' {
            escaped.push_str("\u{FFFD}");
        } else if ch < (0x20 as char) || (0x7f as char <= ch && ch < (0xA0 as char)) {
            // Escape U+0000 through U+001F and U+007F through U+009F numerically.
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            // Escape ASCII non-identifier printables as a backslash plus
            // the character.
            if (ch < (0x7F as char)) &&
                ch != '_' && ch != '-' &&
                (ch < '0' || '9' < ch) &&
                (ch < 'A' || 'Z' < ch) &&
                (ch < 'a' || 'z' < ch)
            {
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }

    escaped
}

// This is identical to the behaviour of cssparser::CssStringWriter, except that
// the characters between 0x7F and 0xA0 as numerically escaped as well.
fn escape_css_string(s: &str) -> String {
    let mut escaped = String::new();
    for ch in s.chars() {
        if ch < ' ' || (ch >= (0x7F as char) && ch < (0xA0 as char)) {
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            if ch == '"' || ch == '\'' || ch == '\\' {
                // Escape backslash and quote characters symbolically.
                // It's not technically necessary to escape the quote
                // character that isn't being used to delimit the string,
                // but we do it anyway because that makes testing simpler.
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }
    escaped
}

fn token_to_str<'a>(t: Token<'a>) -> String {
    match t {
        Token::Ident(i) => escape_css_ident(&i),
        Token::AtKeyword(kw) => format!("@{}", escape_css_ident(&kw)),
        Token::Hash(h) | Token::IDHash(h) => format!("#{}", escape_css_ident(&h)),
        Token::QuotedString(s) => format!("'{}'", escape_css_string(&s)),
        Token::UnquotedUrl(u) => format!("'{}'", escape_css_string(&u)),
        Token::Delim(d) => d.to_string(),
        Token::Number { int_value: Some(i), .. } => i.to_string(),
        Token::Number { value, .. } => value.to_string(),
        Token::Percentage { int_value: Some(i), .. } => i.to_string(),
        Token::Percentage { unit_value, .. } => unit_value.to_string(),
        Token::Dimension { int_value: Some(i), ref unit, .. } =>
            format!("{}{}", i.to_string(), escape_css_ident(&unit.to_string())),
        Token::Dimension { value, ref unit, .. } =>
            format!("{}{}", value.to_string(), escape_css_ident(&unit.to_string())),
        Token::WhiteSpace(_) => "whitespace".into(),
        Token::Comment(_) => "comment".into(),
        Token::Colon => ":".into(),
        Token::Semicolon => ";".into(),
        Token::Comma => ",".into(),
        Token::IncludeMatch => "~=".into(),
        Token::DashMatch => "|=".into(),
        Token::PrefixMatch => "^=".into(),
        Token::SuffixMatch => "$=".into(),
        Token::SubstringMatch => "*=".into(),
        Token::Column => "||".into(),
        Token::CDO => "<!--".into(),
        Token::CDC => "-->".into(),
        Token::Function(f) => format!("{}(", escape_css_ident(&f)),
        Token::ParenthesisBlock => "(".into(),
        Token::SquareBracketBlock => "[".into(),
        Token::CurlyBracketBlock => "{".into(),
        Token::BadUrl(url) => format!("url('{}", escape_css_string(&url)).into(),
        Token::BadString(s) => format!("'{}", escape_css_string(&s)).into(),
        Token::CloseParenthesis => "unmatched close parenthesis".into(),
        Token::CloseSquareBracket => "unmatched close square bracket".into(),
        Token::CloseCurlyBracket => "unmatched close curly bracket".into(),
    }
}

enum Action {
    Nothing,
    Skip,
    Drop,
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>);
    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
}

fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
    Some(match err {
        CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
            ErrorString::UnexpectedToken(t),

        CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) =>
            ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, None)))) =>
            ErrorString::Snippet(property),

        CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) =>
            ErrorString::Ident(ident),

        CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
            ErrorString::Ident(namespace),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::UnknownProperty(property)))) =>
            ErrorString::Ident(property),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::UnexpectedTokenWithinNamespace(token))) =>
            ErrorString::UnexpectedToken(token),

        _ => return None,
    })
}

fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
    match err {
        ValueParseError::InvalidColor(t) => ErrorString::UnexpectedToken(t),
    }
}

/// If an error parameter is present in the given error, return it. Additionally return
/// a second parameter if it exists, for use in the prefix for the eventual error message.
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<(ErrorString<'a>, Option<ErrorString<'a>>)> {
    match err {
        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
            Some((ErrorString::Snippet(property.into()), Some(extract_value_error_param(e)))),

        err => extract_error_param(err).map(|e| (e, None)),
    }
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) =>
                (s.into(), err),
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
                (s.into(), StyleParseError::UnspecifiedError.into()),
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                ("".into(), StyleParseError::UnspecifiedError.into())
        }
    }

    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>) {
        let (s, error) = self.error_data();
        extract_error_params(error).unwrap_or((ErrorString::Snippet(s), None))
    }

    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
        let (msg, action): (&[u8], Action) = match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) =>
                (b"PEParseDeclarationDeclExpected\0", Action::Skip),
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Custom(SelectorParseError::Custom(
                    StyleParseError::PropertyDeclaration(
                        PropertyDeclarationParseError::InvalidValue(_, ref err))))) => {
                let prefix = match *err {
                    Some(ValueParseError::InvalidColor(_)) => Some(&b"PEColorNotColor\0"[..]),
                    _ => None,
                };
                return (prefix, b"PEValueParsingError\0", Action::Drop);
            }
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                (b"PEUnknownProperty\0", Action::Drop),
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                (b"PEUnknwnFontDesc\0", Action::Skip),
            ContextualParseError::InvalidKeyframeRule(..) =>
                (b"PEKeyframeBadName\0", Action::Nothing),
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                (b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
                (b"PEUnknownNamespacePrefix\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
                (b"PEAtNSUnexpected\0", Action::Nothing),
            ContextualParseError::InvalidRule(..) =>
                (b"PEBadSelectorRSIgnored\0", Action::Nothing),
            ContextualParseError::UnsupportedRule(..) =>
                (b"PEDeclDropped\0", Action::Nothing),
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
            ContextualParseError::InvalidFontFeatureValuesRule(..) =>
                (b"PEUnknownAtRule\0", Action::Skip),
        };
        (None, msg, action)
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error(&self,
                    _url: &UrlExtraData,
                    location: SourceLocation,
                    error: ContextualParseError) {
        let (pre, name, action) = error.to_gecko_message();
        let suffix = match action {
            Action::Nothing => ptr::null(),
            Action::Skip => b"PEDeclSkipped\0".as_ptr(),
            Action::Drop => b"PEDeclDropped\0".as_ptr(),
        };
        let (param, pre_param) = error.error_params();
        let param = param.into_str();
        let pre_param = pre_param.map(|p| p.into_str());
        let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        // The CSS source text is unused and will be removed in bug 1381188.
        let source = "";
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param.as_ptr() as *const _,
                                           param.len() as u32,
                                           pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
                                           pre_param_ptr as *const _,
                                           pre_param.as_ref().map_or(0, |p| p.len()) as u32,
                                           suffix as *const _,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           location.line,
                                           location.column);
        }
    }
}
<commit_msg>Use fewer allocations when reporting Gecko errors.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{SourceLocation, ParseError as CssParseError, Token, BasicParseError};
use cssparser::CowRcStr;
use selectors::parser::SelectorParseError;
use std::ptr;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::stylesheets::UrlExtraData;
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError, ValueParseError};

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               url: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = RefPtr::from_ptr_ref(&url);
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url.mBaseURI.raw::<nsIURI>()))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CowRcStr<'a>),
    Ident(CowRcStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> CowRcStr<'a> {
        match self {
            ErrorString::Snippet(s) => s,
            ErrorString::Ident(i) => escape_css_ident(&i).into(),
            ErrorString::UnexpectedToken(t) => token_to_str(t).into(),
        }
    }
}

// This is identical to the behaviour of cssparser::serialize_identifier, except that
// it uses numerical escapes for a larger set of characters.
fn escape_css_ident(ident: &str) -> String {
    // The relevant parts of the CSS grammar are:
    //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
    //   nmstart  [_a-z]|{nonascii}|{escape}
    //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
    //   nonascii [^\0-\177]
    //   escape   {unicode}|\\[^\n\r\f0-9a-f]
    //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
    // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
    // modified for idents by
    // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
    // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
    if ident.is_empty() {
        return ident.into()
    }

    let mut escaped = String::with_capacity(ident.len());

    // A leading dash does not need to be escaped as long as it is not the
    // *only* character in the identifier.
    let mut iter = ident.chars().peekable();
    if iter.peek() == Some(&'-') {
        if ident.len() == 1 {
            return "\\-".into();
        }

        escaped.push('-');
        // Skip the first character.
        let _ = iter.next();
    }

    // Escape a digit at the start (including after a dash),
    // numerically.  If we didn't escape it numerically, it would get
    // interpreted as a numeric escape for the wrong character.
    if iter.peek().map_or(false, |&c| '0' <= c && c <= '9') {
        let ch = iter.next().unwrap();
        escaped.push_str(&format!("\\{:x} ", ch as u32));
    }

    while let Some(ch) = iter.next() {
        if ch == '\0' {
            escaped.push_str("\u{FFFD}");
        } else if ch < (0x20 as char) || (0x7f as char <= ch && ch < (0xA0 as char)) {
            // Escape U+0000 through U+001F and U+007F through U+009F numerically.
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            // Escape ASCII non-identifier printables as a backslash plus
            // the character.
            if (ch < (0x7F as char)) &&
                ch != '_' && ch != '-' &&
                (ch < '0' || '9' < ch) &&
                (ch < 'A' || 'Z' < ch) &&
                (ch < 'a' || 'z' < ch)
            {
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }

    escaped
}

// This is identical to the behaviour of cssparser::CssStringWriter, except that
// the characters between 0x7F and 0xA0 as numerically escaped as well.
fn escape_css_string(s: &str) -> String {
    let mut escaped = String::new();
    for ch in s.chars() {
        if ch < ' ' || (ch >= (0x7F as char) && ch < (0xA0 as char)) {
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            if ch == '"' || ch == '\'' || ch == '\\' {
                // Escape backslash and quote characters symbolically.
                // It's not technically necessary to escape the quote
                // character that isn't being used to delimit the string,
                // but we do it anyway because that makes testing simpler.
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }
    escaped
}

fn token_to_str<'a>(t: Token<'a>) -> String {
    match t {
        Token::Ident(i) => escape_css_ident(&i),
        Token::AtKeyword(kw) => format!("@{}", escape_css_ident(&kw)),
        Token::Hash(h) | Token::IDHash(h) => format!("#{}", escape_css_ident(&h)),
        Token::QuotedString(s) => format!("'{}'", escape_css_string(&s)),
        Token::UnquotedUrl(u) => format!("'{}'", escape_css_string(&u)),
        Token::Delim(d) => d.to_string(),
        Token::Number { int_value: Some(i), .. } => i.to_string(),
        Token::Number { value, .. } => value.to_string(),
        Token::Percentage { int_value: Some(i), .. } => i.to_string(),
        Token::Percentage { unit_value, .. } => unit_value.to_string(),
        Token::Dimension { int_value: Some(i), ref unit, .. } =>
            format!("{}{}", i, escape_css_ident(&*unit)),
        Token::Dimension { value, ref unit, .. } =>
            format!("{}{}", value, escape_css_ident(&*unit)),
        Token::WhiteSpace(_) => "whitespace".into(),
        Token::Comment(_) => "comment".into(),
        Token::Colon => ":".into(),
        Token::Semicolon => ";".into(),
        Token::Comma => ",".into(),
        Token::IncludeMatch => "~=".into(),
        Token::DashMatch => "|=".into(),
        Token::PrefixMatch => "^=".into(),
        Token::SuffixMatch => "$=".into(),
        Token::SubstringMatch => "*=".into(),
        Token::Column => "||".into(),
        Token::CDO => "<!--".into(),
        Token::CDC => "-->".into(),
        Token::Function(f) => format!("{}(", escape_css_ident(&f)),
        Token::ParenthesisBlock => "(".into(),
        Token::SquareBracketBlock => "[".into(),
        Token::CurlyBracketBlock => "{".into(),
        Token::BadUrl(url) => format!("url('{}", escape_css_string(&url)).into(),
        Token::BadString(s) => format!("'{}", escape_css_string(&s)).into(),
        Token::CloseParenthesis => "unmatched close parenthesis".into(),
        Token::CloseSquareBracket => "unmatched close square bracket".into(),
        Token::CloseCurlyBracket => "unmatched close curly bracket".into(),
    }
}

enum Action {
    Nothing,
    Skip,
    Drop,
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>);
    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
}

fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
    Some(match err {
        CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
            ErrorString::UnexpectedToken(t),

        CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) =>
            ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, None)))) =>
            ErrorString::Snippet(property),

        CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) =>
            ErrorString::Ident(ident),

        CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
            ErrorString::Ident(namespace),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::UnknownProperty(property)))) =>
            ErrorString::Ident(property),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::UnexpectedTokenWithinNamespace(token))) =>
            ErrorString::UnexpectedToken(token),

        _ => return None,
    })
}

fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
    match err {
        ValueParseError::InvalidColor(t) => ErrorString::UnexpectedToken(t),
    }
}

/// If an error parameter is present in the given error, return it. Additionally return
/// a second parameter if it exists, for use in the prefix for the eventual error message.
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<(ErrorString<'a>, Option<ErrorString<'a>>)> {
    match err {
        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
            Some((ErrorString::Snippet(property.into()), Some(extract_value_error_param(e)))),

        err => extract_error_param(err).map(|e| (e, None)),
    }
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) =>
                (s.into(), err),
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
                (s.into(), StyleParseError::UnspecifiedError.into()),
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                ("".into(), StyleParseError::UnspecifiedError.into())
        }
    }

    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>) {
        let (s, error) = self.error_data();
        extract_error_params(error).unwrap_or((ErrorString::Snippet(s), None))
    }

    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
        let (msg, action): (&[u8], Action) = match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) =>
                (b"PEParseDeclarationDeclExpected\0", Action::Skip),
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Custom(SelectorParseError::Custom(
                    StyleParseError::PropertyDeclaration(
                        PropertyDeclarationParseError::InvalidValue(_, ref err))))) => {
                let prefix = match *err {
                    Some(ValueParseError::InvalidColor(_)) => Some(&b"PEColorNotColor\0"[..]),
                    _ => None,
                };
                return (prefix, b"PEValueParsingError\0", Action::Drop);
            }
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                (b"PEUnknownProperty\0", Action::Drop),
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                (b"PEUnknwnFontDesc\0", Action::Skip),
            ContextualParseError::InvalidKeyframeRule(..) =>
                (b"PEKeyframeBadName\0", Action::Nothing),
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                (b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
                (b"PEUnknownNamespacePrefix\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
                (b"PEAtNSUnexpected\0", Action::Nothing),
            ContextualParseError::InvalidRule(..) =>
                (b"PEBadSelectorRSIgnored\0", Action::Nothing),
            ContextualParseError::UnsupportedRule(..) =>
                (b"PEDeclDropped\0", Action::Nothing),
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
            ContextualParseError::InvalidFontFeatureValuesRule(..) =>
                (b"PEUnknownAtRule\0", Action::Skip),
        };
        (None, msg, action)
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error(&self,
                    _url: &UrlExtraData,
                    location: SourceLocation,
                    error: ContextualParseError) {
        let (pre, name, action) = error.to_gecko_message();
        let suffix = match action {
            Action::Nothing => ptr::null(),
            Action::Skip => b"PEDeclSkipped\0".as_ptr(),
            Action::Drop => b"PEDeclDropped\0".as_ptr(),
        };
        let (param, pre_param) = error.error_params();
        let param = param.into_str();
        let pre_param = pre_param.map(|p| p.into_str());
        let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        // The CSS source text is unused and will be removed in bug 1381188.
        let source = "";
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param.as_ptr() as *const _,
                                           param.len() as u32,
                                           pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
                                           pre_param_ptr as *const _,
                                           pre_param.as_ref().map_or(0, |p| p.len()) as u32,
                                           suffix as *const _,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           location.line,
                                           location.column);
        }
    }
}
<|endoftext|>


<commit_before>use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use core::marker::PhantomData;
use core::mem;
use super::BrotliAlloc;
use super::encode::{
  BrotliEncoderOperation,
  BrotliEncoderCreateInstance,
  BrotliEncoderSetCustomDictionary,
  BrotliEncoderDestroyInstance,
  BrotliEncoderMaxCompressedSize,
  BrotliEncoderCompressStream,
};
use concat::{
  BroCatli,
  BroCatliResult,
};
use core::ops::Range;
use super::backward_references::BrotliEncoderParams;
pub trait Joinable<T:Send+'static, U:Send+'static>:Sized {
  fn join(self) -> Result<T, U>;
}

pub struct CompressionThreadResult<Alloc:BrotliAlloc+Send+'static> where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  compressed: <Alloc as Allocator<u8>>::AllocatedMemory,
  compressed_size: usize,
  alloc: Alloc,
}
pub enum InternalSendAlloc<T:Send+'static, Alloc:BrotliAlloc+Send+'static, Join: Joinable<T, Alloc>>
  where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  A(Alloc),
  Join(Join),
  SpawningOrJoining(PhantomData<T>),
}
pub struct SendAlloc<T:Send+'static,
                     Alloc:BrotliAlloc +Send+'static,
                     Join:Joinable<T, Alloc>>(pub InternalSendAlloc<T, Alloc, Join>)//FIXME pub
  where <Alloc as Allocator<u8>>::AllocatedMemory: Send;

impl<T:Send+'static, Alloc:BrotliAlloc+Send+'static,Join:Joinable<T, Alloc>> SendAlloc<T, Alloc, Join>
  where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  pub fn new(alloc: Alloc) -> Self {
    SendAlloc::<T, Alloc, Join>(InternalSendAlloc::A(alloc))
  }
  pub fn unwrap_or(self, other: Alloc) -> Alloc {
    match self.0 {
      InternalSendAlloc::A(alloc) => {
        alloc
      },
      InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => {
        other
      },
    }
  }
  pub fn unwrap(self) -> Alloc {
    match self.0 {
      InternalSendAlloc::A(alloc) => {
        alloc
      },
      InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => panic!("Item permanently borrowed/leaked"),
    }
  }
  pub fn replace_with_default(&mut self) -> Alloc {
    match mem::replace(&mut self.0, InternalSendAlloc::SpawningOrJoining(PhantomData::default())) {
      InternalSendAlloc::A(alloc) => {
        alloc
      },
      InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => panic!("Item permanently borrowed/leaked"),
    }
  }
}

pub enum InternalOwned<T> { // FIXME pub
  Item(T),
  Borrowed,
}

pub struct Owned<T>(pub InternalOwned<T>); // FIXME pub
impl<T> Owned<T> {
  pub fn new(data:T) -> Self {
    Owned::<T>(InternalOwned::Item(data))
  }
  pub fn unwrap_or(self, other: T) -> T {
    if let InternalOwned::Item(x) = self.0 {
      x
    } else {
      other
    }
  }
  pub fn unwrap(self) -> T {
    if let InternalOwned::Item(x) = self.0 {
      x
    } else {
      panic!("Item permanently borrowed")
    }
  }
  pub fn view(&self) -> &T {
    if let InternalOwned::Item(ref x) = self.0 {
      x
    } else {
      panic!("Item permanently borrowed")
    }
  }
}

pub trait OwnedRetriever<U:Send+'static> {
  fn view(&self) -> &U;
  fn unwrap(self) -> U;
}

pub trait BatchSpawnable<T:Send+'static,
                         Alloc:BrotliAlloc+Send+'static,
                         U:Send+'static>
  where <Alloc as Allocator<u8>>::AllocatedMemory:Send+'static
{
  type JoinHandle: Joinable<T, Alloc>;
  type FinalJoinHandle: OwnedRetriever<U>;
  // this function takes in an input slice
  // a SendAlloc per thread and converts them all into JoinHandle
  // the input is borrowed until the joins complete
  // owned is set to borrowed
  // the final join handle is a r/w lock which will return the SliceW to the owner
  // the FinalJoinHandle is only to be called when each individual JoinHandle has been examined
  // the function is called with the thread_index, the num_threads, a reference to the slice under a read lock,
  // and an allocator from the alloc_per_thread
  fn batch_spawn<F: Fn(usize, usize, &U, Alloc) -> T>(
    &mut self,
    input: &mut Owned<U>,
    alloc_per_thread:&mut [SendAlloc<T, Alloc, Self::JoinHandle>],
    f: F,
  ) -> Self::FinalJoinHandle;
}

pub fn CompressMultiSlice<Alloc:BrotliAlloc+Send+'static,
                          Spawner:BatchSpawnable<CompressionThreadResult<Alloc>,
                                                 Alloc,
                                                 (<Alloc as Allocator<u8>>::AllocatedMemory, BrotliEncoderParams)>> (
  params:&BrotliEncoderParams,
  input_slice: &[u8],
  output: &mut [u8],
  alloc_per_thread:&mut [SendAlloc<CompressionThreadResult<Alloc>, Alloc, Spawner::JoinHandle>],
  mut thread_spawner: Spawner,
) -> Result<usize, ()> where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  let input = if let InternalSendAlloc::A(ref mut alloc) = alloc_per_thread[0].0 {
    let mut input = <Alloc as Allocator<u8>>::alloc_cell(alloc, input_slice.len());
    input.slice_mut().clone_from_slice(input_slice);
    input
  } else {
    <Alloc as Allocator<u8>>::AllocatedMemory::default()
  };
  let mut owned_input = Owned::new(input);
  let ret = CompressMulti(params, &mut owned_input, output, alloc_per_thread,thread_spawner);
  if let InternalSendAlloc::A(ref mut alloc) = alloc_per_thread[0].0 {
    <Alloc as Allocator<u8>>::free_cell(alloc, owned_input.unwrap());
  }
  ret
}

fn get_range(thread_index: usize, num_threads: usize, file_size: usize) -> Range<usize> {
    ((thread_index * file_size) / num_threads)..(((thread_index + 1) * file_size) / num_threads)
}

fn compress_part<Alloc: BrotliAlloc+Send+'static,
                 SliceW:SliceWrapper<u8>>(
  thread_index: usize,
  num_threads: usize,
  input_and_params:&(SliceW, BrotliEncoderParams),
  mut alloc: Alloc,
) -> CompressionThreadResult<Alloc> where <Alloc as Allocator<u8>>::AllocatedMemory:Send+'static {
  let mut range = get_range(thread_index + 1, num_threads + 1, input_and_params.0.len());
  let mut mem = <Alloc as Allocator<u8>>::alloc_cell(&mut alloc,
                                                     BrotliEncoderMaxCompressedSize(range.end - range.start));
  let mut state = BrotliEncoderCreateInstance(alloc);
  state.params = input_and_params.1.clone();
  state.params.catable = true; // make sure we can concatenate this to the other work results
  state.params.appendable = true; // make sure we are at least appendable, so that future items can be catted in
  state.params.magic_number = false; // no reason to pepper this around
  BrotliEncoderSetCustomDictionary(&mut state, range.start, &input_and_params.0.slice()[..range.start]);
  assert_eq!(range.start, 0);
  let mut out_offset = 0usize;
  let mut compression_result = Err(());
  let mut available_out = mem.len();
  loop {
    let mut next_in_offset = 0usize;
    let mut available_in = range.end - range.start;
    let result = BrotliEncoderCompressStream(&mut state,
                                             BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
                                             &mut available_in,
                                             &input_and_params.0.slice()[range.clone()],
                                             &mut next_in_offset,
                                             &mut available_out,
                                             mem.slice_mut(),
                                             &mut out_offset,
                                             &mut None,
                                             &mut |_a,_b,_c,_d|());
    let new_range = range.start + next_in_offset..range.end;
    range = new_range;
    if result != 0 {
      compression_result = Ok(out_offset);
      break;
    } else if available_out == 0 {
      compression_result = Err(()); // mark no space??
      break
    }
  }
  BrotliEncoderDestroyInstance(&mut state);

  CompressionThreadResult::<Alloc>{
    compressed:mem,
    compressed_size:out_offset,
    alloc:state.m8,
  }
}

pub fn CompressMulti<Alloc:BrotliAlloc+Send+'static,
                     SliceW: SliceWrapper<u8>+Send+'static,
                     Spawner:BatchSpawnable<CompressionThreadResult<Alloc>,
                                            Alloc,
                                            (SliceW, BrotliEncoderParams)>> (
  params:&BrotliEncoderParams,
  owned_input: &mut Owned<SliceW>,
  output: &mut [u8],
  alloc_per_thread:&mut [SendAlloc<CompressionThreadResult<Alloc>, Alloc, Spawner::JoinHandle>],
  mut thread_spawner: Spawner,
) -> Result<usize, ()> where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
    let num_threads = alloc_per_thread.len();
    let (mut alloc, alloc_rest) = alloc_per_thread.split_at_mut(1);
    let actually_owned_mem = mem::replace(owned_input, Owned(InternalOwned::Borrowed));
    let mut owned_input_pair = Owned::new((actually_owned_mem.unwrap(), params.clone()));
    let mut retrieve_owned_input = thread_spawner.batch_spawn(&mut owned_input_pair, alloc_rest, compress_part);
    let mut compression_result = Err(());
    let output_len = output.len();
    let first_thread_output_max_len = if alloc_rest.len() != 0 { output_len / 2 } else {output.len()};
    let mut available_out = first_thread_output_max_len;
    {
        let first_thread_output = &mut output[output_len - first_thread_output_max_len..];
        let input_and_params = retrieve_owned_input.view();
        let mut state = BrotliEncoderCreateInstance(match mem::replace(&mut alloc[0].0,
                                                                       InternalSendAlloc::SpawningOrJoining(PhantomData::default())) {
            InternalSendAlloc::A(a) => a,
            _ => panic!("all public interfaces which create SendAlloc can only specify subtype A")
        });
        state.params = params.clone();
        state.params.appendable = true; // make sure we are at least appendable, so that future items can be catted in
        let mut range = get_range(0, num_threads, input_and_params.0.len());
        assert_eq!(range.start, 0);
        let mut out_offset = 0usize;
        loop {
            let mut next_in_offset = 0usize;
            let mut available_in = range.end - range.start;
            let result = BrotliEncoderCompressStream(&mut state,
                                                 BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
                                                 &mut available_in,
                                                 &input_and_params.0.slice()[range.clone()],
                                                 &mut next_in_offset,
                                                 &mut available_out,
                                                 first_thread_output,
                                                 &mut out_offset,
                                                 &mut None,
                                                 &mut |_a,_b,_c,_d|());
            let new_range = range.start + next_in_offset..range.end;
            range = new_range;
            if result != 0 {
                compression_result = Ok(out_offset);
                break;
            } else if available_out == 0 {
                compression_result = Err(()); // mark no space??
                break
            }
        }
        BrotliEncoderDestroyInstance(&mut state);
        alloc[0].0 = InternalSendAlloc::A(state.m8);
    }
    let mut bro_cat_li = BroCatli::new();
    if alloc_rest.len() != 0 {
      if let Ok(first_file_size) = compression_result {
        bro_cat_li.new_brotli_file();
        let (cat_output, cat_input) = output.split_at_mut(output_len - first_thread_output_max_len);
        let mut in_offset = 0usize;
        let mut out_offset = 0usize;
        let cat_result = bro_cat_li.stream(&cat_input[..first_file_size],
                                           &mut in_offset,
                                           cat_output,
                                           &mut out_offset);
        if in_offset != first_file_size {
          compression_result = Err(()); // wasn't able to ingest the full file
        }
        match cat_result {
          BroCatliResult::Success | BroCatliResult::NeedsMoreInput  => {
            compression_result = Ok(out_offset);
          },
          BroCatliResult::NeedsMoreOutput => {
            compression_result = Err(()); // not enough space
          },
          _ => {
            compression_result = Err(()); // misc error
          },
        }
      }
    }
    if let Ok(mut out_file_size) = compression_result {
     for thread in alloc_rest.iter_mut() {
       let alloc;
       match mem::replace(&mut thread.0, InternalSendAlloc::SpawningOrJoining(PhantomData::default())) {
         InternalSendAlloc::A(_) | InternalSendAlloc::SpawningOrJoining(_) => panic!("Thread not properly spawned"),
         InternalSendAlloc::Join(join) => match join.join() {
           Ok(mut result) => {
             bro_cat_li.new_brotli_file();
             let mut in_offset = 0usize;
             let cat_result = bro_cat_li.stream(&result.compressed.slice()[..result.compressed_size],
                                                &mut in_offset,
                                                output,
                                                &mut out_file_size);
             match cat_result {
               BroCatliResult::Success | BroCatliResult::NeedsMoreInput  => {
                 compression_result = Ok(out_file_size);
               },
               BroCatliResult::NeedsMoreOutput => {
                 compression_result = Err(()); // not enough space
               }
               _ => {
                 compression_result = Err(()); // misc error
               }
             }
             <Alloc as Allocator<u8>>::free_cell(&mut result.alloc, result.compressed);
             alloc = result.alloc;
           },
           Err(allocator) => {
             alloc = allocator;
             compression_result = Err(());
           },
         },
       }
       thread.0 = InternalSendAlloc::A(alloc);
     }
     if alloc_rest.len() != 0 {
       match bro_cat_li.finish(output, &mut out_file_size) {
         BroCatliResult::Success => compression_result = Ok(out_file_size),
         _ => compression_result = Err(()),
       }
     }
   }

  *owned_input = Owned::new(retrieve_owned_input.unwrap().0); // return the input to its rightful owner before returning
  compression_result
}

<commit_msg>allow pass back of errors from threads<commit_after>use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use core::marker::PhantomData;
use core::mem;
use super::BrotliAlloc;
use super::encode::{
  BrotliEncoderOperation,
  BrotliEncoderCreateInstance,
  BrotliEncoderSetCustomDictionary,
  BrotliEncoderDestroyInstance,
  BrotliEncoderMaxCompressedSize,
  BrotliEncoderCompressStream,
};
use concat::{
  BroCatli,
  BroCatliResult,
};
use core::ops::Range;
use super::backward_references::BrotliEncoderParams;
pub trait Joinable<T:Send+'static, U:Send+'static>:Sized {
  fn join(self) -> Result<T, U>;
}

pub struct CompressionThreadResult<Alloc:BrotliAlloc+Send+'static> where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  compressed: Result<(<Alloc as Allocator<u8>>::AllocatedMemory, usize), ()>,
  alloc: Alloc,
}
pub enum InternalSendAlloc<T:Send+'static, Alloc:BrotliAlloc+Send+'static, Join: Joinable<T, Alloc>>
  where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  A(Alloc),
  Join(Join),
  SpawningOrJoining(PhantomData<T>),
}
pub struct SendAlloc<T:Send+'static,
                     Alloc:BrotliAlloc +Send+'static,
                     Join:Joinable<T, Alloc>>(pub InternalSendAlloc<T, Alloc, Join>)//FIXME pub
  where <Alloc as Allocator<u8>>::AllocatedMemory: Send;

impl<T:Send+'static, Alloc:BrotliAlloc+Send+'static,Join:Joinable<T, Alloc>> SendAlloc<T, Alloc, Join>
  where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  pub fn new(alloc: Alloc) -> Self {
    SendAlloc::<T, Alloc, Join>(InternalSendAlloc::A(alloc))
  }
  pub fn unwrap_or(self, other: Alloc) -> Alloc {
    match self.0 {
      InternalSendAlloc::A(alloc) => {
        alloc
      },
      InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => {
        other
      },
    }
  }
  pub fn unwrap(self) -> Alloc {
    match self.0 {
      InternalSendAlloc::A(alloc) => {
        alloc
      },
      InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => panic!("Item permanently borrowed/leaked"),
    }
  }
  pub fn replace_with_default(&mut self) -> Alloc {
    match mem::replace(&mut self.0, InternalSendAlloc::SpawningOrJoining(PhantomData::default())) {
      InternalSendAlloc::A(alloc) => {
        alloc
      },
      InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => panic!("Item permanently borrowed/leaked"),
    }
  }
}

pub enum InternalOwned<T> { // FIXME pub
  Item(T),
  Borrowed,
}

pub struct Owned<T>(pub InternalOwned<T>); // FIXME pub
impl<T> Owned<T> {
  pub fn new(data:T) -> Self {
    Owned::<T>(InternalOwned::Item(data))
  }
  pub fn unwrap_or(self, other: T) -> T {
    if let InternalOwned::Item(x) = self.0 {
      x
    } else {
      other
    }
  }
  pub fn unwrap(self) -> T {
    if let InternalOwned::Item(x) = self.0 {
      x
    } else {
      panic!("Item permanently borrowed")
    }
  }
  pub fn view(&self) -> &T {
    if let InternalOwned::Item(ref x) = self.0 {
      x
    } else {
      panic!("Item permanently borrowed")
    }
  }
}

pub trait OwnedRetriever<U:Send+'static> {
  fn view(&self) -> &U;
  fn unwrap(self) -> U;
}

pub trait BatchSpawnable<T:Send+'static,
                         Alloc:BrotliAlloc+Send+'static,
                         U:Send+'static>
  where <Alloc as Allocator<u8>>::AllocatedMemory:Send+'static
{
  type JoinHandle: Joinable<T, Alloc>;
  type FinalJoinHandle: OwnedRetriever<U>;
  // this function takes in an input slice
  // a SendAlloc per thread and converts them all into JoinHandle
  // the input is borrowed until the joins complete
  // owned is set to borrowed
  // the final join handle is a r/w lock which will return the SliceW to the owner
  // the FinalJoinHandle is only to be called when each individual JoinHandle has been examined
  // the function is called with the thread_index, the num_threads, a reference to the slice under a read lock,
  // and an allocator from the alloc_per_thread
  fn batch_spawn<F: Fn(usize, usize, &U, Alloc) -> T>(
    &mut self,
    input: &mut Owned<U>,
    alloc_per_thread:&mut [SendAlloc<T, Alloc, Self::JoinHandle>],
    f: F,
  ) -> Self::FinalJoinHandle;
}

pub fn CompressMultiSlice<Alloc:BrotliAlloc+Send+'static,
                          Spawner:BatchSpawnable<CompressionThreadResult<Alloc>,
                                                 Alloc,
                                                 (<Alloc as Allocator<u8>>::AllocatedMemory, BrotliEncoderParams)>> (
  params:&BrotliEncoderParams,
  input_slice: &[u8],
  output: &mut [u8],
  alloc_per_thread:&mut [SendAlloc<CompressionThreadResult<Alloc>, Alloc, Spawner::JoinHandle>],
  mut thread_spawner: Spawner,
) -> Result<usize, ()> where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
  let input = if let InternalSendAlloc::A(ref mut alloc) = alloc_per_thread[0].0 {
    let mut input = <Alloc as Allocator<u8>>::alloc_cell(alloc, input_slice.len());
    input.slice_mut().clone_from_slice(input_slice);
    input
  } else {
    <Alloc as Allocator<u8>>::AllocatedMemory::default()
  };
  let mut owned_input = Owned::new(input);
  let ret = CompressMulti(params, &mut owned_input, output, alloc_per_thread,thread_spawner);
  if let InternalSendAlloc::A(ref mut alloc) = alloc_per_thread[0].0 {
    <Alloc as Allocator<u8>>::free_cell(alloc, owned_input.unwrap());
  }
  ret
}

fn get_range(thread_index: usize, num_threads: usize, file_size: usize) -> Range<usize> {
    ((thread_index * file_size) / num_threads)..(((thread_index + 1) * file_size) / num_threads)
}

fn compress_part<Alloc: BrotliAlloc+Send+'static,
                 SliceW:SliceWrapper<u8>>(
  thread_index: usize,
  num_threads: usize,
  input_and_params:&(SliceW, BrotliEncoderParams),
  mut alloc: Alloc,
) -> CompressionThreadResult<Alloc> where <Alloc as Allocator<u8>>::AllocatedMemory:Send+'static {
  let mut range = get_range(thread_index + 1, num_threads + 1, input_and_params.0.len());
  let mut mem = <Alloc as Allocator<u8>>::alloc_cell(&mut alloc,
                                                     BrotliEncoderMaxCompressedSize(range.end - range.start));
  let mut state = BrotliEncoderCreateInstance(alloc);
  state.params = input_and_params.1.clone();
  state.params.catable = true; // make sure we can concatenate this to the other work results
  state.params.appendable = true; // make sure we are at least appendable, so that future items can be catted in
  state.params.magic_number = false; // no reason to pepper this around
  BrotliEncoderSetCustomDictionary(&mut state, range.start, &input_and_params.0.slice()[..range.start]);
  assert_eq!(range.start, 0);
  let mut out_offset = 0usize;
  let mut compression_result;
  let mut available_out = mem.len();
  loop {
    let mut next_in_offset = 0usize;
    let mut available_in = range.end - range.start;
    let result = BrotliEncoderCompressStream(&mut state,
                                             BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
                                             &mut available_in,
                                             &input_and_params.0.slice()[range.clone()],
                                             &mut next_in_offset,
                                             &mut available_out,
                                             mem.slice_mut(),
                                             &mut out_offset,
                                             &mut None,
                                             &mut |_a,_b,_c,_d|());
    let new_range = range.start + next_in_offset..range.end;
    range = new_range;
    if result != 0 {
      compression_result = Ok(out_offset);
      break;
    } else if available_out == 0 {
      compression_result = Err(()); // mark no space??
      break
    }
  }
  BrotliEncoderDestroyInstance(&mut state);
    match compression_result {
        Ok(size) => {
            CompressionThreadResult::<Alloc>{
                compressed:Ok((mem, size)),
                alloc:state.m8,
            }
        },
        Err(e) => {
            <Alloc as Allocator<u8>>::free_cell(&mut state.m8, mem);
            CompressionThreadResult::<Alloc>{
                compressed:Err(e),
                alloc:state.m8,
            }
        },
    }
}

pub fn CompressMulti<Alloc:BrotliAlloc+Send+'static,
                     SliceW: SliceWrapper<u8>+Send+'static,
                     Spawner:BatchSpawnable<CompressionThreadResult<Alloc>,
                                            Alloc,
                                            (SliceW, BrotliEncoderParams)>> (
  params:&BrotliEncoderParams,
  owned_input: &mut Owned<SliceW>,
  output: &mut [u8],
  alloc_per_thread:&mut [SendAlloc<CompressionThreadResult<Alloc>, Alloc, Spawner::JoinHandle>],
  mut thread_spawner: Spawner,
) -> Result<usize, ()> where <Alloc as Allocator<u8>>::AllocatedMemory: Send {
    let num_threads = alloc_per_thread.len();
    let (mut alloc, alloc_rest) = alloc_per_thread.split_at_mut(1);
    let actually_owned_mem = mem::replace(owned_input, Owned(InternalOwned::Borrowed));
    let mut owned_input_pair = Owned::new((actually_owned_mem.unwrap(), params.clone()));
    let mut retrieve_owned_input = thread_spawner.batch_spawn(&mut owned_input_pair, alloc_rest, compress_part);
    let mut compression_result = Err(());
    let output_len = output.len();
    let first_thread_output_max_len = if alloc_rest.len() != 0 { output_len / 2 } else {output.len()};
    let mut available_out = first_thread_output_max_len;
    {
        let first_thread_output = &mut output[output_len - first_thread_output_max_len..];
        let input_and_params = retrieve_owned_input.view();
        let mut state = BrotliEncoderCreateInstance(match mem::replace(&mut alloc[0].0,
                                                                       InternalSendAlloc::SpawningOrJoining(PhantomData::default())) {
            InternalSendAlloc::A(a) => a,
            _ => panic!("all public interfaces which create SendAlloc can only specify subtype A")
        });
        state.params = params.clone();
        state.params.appendable = true; // make sure we are at least appendable, so that future items can be catted in
        let mut range = get_range(0, num_threads, input_and_params.0.len());
        assert_eq!(range.start, 0);
        let mut out_offset = 0usize;
        loop {
            let mut next_in_offset = 0usize;
            let mut available_in = range.end - range.start;
            let result = BrotliEncoderCompressStream(&mut state,
                                                 BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
                                                 &mut available_in,
                                                 &input_and_params.0.slice()[range.clone()],
                                                 &mut next_in_offset,
                                                 &mut available_out,
                                                 first_thread_output,
                                                 &mut out_offset,
                                                 &mut None,
                                                 &mut |_a,_b,_c,_d|());
            let new_range = range.start + next_in_offset..range.end;
            range = new_range;
            if result != 0 {
                compression_result = Ok(out_offset);
                break;
            } else if available_out == 0 {
                compression_result = Err(()); // mark no space??
                break
            }
        }
        BrotliEncoderDestroyInstance(&mut state);
        alloc[0].0 = InternalSendAlloc::A(state.m8);
    }
    let mut bro_cat_li = BroCatli::new();
    if alloc_rest.len() != 0 {
      if let Ok(first_file_size) = compression_result {
        bro_cat_li.new_brotli_file();
        let (cat_output, cat_input) = output.split_at_mut(output_len - first_thread_output_max_len);
        let mut in_offset = 0usize;
        let mut out_offset = 0usize;
        let cat_result = bro_cat_li.stream(&cat_input[..first_file_size],
                                           &mut in_offset,
                                           cat_output,
                                           &mut out_offset);
        if in_offset != first_file_size {
          compression_result = Err(()); // wasn't able to ingest the full file
        }
        match cat_result {
          BroCatliResult::Success | BroCatliResult::NeedsMoreInput  => {
            compression_result = Ok(out_offset);
          },
          BroCatliResult::NeedsMoreOutput => {
            compression_result = Err(()); // not enough space
          },
          _ => {
            compression_result = Err(()); // misc error
          },
        }
      }
    }
    if let Ok(mut out_file_size) = compression_result {
     for thread in alloc_rest.iter_mut() {
       let alloc;
       match mem::replace(&mut thread.0, InternalSendAlloc::SpawningOrJoining(PhantomData::default())) {
         InternalSendAlloc::A(_) | InternalSendAlloc::SpawningOrJoining(_) => panic!("Thread not properly spawned"),
         InternalSendAlloc::Join(join) => match join.join() {
           Ok(mut result) => {
             match result.compressed {
               Ok(compressed_out) => {
                 bro_cat_li.new_brotli_file();
                 let mut in_offset = 0usize;
                 let cat_result = bro_cat_li.stream(&compressed_out.0.slice()[..compressed_out.1],
                                                    &mut in_offset,
                                                    output,
                                                    &mut out_file_size);
                 match cat_result {
                   BroCatliResult::Success | BroCatliResult::NeedsMoreInput  => {
                     compression_result = Ok(out_file_size);
                   },
                   BroCatliResult::NeedsMoreOutput => {
                     compression_result = Err(()); // not enough space
                   },
                   _ => {
                     compression_result = Err(()); // misc error
                   },
                 }
                 <Alloc as Allocator<u8>>::free_cell(&mut result.alloc, compressed_out.0);
               }
               Err(e) => {
                   compression_result = Err(e);
               }
             }
             alloc = result.alloc;
           },
           Err(allocator) => {
             alloc = allocator;
             compression_result = Err(());
           },
         },
       }
       thread.0 = InternalSendAlloc::A(alloc);
     }
     if alloc_rest.len() != 0 {
       match bro_cat_li.finish(output, &mut out_file_size) {
         BroCatliResult::Success => compression_result = Ok(out_file_size),
         _ => compression_result = Err(()),
       }
     }
   }

  *owned_input = Owned::new(retrieve_owned_input.unwrap().0); // return the input to its rightful owner before returning
  compression_result
}

<|endoftext|>


<commit_before>use basetypes::*;
use position::{Position, Value};

pub fn search(p: Position,
              alpha: Value, // lower bound
              beta: Value, // upper bound
              depth: usize,
              send_node_count: &Fn(NodeCount) -> bool) {

}
<commit_msg>implement dummy search<commit_after>use basetypes::*;
use chess_move::MoveStack;
use tt::*;
use position::{Position, Value};

pub fn search(tt: &TranspositionTable,
              p: &mut Position,
              mut alpha: Value, // lower bound
              beta: Value, // upper bound
              depth: usize,
              node_count: &mut NodeCount,
              report_node_count: &Fn(NodeCount) -> bool)
              -> Value {
    if depth == 0 {
        let (value, nodes) = p.evaluate_quiescence(alpha, beta);
        *node_count += nodes;
        value
    } else {
        // TODO: use something faster than `Vec`.
        let mut move_stack = MoveStack::new();
        move_stack.save();
        p.generate_moves(&mut move_stack);
        while let Some(next_move) = move_stack.remove_best_move() {
            if p.do_move(next_move) {
                *node_count += 1;
                let value = -search(tt,
                                    p,
                                    -beta,
                                    -alpha,
                                    depth - 1,
                                    node_count,
                                    report_node_count);
                p.undo_move();
                if value >= beta {
                    alpha = value;
                    break;
                }
            }

        }
        move_stack.restore();
        alpha
    }
}
<|endoftext|>


<commit_before>use basetypes::*;
use chess_move::MoveStack;
use tt::*;
use position::{Position, Value};

/// Represents a terminated search condition.
pub struct TerminatedSearch;

pub fn search(tt: &TranspositionTable,
              p: &mut Position,
              moves: &mut MoveStack,
              nc: &mut NodeCount,
              report: &Fn(NodeCount) -> Result<(), TerminatedSearch>,
              mut alpha: Value, // lower bound
              beta: Value, // upper bound
              depth: usize)
              -> Result<Value, TerminatedSearch> {
    assert!(alpha <= beta);
    if depth == 0 {
        // On leaf nodes, do quiescence search.
        let (value, nodes) = p.evaluate_quiescence(alpha, beta);
        *nc += nodes;
        Ok(value)
    } else {
        moves.save();
        p.generate_moves(moves);
        let mut no_moves_yet = true;
        while let Some(m) = moves.remove_best_move() {
            if p.do_move(m) {
                // From time to time, we report how many nodes had
                // been searched since the last report. This also
                // gives an opportunity for the search to be
                // terminated.
                *nc += 1;
                if *nc > NODE_COUNT_REPORT_INTERVAL {
                    try!(report(*nc));
                    *nc = 0;
                }

                let value = if no_moves_yet {
                    // The first move we analyze with a fully open
                    // window (alpha, beta).
                    -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that
                    // they are not better than our current
                    // favorite. For this purpose we analyze them with
                    // a null window (alpha, alpha + 1). This is
                    // faster than a full window search. Only when we
                    // are certain that the move is better than our
                    // current favorite, we do a full-window search.
                    match -try!(search(tt, p, moves, nc, report, -alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1)),
                    }
                };
                no_moves_yet = false;
                p.undo_move();

                if value >= beta {
                    // This move is too good, so that the opponent
                    // will not allow this line of play to
                    // happen. Therefore we can stop here.
                    alpha = value;
                    break;
                }
                if value > alpha {
                    // We found ourselves a new favorite.
                    alpha = value;
                }
            }
        }
        moves.restore();
        if no_moves_yet {
            // This is a final position.
            Ok(p.evaluate_final())
        } else {
            Ok(alpha)
        }
    }
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;
<commit_msg>comment<commit_after>use basetypes::*;
use chess_move::MoveStack;
use tt::*;
use position::{Position, Value};

/// Represents a terminated search condition.
pub struct TerminatedSearch;

pub fn search(tt: &TranspositionTable,
              p: &mut Position,
              moves: &mut MoveStack,
              nc: &mut NodeCount,
              report: &Fn(NodeCount) -> Result<(), TerminatedSearch>,
              mut alpha: Value, // lower bound
              beta: Value, // upper bound
              depth: usize)
              -> Result<Value, TerminatedSearch> {
    assert!(alpha <= beta);
    if depth == 0 {
        // On leaf nodes, do quiescence search.
        let (value, nodes) = p.evaluate_quiescence(alpha, beta);
        *nc += nodes;
        Ok(value)
    } else {
        moves.save();
        p.generate_moves(moves);
        let mut no_moves_yet = true;
        while let Some(m) = moves.remove_best_move() {
            if p.do_move(m) {
                // From time to time, we report how many nodes had
                // been searched since the last report. This also
                // gives an opportunity for the search to be
                // terminated.
                *nc += 1;
                if *nc > NODE_COUNT_REPORT_INTERVAL {
                    try!(report(*nc));
                    *nc = 0;
                }

                let value = if no_moves_yet {
                    // The first move we analyze with a fully open
                    // window (alpha, beta).
                    -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that
                    // they are not better than our current
                    // favorite. For this purpose we analyze them with
                    // a null window (alpha, alpha + 1). This is
                    // faster than a full window search. Only when we
                    // are certain that the move is better than our
                    // current favorite, we do a full-window search.
                    match -try!(search(tt, p, moves, nc, report, -alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1)),
                    }
                };
                no_moves_yet = false;
                p.undo_move();

                if value >= beta {
                    // This move is too good, so that the opponent
                    // will not allow this line of play to
                    // happen. Therefore we can stop here.
                    alpha = value;
                    break;
                }
                if value > alpha {
                    // We found ourselves a new favorite.
                    alpha = value;
                }
            }
        }
        moves.restore();
        if no_moves_yet {
            // No legal moves -- this is a final position.
            Ok(p.evaluate_final())
        } else {
            Ok(alpha)
        }
    }
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;
<|endoftext|>


<commit_before>extern crate inotify;
extern crate libc;

use inotify::INotify;
use inotify::wrapper::Event;
use inotify::wrapper::Watch;
use inotify::ffi::*;

use std::path::Path;

pub struct FilesWatcher {
    inotify: INotify
}

impl FilesWatcher {
    pub fn new() -> FilesWatcher {
        let inotify = INotify::init().unwrap();

        FilesWatcher { inotify: inotify }
    }

    pub fn add_file(&mut self, path: &Path) -> Watch {
        self.inotify.add_watch(path, IN_MODIFY | IN_DELETE).unwrap()
    }

    pub fn wait_for_events(&mut self) -> &[Event] {
        self.inotify.wait_for_events().unwrap()
    }

    pub fn close(&mut self) {
        self.inotify.close().unwrap();
    }
}

#[cfg(test)]
mod test {
    extern crate rand;

    use super::*;

    use self::rand::{thread_rng, Rng};
    use std::env::temp_dir;
    use std::fs::File;
    use std::fs::remove_file;
    use std::io::Write;
    use std::path::Path;
    use std::path::PathBuf;

    #[test]
    fn watch_a_single_file() {
        let (path, mut file) = create_temp_file();

        let mut fw = FilesWatcher::new();
        let watch = fw.add_file(&path);

        write_to(&mut file);

        {
            let events = fw.wait_for_events();
            assert_eq!(1, events.len());
            for event in events.iter() {
                assert_eq!(watch, event.wd);
            }
        }

        fw.close();
        remove_temp_file(&path);
    }

    #[test]
    fn watch_two_files() {
        let (path1, mut file1) = create_temp_file();
        let (path2, mut file2) = create_temp_file();

        let mut fw = FilesWatcher::new();
        let watch1 = fw.add_file(&path1);
        let watch2 = fw.add_file(&path2);

        write_to(&mut file1);
        write_to(&mut file2);

        {
            let events = fw.wait_for_events();
            assert_eq!(2, events.len());
            for event in events.iter() {
                assert!(event.wd == watch1 || event.wd == watch2);
            }
        }

        fw.close();
        remove_temp_file(&path1);
        remove_temp_file(&path2);
    }

    fn create_temp_file() -> (PathBuf, File) {
        let rand_part: String = thread_rng()
            .gen_ascii_chars()
            .take(8)
            .collect();

        let filename = "eagle-test-".to_string() + &rand_part;
        let path = temp_dir().join(filename);
        let file = File::create(&path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
        let path_buf = PathBuf::from(path);

        (path_buf, file)
    }

    fn remove_temp_file(path: &Path) {
        remove_file(path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
    }

    fn write_to(file: &mut File) {
        file
            .write(b"This should trigger an inotify event.")
            .unwrap_or_else(|error|
                panic!("Failed to write to file: {}", error)
            );
    }

}

<commit_msg>Return file path information with events<commit_after>extern crate inotify;
extern crate libc;

use inotify::INotify;
use inotify::wrapper::Event;
use inotify::wrapper::Watch;
use inotify::ffi::*;

use std::collections::HashMap;
use std::path::PathBuf;

pub struct FilesWatcher {
    inotify: INotify,
    watches: HashMap<Watch, PathBuf>
}

impl FilesWatcher {
    pub fn new() -> FilesWatcher {
        let inotify = INotify::init().unwrap();

        FilesWatcher {
            inotify: inotify,
            watches: HashMap::new()
        }
    }

    pub fn add_file(&mut self, path: PathBuf) {
        let watch_id = self.inotify.add_watch(&path, IN_MODIFY | IN_DELETE).unwrap();
        self.watches.insert(watch_id, path);
    }

    pub fn wait_for_events(&mut self) -> Vec<EventPath> {
        let events = self.inotify.wait_for_events().unwrap();

        let mut event_paths: Vec<EventPath> = vec![];
        for event in events.iter() {
            let path: PathBuf = self.watches.get(&event.wd).unwrap().clone();
            event_paths.push(EventPath::new(path, event.clone()));
        }

        event_paths
    }

    pub fn close(&mut self) {
        self.inotify.close().unwrap();
    }
}

pub struct EventPath {
    event: Event,
    path: PathBuf
}

impl EventPath {
    fn new(path: PathBuf, event: Event) -> EventPath {
        EventPath {
            event: event,
            path: path
        }
    }
}

#[cfg(test)]
mod test {
    extern crate rand;

    use super::*;

    use self::rand::{thread_rng, Rng};
    use std::env::temp_dir;
    use std::fs::File;
    use std::fs::remove_file;
    use std::io::Write;
    use std::path::Path;
    use std::path::PathBuf;

    #[test]
    fn watch_a_single_file() {
        let (path, mut file) = create_temp_file();
        let filepath = path.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path);

        write_to(&mut file);

        {
            let event_paths = fw.wait_for_events();
            assert_eq!(1, event_paths.len());
            for event_path in event_paths.iter() {
                assert_eq!(filepath, event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    #[test]
    fn watch_two_files() {
        let (path1, mut file1) = create_temp_file();
        let (path2, mut file2) = create_temp_file();

        let filepath1 = path1.clone();
        let filepath2 = path2.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path1);
        fw.add_file(path2);

        write_to(&mut file1);
        write_to(&mut file2);

        {
            let event_paths = fw.wait_for_events();
            assert_eq!(2, event_paths.len());
            for event_path in event_paths.iter() {
                assert!(filepath1 == event_path.path || filepath2 == event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    fn create_temp_file() -> (PathBuf, File) {
        let rand_part: String = thread_rng()
            .gen_ascii_chars()
            .take(8)
            .collect();

        let filename = "eagleeye-test-".to_string() + &rand_part;
        let path = temp_dir().join(filename);
        let file = File::create(&path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
        let path_buf = PathBuf::from(path);

        (path_buf, file)
    }

    fn remove_temp_file(path: &Path) {
        remove_file(path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
    }

    fn write_to(file: &mut File) {
        file
            .write(b"This should trigger an inotify event.")
            .unwrap_or_else(|error|
                panic!("Failed to write to file: {}", error)
            );
    }

}

<|endoftext|>


<commit_before>//
//
//
use core::iter::range;
use core::iter::{FromIterator,Iterator};
use core::slice::{Slice,ImmutableSlice,MutableSlice,Items,MutItems};
use core::option::{Option,Some,None};
use core::ptr::RawPtr;
use core::num::Int;
use core::ops::{Drop,Index};
use lib::clone::Clone;
use core::collections::{Collection};
use lib::collections::{MutableSeq};

pub struct Vec<T>
{
	data: *mut T,
	size: uint,
	capacity: uint,
}

impl<T> Vec<T>
{
	pub fn new() -> Vec<T>
	{
		Vec {
			data: RawPtr::null(),
			size: 0,
			capacity: 0,
		}
	}
	pub fn with_capacity(size: uint) -> Vec<T>
	{
		let mut ret = Vec::new();
		ret.reserve(size);
		ret
	}
	pub fn from_fn(length: uint, op: |uint| -> T) -> Vec<T>
	{
		let mut ret = Vec::with_capacity(length);
		for i in range(0, length) {
			ret.push( op(i) );
		}
		ret
	}

	pub fn get_mut<'s>(&'s mut self, index: uint) -> &'s mut T
	{
		if index >= self.size {
			fail!("Index out of range, {} >= {}", index, self.size);
		}
		unsafe { &mut *self.data.offset(index as int) }
	}
	pub fn iter<'s>(&'s self) -> Items<'s,T>
	{
		self.as_slice().iter()
	}
	pub fn iter_mut<'s>(&'s mut self) -> MutItems<'s,T>
	{
		self.slice_mut().iter_mut()
	}

	fn reserve(&mut self, size: uint)
	{
		let newcap = ::lib::num::round_up(size, 1 << (64-size.leading_zeros()));
		if newcap > self.capacity
		{
			unsafe {
				let newptr = ::memory::heap::alloc_array::<T>( newcap );
				for i in range(0, self.size as int)
				{
					::core::ptr::write(newptr.offset(i), self.move_ent(i as uint));
				}
				if self.capacity > 0 {
					::memory::heap::deallocate( self.data );
				}
				self.data = newptr;
				self.capacity = newcap;
			}
		}
	}

	pub fn slice_mut<'a>(&'a mut self) -> &'a mut [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: self.data as *const T, len: self.size } ) }
	}

	/// Move out of a slot in the vector, leaving unitialise memory in its place
	unsafe fn move_ent(&mut self, pos: uint) -> T
	{
		::core::ptr::replace(self.data.offset(pos as int), ::core::mem::uninitialized())
	}
}

impl<T:Clone> Vec<T>
{
	pub fn from_elem(size: uint, elem: T) -> Vec<T>
	{
		Vec::from_fn( size, |_| elem.clone() )
	}
}

#[unsafe_destructor]
impl<T> Drop for Vec<T>
{
	fn drop(&mut self)
	{
		if self.capacity > 0
		{
			unsafe {
				for i in range(0, self.size) {
					*self.get_mut(i) = ::core::mem::uninitialized();
				}
				::memory::heap::deallocate( self.data );
			}
		}
	}
}

impl<T> Index<uint, T> for Vec<T>
{
	fn index<'a>(&'a self, index: &uint) -> &'a T
	{
		if *index >= self.size {
			fail!("Index out of range, {} >= {}", index, self.size);
		}
		unsafe { &*self.data.offset(*index as int) }
	}
}

impl<T> ::core::slice::Slice<T> for Vec<T>
{
	fn as_slice<'a>(&'a self) -> &'a [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: self.data as *const T, len: self.size } ) }
	}
}

impl<T> Collection for Vec<T>
{
	fn len(&self) -> uint { self.size }
}

impl<T> MutableSeq<T> for Vec<T>
{
	fn push(&mut self, t: T)
	{
		let pos = self.size;
		self.size += 1;
		let newsize = self.size;
		self.reserve(newsize);
		unsafe { ::core::ptr::write(self.get_mut(pos), t); }
	}
	fn pop(&mut self) -> Option<T>
	{
		if self.size == 0
		{
			None
		}
		else
		{
			self.size -= 1;
			let pos = self.size;
			Some( unsafe { self.move_ent(pos) } )
		}
	}
}

impl<T> FromIterator<T> for Vec<T>
{
	fn from_iter<IT: Iterator<T>>(iterator: IT) -> Vec<T>
	{
		let mut it = iterator;
		let mut ret = Vec::new();
		for val in it
		{
			ret.push(val);
		}
		ret
	}
}

// vim: ft=rust
<commit_msg>Core - Fix broken Vec implementation<commit_after>//
//
//
use core::iter::range;
use core::iter::{FromIterator,Iterator};
use core::slice::{Slice,ImmutableSlice,MutableSlice,Items,MutItems};
use core::option::{Option,Some,None};
use core::ptr::RawPtr;
use core::num::Int;
use core::ops::{Drop,Index};
use lib::clone::Clone;
use core::collections::{Collection};
use lib::collections::{MutableSeq};

pub struct Vec<T>
{
	data: *mut T,
	size: uint,
	capacity: uint,
}

impl<T> Vec<T>
{
	pub fn new() -> Vec<T>
	{
		Vec {
			data: RawPtr::null(),
			size: 0,
			capacity: 0,
		}
	}
	pub fn with_capacity(size: uint) -> Vec<T>
	{
		let mut ret = Vec::new();
		ret.reserve(size);
		ret
	}
	pub fn from_fn(length: uint, op: |uint| -> T) -> Vec<T>
	{
		let mut ret = Vec::with_capacity(length);
		for i in range(0, length) {
			ret.push( op(i) );
		}
		ret
	}

	pub fn get_mut<'s>(&'s mut self, index: uint) -> &'s mut T
	{
		if index >= self.size {
			fail!("Index out of range, {} >= {}", index, self.size);
		}
		unsafe { &mut *self.data.offset(index as int) }
	}
	pub fn iter<'s>(&'s self) -> Items<'s,T>
	{
		self.as_slice().iter()
	}
	pub fn iter_mut<'s>(&'s mut self) -> MutItems<'s,T>
	{
		self.slice_mut().iter_mut()
	}

	fn reserve(&mut self, size: uint)
	{
		let newcap = ::lib::num::round_up(size, 1 << (64-size.leading_zeros()));
		if newcap > self.capacity
		{
			unsafe {
				let newptr = ::memory::heap::alloc_array::<T>( newcap );
				for i in range(0, self.size)
				{
					::core::ptr::write(newptr.offset(i as int), self.move_ent(i as uint));
				}
				if self.capacity > 0 {
					::memory::heap::deallocate( self.data );
				}
				self.data = newptr;
				self.capacity = newcap;
			}
		}
	}

	pub fn slice_mut<'a>(&'a mut self) -> &'a mut [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: self.data as *const T, len: self.size } ) }
	}

	/// Move out of a slot in the vector, leaving unitialise memory in its place
	unsafe fn move_ent(&mut self, pos: uint) -> T
	{
		::core::ptr::replace(self.data.offset(pos as int), ::core::mem::uninitialized())
	}
}

impl<T:Clone> Vec<T>
{
	pub fn from_elem(size: uint, elem: T) -> Vec<T>
	{
		Vec::from_fn( size, |_| elem.clone() )
	}
}

#[unsafe_destructor]
impl<T> Drop for Vec<T>
{
	fn drop(&mut self)
	{
		if self.capacity > 0
		{
			unsafe {
				for i in range(0, self.size) {
					*self.get_mut(i) = ::core::mem::uninitialized();
				}
				::memory::heap::deallocate( self.data );
			}
		}
	}
}

impl<T> Index<uint, T> for Vec<T>
{
	fn index<'a>(&'a self, index: &uint) -> &'a T
	{
		if *index >= self.size {
			fail!("Index out of range, {} >= {}", index, self.size);
		}
		unsafe { &*self.data.offset(*index as int) }
	}
}

impl<T> ::core::slice::Slice<T> for Vec<T>
{
	fn as_slice<'a>(&'a self) -> &'a [T]
	{
		unsafe { ::core::mem::transmute( ::core::raw::Slice { data: self.data as *const T, len: self.size } ) }
	}
}

impl<T> Collection for Vec<T>
{
	fn len(&self) -> uint { self.size }
}

impl<T> MutableSeq<T> for Vec<T>
{
	fn push(&mut self, t: T)
	{
		let pos = self.size;
		self.reserve(pos + 1);
		self.size += 1;
		let ptr = self.get_mut(pos);
		unsafe { ::core::ptr::write(ptr, t); }
	}
	fn pop(&mut self) -> Option<T>
	{
		if self.size == 0
		{
			None
		}
		else
		{
			self.size -= 1;
			let pos = self.size;
			Some( unsafe { self.move_ent(pos) } )
		}
	}
}

impl<T> FromIterator<T> for Vec<T>
{
	fn from_iter<IT: Iterator<T>>(mut iterator: IT) -> Vec<T>
	{
		let mut ret = Vec::new();
		for val in iterator
		{
			ret.push(val);
		}
		ret
	}
}

// vim: ft=rust
<|endoftext|>