<commit_before>use self::Token::{Begin, End, Identifier, Number, Text, Whitespace};
use self::LexError::{UnexpectedChar, UnexpectedEOF, UnclosedString, UnparseableInt};

use parsell::{Parser, Committed, CHARACTER, character};
use std::num::ParseIntError;
use std::borrow::Cow;

#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
pub enum Token<'a> {
    Begin(Cow<'a,str>),
    End,
    Identifier(Cow<'a,str>),
    Number(usize),
    Text(Cow<'a,str>),
    Whitespace(Cow<'a,str>),
}

#[derive(Clone, PartialEq, Debug)]
pub enum LexError {
    UnexpectedChar(char),
    UnclosedString(char),
    UnparseableInt(ParseIntError),
    UnexpectedEOF,
}

impl From<ParseIntError> for LexError {
    fn from(err: ParseIntError) -> LexError {
        UnparseableInt(err)
    }
}

fn ignore() {}
fn discard<T>(_: T) {}

fn is_lparen(ch: char) -> bool { ch == '(' }
fn is_rparen(ch: char) -> bool { ch == ')' }
fn is_dbl_quote(ch: char) -> bool { ch == '"' }
fn is_backslash(ch: char) -> bool { ch == '\\' }
fn is_dollar(ch: char) -> bool { ch == '$' }
fn is_keyword_char(ch: char) -> bool { ch.is_alphanumeric() || (ch == '.') }
fn is_identifier_char(ch: char) -> bool { ch.is_alphanumeric() || (ch == '.') || (ch == '$') }
fn is_unescaped_char(ch: char) -> bool { ch != '"' && ch != '\\' && ch != '\r' && ch != '\n' }

fn mk_begin<'a>(_: char, s: Cow<'a,str>) -> Token<'a> { Begin(s) }
fn mk_end<'a>(_: char) -> Token<'a> { End }
fn mk_identifier<'a>(s: Cow<'a,str>) -> Token<'a> { Identifier(s) }
fn mk_text<'a>(_: char, s: Cow<'a,str>, _: char) -> Token<'a> { Text(s) }
fn mk_whitespace<'a>(s: Cow<'a,str>) -> Token<'a> { Whitespace(s) }

fn mk_number<'a>(s: Cow<'a,str>) -> Result<Token<'a>, LexError> { Ok(Number(try!(usize::from_str_radix(&*s, 10)))) }

fn mk_ok_char(ch: char) -> Result<char,LexError> { Ok(ch) }
fn mk_ok_token<'a>(tok: Token<'a>) -> Result<Token<'a>,LexError> { Ok(tok) }
fn mk_unexpected_char_err<'a>(ch: Option<char>) -> Result<Token<'a>,LexError> { Err(ch.map_or(UnexpectedEOF, UnexpectedChar)) }
fn mk_unclosed_string_err(ch: Option<char>) -> Result<char,LexError> { Err(ch.map_or(UnexpectedEOF, UnclosedString)) }

// Work-around for not having impl results yet.

pub trait LexerTo<T> {
    fn apply<P>(self, lexer: P) -> T where
        P: for<'a> Committed<&'a str, Output=Result<Token<'a>,LexError>>;
}

#[allow(non_snake_case)]
pub fn lexer<T,F> (f: F) -> T where F: LexerTo<T> {

    let BEGIN = character(is_lparen)
        .and_then(character(is_keyword_char).star(ignore).buffer())
        .map2(mk_begin);

    let END = character(is_rparen)
        .map(mk_end);

    let IDENTIFIER = character(is_dollar)
        .and_then(character(is_identifier_char).star(ignore))
        .buffer()
        .map(mk_identifier);

    let WHITESPACE = character(char::is_whitespace).plus(ignore).buffer()
        .map(mk_whitespace);

    let NUMBER = character(char::is_numeric).plus(ignore).buffer()
        .map(mk_number);

    let OPEN_QUOTE = character(is_dbl_quote);

    let ESCAPED = character(is_backslash)
        .and_then(CHARACTER)
        .map(discard);

    let UNESCAPED = character(is_unescaped_char)
        .map(discard);

    let CLOSE_QUOTE = character(is_dbl_quote)
        .map(mk_ok_char)
        .or_else(CHARACTER.map(mk_unclosed_string_err));

    let TEXT = OPEN_QUOTE
        .and_then(ESCAPED.or_else(UNESCAPED).star(ignore).buffer())
        .and_then_try(CLOSE_QUOTE)
        .try_map3(mk_text);

    let UNRECOGNIZED = CHARACTER
        .map(mk_unexpected_char_err);

    let TOKEN = BEGIN
        .or_else(END)
        .or_else(IDENTIFIER)
        .or_else(WHITESPACE)
        .map(mk_ok_token)
        .or_else(TEXT)
        .or_else(NUMBER)
        .or_else(UNRECOGNIZED);

    f.apply(TOKEN)

}

#[test]
#[allow(non_snake_case)]
fn test_lexer() {
    use parsell::Stateful;
    use parsell::ParseResult::{Done};
    use std::borrow::Cow::{Borrowed};
    struct TestLexer;
    impl LexerTo<bool> for TestLexer {
        fn apply<P>(self, TOKEN: P) -> bool
            where P: for<'a> Committed<&'a str, Output=Result<Token<'a>,LexError>>
        {
            let overflow = usize::from_str_radix("983748948934789348763894786345786", 10).unwrap_err();
            assert_eq!(TOKEN.init().parse("(foo!"),Done("!",Ok(Begin(Borrowed("foo")))));
            assert_eq!(TOKEN.init().parse(")!"),Done("!",Ok(End)));
            assert_eq!(TOKEN.init().parse("$abc!"),Done("!",Ok(Identifier(Borrowed("$abc")))));
            assert_eq!(TOKEN.init().parse(" \t\r\n !"),Done("!",Ok(Whitespace(Borrowed(" \t\r\n ")))));
            assert_eq!(TOKEN.init().parse("\"xyz\\t\\\"abc\"!"),Done("!",Ok(Text(Borrowed("xyz\\t\\\"abc")))));
            assert_eq!(TOKEN.init().parse(" \t\r\n !"),Done("!",Ok(Whitespace(Borrowed(" \t\r\n ")))));
            assert_eq!(TOKEN.init().parse("!!"),Done("!",Err(UnexpectedChar('!'))));
            assert_eq!(TOKEN.init().parse("\"abc\r\"!"),Done("\"!",Err(UnclosedString('\r'))));
            assert_eq!(TOKEN.init().parse("1234567890123456789012345678901234567890!"),Done("!",Err(UnparseableInt(overflow))));
            assert_eq!(TOKEN.init().done(),Err(UnexpectedEOF));
            true
        }
    }
    assert!(lexer(TestLexer))
}
<commit_msg>Export a boxed lexer as a work-around for not having impl trait results yet.<commit_after>use self::Token::{Begin, End, Identifier, Number, Text, Whitespace};
use self::LexError::{UnexpectedChar, UnexpectedEOF, UnclosedString, UnparseableInt};
use self::ParseError::{LexErr};

use parsell::{Parser, Committed, Stateful, Boxable, CHARACTER, character};
use std::num::ParseIntError;
use std::borrow::Cow;

#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
pub enum Token<'a> {
    Begin(Cow<'a,str>),
    End,
    Identifier(Cow<'a,str>),
    Number(usize),
    Text(Cow<'a,str>),
    Whitespace(Cow<'a,str>),
}

#[derive(Clone, PartialEq, Debug)]
pub enum LexError {
    UnexpectedChar(char),
    UnclosedString(char),
    UnparseableInt(ParseIntError),
    UnexpectedEOF,
}

#[derive(Clone, PartialEq, Debug)]
pub enum ParseError {
    LexErr(LexError),
}

impl From<ParseIntError> for LexError {
    fn from(err: ParseIntError) -> LexError {
        UnparseableInt(err)
    }
}

impl From<LexError> for ParseError {
    fn from(err: LexError) -> ParseError {
        LexErr(err)
    }
}

fn ignore() {}
fn discard_char1(_: char) {}
fn discard_char2(_: char, _: Option<char>) {}

fn is_lparen(ch: char) -> bool { ch == '(' }
fn is_rparen(ch: char) -> bool { ch == ')' }
fn is_dbl_quote(ch: char) -> bool { ch == '"' }
fn is_backslash(ch: char) -> bool { ch == '\\' }
fn is_dollar(ch: char) -> bool { ch == '$' }
fn is_keyword_char(ch: char) -> bool { ch.is_alphanumeric() || (ch == '.') }
fn is_identifier_char(ch: char) -> bool { ch.is_alphanumeric() || (ch == '.') || (ch == '$') }
fn is_unescaped_char(ch: char) -> bool { ch != '"' && ch != '\\' && ch != '\r' && ch != '\n' }

fn mk_begin<'a>(_: char, s: Cow<'a,str>) -> Token<'a> { Begin(s) }
fn mk_end<'a>(_: char) -> Token<'a> { End }
fn mk_identifier<'a>(s: Cow<'a,str>) -> Token<'a> { Identifier(s) }
fn mk_whitespace<'a>(s: Cow<'a,str>) -> Token<'a> { Whitespace(s) }

fn mk_number<'a>(s: Cow<'a,str>) -> Result<Token<'a>, LexError> {
    Ok(Number(try!(usize::from_str_radix(&*s, 10))))

}

fn mk_text<'a>(s: Cow<'a,str>) -> Result<Token<'a>,LexError> {
    {
        let mut chs = s.chars();
        match chs.next() {
            Some('"') => match chs.next_back() {
                Some('"') => (),
                Some(ch) if !is_unescaped_char(ch) => return Err(UnclosedString(ch)),
                _ => return Err(UnexpectedEOF),
            },
            _ => panic!("mk_text(s) where s doesn't start with '\"'."),
        }
    }
    Ok(Text(s))
}

fn mk_ok_token<'a>(tok: Token<'a>) -> Result<Token<'a>,LexError> { Ok(tok) }
fn mk_unexpected_char_err<'a>(ch: Option<char>) -> Result<Token<'a>,LexError> { Err(ch.map_or(UnexpectedEOF, UnexpectedChar)) }

// Work-around for not having impl results yet.

pub struct WasmLexer;
pub type WasmLexerState = Box<for<'b> Boxable<&'b str, Output=Result<Token<'b>, LexError>>>;

impl Parser for WasmLexer {}
impl<'a> Committed<&'a str> for WasmLexer {

    type Output = Result<Token<'a>, LexError>;
    type State = WasmLexerState;

    #[allow(non_snake_case)]
    fn init(&self) -> WasmLexerState {

        let BEGIN = character(is_lparen)
            .and_then(character(is_keyword_char).star(ignore).buffer())
            .map2(mk_begin);

        let END = character(is_rparen)
            .map(mk_end);

        let IDENTIFIER = character(is_dollar)
            .and_then(character(is_identifier_char).star(ignore))
            .buffer()
            .map(mk_identifier);

        let WHITESPACE = character(char::is_whitespace).plus(ignore).buffer()
            .map(mk_whitespace);

        let NUMBER = character(char::is_numeric).plus(ignore).buffer()
            .map(mk_number);

        let OPEN_QUOTE = character(is_dbl_quote);

        let ESCAPED = character(is_backslash)
            .and_then(CHARACTER)
            .map2(discard_char2);

        let UNESCAPED = character(is_unescaped_char)
            .map(discard_char1);

        let TEXT = OPEN_QUOTE
            .and_then(ESCAPED.or_else(UNESCAPED).star(ignore))
            .and_then(CHARACTER)
            .buffer()
            .map(mk_text);

        let UNRECOGNIZED = CHARACTER
            .map(mk_unexpected_char_err);

        let TOKEN = BEGIN
            .or_else(END)
            .or_else(IDENTIFIER)
            .or_else(WHITESPACE)
            .map(mk_ok_token)
            .or_else(TEXT)
            .or_else(NUMBER)
            .or_else(UNRECOGNIZED);

        Box::new(TOKEN.init().boxable())

    }

}

pub const LEXER: WasmLexer = WasmLexer;

#[test]
#[allow(non_snake_case)]
fn test_lexer() {
    use parsell::Stateful;
    use parsell::ParseResult::{Done};
    use std::borrow::Cow::{Borrowed};
    let overflow = usize::from_str_radix("983748948934789348763894786345786", 10).unwrap_err();
    assert_eq!(LEXER.init().parse("(foo!"),Done("!",Ok(Begin(Borrowed("foo")))));
    assert_eq!(LEXER.init().parse(")!"),Done("!",Ok(End)));
    assert_eq!(LEXER.init().parse("$abc!"),Done("!",Ok(Identifier(Borrowed("$abc")))));
    assert_eq!(LEXER.init().parse(" \t\r\n !"),Done("!",Ok(Whitespace(Borrowed(" \t\r\n ")))));
    assert_eq!(LEXER.init().parse("\"xyz\\t\\\"abc\"!"),Done("!",Ok(Text(Borrowed("\"xyz\\t\\\"abc\"")))));
    assert_eq!(LEXER.init().parse(" \t\r\n !"),Done("!",Ok(Whitespace(Borrowed(" \t\r\n ")))));
    assert_eq!(LEXER.init().parse("!!"),Done("!",Err(UnexpectedChar('!'))));
    assert_eq!(LEXER.init().parse("\"abc\r\"!"),Done("\"!",Err(UnclosedString('\r'))));
    assert_eq!(LEXER.init().parse("1234567890123456789012345678901234567890!"),Done("!",Err(UnparseableInt(overflow))));
    assert_eq!(LEXER.init().done(),Err(UnexpectedEOF));
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::fmt;

use log::trace;

use rustc_span::DUMMY_SP;

use crate::*;

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort(Option<String>),
    UnsupportedInIsolation(String),
    ExperimentalUb { msg: String, url: String }
}

impl fmt::Debug for TerminationInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use TerminationInfo::*;
        match self {
            Exit(code) =>
                write!(f, "the evaluated program completed with exit code {}", code),
            Abort(None) =>
                write!(f, "the evaluated program aborted execution"),
            Abort(Some(msg)) =>
                write!(f, "the evaluated program aborted execution: {}", msg),
            UnsupportedInIsolation(msg) =>
                write!(f, "{}", msg),
            ExperimentalUb { msg, .. } =>
                write!(f, "{}", msg),
        }
    }
}

impl MachineStopType for TerminationInfo {}

/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
    PoppedTrackedPointerTag(Item),
    CreatedAlloc(AllocId),
}

/// Emit a custom diagnostic without going through the miri-engine machinery
pub fn report_error<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    mut e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    use InterpError::*;

    let (title, helps) = match e.kind {
        MachineStop(ref info) => {
            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
            use TerminationInfo::*;
            let title = match info {
                Exit(code) => return Some(*code),
                Abort(_) =>
                    "abnormal termination",
                UnsupportedInIsolation(_) =>
                    "unsupported operation",
                ExperimentalUb { .. } =>
                    "Undefined Behavior",
            };
            let helps = match info {
                UnsupportedInIsolation(_) =>
                    vec![format!("pass the flag `-Zmiri-disable-isolation` to disable isolation")],
                ExperimentalUb { url, .. } =>
                    vec![
                        format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
                        format!("see {} for further information", url),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
        _ => {
            let title = match e.kind {
                Unsupported(_) =>
                    "unsupported operation",
                UndefinedBehavior(_) =>
                    "Undefined Behavior",
                ResourceExhaustion(_) =>
                    "resource exhaustion",
                _ =>
                    bug!("This error should be impossible in Miri: {}", e),
            };
            let helps = match e.kind {
                Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
                    vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")],
                Unsupported(_) =>
                    vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
                UndefinedBehavior(_) =>
                    vec![
                        format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
                        format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
    };

    e.print_backtrace();
    let msg = e.to_string();
    report_msg(ecx, &format!("{}: {}", title, msg), msg, &helps, true)
}

/// Report an error or note (depending on the `error` argument) at the current frame's current statement.
/// Also emits a full stacktrace of the interpreter stack.
fn report_msg<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    title: &str,
    span_msg: String,
    helps: &[String],
    error: bool,
) -> Option<i64> {
    let span = if let Some(frame) = ecx.stack().last() {
        frame.current_source_info().unwrap().span
    } else {
        DUMMY_SP
    };
    let mut err = if error {
        ecx.tcx.sess.struct_span_err(span, title)
    } else {
        ecx.tcx.sess.diagnostic().span_note_diag(span, title)
    };
    err.span_label(span, span_msg);
    for help in helps {
        err.help(help);
    }
    // Add backtrace
    let frames = ecx.generate_stacktrace();
    for (idx, frame_info) in frames.iter().enumerate() {
        let is_local = frame_info.instance.def_id().is_local();
        // No span for non-local frames and the first frame (which is the error site).
        if is_local && idx > 0 {
            err.span_note(frame_info.span, &frame_info.to_string());
        } else {
            err.note(&frame_info.to_string());
        }
    }

    err.emit();

    for (i, frame) in ecx.stack().iter().enumerate() {
        trace!("-------------------");
        trace!("Frame {}", i);
        trace!("    return: {:?}", frame.return_place.map(|p| *p));
        for (i, local) in frame.locals.iter().enumerate() {
            trace!("    local {}: {:?}", i, local.value);
        }
    }
    // Let the reported error determine the return code.
    return None;
}

thread_local! {
    static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
}

/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
/// The diagnostic will be emitted after the current interpreter step is finished.
pub fn register_diagnostic(e: NonHaltingDiagnostic) {
    DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Emit all diagnostics that were registed with `register_diagnostics`
    fn process_diagnostics(&self) {
        let this = self.eval_context_ref();
        DIAGNOSTICS.with(|diagnostics| {
            for e in diagnostics.borrow_mut().drain(..) {
                use NonHaltingDiagnostic::*;
                let msg = match e {
                    PoppedTrackedPointerTag(item) =>
                        format!("popped tracked tag for item {:?}", item),
                    CreatedAlloc(AllocId(id)) =>
                        format!("created allocation with id {}", id),
                };
                report_msg(this, "tracking was triggered", msg, &[], false);
            }
        });
    }
}
<commit_msg>add empty line before backtrace, to separate it from help text<commit_after>use std::cell::RefCell;
use std::fmt;

use log::trace;

use rustc_span::DUMMY_SP;

use crate::*;

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort(Option<String>),
    UnsupportedInIsolation(String),
    ExperimentalUb { msg: String, url: String }
}

impl fmt::Debug for TerminationInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use TerminationInfo::*;
        match self {
            Exit(code) =>
                write!(f, "the evaluated program completed with exit code {}", code),
            Abort(None) =>
                write!(f, "the evaluated program aborted execution"),
            Abort(Some(msg)) =>
                write!(f, "the evaluated program aborted execution: {}", msg),
            UnsupportedInIsolation(msg) =>
                write!(f, "{}", msg),
            ExperimentalUb { msg, .. } =>
                write!(f, "{}", msg),
        }
    }
}

impl MachineStopType for TerminationInfo {}

/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
    PoppedTrackedPointerTag(Item),
    CreatedAlloc(AllocId),
}

/// Emit a custom diagnostic without going through the miri-engine machinery
pub fn report_error<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    mut e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    use InterpError::*;

    let (title, helps) = match e.kind {
        MachineStop(ref info) => {
            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
            use TerminationInfo::*;
            let title = match info {
                Exit(code) => return Some(*code),
                Abort(_) =>
                    "abnormal termination",
                UnsupportedInIsolation(_) =>
                    "unsupported operation",
                ExperimentalUb { .. } =>
                    "Undefined Behavior",
            };
            let helps = match info {
                UnsupportedInIsolation(_) =>
                    vec![format!("pass the flag `-Zmiri-disable-isolation` to disable isolation")],
                ExperimentalUb { url, .. } =>
                    vec![
                        format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
                        format!("see {} for further information", url),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
        _ => {
            let title = match e.kind {
                Unsupported(_) =>
                    "unsupported operation",
                UndefinedBehavior(_) =>
                    "Undefined Behavior",
                ResourceExhaustion(_) =>
                    "resource exhaustion",
                _ =>
                    bug!("This error should be impossible in Miri: {}", e),
            };
            let helps = match e.kind {
                Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
                    vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")],
                Unsupported(_) =>
                    vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
                UndefinedBehavior(_) =>
                    vec![
                        format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
                        format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
                    ],
                _ => vec![],
            };
            (title, helps)
        }
    };

    e.print_backtrace();
    let msg = e.to_string();
    report_msg(ecx, &format!("{}: {}", title, msg), msg, helps, true)
}

/// Report an error or note (depending on the `error` argument) at the current frame's current statement.
/// Also emits a full stacktrace of the interpreter stack.
fn report_msg<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    title: &str,
    span_msg: String,
    mut helps: Vec<String>,
    error: bool,
) -> Option<i64> {
    let span = if let Some(frame) = ecx.stack().last() {
        frame.current_source_info().unwrap().span
    } else {
        DUMMY_SP
    };
    let mut err = if error {
        ecx.tcx.sess.struct_span_err(span, title)
    } else {
        ecx.tcx.sess.diagnostic().span_note_diag(span, title)
    };
    err.span_label(span, span_msg);
    if !helps.is_empty() {
        // Add visual separator before backtrace.
        helps.last_mut().unwrap().push_str("\n");
        for help in helps {
            err.help(&help);
        }
    }
    // Add backtrace
    let frames = ecx.generate_stacktrace();
    for (idx, frame_info) in frames.iter().enumerate() {
        let is_local = frame_info.instance.def_id().is_local();
        // No span for non-local frames and the first frame (which is the error site).
        if is_local && idx > 0 {
            err.span_note(frame_info.span, &frame_info.to_string());
        } else {
            err.note(&frame_info.to_string());
        }
    }

    err.emit();

    for (i, frame) in ecx.stack().iter().enumerate() {
        trace!("-------------------");
        trace!("Frame {}", i);
        trace!("    return: {:?}", frame.return_place.map(|p| *p));
        for (i, local) in frame.locals.iter().enumerate() {
            trace!("    local {}: {:?}", i, local.value);
        }
    }
    // Let the reported error determine the return code.
    return None;
}

thread_local! {
    static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
}

/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
/// The diagnostic will be emitted after the current interpreter step is finished.
pub fn register_diagnostic(e: NonHaltingDiagnostic) {
    DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Emit all diagnostics that were registed with `register_diagnostics`
    fn process_diagnostics(&self) {
        let this = self.eval_context_ref();
        DIAGNOSTICS.with(|diagnostics| {
            for e in diagnostics.borrow_mut().drain(..) {
                use NonHaltingDiagnostic::*;
                let msg = match e {
                    PoppedTrackedPointerTag(item) =>
                        format!("popped tracked tag for item {:?}", item),
                    CreatedAlloc(AllocId(id)) =>
                        format!("created allocation with id {}", id),
                };
                report_msg(this, "tracking was triggered", msg, vec![], false);
            }
        });
    }
}
<|endoftext|>


<commit_before>use std::{io::Cursor, path::Path};

use rocksdb::{
    merge_operator::MergeFn, BlockBasedIndexType, BlockBasedOptions, ColumnFamily,
    ColumnFamilyDescriptor, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform,
    WriteBatch, DB,
};

use crate::model::{
    GameId, Key, KeyPrefix, LichessEntry, LichessGame, MastersEntry, MastersGame, Month,
    PlayerEntry, PlayerStatus, UserId, Year,
};

#[derive(Debug)]
pub struct Database {
    pub inner: DB,
}

fn column_family(
    name: &str,
    merge: Option<&str>,
    merge_fn: impl MergeFn + Clone,
    prefix: Option<usize>,
    block_size: usize,
    bloom_filter: i32,
) -> ColumnFamilyDescriptor {
    let mut opts = Options::default();
    if let Some(merge) = merge {
        opts.set_merge_operator_associative(merge, merge_fn);
    }
    opts.set_prefix_extractor(match prefix {
        Some(prefix) => SliceTransform::create_fixed_prefix(prefix),
        None => SliceTransform::create_noop(),
    });
    let mut block_opts = BlockBasedOptions::default();
    block_opts.set_index_type(BlockBasedIndexType::HashSearch);
    block_opts.set_block_size(block_size);
    if bloom_filter > 0 {
        block_opts.set_bloom_filter(bloom_filter, true);
    }
    opts.set_block_based_table_factory(&block_opts);
    ColumnFamilyDescriptor::new(name, opts)
}

impl Database {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Database, rocksdb::Error> {
        let mut db_opts = Options::default();
        db_opts.create_if_missing(true);
        db_opts.create_missing_column_families(true);

        let mut inner = DB::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                // Masters database
                column_family(
                    "masters",
                    Some("masters_merge"),
                    masters_merge,
                    Some(KeyPrefix::SIZE),
                    8 * 1024,
                    3,
                ),
                column_family("masters_game", None, void_merge, None, 4 * 1024, 0),
                // Lichess database
                column_family(
                    "lichess_2",
                    Some("lichess_merge"),
                    lichess_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family(
                    "lichess_game_2",
                    Some("lichess_game_merge"),
                    lichess_game_merge,
                    None,
                    4 * 1024,
                    0,
                ),
                // Player database (also shares lichess_game)
                column_family(
                    "player_2",
                    Some("player_merge"),
                    player_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family("player_status_2", None, void_merge, None, 4 * 1024, 0),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn masters(&self) -> MastersDatabase<'_> {
        MastersDatabase {
            inner: &self.inner,
            cf_masters: self.inner.cf_handle("masters").expect("cf masters"),
            cf_masters_game: self
                .inner
                .cf_handle("masters_game")
                .expect("cf masters_game"),
        }
    }

    pub fn lichess(&self) -> LichessDatabase<'_> {
        LichessDatabase {
            inner: &self.inner,
            cf_lichess: self.inner.cf_handle("lichess_2").expect("cf lichess"),
            cf_lichess_game: self
                .inner
                .cf_handle("lichess_game_2")
                .expect("cf lichess_game_2"),

            cf_player: self.inner.cf_handle("player_2").expect("cf player"),
            cf_player_status: self
                .inner
                .cf_handle("player_status_2")
                .expect("cf player_status_2"),
        }
    }
}

pub struct MastersDatabase<'a> {
    inner: &'a DB,
    cf_masters: &'a ColumnFamily,
    cf_masters_game: &'a ColumnFamily,
}

impl MastersDatabase<'_> {
    pub fn has_game(&self, id: GameId) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters_game, id.to_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn game(&self, id: GameId) -> Result<Option<MastersGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_masters_game, id.to_bytes())?
            .map(|buf| serde_json::from_slice(&buf).expect("deserialize masters game")))
    }

    pub fn has(&self, key: Key) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters, key.into_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn read(
        &self,
        key: KeyPrefix,
        since: Year,
        until: Year,
    ) -> Result<MastersEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_year(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_year(until.add_years_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_masters, opt, IteratorMode::Start);

        let mut entry = MastersEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize masters entry");
        }

        Ok(entry)
    }

    pub fn batch(&self) -> MastersBatch<'_> {
        MastersBatch {
            db: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct MastersBatch<'a> {
    db: &'a MastersDatabase<'a>,
    batch: WriteBatch,
}

impl MastersBatch<'_> {
    pub fn merge(&mut self, key: Key, entry: MastersEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(MastersEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize masters entry");
        self.batch
            .merge_cf(self.db.cf_masters, key.into_bytes(), cursor.into_inner());
    }

    pub fn put_game(&mut self, id: GameId, game: &MastersGame) {
        self.batch.put_cf(
            self.db.cf_masters_game,
            id.to_bytes(),
            serde_json::to_vec(game).expect("serialize masters game"),
        );
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        self.db.inner.write(self.batch)
    }
}

pub struct LichessDatabase<'a> {
    inner: &'a DB,
    cf_lichess: &'a ColumnFamily,
    cf_lichess_game: &'a ColumnFamily,

    cf_player: &'a ColumnFamily,
    cf_player_status: &'a ColumnFamily,
}

impl LichessDatabase<'_> {
    pub fn game(&self, id: GameId) -> Result<Option<LichessGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_lichess_game, id.to_bytes())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                LichessGame::read(&mut cursor).expect("deserialize game info")
            }))
    }

    pub fn read_lichess(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<LichessEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_month(until.add_months_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_lichess, opt, IteratorMode::Start);

        let mut entry = LichessEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize lichess entry");
        }

        Ok(entry)
    }

    pub fn read_player(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<PlayerEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_month(until.add_months_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_player, opt, IteratorMode::Start);

        let mut entry = PlayerEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize player entry");
        }

        Ok(entry)
    }

    pub fn player_status(&self, id: &UserId) -> Result<Option<PlayerStatus>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_player_status, id.as_lowercase_str())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                PlayerStatus::read(&mut cursor).expect("deserialize status")
            }))
    }

    pub fn put_player_status(
        &self,
        id: &UserId,
        status: &PlayerStatus,
    ) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerStatus::SIZE_HINT));
        status.write(&mut cursor).expect("serialize status");
        self.inner.put_cf(
            self.cf_player_status,
            id.as_lowercase_str(),
            cursor.into_inner(),
        )
    }

    pub fn batch(&self) -> LichessBatch<'_> {
        LichessBatch {
            inner: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct LichessBatch<'a> {
    inner: &'a LichessDatabase<'a>,
    batch: WriteBatch,
}

impl LichessBatch<'_> {
    pub fn merge_lichess(&mut self, key: Key, entry: LichessEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize lichess entry");
        self.batch
            .merge_cf(self.inner.cf_lichess, key.into_bytes(), cursor.into_inner());
    }

    pub fn merge_game(&mut self, id: GameId, info: LichessGame) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessGame::SIZE_HINT));
        info.write(&mut cursor).expect("serialize game info");
        self.batch.merge_cf(
            self.inner.cf_lichess_game,
            id.to_bytes(),
            cursor.into_inner(),
        );
    }

    pub fn merge_player(&mut self, key: Key, entry: PlayerEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize player entry");
        self.batch
            .merge_cf(self.inner.cf_player, key.into_bytes(), cursor.into_inner());
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        self.inner.inner.write(self.batch)
    }
}

fn lichess_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = LichessEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for lichess merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write lichess entry");
    Some(cursor.into_inner())
}

fn lichess_game_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    // Take latest game info, but merge index status.
    let mut info: Option<LichessGame> = None;
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = LichessGame::read(&mut cursor).expect("read for lichess game merge");
        if let Some(old_info) = info {
            new_info.indexed_player.white |= old_info.indexed_player.white;
            new_info.indexed_player.black |= old_info.indexed_player.black;
            new_info.indexed_lichess |= old_info.indexed_lichess;
        }
        info = Some(new_info);
        size_hint = op.len();
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
        info.write(&mut cursor).expect("write lichess game");
        cursor.into_inner()
    })
}

fn player_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PlayerEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for player merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write player entry");
    Some(cursor.into_inner())
}

fn masters_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = MastersEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for masters merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write masters entry");
    Some(cursor.into_inner())
}

fn void_merge(
    _key: &[u8],
    _existing: Option<&[u8]>,
    _operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    unreachable!("void merge")
}
<commit_msg>remove useless mut<commit_after>use std::{io::Cursor, path::Path};

use rocksdb::{
    merge_operator::MergeFn, BlockBasedIndexType, BlockBasedOptions, ColumnFamily,
    ColumnFamilyDescriptor, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform,
    WriteBatch, DB,
};

use crate::model::{
    GameId, Key, KeyPrefix, LichessEntry, LichessGame, MastersEntry, MastersGame, Month,
    PlayerEntry, PlayerStatus, UserId, Year,
};

#[derive(Debug)]
pub struct Database {
    pub inner: DB,
}

fn column_family(
    name: &str,
    merge: Option<&str>,
    merge_fn: impl MergeFn + Clone,
    prefix: Option<usize>,
    block_size: usize,
    bloom_filter: i32,
) -> ColumnFamilyDescriptor {
    let mut opts = Options::default();
    if let Some(merge) = merge {
        opts.set_merge_operator_associative(merge, merge_fn);
    }
    opts.set_prefix_extractor(match prefix {
        Some(prefix) => SliceTransform::create_fixed_prefix(prefix),
        None => SliceTransform::create_noop(),
    });
    let mut block_opts = BlockBasedOptions::default();
    block_opts.set_index_type(BlockBasedIndexType::HashSearch);
    block_opts.set_block_size(block_size);
    if bloom_filter > 0 {
        block_opts.set_bloom_filter(bloom_filter, true);
    }
    opts.set_block_based_table_factory(&block_opts);
    ColumnFamilyDescriptor::new(name, opts)
}

impl Database {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Database, rocksdb::Error> {
        let mut db_opts = Options::default();
        db_opts.create_if_missing(true);
        db_opts.create_missing_column_families(true);

        let inner = DB::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                // Masters database
                column_family(
                    "masters",
                    Some("masters_merge"),
                    masters_merge,
                    Some(KeyPrefix::SIZE),
                    8 * 1024,
                    3,
                ),
                column_family("masters_game", None, void_merge, None, 4 * 1024, 0),
                // Lichess database
                column_family(
                    "lichess_2",
                    Some("lichess_merge"),
                    lichess_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family(
                    "lichess_game_2",
                    Some("lichess_game_merge"),
                    lichess_game_merge,
                    None,
                    4 * 1024,
                    0,
                ),
                // Player database (also shares lichess_game)
                column_family(
                    "player_2",
                    Some("player_merge"),
                    player_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family("player_status_2", None, void_merge, None, 4 * 1024, 0),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn masters(&self) -> MastersDatabase<'_> {
        MastersDatabase {
            inner: &self.inner,
            cf_masters: self.inner.cf_handle("masters").expect("cf masters"),
            cf_masters_game: self
                .inner
                .cf_handle("masters_game")
                .expect("cf masters_game"),
        }
    }

    pub fn lichess(&self) -> LichessDatabase<'_> {
        LichessDatabase {
            inner: &self.inner,
            cf_lichess: self.inner.cf_handle("lichess_2").expect("cf lichess"),
            cf_lichess_game: self
                .inner
                .cf_handle("lichess_game_2")
                .expect("cf lichess_game_2"),

            cf_player: self.inner.cf_handle("player_2").expect("cf player"),
            cf_player_status: self
                .inner
                .cf_handle("player_status_2")
                .expect("cf player_status_2"),
        }
    }
}

pub struct MastersDatabase<'a> {
    inner: &'a DB,
    cf_masters: &'a ColumnFamily,
    cf_masters_game: &'a ColumnFamily,
}

impl MastersDatabase<'_> {
    pub fn has_game(&self, id: GameId) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters_game, id.to_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn game(&self, id: GameId) -> Result<Option<MastersGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_masters_game, id.to_bytes())?
            .map(|buf| serde_json::from_slice(&buf).expect("deserialize masters game")))
    }

    pub fn has(&self, key: Key) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters, key.into_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn read(
        &self,
        key: KeyPrefix,
        since: Year,
        until: Year,
    ) -> Result<MastersEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_year(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_year(until.add_years_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_masters, opt, IteratorMode::Start);

        let mut entry = MastersEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize masters entry");
        }

        Ok(entry)
    }

    pub fn batch(&self) -> MastersBatch<'_> {
        MastersBatch {
            db: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct MastersBatch<'a> {
    db: &'a MastersDatabase<'a>,
    batch: WriteBatch,
}

impl MastersBatch<'_> {
    pub fn merge(&mut self, key: Key, entry: MastersEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(MastersEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize masters entry");
        self.batch
            .merge_cf(self.db.cf_masters, key.into_bytes(), cursor.into_inner());
    }

    pub fn put_game(&mut self, id: GameId, game: &MastersGame) {
        self.batch.put_cf(
            self.db.cf_masters_game,
            id.to_bytes(),
            serde_json::to_vec(game).expect("serialize masters game"),
        );
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        self.db.inner.write(self.batch)
    }
}

pub struct LichessDatabase<'a> {
    inner: &'a DB,
    cf_lichess: &'a ColumnFamily,
    cf_lichess_game: &'a ColumnFamily,

    cf_player: &'a ColumnFamily,
    cf_player_status: &'a ColumnFamily,
}

impl LichessDatabase<'_> {
    pub fn game(&self, id: GameId) -> Result<Option<LichessGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_lichess_game, id.to_bytes())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                LichessGame::read(&mut cursor).expect("deserialize game info")
            }))
    }

    pub fn read_lichess(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<LichessEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_month(until.add_months_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_lichess, opt, IteratorMode::Start);

        let mut entry = LichessEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize lichess entry");
        }

        Ok(entry)
    }

    pub fn read_player(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<PlayerEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_month(until.add_months_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_player, opt, IteratorMode::Start);

        let mut entry = PlayerEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize player entry");
        }

        Ok(entry)
    }

    pub fn player_status(&self, id: &UserId) -> Result<Option<PlayerStatus>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_player_status, id.as_lowercase_str())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                PlayerStatus::read(&mut cursor).expect("deserialize status")
            }))
    }

    pub fn put_player_status(
        &self,
        id: &UserId,
        status: &PlayerStatus,
    ) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerStatus::SIZE_HINT));
        status.write(&mut cursor).expect("serialize status");
        self.inner.put_cf(
            self.cf_player_status,
            id.as_lowercase_str(),
            cursor.into_inner(),
        )
    }

    pub fn batch(&self) -> LichessBatch<'_> {
        LichessBatch {
            inner: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct LichessBatch<'a> {
    inner: &'a LichessDatabase<'a>,
    batch: WriteBatch,
}

impl LichessBatch<'_> {
    pub fn merge_lichess(&mut self, key: Key, entry: LichessEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize lichess entry");
        self.batch
            .merge_cf(self.inner.cf_lichess, key.into_bytes(), cursor.into_inner());
    }

    pub fn merge_game(&mut self, id: GameId, info: LichessGame) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessGame::SIZE_HINT));
        info.write(&mut cursor).expect("serialize game info");
        self.batch.merge_cf(
            self.inner.cf_lichess_game,
            id.to_bytes(),
            cursor.into_inner(),
        );
    }

    pub fn merge_player(&mut self, key: Key, entry: PlayerEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize player entry");
        self.batch
            .merge_cf(self.inner.cf_player, key.into_bytes(), cursor.into_inner());
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        self.inner.inner.write(self.batch)
    }
}

fn lichess_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = LichessEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for lichess merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write lichess entry");
    Some(cursor.into_inner())
}

fn lichess_game_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    // Take latest game info, but merge index status.
    let mut info: Option<LichessGame> = None;
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = LichessGame::read(&mut cursor).expect("read for lichess game merge");
        if let Some(old_info) = info {
            new_info.indexed_player.white |= old_info.indexed_player.white;
            new_info.indexed_player.black |= old_info.indexed_player.black;
            new_info.indexed_lichess |= old_info.indexed_lichess;
        }
        info = Some(new_info);
        size_hint = op.len();
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
        info.write(&mut cursor).expect("write lichess game");
        cursor.into_inner()
    })
}

fn player_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PlayerEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for player merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write player entry");
    Some(cursor.into_inner())
}

fn masters_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = MastersEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for masters merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write masters entry");
    Some(cursor.into_inner())
}

fn void_merge(
    _key: &[u8],
    _existing: Option<&[u8]>,
    _operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    unreachable!("void merge")
}
<|endoftext|>


<commit_before>use conduit::RequestExt;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection};
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
use std::ops::Deref;
use std::sync::Arc;
use url::Url;

use crate::middleware::app::RequestApp;
use crate::Env;

#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub enum DieselPool {
    Pool(r2d2::Pool<ConnectionManager<PgConnection>>),
    Test(Arc<ReentrantMutex<PgConnection>>),
}

impl DieselPool {
    pub fn get(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError> {
        match self {
            DieselPool::Pool(pool) => Ok(DieselPooledConn::Pool(pool.get()?)),
            DieselPool::Test(conn) => Ok(DieselPooledConn::Test(conn.lock())),
        }
    }

    pub fn state(&self) -> r2d2::State {
        match self {
            DieselPool::Pool(pool) => pool.state(),
            DieselPool::Test(_) => panic!("Cannot get the state of a test pool"),
        }
    }

    fn test_conn(conn: PgConnection) -> Self {
        DieselPool::Test(Arc::new(ReentrantMutex::new(conn)))
    }
}

#[allow(missing_debug_implementations)]
pub enum DieselPooledConn<'a> {
    Pool(r2d2::PooledConnection<ConnectionManager<PgConnection>>),
    Test(ReentrantMutexGuard<'a, PgConnection>),
}

unsafe impl<'a> Send for DieselPooledConn<'a> {}

impl Deref for DieselPooledConn<'_> {
    type Target = PgConnection;

    fn deref(&self) -> &Self::Target {
        match self {
            DieselPooledConn::Pool(conn) => conn.deref(),
            DieselPooledConn::Test(conn) => conn.deref(),
        }
    }
}

pub fn connect_now() -> ConnectionResult<PgConnection> {
    let mut url = Url::parse(&crate::env("DATABASE_URL")).expect("Invalid database URL");
    if dotenv::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
        url.query_pairs_mut().append_pair("sslmode", "require");
    }
    PgConnection::establish(&url.to_string())
}

pub fn connection_url(url: &str) -> String {
    let mut url = Url::parse(url).expect("Invalid database URL");
    if dotenv::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
        url.query_pairs_mut().append_pair("sslmode", "require");
    }
    url.into_string()
}

pub fn diesel_pool(
    url: &str,
    env: Env,
    config: r2d2::Builder<ConnectionManager<PgConnection>>,
) -> DieselPool {
    let url = connection_url(url);
    if env == Env::Test {
        let conn = PgConnection::establish(&url).expect("failed to establish connection");
        DieselPool::test_conn(conn)
    } else {
        let manager = ConnectionManager::new(url);
        DieselPool::Pool(config.build(manager).unwrap())
    }
}

pub trait RequestTransaction {
    /// Obtain a read/write database connection from the primary pool
    fn db_conn(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError>;

    /// Obtain a readonly database connection from the replica pool
    ///
    /// If there is no replica pool, the primary pool is used instead.
    fn db_read_only(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError>;
}

impl<T: RequestExt + ?Sized> RequestTransaction for T {
    fn db_conn(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError> {
        self.app().primary_database.get().map_err(Into::into)
    }

    fn db_read_only(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError> {
        match &self.app().read_only_replica_database {
            Some(pool) => pool.get().map_err(Into::into),
            None => self.app().primary_database.get().map_err(Into::into),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ConnectionConfig {
    pub statement_timeout: u64,
    pub read_only: bool,
}

impl CustomizeConnection<PgConnection, r2d2::Error> for ConnectionConfig {
    fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), r2d2::Error> {
        use diesel::sql_query;

        sql_query(format!(
            "SET statement_timeout = {}",
            self.statement_timeout * 1000
        ))
        .execute(conn)
        .map_err(r2d2::Error::QueryError)?;
        if self.read_only {
            sql_query("SET default_transaction_read_only = 't'")
                .execute(conn)
                .map_err(r2d2::Error::QueryError)?;
        }
        Ok(())
    }
}
<commit_msg>Auto merge of #3193 - Turbo87:connect-now, r=pietroalbini<commit_after>use conduit::RequestExt;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection};
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
use std::ops::Deref;
use std::sync::Arc;
use url::Url;

use crate::middleware::app::RequestApp;
use crate::Env;

#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub enum DieselPool {
    Pool(r2d2::Pool<ConnectionManager<PgConnection>>),
    Test(Arc<ReentrantMutex<PgConnection>>),
}

impl DieselPool {
    pub fn get(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError> {
        match self {
            DieselPool::Pool(pool) => Ok(DieselPooledConn::Pool(pool.get()?)),
            DieselPool::Test(conn) => Ok(DieselPooledConn::Test(conn.lock())),
        }
    }

    pub fn state(&self) -> r2d2::State {
        match self {
            DieselPool::Pool(pool) => pool.state(),
            DieselPool::Test(_) => panic!("Cannot get the state of a test pool"),
        }
    }

    fn test_conn(conn: PgConnection) -> Self {
        DieselPool::Test(Arc::new(ReentrantMutex::new(conn)))
    }
}

#[allow(missing_debug_implementations)]
pub enum DieselPooledConn<'a> {
    Pool(r2d2::PooledConnection<ConnectionManager<PgConnection>>),
    Test(ReentrantMutexGuard<'a, PgConnection>),
}

unsafe impl<'a> Send for DieselPooledConn<'a> {}

impl Deref for DieselPooledConn<'_> {
    type Target = PgConnection;

    fn deref(&self) -> &Self::Target {
        match self {
            DieselPooledConn::Pool(conn) => conn.deref(),
            DieselPooledConn::Test(conn) => conn.deref(),
        }
    }
}

pub fn connect_now() -> ConnectionResult<PgConnection> {
    let url = connection_url(&crate::env("DATABASE_URL"));
    PgConnection::establish(&url)
}

pub fn connection_url(url: &str) -> String {
    let mut url = Url::parse(url).expect("Invalid database URL");
    if dotenv::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
        url.query_pairs_mut().append_pair("sslmode", "require");
    }
    url.into_string()
}

pub fn diesel_pool(
    url: &str,
    env: Env,
    config: r2d2::Builder<ConnectionManager<PgConnection>>,
) -> DieselPool {
    let url = connection_url(url);
    if env == Env::Test {
        let conn = PgConnection::establish(&url).expect("failed to establish connection");
        DieselPool::test_conn(conn)
    } else {
        let manager = ConnectionManager::new(url);
        DieselPool::Pool(config.build(manager).unwrap())
    }
}

pub trait RequestTransaction {
    /// Obtain a read/write database connection from the primary pool
    fn db_conn(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError>;

    /// Obtain a readonly database connection from the replica pool
    ///
    /// If there is no replica pool, the primary pool is used instead.
    fn db_read_only(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError>;
}

impl<T: RequestExt + ?Sized> RequestTransaction for T {
    fn db_conn(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError> {
        self.app().primary_database.get().map_err(Into::into)
    }

    fn db_read_only(&self) -> Result<DieselPooledConn<'_>, r2d2::PoolError> {
        match &self.app().read_only_replica_database {
            Some(pool) => pool.get().map_err(Into::into),
            None => self.app().primary_database.get().map_err(Into::into),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ConnectionConfig {
    pub statement_timeout: u64,
    pub read_only: bool,
}

impl CustomizeConnection<PgConnection, r2d2::Error> for ConnectionConfig {
    fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), r2d2::Error> {
        use diesel::sql_query;

        sql_query(format!(
            "SET statement_timeout = {}",
            self.statement_timeout * 1000
        ))
        .execute(conn)
        .map_err(r2d2::Error::QueryError)?;
        if self.read_only {
            sql_query("SET default_transaction_read_only = 't'")
                .execute(conn)
                .map_err(r2d2::Error::QueryError)?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::{
    os::{
        raw::{c_char, c_int},
        unix::{
            net::UnixStream,
            prelude::{AsRawFd, IntoRawFd},
        },
    },
    thread::JoinHandle,
};

use smithay::reexports::calloop::channel::{channel, Sender};
use wayland_sys::{
    client::*,
    common::{wl_fixed_t, wl_fixed_to_double},
    ffi_dispatch,
};

use crate::{ffi_api::*, WlcsEvent};

macro_rules! container_of(
    ($ptr: expr, $container: ident, $field: ident) => {
        ($ptr as *mut u8).offset(-(memoffset::offset_of!($container, $field) as isize)) as *mut $container
    }
);

#[no_mangle]
pub static wlcs_server_integration: WlcsServerIntegration = WlcsServerIntegration {
    version: 1,
    create_server,
    destroy_server,
};

unsafe extern "C" fn create_server(_argc: c_int, _argv: *mut *const c_char) -> *mut WlcsDisplayServer {
    // block the SIGPIPE signal here, we are a cdylib so Rust does not do it for us
    use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};
    sigaction(
        Signal::SIGPIPE,
        &SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty()),
    )
    .unwrap();
    let handle = Box::into_raw(Box::new(DisplayServerHandle::new()));
    &mut (*handle).wlcs_display_server
}

unsafe extern "C" fn destroy_server(ptr: *mut WlcsDisplayServer) {
    let _server = Box::from_raw(container_of!(ptr, DisplayServerHandle, wlcs_display_server));
}

struct DisplayServerHandle {
    wlcs_display_server: WlcsDisplayServer,
    server: Option<(Sender<WlcsEvent>, JoinHandle<()>)>,
    next_device_id: u32,
}

impl DisplayServerHandle {
    fn new() -> DisplayServerHandle {
        DisplayServerHandle {
            wlcs_display_server: WlcsDisplayServer {
                version: 3,
                start: Self::start,
                stop: Self::stop,
                create_client_socket: Self::create_client_socket,
                position_window_absolute: Self::position_window_absolute,
                create_pointer: Self::create_pointer,
                create_touch: Self::create_touch,
                get_descriptor: Self::get_descriptor,
                start_on_this_thread: None,
            },
            server: None,
            next_device_id: 1,
        }
    }

    unsafe extern "C" fn start(ptr: *mut WlcsDisplayServer) {
        let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
        let (tx, rx) = channel();
        let join = crate::start_anvil(rx);
        me.server = Some((tx, join));
    }

    unsafe extern "C" fn stop(ptr: *mut WlcsDisplayServer) {
        let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
        if let Some((sender, join)) = me.server.take() {
            let _ = sender.send(WlcsEvent::Exit);
            let _ = join.join();
        }
    }

    unsafe extern "C" fn create_client_socket(ptr: *mut WlcsDisplayServer) -> c_int {
        let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
        if let Some((ref sender, _)) = me.server {
            if let Ok((client_side, server_side)) = UnixStream::pair() {
                if sender
                    .send(WlcsEvent::NewClient {
                        stream: server_side,
                        client_id: client_side.as_raw_fd(),
                    })
                    .is_err()
                {
                    return -1;
                }
                return client_side.into_raw_fd();
            }
        }
        -1
    }

    unsafe extern "C" fn position_window_absolute(
        ptr: *mut WlcsDisplayServer,
        display: *mut wl_display,
        surface: *mut wl_proxy,
        x: c_int,
        y: c_int,
    ) {
        let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
        let client_id = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_get_fd, display);
        let surface_id = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_id, surface);
        if let Some((ref sender, _)) = me.server {
            let _ = sender.send(WlcsEvent::PositionWindow {
                client_id,
                surface_id,
                location: (x, y).into(),
            });
        }
    }

    unsafe extern "C" fn create_pointer(ptr: *mut WlcsDisplayServer) -> *mut WlcsPointer {
        let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
        if let Some((ref sender, _)) = me.server {
            let pointer = Box::into_raw(Box::new(PointerHandle::new(me.next_device_id, sender.clone())));
            me.next_device_id += 1;
            &mut (*pointer).wlcs_pointer
        } else {
            std::ptr::null_mut()
        }
    }

    unsafe extern "C" fn create_touch(ptr: *mut WlcsDisplayServer) -> *mut WlcsTouch {
        let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
        if let Some((ref sender, _)) = me.server {
            let pointer = Box::into_raw(Box::new(TouchHandle::new(me.next_device_id, sender.clone())));
            me.next_device_id += 1;
            &mut (*pointer).wlcs_touch
        } else {
            std::ptr::null_mut()
        }
    }

    unsafe extern "C" fn get_descriptor(_: *const WlcsDisplayServer) -> *const WlcsIntegrationDescriptor {
        &crate::DESCRIPTOR
    }
}

struct PointerHandle {
    wlcs_pointer: WlcsPointer,
    device_id: u32,
    sender: Sender<WlcsEvent>,
}

impl PointerHandle {
    fn new(device_id: u32, sender: Sender<WlcsEvent>) -> PointerHandle {
        PointerHandle {
            wlcs_pointer: WlcsPointer {
                version: 1,
                move_absolute: Self::move_absolute,
                move_relative: Self::move_relative,
                button_down: Self::button_down,
                button_up: Self::button_up,
                destroy: Self::destroy,
            },
            device_id,
            sender,
        }
    }

    unsafe extern "C" fn move_absolute(ptr: *mut WlcsPointer, x: wl_fixed_t, y: wl_fixed_t) {
        let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
        let _ = me.sender.send(WlcsEvent::PointerMoveAbsolute {
            device_id: me.device_id,
            location: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
        });
    }

    unsafe extern "C" fn move_relative(ptr: *mut WlcsPointer, x: wl_fixed_t, y: wl_fixed_t) {
        let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
        let _ = me.sender.send(WlcsEvent::PointerMoveRelative {
            device_id: me.device_id,
            delta: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
        });
    }

    unsafe extern "C" fn button_up(ptr: *mut WlcsPointer, button_id: i32) {
        let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
        let _ = me.sender.send(WlcsEvent::PointerButtonUp {
            device_id: me.device_id,
            button_id,
        });
    }

    unsafe extern "C" fn button_down(ptr: *mut WlcsPointer, button_id: i32) {
        let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
        let _ = me.sender.send(WlcsEvent::PointerButtonDown {
            device_id: me.device_id,
            button_id,
        });
    }

    unsafe extern "C" fn destroy(ptr: *mut WlcsPointer) {
        let _me = Box::from_raw(container_of!(ptr, PointerHandle, wlcs_pointer));
    }
}

struct TouchHandle {
    wlcs_touch: WlcsTouch,
    device_id: u32,
    sender: Sender<WlcsEvent>,
}

impl TouchHandle {
    fn new(device_id: u32, sender: Sender<WlcsEvent>) -> TouchHandle {
        TouchHandle {
            wlcs_touch: WlcsTouch {
                version: 1,
                touch_down: Self::touch_down,
                touch_move: Self::touch_move,
                touch_up: Self::touch_up,
                destroy: Self::destroy,
            },
            device_id,
            sender,
        }
    }

    unsafe extern "C" fn touch_down(ptr: *mut WlcsTouch, x: wl_fixed_t, y: wl_fixed_t) {
        let me = &mut *container_of!(ptr, TouchHandle, wlcs_touch);
        let _ = me.sender.send(WlcsEvent::TouchDown {
            device_id: me.device_id,
            location: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
        });
    }

    unsafe extern "C" fn touch_move(ptr: *mut WlcsTouch, x: wl_fixed_t, y: wl_fixed_t) {
        let me = &mut *container_of!(ptr, TouchHandle, wlcs_touch);
        let _ = me.sender.send(WlcsEvent::TouchMove {
            device_id: me.device_id,
            location: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
        });
    }

    unsafe extern "C" fn touch_up(ptr: *mut WlcsTouch) {
        let me = &mut *container_of!(ptr, TouchHandle, wlcs_touch);
        let _ = me.sender.send(WlcsEvent::TouchUp {
            device_id: me.device_id,
        });
    }

    unsafe extern "C" fn destroy(ptr: *mut WlcsTouch) {
        let _me = Box::from_raw(container_of!(ptr, TouchHandle, wlcs_touch));
    }
}
<commit_msg>wlcs_anvil: Add catch_unwind safeguards<commit_after>use std::{
    os::{
        raw::{c_char, c_int},
        unix::{
            net::UnixStream,
            prelude::{AsRawFd, IntoRawFd},
        },
    },
    thread::JoinHandle,
};

use smithay::reexports::calloop::channel::{channel, Sender};
use wayland_sys::{
    client::*,
    common::{wl_fixed_t, wl_fixed_to_double},
    ffi_dispatch,
};

use crate::{ffi_api::*, WlcsEvent};

macro_rules! container_of(
    ($ptr: expr, $container: ident, $field: ident) => {
        ($ptr as *mut u8).offset(-(memoffset::offset_of!($container, $field) as isize)) as *mut $container
    }
);

#[no_mangle]
pub static wlcs_server_integration: WlcsServerIntegration = WlcsServerIntegration {
    version: 1,
    create_server,
    destroy_server,
};

unsafe extern "C" fn create_server(_argc: c_int, _argv: *mut *const c_char) -> *mut WlcsDisplayServer {
    // block the SIGPIPE signal here, we are a cdylib so Rust does not do it for us
    match std::panic::catch_unwind(|| {
        use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};
        sigaction(
            Signal::SIGPIPE,
            &SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty()),
        )
        .unwrap();
        let handle = Box::into_raw(Box::new(DisplayServerHandle::new()));
        &mut (*handle).wlcs_display_server
    }) {
        Ok(ptr) => ptr,
        Err(err) => {
            println!(
                "panic in create_server on ptr: {:p} (type {:?})",
                err.as_ref() as *const _,
                err.type_id()
            );
            std::ptr::null_mut()
        }
    }
}

unsafe extern "C" fn destroy_server(ptr: *mut WlcsDisplayServer) {
    match std::panic::catch_unwind(|| {
        let _server = Box::from_raw(container_of!(ptr, DisplayServerHandle, wlcs_display_server));
    }) {
        Ok(()) => {}
        Err(err) => {
            println!(
                "panic in destroy_server on ptr: {:p} (type {:?})",
                err.as_ref() as *const _,
                err.type_id()
            );
        }
    }
}

struct DisplayServerHandle {
    wlcs_display_server: WlcsDisplayServer,
    server: Option<(Sender<WlcsEvent>, JoinHandle<()>)>,
    next_device_id: u32,
}

impl DisplayServerHandle {
    fn new() -> DisplayServerHandle {
        DisplayServerHandle {
            wlcs_display_server: WlcsDisplayServer {
                version: 3,
                start: Self::start,
                stop: Self::stop,
                create_client_socket: Self::create_client_socket,
                position_window_absolute: Self::position_window_absolute,
                create_pointer: Self::create_pointer,
                create_touch: Self::create_touch,
                get_descriptor: Self::get_descriptor,
                start_on_this_thread: None,
            },
            server: None,
            next_device_id: 1,
        }
    }

    unsafe extern "C" fn start(ptr: *mut WlcsDisplayServer) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
            let (tx, rx) = channel();
            let join = crate::start_anvil(rx);
            me.server = Some((tx, join));
        }) {
            Ok(()) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_display_server::start on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn stop(ptr: *mut WlcsDisplayServer) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
            if let Some((sender, join)) = me.server.take() {
                let _ = sender.send(WlcsEvent::Exit);
                let _ = join.join();
            }
        }) {
            Ok(()) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_display_server::stop on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn create_client_socket(ptr: *mut WlcsDisplayServer) -> c_int {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
            if let Some((ref sender, _)) = me.server {
                if let Ok((client_side, server_side)) = UnixStream::pair() {
                    if sender
                        .send(WlcsEvent::NewClient {
                            stream: server_side,
                            client_id: client_side.as_raw_fd(),
                        })
                        .is_err()
                    {
                        return -1;
                    }
                    return client_side.into_raw_fd();
                }
            }
            -1
        }) {
            Ok(val) => val,
            Err(err) => {
                println!(
                    "panic in wlcs_display_server::create_client_socket on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
                -1
            }
        }
    }

    unsafe extern "C" fn position_window_absolute(
        ptr: *mut WlcsDisplayServer,
        display: *mut wl_display,
        surface: *mut wl_proxy,
        x: c_int,
        y: c_int,
    ) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
            let client_id = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_get_fd, display);
            let surface_id = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_id, surface);
            if let Some((ref sender, _)) = me.server {
                let _ = sender.send(WlcsEvent::PositionWindow {
                    client_id,
                    surface_id,
                    location: (x, y).into(),
                });
            }
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_display_server::position_window_absolute on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn create_pointer(ptr: *mut WlcsDisplayServer) -> *mut WlcsPointer {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
            if let Some((ref sender, _)) = me.server {
                let pointer = Box::into_raw(Box::new(PointerHandle::new(me.next_device_id, sender.clone())));
                me.next_device_id += 1;
                &mut (*pointer).wlcs_pointer
            } else {
                std::ptr::null_mut()
            }
        }) {
            Ok(ptr) => ptr,
            Err(err) => {
                println!(
                    "panic in wlcs_display_server::create_pointer on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
                std::ptr::null_mut()
            }
        }
    }

    unsafe extern "C" fn create_touch(ptr: *mut WlcsDisplayServer) -> *mut WlcsTouch {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, DisplayServerHandle, wlcs_display_server);
            if let Some((ref sender, _)) = me.server {
                let pointer = Box::into_raw(Box::new(TouchHandle::new(me.next_device_id, sender.clone())));
                me.next_device_id += 1;
                &mut (*pointer).wlcs_touch
            } else {
                std::ptr::null_mut()
            }
        }) {
            Ok(ptr) => ptr,
            Err(err) => {
                println!(
                    "panic in wlcs_display_server::create_touch on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
                std::ptr::null_mut()
            }
        }
    }

    unsafe extern "C" fn get_descriptor(_: *const WlcsDisplayServer) -> *const WlcsIntegrationDescriptor {
        &crate::DESCRIPTOR
    }
}

struct PointerHandle {
    wlcs_pointer: WlcsPointer,
    device_id: u32,
    sender: Sender<WlcsEvent>,
}

impl PointerHandle {
    fn new(device_id: u32, sender: Sender<WlcsEvent>) -> PointerHandle {
        PointerHandle {
            wlcs_pointer: WlcsPointer {
                version: 1,
                move_absolute: Self::move_absolute,
                move_relative: Self::move_relative,
                button_down: Self::button_down,
                button_up: Self::button_up,
                destroy: Self::destroy,
            },
            device_id,
            sender,
        }
    }

    unsafe extern "C" fn move_absolute(ptr: *mut WlcsPointer, x: wl_fixed_t, y: wl_fixed_t) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
            let _ = me.sender.send(WlcsEvent::PointerMoveAbsolute {
                device_id: me.device_id,
                location: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
            });
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_pointer::move_absolute on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn move_relative(ptr: *mut WlcsPointer, x: wl_fixed_t, y: wl_fixed_t) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
            let _ = me.sender.send(WlcsEvent::PointerMoveRelative {
                device_id: me.device_id,
                delta: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
            });
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_pointer::move_relative on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn button_up(ptr: *mut WlcsPointer, button_id: i32) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
            let _ = me.sender.send(WlcsEvent::PointerButtonUp {
                device_id: me.device_id,
                button_id,
            });
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_pointer::button_up on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn button_down(ptr: *mut WlcsPointer, button_id: i32) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, PointerHandle, wlcs_pointer);
            let _ = me.sender.send(WlcsEvent::PointerButtonDown {
                device_id: me.device_id,
                button_id,
            });
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_pointer::button_down on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn destroy(ptr: *mut WlcsPointer) {
        match std::panic::catch_unwind(|| {
            let _me = Box::from_raw(container_of!(ptr, PointerHandle, wlcs_pointer));
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_pointer::destroy on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }
}

struct TouchHandle {
    wlcs_touch: WlcsTouch,
    device_id: u32,
    sender: Sender<WlcsEvent>,
}

impl TouchHandle {
    fn new(device_id: u32, sender: Sender<WlcsEvent>) -> TouchHandle {
        TouchHandle {
            wlcs_touch: WlcsTouch {
                version: 1,
                touch_down: Self::touch_down,
                touch_move: Self::touch_move,
                touch_up: Self::touch_up,
                destroy: Self::destroy,
            },
            device_id,
            sender,
        }
    }

    unsafe extern "C" fn touch_down(ptr: *mut WlcsTouch, x: wl_fixed_t, y: wl_fixed_t) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, TouchHandle, wlcs_touch);
            let _ = me.sender.send(WlcsEvent::TouchDown {
                device_id: me.device_id,
                location: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
            });
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_touch::touch_down on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn touch_move(ptr: *mut WlcsTouch, x: wl_fixed_t, y: wl_fixed_t) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, TouchHandle, wlcs_touch);
            let _ = me.sender.send(WlcsEvent::TouchMove {
                device_id: me.device_id,
                location: (wl_fixed_to_double(x), wl_fixed_to_double(y)).into(),
            });
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_touch::touch_move on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn touch_up(ptr: *mut WlcsTouch) {
        match std::panic::catch_unwind(|| {
            let me = &mut *container_of!(ptr, TouchHandle, wlcs_touch);
            let _ = me.sender.send(WlcsEvent::TouchUp {
                device_id: me.device_id,
            });
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_touch::touch_up on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }

    unsafe extern "C" fn destroy(ptr: *mut WlcsTouch) {
        match std::panic::catch_unwind(|| {
            let _me = Box::from_raw(container_of!(ptr, TouchHandle, wlcs_touch));
        }) {
            Ok(_) => {}
            Err(err) => {
                println!(
                    "panic in wlcs_touch::destroy on ptr: {:p} (type {:?})",
                    err.as_ref() as *const _,
                    err.type_id()
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::effect_node::{EffectNode, EffectNodeBi};
use effects::effect::Effect;
use effects::sum::Sum;

pub struct EffectTree<'a> {
    root : EffectNodeBi<'a>,
}

pub struct EffectTreeIter<'a> {
    // Given a tree like this (a is root):
    //
    //  e      f g
    //  |      |/
    //  b      c      d
    //   \     |     /
    //    -----a-----
    // Path starts as (a, 0)
    // Iteration proceeds as:
    // [(a, 0), (b, 0)]
    // [(a, 0), (b, 0), (e, 0)]
    // [(a, 1), (c, 0)]
    // [(a, 1), (c, 0), (f, 0)]
    // [(a, 1), (c, 1), (g, 0)]
    // [(a, 2), (d, 0)]
    // []
    // Thus to advance the iterator, we look at the last part of the path (node,idx):
    //   check if there exists a node at node.children[idx]
    //     if so, append (node.children[idx], 0) to path
    //   else, pop the last path, increment the index in the new path.last() and repeat
    // Example:
    // [(a, 0), (b, 0)]: yes - b has a child at index 0.
    //   therefore push (e, 0) onto the path
    //   new path = [(a, 0), (b, 0), (e, 0)]
    // [(a, 0), (b, 0), (e, 0)]: no - e has no child at index 0.
    //   pop (e, 0) and increment the index of the new path end:
    //   [(a, 0), (b, 1)]
    //   repeat: does b have a child at index 1? no
    //   pop (b, 1) and increment the index of the new path end:
    //   [(a, 1)]
    //   repeat: does a have a child at index 1? yes
    //   therefore, push (c, 0) onto the path
    //   new path: [(a, 1), (c, 0)]
    path : Vec<(&'a EffectNodeBi<'a>, usize)>,
}

impl<'a> EffectTree<'a> {
    /// creates a new Tree, where the root is a Sum effect
    pub fn new() -> EffectTree<'a> {
        let root_effect = Box::new(Sum::new());
        let root_node = EffectNodeBi::new(root_effect, None);
        EffectTree{ root:root_node }
    }
    /// Iterate the tree in a totally unspecified order.
    /// The primary use of this function is just to obtain a set of all
    /// EffectNodes
    pub fn iter(&'a self) -> EffectTreeIter<'a> {
        EffectTreeIter{ path:vec![(&self.root, 0)] }
    }
    pub fn root(&self) -> &EffectNodeBi {
        &self.root
    }
}

impl<'a> Iterator for EffectTreeIter<'a> {
    type Item=&'a EffectNode<'a>;

    fn next(&mut self) -> Option<&'a EffectNode<'a>> {
        let ret = self.path.last().map(|last| last.0.node());
        loop {
            match self.path.last().cloned() {
                // if path==[] then we have fully iterated the tree
                None => break,
                Some(active_node) => {
                    // if the next node in the tree is valid, then append that.
                    // otherwise, walk back up a step and move one child right
                    match active_node.0.child_node_bi(active_node.1) {
                        Some(node) => self.path.push((node, 0)),
                        None => {
                            self.path.pop();
                            self.path.last_mut().map(|new_last| {
                                new_last.1 += 1
                            });
                        }
                    }
                }
            }
        }
        ret
    }
}
<commit_msg>Fix EffectTreeIter failing for tree sizes > 1 & prefer a cleaner 'while let' loop form<commit_after>use super::effect_node::{EffectNode, EffectNodeBi};
use effects::effect::Effect;
use effects::sum::Sum;

pub struct EffectTree<'a> {
    root : EffectNodeBi<'a>,
}

pub struct EffectTreeIter<'a> {
    // Given a tree like this (a is root):
    //
    //  e      f g
    //  |      |/
    //  b      c      d
    //   \     |     /
    //    -----a-----
    // Path starts as (a, 0)
    // Iteration proceeds as:
    // [(a, 0), (b, 0)]
    // [(a, 0), (b, 0), (e, 0)]
    // [(a, 1), (c, 0)]
    // [(a, 1), (c, 0), (f, 0)]
    // [(a, 1), (c, 1), (g, 0)]
    // [(a, 2), (d, 0)]
    // []
    // Thus to advance the iterator, we look at the last part of the path (node,idx):
    //   check if there exists a node at node.children[idx]
    //     if so, append (node.children[idx], 0) to path
    //   else, pop the last path, increment the index in the new path.last() and repeat
    // Example:
    // [(a, 0), (b, 0)]: yes - b has a child at index 0.
    //   therefore push (e, 0) onto the path
    //   new path = [(a, 0), (b, 0), (e, 0)]
    // [(a, 0), (b, 0), (e, 0)]: no - e has no child at index 0.
    //   pop (e, 0) and increment the index of the new path end:
    //   [(a, 0), (b, 1)]
    //   repeat: does b have a child at index 1? no
    //   pop (b, 1) and increment the index of the new path end:
    //   [(a, 1)]
    //   repeat: does a have a child at index 1? yes
    //   therefore, push (c, 0) onto the path
    //   new path: [(a, 1), (c, 0)]
    path : Vec<(&'a EffectNodeBi<'a>, usize)>,
}

impl<'a> EffectTree<'a> {
    /// creates a new Tree, where the root is a Sum effect
    pub fn new() -> EffectTree<'a> {
        let root_effect = Box::new(Sum::new());
        let root_node = EffectNodeBi::new(root_effect, None);
        EffectTree{ root:root_node }
    }
    /// Iterate the tree in a totally unspecified order.
    /// The primary use of this function is just to obtain a set of all
    /// EffectNodes
    pub fn iter(&'a self) -> EffectTreeIter<'a> {
        EffectTreeIter{ path:vec![(&self.root, 0)] }
    }
    pub fn root(&self) -> &EffectNodeBi {
        &self.root
    }
}

impl<'a> Iterator for EffectTreeIter<'a> {
    type Item=&'a EffectNode<'a>;

    fn next(&mut self) -> Option<&'a EffectNode<'a>> {
        let ret = self.path.last().map(|last| last.0.node());
        // Walk the tree until we find the next node, or reach its end
        while let Some(active_node) = self.path.last().cloned() {
            // if the next node in the tree is valid, then append that and break.
            // otherwise, walk back up a step and move one child right
            match active_node.0.child_node_bi(active_node.1) {
                Some(node) => {
                    self.path.push((node, 0));
                    break;
                },
                None => {
                    self.path.pop();
                    self.path.last_mut().map(|new_last| {
                        new_last.1 += 1
                    });
                }
            }
        }
        ret
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::io::BufReader;

use color::{ColorType};
use image::{DecodingResult, ImageDecoder, ImageResult, ImageError};
extern crate byteorder;
use self::byteorder::{BigEndian, ByteOrder};

/// PPM decoder
pub struct PPMDecoder<R> {
    reader: BufReader<R>,
    width: u32,
    height: u32,
    maxwhite: u32,
}

impl<R: Read> PPMDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<PPMDecoder<R>> {
        let mut buf = BufReader::new(r);
        try!(PPMDecoder::read_next_string(&mut buf)); // Skip P6
        let width = try!(PPMDecoder::read_next_u32(&mut buf));
        let height = try!(PPMDecoder::read_next_u32(&mut buf));
        let maxwhite = try!(PPMDecoder::read_next_u32(&mut buf));
        Ok(PPMDecoder {
            reader: buf,
            width: width,
            height: height,
            maxwhite: maxwhite,
        })
    }

  fn read_next_string(reader: &mut BufReader<R>) -> ImageResult<String> {
      let mut bytes = Vec::new();
      loop {
          let mut buffer: [u8;1] = [0];
          try!(reader.read_exact(&mut buffer));
          match buffer[0] {
              b'\n' | b' ' | b'\r' => {
                  if bytes.len() > 0 {
                    break // We're done as we already have some content
                  }
              },
              _ => {
                  bytes.push(buffer[0]);
              },
          }
      }

      match String::from_utf8(bytes) {
          Ok(s) => Ok(s),
          Err(_) => Err(ImageError::FormatError("Couldn't read preamble".to_string())),
      }
  }

  fn read_next_u32(reader: &mut BufReader<R>) -> ImageResult<u32> {
      let s = try!(PPMDecoder::read_next_string(reader));
      match s.parse::<u32>() {
          Ok(v) => Ok(v),
          Err(_) => Err(ImageError::FormatError("Couldn't read preamble".to_string())),
      }
  }
}

impl<R: Read> ImageDecoder for PPMDecoder<R> {
    fn dimensions(&mut self) -> ImageResult<(u32, u32)> {
        Ok((self.width, self.height))
    }

    fn colortype(&mut self) -> ImageResult<ColorType> {
        match self.bytewidth() {
            1 => Ok(ColorType::RGB(8)),
            2 => Ok(ColorType::RGB(16)),
            _ => Err(ImageError::FormatError("Don't know how to decode PPM with more than 16 bits".to_string())),
        }
    }

    fn row_len(&mut self) -> ImageResult<usize> {
        Ok((self.width*3*self.bytewidth()) as usize)
    }

    fn read_scanline(&mut self, _buf: &mut [u8]) -> ImageResult<u32> {
        unimplemented!();
    }

    fn read_image(&mut self) -> ImageResult<DecodingResult> {
        let mut data = vec![0 as u8; (self.width*self.height*3*self.bytewidth()) as usize];
        match self.reader.read_exact(&mut data) {
            Ok(_) => {},
            Err(e) => return Err(ImageError::IoError(e)),
        };

        if self.bytewidth() == 1 {
            Ok(DecodingResult::U8(data))
        } else {
            let mut out = vec![0 as u16; (self.width*self.height*3) as usize];
            for (o, i) in out.chunks_mut(1).zip(data.chunks(2)) {
                o[0] = BigEndian::read_u16(i);
            }
            Ok(DecodingResult::U16(out))
        }
    }
}

impl<R: Read> PPMDecoder<R> {
    fn bytewidth(&self) -> u32 {
        if self.maxwhite < 256 { 1 } else { 2 }
    }
}
<commit_msg>Also accept tab delimited PPM headers<commit_after>use std::io::Read;
use std::io::BufReader;

use color::{ColorType};
use image::{DecodingResult, ImageDecoder, ImageResult, ImageError};
extern crate byteorder;
use self::byteorder::{BigEndian, ByteOrder};

/// PPM decoder
pub struct PPMDecoder<R> {
    reader: BufReader<R>,
    width: u32,
    height: u32,
    maxwhite: u32,
}

impl<R: Read> PPMDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<PPMDecoder<R>> {
        let mut buf = BufReader::new(r);
        try!(PPMDecoder::read_next_string(&mut buf)); // Skip P6
        let width = try!(PPMDecoder::read_next_u32(&mut buf));
        let height = try!(PPMDecoder::read_next_u32(&mut buf));
        let maxwhite = try!(PPMDecoder::read_next_u32(&mut buf));
        Ok(PPMDecoder {
            reader: buf,
            width: width,
            height: height,
            maxwhite: maxwhite,
        })
    }

  fn read_next_string(reader: &mut BufReader<R>) -> ImageResult<String> {
      let mut bytes = Vec::new();
      loop {
          let mut buffer: [u8;1] = [0];
          try!(reader.read_exact(&mut buffer));
          match buffer[0] {
              b'\n' | b' ' | b'\r' | b'\t' => {
                  if bytes.len() > 0 {
                    break // We're done as we already have some content
                  }
              },
              _ => {
                  bytes.push(buffer[0]);
              },
          }
      }

      match String::from_utf8(bytes) {
          Ok(s) => Ok(s),
          Err(_) => Err(ImageError::FormatError("Couldn't read preamble".to_string())),
      }
  }

  fn read_next_u32(reader: &mut BufReader<R>) -> ImageResult<u32> {
      let s = try!(PPMDecoder::read_next_string(reader));
      match s.parse::<u32>() {
          Ok(v) => Ok(v),
          Err(_) => Err(ImageError::FormatError("Couldn't read preamble".to_string())),
      }
  }
}

impl<R: Read> ImageDecoder for PPMDecoder<R> {
    fn dimensions(&mut self) -> ImageResult<(u32, u32)> {
        Ok((self.width, self.height))
    }

    fn colortype(&mut self) -> ImageResult<ColorType> {
        match self.bytewidth() {
            1 => Ok(ColorType::RGB(8)),
            2 => Ok(ColorType::RGB(16)),
            _ => Err(ImageError::FormatError("Don't know how to decode PPM with more than 16 bits".to_string())),
        }
    }

    fn row_len(&mut self) -> ImageResult<usize> {
        Ok((self.width*3*self.bytewidth()) as usize)
    }

    fn read_scanline(&mut self, _buf: &mut [u8]) -> ImageResult<u32> {
        unimplemented!();
    }

    fn read_image(&mut self) -> ImageResult<DecodingResult> {
        let mut data = vec![0 as u8; (self.width*self.height*3*self.bytewidth()) as usize];
        match self.reader.read_exact(&mut data) {
            Ok(_) => {},
            Err(e) => return Err(ImageError::IoError(e)),
        };

        if self.bytewidth() == 1 {
            Ok(DecodingResult::U8(data))
        } else {
            let mut out = vec![0 as u16; (self.width*self.height*3) as usize];
            for (o, i) in out.chunks_mut(1).zip(data.chunks(2)) {
                o[0] = BigEndian::read_u16(i);
            }
            Ok(DecodingResult::U16(out))
        }
    }
}

impl<R: Read> PPMDecoder<R> {
    fn bytewidth(&self) -> u32 {
        if self.maxwhite < 256 { 1 } else { 2 }
    }
}
<|endoftext|>