<commit_before>extern crate env_logger;
extern crate tempfile;
extern crate gluon;
extern crate tokio;

use std::fs;
use tempfile::NamedTempFile;
use gluon::vm::api::{Hole, OpaqueValue, ValueRef, IO};
use gluon::{new_vm, Compiler, Thread};

#[macro_use]
mod support;

use support::*;

#[test]
fn read_file() {
    let _ = ::env_logger::try_init();

    let thread = new_vm();
    let text = r#"
        let prelude = import! std.prelude
        let array = import! std.array
        let { assert }  = import! std.test
        let io @ { ? } = import! std.io
        let { wrap } = io.applicative
        let { ? } = import! std.byte
        let { unwrap } = import! std.option
        let { flat_map, (>>=) } = import! std.monad

        do file = io.open_file "Cargo.toml"
        do bytes = io.read_file file 9
        let bytes = unwrap bytes

        assert (array.len bytes == 9)
        assert (array.index bytes 0 == 91b) // [
        assert (array.index bytes 1 == 112b) // p

        wrap (array.index bytes 8)
        "#;
    let result = Compiler::new()
        .run_io(true)
        .run_expr::<IO<u8>>(&thread, "<top>", text);

    match result {
        Ok((IO::Value(value), _)) => assert_eq!(value, b']'),
        Ok((IO::Exception(err), _)) => assert!(false, "{}", err),
        Err(err) => assert!(false, "{}", err),
    }
}

#[test]
fn write_and_flush_file() {
    let _ = ::env_logger::try_init();

    let file =  NamedTempFile::new().unwrap();

    let thread = new_vm();
    let text = format!(
        r#"
        let {{ assert }} = import! std.test
        let {{ OpenOptions, open_file_with, write_slice_file, flush_file, ? }} = import! std.io

        do file = open_file_with "{path}" [Write]
        do bytes_written = write_slice_file file [1b, 2b, 3b, 4b] 0 4
        assert (bytes_written == 4)
        flush_file file
        "#,
        path = file.path().to_str().unwrap()
    );

    Compiler::new()
        .run_io(true)
        .run_expr::<IO<()>>(&thread, "<top>", &text)
        .unwrap_or_else(|err| panic!("{}", err));

    let content = fs::read(file.path()).unwrap();
    assert_eq!(content, vec![1, 2, 3, 4]);
}

test_expr!{ no_io_eval,
r#"
let { error } = import! std.prim
let io = import! std.io
let x = io.flat_map (\x -> error "NOOOOOOOO") (io.println "1")
in { x }
"#
}

test_expr! { io_print,
r#"
let io = import! std.io
io.print "123"
"#
}

#[test]
fn run_expr_int() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io = import! std.io
        let { flat_map } = io.monad
        do result = io.run_expr "123"
        io.applicative.wrap result.value
    "#;
    let mut vm = make_vm();
    let (result, _) = Compiler::new()
        .run_io(true)
        .run_expr::<IO<String>>(&mut vm, "<top>", text)
        .unwrap();
    match result {
        IO::Value(result) => {
            let expected = "123";
            assert_eq!(result, expected);
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

test_expr! { io run_expr_io,
r#"
let io = import! std.io
io.flat_map (\x -> io.wrap 100)
            (io.run_expr "
                let io = import! \"std/io.glu\"
                io.print \"123\"
            ")
"#,
100i32
}

#[test]
fn dont_execute_io_in_run_expr_async() {
    let _ = ::env_logger::try_init();
    let vm = make_vm();
    let expr = r#"
let prelude  = import! std.prelude
let io = import! std.io
let { wrap } = io.applicative
wrap 123
"#;
    let value = Compiler::new()
        .run_expr::<OpaqueValue<&Thread, Hole>>(&vm, "example", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    assert!(
        value.0.get_ref() != ValueRef::Int(123),
        "Unexpected {:?}",
        value.0
    );
}

#[test]
fn spawn_on_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { applicative = { wrap }, monad = { flat_map } } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> wrap "abc")
        action
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_runexpr() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io@{ applicative = applicative@{ wrap }, monad = { flat_map }, ? } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> io.run_expr "123")
        do x = action
        do _ = io.println x.value
        wrap x.value
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_do_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative
        let { join } = import! std.monad

        let counter = ref 0

        do child = thread.new_thread ()
        let action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = join action
        do _ = join action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(2));
}

#[test]
fn spawn_on_force_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative

        let counter = ref 0

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = action
        do _ = action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(1));
}

#[test]
fn spawn_on_runexpr_in_catch() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let prelude = import! std.prelude
        let io @ { ? } = import! std.io
        let { Applicative, (*>), wrap } = import! std.applicative
        let { flat_map, (>>=) } = import! std.monad
        let thread = import! std.thread

        let action =
            do eval_thread = thread.new_thread ()
            let f _ = io.run_expr "123"
            do a = thread.spawn_on eval_thread f
            do result = a
            wrap result.value
        (io.catch action wrap >>= io.println) *> wrap "123"
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn io_error_in_catch1() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io = import! std.io
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch2() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let { (*>) } = import! std.applicative
        let io @ { ? } = import! std.io
        io.catch (io.println "asd" *> io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch3() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io @ { ? } = import! std.io
        let { flat_map } = import! std.monad
        do _ = io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error2")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error2".to_string());

    assert_eq!(result, expected);
}
<commit_msg>Do not use format string in write_and_flush_file test<commit_after>extern crate env_logger;
extern crate gluon;
extern crate tempfile;
extern crate tokio;

use gluon::vm::api::{Hole, OpaqueValue, OwnedFunction, ValueRef, IO};
use gluon::{new_vm, Compiler, Thread};
use std::fs;
use tempfile::NamedTempFile;

#[macro_use]
mod support;

use support::*;

#[test]
fn read_file() {
    let _ = ::env_logger::try_init();

    let thread = new_vm();
    let text = r#"
        let prelude = import! std.prelude
        let array = import! std.array
        let { assert }  = import! std.test
        let io @ { ? } = import! std.io
        let { wrap } = io.applicative
        let { ? } = import! std.byte
        let { unwrap } = import! std.option
        let { flat_map, (>>=) } = import! std.monad

        do file = io.open_file "Cargo.toml"
        do bytes = io.read_file file 9
        let bytes = unwrap bytes

        assert (array.len bytes == 9)
        assert (array.index bytes 0 == 91b) // [
        assert (array.index bytes 1 == 112b) // p

        wrap (array.index bytes 8)
        "#;
    let result = Compiler::new()
        .run_io(true)
        .run_expr::<IO<u8>>(&thread, "<top>", text);

    match result {
        Ok((IO::Value(value), _)) => assert_eq!(value, b']'),
        Ok((IO::Exception(err), _)) => assert!(false, "{}", err),
        Err(err) => assert!(false, "{}", err),
    }
}

#[test]
fn write_and_flush_file() {
    let _ = ::env_logger::try_init();

    let file = NamedTempFile::new().unwrap();

    let thread = new_vm();
    let text = r#"
        let { assert } = import! std.test
        let { OpenOptions, open_file_with, write_slice_file, flush_file, ? } = import! std.io

        \path ->
            do file = open_file_with path [Write]
            do bytes_written = write_slice_file file [1b, 2b, 3b, 4b] 0 4
            assert (bytes_written == 4)
            flush_file file
    "#;

    let (mut test, _) = Compiler::new()
        .run_io(true)
        .run_expr::<OwnedFunction<fn(String) -> IO<()>>>(&thread, "<top>", &text)
        .unwrap_or_else(|err| panic!("{}", err));

    test.call(file.path().to_str().unwrap().to_owned())
        .unwrap_or_else(|err| panic!("{}", err));

    let content = fs::read(file.path()).unwrap();
    assert_eq!(content, vec![1, 2, 3, 4]);
}

test_expr!{ no_io_eval,
r#"
let { error } = import! std.prim
let io = import! std.io
let x = io.flat_map (\x -> error "NOOOOOOOO") (io.println "1")
in { x }
"#
}

test_expr! { io_print,
r#"
let io = import! std.io
io.print "123"
"#
}

#[test]
fn run_expr_int() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io = import! std.io
        let { flat_map } = io.monad
        do result = io.run_expr "123"
        io.applicative.wrap result.value
    "#;
    let mut vm = make_vm();
    let (result, _) = Compiler::new()
        .run_io(true)
        .run_expr::<IO<String>>(&mut vm, "<top>", text)
        .unwrap();
    match result {
        IO::Value(result) => {
            let expected = "123";
            assert_eq!(result, expected);
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

test_expr! { io run_expr_io,
r#"
let io = import! std.io
io.flat_map (\x -> io.wrap 100)
            (io.run_expr "
                let io = import! \"std/io.glu\"
                io.print \"123\"
            ")
"#,
100i32
}

#[test]
fn dont_execute_io_in_run_expr_async() {
    let _ = ::env_logger::try_init();
    let vm = make_vm();
    let expr = r#"
let prelude  = import! std.prelude
let io = import! std.io
let { wrap } = io.applicative
wrap 123
"#;
    let value = Compiler::new()
        .run_expr::<OpaqueValue<&Thread, Hole>>(&vm, "example", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    assert!(
        value.0.get_ref() != ValueRef::Int(123),
        "Unexpected {:?}",
        value.0
    );
}

#[test]
fn spawn_on_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { applicative = { wrap }, monad = { flat_map } } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> wrap "abc")
        action
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        ).unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        ).unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_runexpr() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io@{ applicative = applicative@{ wrap }, monad = { flat_map }, ? } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> io.run_expr "123")
        do x = action
        do _ = io.println x.value
        wrap x.value
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        ).unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_do_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative
        let { join } = import! std.monad

        let counter = ref 0

        do child = thread.new_thread ()
        let action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = join action
        do _ = join action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        ).unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(2));
}

#[test]
fn spawn_on_force_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative

        let counter = ref 0

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = action
        do _ = action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        ).unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(1));
}

#[test]
fn spawn_on_runexpr_in_catch() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let prelude = import! std.prelude
        let io @ { ? } = import! std.io
        let { Applicative, (*>), wrap } = import! std.applicative
        let { flat_map, (>>=) } = import! std.monad
        let thread = import! std.thread

        let action =
            do eval_thread = thread.new_thread ()
            let f _ = io.run_expr "123"
            do a = thread.spawn_on eval_thread f
            do result = a
            wrap result.value
        (io.catch action wrap >>= io.println) *> wrap "123"
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        ).unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        ).unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn io_error_in_catch1() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io = import! std.io
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch2() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let { (*>) } = import! std.applicative
        let io @ { ? } = import! std.io
        io.catch (io.println "asd" *> io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch3() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io @ { ? } = import! std.io
        let { flat_map } = import! std.monad
        do _ = io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error2")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error2".to_string());

    assert_eq!(result, expected);
}
<|endoftext|>


<commit_before>// Copyright (c) 2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use thiserror::Error;

use std::sync::Arc;

use crate::api::{ChromaSampling, Context, ContextInner, PixelRange};
use crate::cpu_features::CpuFeatureLevel;
use crate::rayon::{ThreadPool, ThreadPoolBuilder};
use crate::tiling::TilingInfo;
use crate::util::Pixel;

mod encoder;
pub use encoder::*;

mod rate;
pub use rate::Error as RateControlError;
pub use rate::{RateControlConfig, RateControlSummary};

mod speedsettings;
pub use speedsettings::*;

/// Enumeration of possible invalid configuration errors.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
#[non_exhaustive]
pub enum InvalidConfig {
  /// The width is invalid.
  #[error("invalid width {0} (expected >= 16, <= 65535)")]
  InvalidWidth(usize),
  /// The height is invalid.
  #[error("invalid height {0} (expected >= 16, <= 65535)")]
  InvalidHeight(usize),
  /// Aspect ratio numerator is invalid.
  #[error("invalid aspect ratio numerator {0} (expected > 0)")]
  InvalidAspectRatioNum(usize),
  /// Aspect ratio denominator is invalid.
  #[error("invalid aspect ratio denominator {0} (expected > 0)")]
  InvalidAspectRatioDen(usize),
  /// The render width (width adjusted based on the aspect ratio) is invalid.
  #[error("invalid render width {0} (expected >= 1, <= 65535")]
  InvalidRenderWidth(usize),
  /// The render height (height adjusted based on the aspect ratio) is invalid.
  #[error("invalid render height {0} (expected >= 1, <= 65535")]
  InvalidRenderHeight(usize),
  /// RDO lookahead frame count is invalid.
  #[error(
    "invalid rdo lookahead frames {actual} (expected <= {max} and >= {min})"
  )]
  InvalidRdoLookaheadFrames {
    /// The actual value.
    actual: usize,
    /// The maximal supported value.
    max: usize,
    /// The minimal supported value.
    min: usize,
  },
  /// Maximal keyframe interval is invalid.
  #[error("invalid max keyframe interval {actual} (expected <= {max})")]
  InvalidMaxKeyFrameInterval {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Tile columns is invalid.
  #[error("invalid tile cols {0} (expected power of 2)")]
  InvalidTileCols(usize),
  /// Tile rows is invalid.
  #[error("invalid tile rows {0} (expected power of 2)")]
  InvalidTileRows(usize),
  /// Framerate numerator is invalid.
  #[error("invalid framerate numerator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateNum {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Framerate denominator is invalid.
  #[error("invalid framerate denominator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateDen {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Reservoir frame delay is invalid.
  #[error("invalid reservoir frame delay {0} (expected >= 12, <= 131072)")]
  InvalidReservoirFrameDelay(i32),
  /// Reservoir frame delay is invalid.
  #[error(
    "invalid switch frame interval {0} (must only be used with low latency mode)"
  )]
  InvalidSwitchFrameInterval(u64),

  /// An option unsupported in still picture mode was enabled along with it.
  #[error("invalid option {0} specified with still picture mode")]
  InvalidOptionWithStillPicture(&'static str),

  /// The rate control needs a target bitrate in order to produce results
  #[error("The rate control requires a target bitrate")]
  TargetBitrateNeeded,

  /// The configuration
  #[error("Mismatch in the rate control configuration")]
  RateControlConfigurationMismatch,

  /// The color configuration mismatches AV1 constraints.
  #[error("Mismatch in the color configuration")]
  ColorConfigurationMismatch,
}

/// Contains the encoder configuration.
#[derive(Clone, Debug, Default)]
pub struct Config {
  /// Settings which impact the produced bitstream.
  pub(crate) enc: EncoderConfig,
  /// Rate control configuration
  pub(crate) rate_control: RateControlConfig,
  /// The number of threads in the threadpool.
  pub(crate) threads: usize,
  /// Shared thread pool
  pub(crate) pool: Option<Arc<ThreadPool>>,
}

impl Config {
  /// Create a default configuration
  ///
  /// same as Default::default()
  pub fn new() -> Self {
    Config::default()
  }

  /// Set the encoder configuration
  ///
  /// EncoderConfig contains the settings impacting the
  /// codec features used in the produced bitstream.
  pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
    self.enc = enc;
    self
  }

  /// Set the number of workers in the threadpool
  ///
  /// The threadpool is shared across all the different parallel
  /// components in the encoder.
  pub fn with_threads(mut self, threads: usize) -> Self {
    self.threads = threads;
    self
  }

  /// Set the rate control configuration
  ///
  /// The default configuration is single pass
  pub fn with_rate_control(mut self, rate_control: RateControlConfig) -> Self {
    self.rate_control = rate_control;
    self
  }

  #[cfg(feature = "unstable")]
  /// Use the provided threadpool
  pub fn with_thread_pool(mut self, pool: Arc<ThreadPool>) -> Self {
    self.pool = Some(pool);
    self
  }
}

fn check_tile_log2(n: usize) -> bool {
  let tile_log2 = TilingInfo::tile_log2(1, n);
  if tile_log2.is_none() {
    return false;
  }
  let tile_log2 = tile_log2.unwrap();

  ((1 << tile_log2) - n) == 0 || n == 0
}

impl Config {
  pub(crate) fn new_inner<T: Pixel>(
    &self,
  ) -> Result<ContextInner<T>, InvalidConfig> {
    assert!(
      8 * std::mem::size_of::<T>() >= self.enc.bit_depth,
      "The Pixel u{} does not match the Config bit_depth {}",
      8 * std::mem::size_of::<T>(),
      self.enc.bit_depth
    );

    self.validate()?;

    // Because we don't have a FrameInvariants yet,
    // this is the only way to get the CpuFeatureLevel in use.
    // Since we only call this once, this shouldn't cause
    // performance issues.
    info!("CPU Feature Level: {}", CpuFeatureLevel::default());

    let mut config = self.enc;
    config.set_key_frame_interval(
      config.min_key_frame_interval,
      config.max_key_frame_interval,
    );

    // FIXME: inter unsupported with 4:2:2 and 4:4:4 chroma sampling
    let chroma_sampling = config.chroma_sampling;

    // FIXME: tx partition for intra not supported for chroma 422
    if chroma_sampling == ChromaSampling::Cs422 {
      config.speed_settings.rdo_tx_decision = false;
    }

    let mut inner = ContextInner::new(&config);

    if self.rate_control.emit_pass_data {
      let params = inner.rc_state.get_twopass_out_params(&inner, 0);
      inner.rc_state.init_first_pass(params.pass1_log_base_q);
    }

    if let Some(ref s) = self.rate_control.summary {
      inner.rc_state.init_second_pass();
      inner.rc_state.setup_second_pass(s);
    }

    Ok(inner)
  }
  /// Creates a [`Context`] with this configuration.
  ///
  /// # Examples
  ///
  /// ```
  /// use rav1e::prelude::*;
  ///
  /// # fn main() -> Result<(), InvalidConfig> {
  /// let cfg = Config::default();
  /// let ctx: Context<u8> = cfg.new_context()?;
  /// # Ok(())
  /// # }
  /// ```
  ///
  /// [`Context`]: struct.Context.html
  pub fn new_context<T: Pixel>(&self) -> Result<Context<T>, InvalidConfig> {
    let inner = self.new_inner()?;
    let config = *inner.config;
    let pool = if let Some(ref p) = self.pool {
      p.clone()
    } else {
      let pool =
        ThreadPoolBuilder::new().num_threads(self.threads).build().unwrap();
      Arc::new(pool)
    };

    Ok(Context { is_flushing: false, inner, pool, config })
  }

  /// Validates the configuration.
  pub fn validate(&self) -> Result<(), InvalidConfig> {
    use InvalidConfig::*;

    let config = &self.enc;

    if (config.still_picture && config.width < 1)
      || (!config.still_picture && config.width < 16)
      || config.width > u16::max_value() as usize
    {
      return Err(InvalidWidth(config.width));
    }
    if (config.still_picture && config.height < 1)
      || (!config.still_picture && config.height < 16)
      || config.height > u16::max_value() as usize
    {
      return Err(InvalidHeight(config.height));
    }

    if config.sample_aspect_ratio.num == 0 {
      return Err(InvalidAspectRatioNum(
        config.sample_aspect_ratio.num as usize,
      ));
    }
    if config.sample_aspect_ratio.den == 0 {
      return Err(InvalidAspectRatioDen(
        config.sample_aspect_ratio.den as usize,
      ));
    }

    let (render_width, render_height) = config.render_size();
    if render_width == 0 || render_width > u16::max_value() as usize {
      return Err(InvalidRenderWidth(render_width));
    }
    if render_height == 0 || render_height > u16::max_value() as usize {
      return Err(InvalidRenderHeight(render_height));
    }

    if config.rdo_lookahead_frames > MAX_RDO_LOOKAHEAD_FRAMES
      || config.rdo_lookahead_frames < 1
    {
      return Err(InvalidRdoLookaheadFrames {
        actual: config.rdo_lookahead_frames,
        max: MAX_RDO_LOOKAHEAD_FRAMES,
        min: 1,
      });
    }
    if config.max_key_frame_interval > MAX_MAX_KEY_FRAME_INTERVAL {
      return Err(InvalidMaxKeyFrameInterval {
        actual: config.max_key_frame_interval,
        max: MAX_MAX_KEY_FRAME_INTERVAL,
      });
    }

    if !check_tile_log2(config.tile_cols) {
      return Err(InvalidTileCols(config.tile_cols));
    }
    if !check_tile_log2(config.tile_rows) {
      return Err(InvalidTileRows(config.tile_rows));
    }

    if config.time_base.num == 0
      || config.time_base.num > u32::max_value() as u64
    {
      return Err(InvalidFrameRateNum {
        actual: config.time_base.num,
        max: u32::max_value() as u64,
      });
    }
    if config.time_base.den == 0
      || config.time_base.den > u32::max_value() as u64
    {
      return Err(InvalidFrameRateDen {
        actual: config.time_base.den,
        max: u32::max_value() as u64,
      });
    }

    if let Some(delay) = config.reservoir_frame_delay {
      if !(12..=131_072).contains(&delay) {
        return Err(InvalidReservoirFrameDelay(delay));
      }
    }

    if config.switch_frame_interval > 0 && !config.low_latency {
      return Err(InvalidSwitchFrameInterval(config.switch_frame_interval));
    }

    if config.enable_timing_info && config.still_picture {
      return Err(InvalidOptionWithStillPicture("enable_timing_info"));
    }

    // <https://aomediacodec.github.io/av1-spec/#color-config-syntax>
    if let Some(color_description) = config.color_description {
      if config.chroma_sampling != ChromaSampling::Cs400
        && color_description.is_srgb_triple()
      {
        if config.pixel_range != PixelRange::Full {
          return Err(ColorConfigurationMismatch);
        }
        if config.chroma_sampling != ChromaSampling::Cs444 {
          return Err(ColorConfigurationMismatch);
        }
      }
    }

    // TODO: add more validation
    let rc = &self.rate_control;

    if (rc.emit_pass_data || rc.summary.is_some()) && config.bitrate == 0 {
      return Err(TargetBitrateNeeded);
    }

    Ok(())
  }
}
<commit_msg>Document the behavior<commit_after>// Copyright (c) 2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use thiserror::Error;

use std::sync::Arc;

use crate::api::{ChromaSampling, Context, ContextInner, PixelRange};
use crate::cpu_features::CpuFeatureLevel;
use crate::rayon::{ThreadPool, ThreadPoolBuilder};
use crate::tiling::TilingInfo;
use crate::util::Pixel;

mod encoder;
pub use encoder::*;

mod rate;
pub use rate::Error as RateControlError;
pub use rate::{RateControlConfig, RateControlSummary};

mod speedsettings;
pub use speedsettings::*;

/// Enumeration of possible invalid configuration errors.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
#[non_exhaustive]
pub enum InvalidConfig {
  /// The width is invalid.
  #[error("invalid width {0} (expected >= 16, <= 65535)")]
  InvalidWidth(usize),
  /// The height is invalid.
  #[error("invalid height {0} (expected >= 16, <= 65535)")]
  InvalidHeight(usize),
  /// Aspect ratio numerator is invalid.
  #[error("invalid aspect ratio numerator {0} (expected > 0)")]
  InvalidAspectRatioNum(usize),
  /// Aspect ratio denominator is invalid.
  #[error("invalid aspect ratio denominator {0} (expected > 0)")]
  InvalidAspectRatioDen(usize),
  /// The render width (width adjusted based on the aspect ratio) is invalid.
  #[error("invalid render width {0} (expected >= 1, <= 65535")]
  InvalidRenderWidth(usize),
  /// The render height (height adjusted based on the aspect ratio) is invalid.
  #[error("invalid render height {0} (expected >= 1, <= 65535")]
  InvalidRenderHeight(usize),
  /// RDO lookahead frame count is invalid.
  #[error(
    "invalid rdo lookahead frames {actual} (expected <= {max} and >= {min})"
  )]
  InvalidRdoLookaheadFrames {
    /// The actual value.
    actual: usize,
    /// The maximal supported value.
    max: usize,
    /// The minimal supported value.
    min: usize,
  },
  /// Maximal keyframe interval is invalid.
  #[error("invalid max keyframe interval {actual} (expected <= {max})")]
  InvalidMaxKeyFrameInterval {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Tile columns is invalid.
  #[error("invalid tile cols {0} (expected power of 2)")]
  InvalidTileCols(usize),
  /// Tile rows is invalid.
  #[error("invalid tile rows {0} (expected power of 2)")]
  InvalidTileRows(usize),
  /// Framerate numerator is invalid.
  #[error("invalid framerate numerator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateNum {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Framerate denominator is invalid.
  #[error("invalid framerate denominator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateDen {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Reservoir frame delay is invalid.
  #[error("invalid reservoir frame delay {0} (expected >= 12, <= 131072)")]
  InvalidReservoirFrameDelay(i32),
  /// Reservoir frame delay is invalid.
  #[error(
    "invalid switch frame interval {0} (must only be used with low latency mode)"
  )]
  InvalidSwitchFrameInterval(u64),

  /// An option unsupported in still picture mode was enabled along with it.
  #[error("invalid option {0} specified with still picture mode")]
  InvalidOptionWithStillPicture(&'static str),

  /// The rate control needs a target bitrate in order to produce results
  #[error("The rate control requires a target bitrate")]
  TargetBitrateNeeded,

  /// The configuration
  #[error("Mismatch in the rate control configuration")]
  RateControlConfigurationMismatch,

  /// The color configuration mismatches AV1 constraints.
  #[error("Mismatch in the color configuration")]
  ColorConfigurationMismatch,
}

/// Contains the encoder configuration.
#[derive(Clone, Debug, Default)]
pub struct Config {
  /// Settings which impact the produced bitstream.
  pub(crate) enc: EncoderConfig,
  /// Rate control configuration
  pub(crate) rate_control: RateControlConfig,
  /// The number of threads in the threadpool.
  pub(crate) threads: usize,
  /// Shared thread pool
  pub(crate) pool: Option<Arc<ThreadPool>>,
}

impl Config {
  /// Create a default configuration
  ///
  /// same as Default::default()
  pub fn new() -> Self {
    Config::default()
  }

  /// Set the encoder configuration
  ///
  /// EncoderConfig contains the settings impacting the
  /// codec features used in the produced bitstream.
  pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
    self.enc = enc;
    self
  }

  /// Set the number of workers in the threadpool
  ///
  /// The threadpool is shared across all the different parallel
  /// components in the encoder.
  ///
  /// If it is left unset `new_context()` will create a new default one
  /// `new_*_channel()` will use the current global threadpool if present.
  pub fn with_threads(mut self, threads: usize) -> Self {
    self.threads = threads;
    self
  }

  /// Set the rate control configuration
  ///
  /// The default configuration is single pass
  pub fn with_rate_control(mut self, rate_control: RateControlConfig) -> Self {
    self.rate_control = rate_control;
    self
  }

  #[cfg(feature = "unstable")]
  /// Use the provided threadpool
  ///
  /// It takes priority over `with_threads()`
  pub fn with_thread_pool(mut self, pool: Arc<ThreadPool>) -> Self {
    self.pool = Some(pool);
    self
  }
}

fn check_tile_log2(n: usize) -> bool {
  let tile_log2 = TilingInfo::tile_log2(1, n);
  if tile_log2.is_none() {
    return false;
  }
  let tile_log2 = tile_log2.unwrap();

  ((1 << tile_log2) - n) == 0 || n == 0
}

impl Config {
  pub(crate) fn new_inner<T: Pixel>(
    &self,
  ) -> Result<ContextInner<T>, InvalidConfig> {
    assert!(
      8 * std::mem::size_of::<T>() >= self.enc.bit_depth,
      "The Pixel u{} does not match the Config bit_depth {}",
      8 * std::mem::size_of::<T>(),
      self.enc.bit_depth
    );

    self.validate()?;

    // Because we don't have a FrameInvariants yet,
    // this is the only way to get the CpuFeatureLevel in use.
    // Since we only call this once, this shouldn't cause
    // performance issues.
    info!("CPU Feature Level: {}", CpuFeatureLevel::default());

    let mut config = self.enc;
    config.set_key_frame_interval(
      config.min_key_frame_interval,
      config.max_key_frame_interval,
    );

    // FIXME: inter unsupported with 4:2:2 and 4:4:4 chroma sampling
    let chroma_sampling = config.chroma_sampling;

    // FIXME: tx partition for intra not supported for chroma 422
    if chroma_sampling == ChromaSampling::Cs422 {
      config.speed_settings.rdo_tx_decision = false;
    }

    let mut inner = ContextInner::new(&config);

    if self.rate_control.emit_pass_data {
      let params = inner.rc_state.get_twopass_out_params(&inner, 0);
      inner.rc_state.init_first_pass(params.pass1_log_base_q);
    }

    if let Some(ref s) = self.rate_control.summary {
      inner.rc_state.init_second_pass();
      inner.rc_state.setup_second_pass(s);
    }

    Ok(inner)
  }
  /// Creates a [`Context`] with this configuration.
  ///
  /// # Examples
  ///
  /// ```
  /// use rav1e::prelude::*;
  ///
  /// # fn main() -> Result<(), InvalidConfig> {
  /// let cfg = Config::default();
  /// let ctx: Context<u8> = cfg.new_context()?;
  /// # Ok(())
  /// # }
  /// ```
  ///
  /// [`Context`]: struct.Context.html
  pub fn new_context<T: Pixel>(&self) -> Result<Context<T>, InvalidConfig> {
    let inner = self.new_inner()?;
    let config = *inner.config;
    let pool = if let Some(ref p) = self.pool {
      p.clone()
    } else {
      let pool =
        ThreadPoolBuilder::new().num_threads(self.threads).build().unwrap();
      Arc::new(pool)
    };

    Ok(Context { is_flushing: false, inner, pool, config })
  }

  /// Validates the configuration.
  pub fn validate(&self) -> Result<(), InvalidConfig> {
    use InvalidConfig::*;

    let config = &self.enc;

    if (config.still_picture && config.width < 1)
      || (!config.still_picture && config.width < 16)
      || config.width > u16::max_value() as usize
    {
      return Err(InvalidWidth(config.width));
    }
    if (config.still_picture && config.height < 1)
      || (!config.still_picture && config.height < 16)
      || config.height > u16::max_value() as usize
    {
      return Err(InvalidHeight(config.height));
    }

    if config.sample_aspect_ratio.num == 0 {
      return Err(InvalidAspectRatioNum(
        config.sample_aspect_ratio.num as usize,
      ));
    }
    if config.sample_aspect_ratio.den == 0 {
      return Err(InvalidAspectRatioDen(
        config.sample_aspect_ratio.den as usize,
      ));
    }

    let (render_width, render_height) = config.render_size();
    if render_width == 0 || render_width > u16::max_value() as usize {
      return Err(InvalidRenderWidth(render_width));
    }
    if render_height == 0 || render_height > u16::max_value() as usize {
      return Err(InvalidRenderHeight(render_height));
    }

    if config.rdo_lookahead_frames > MAX_RDO_LOOKAHEAD_FRAMES
      || config.rdo_lookahead_frames < 1
    {
      return Err(InvalidRdoLookaheadFrames {
        actual: config.rdo_lookahead_frames,
        max: MAX_RDO_LOOKAHEAD_FRAMES,
        min: 1,
      });
    }
    if config.max_key_frame_interval > MAX_MAX_KEY_FRAME_INTERVAL {
      return Err(InvalidMaxKeyFrameInterval {
        actual: config.max_key_frame_interval,
        max: MAX_MAX_KEY_FRAME_INTERVAL,
      });
    }

    if !check_tile_log2(config.tile_cols) {
      return Err(InvalidTileCols(config.tile_cols));
    }
    if !check_tile_log2(config.tile_rows) {
      return Err(InvalidTileRows(config.tile_rows));
    }

    if config.time_base.num == 0
      || config.time_base.num > u32::max_value() as u64
    {
      return Err(InvalidFrameRateNum {
        actual: config.time_base.num,
        max: u32::max_value() as u64,
      });
    }
    if config.time_base.den == 0
      || config.time_base.den > u32::max_value() as u64
    {
      return Err(InvalidFrameRateDen {
        actual: config.time_base.den,
        max: u32::max_value() as u64,
      });
    }

    if let Some(delay) = config.reservoir_frame_delay {
      if !(12..=131_072).contains(&delay) {
        return Err(InvalidReservoirFrameDelay(delay));
      }
    }

    if config.switch_frame_interval > 0 && !config.low_latency {
      return Err(InvalidSwitchFrameInterval(config.switch_frame_interval));
    }

    if config.enable_timing_info && config.still_picture {
      return Err(InvalidOptionWithStillPicture("enable_timing_info"));
    }

    // <https://aomediacodec.github.io/av1-spec/#color-config-syntax>
    if let Some(color_description) = config.color_description {
      if config.chroma_sampling != ChromaSampling::Cs400
        && color_description.is_srgb_triple()
      {
        if config.pixel_range != PixelRange::Full {
          return Err(ColorConfigurationMismatch);
        }
        if config.chroma_sampling != ChromaSampling::Cs444 {
          return Err(ColorConfigurationMismatch);
        }
      }
    }

    // TODO: add more validation
    let rc = &self.rate_control;

    if (rc.emit_pass_data || rc.summary.is_some()) && config.bitrate == 0 {
      return Err(TargetBitrateNeeded);
    }

    Ok(())
  }
}
<|endoftext|>


<commit_before>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os};
use std::io::buffered::BufferedReader;
use std::io::stdin;
use extra::getopts;

struct Shell {
    cmd_prompt: ~str,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        loop {
            print(self.cmd_prompt);
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            self.run_cmd(program, argv);
        }
    }

    fn run_cmd(&mut self, program: &str, argv: &[~str]) {
        if self.cmd_exists(program) {
            run::process_status(program, argv);
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn cmd_exists(&mut self, cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                                                Some(cmd_str) => {cmd_str.to_owned()},
                                                None => {~""}
                                              };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::new("").run_cmdline(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<commit_msg>Initial<commit_after>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;
extern mod native;

use std::{io, run, os};
use std::io::buffered::BufferedReader;
use std::io::stdio::stdin;
use std::run::{Process, ProcessOptions};
use std::io::{File, Open, Read, Write};
use std::path::posix::Path;
use std::libc::{fileno, fopen, pid_t, c_int};
use std::libc;
use std::io::signal::{Listener, Interrupt};
use extra::getopts;

struct Shell {
    cmd_prompt: ~str,
    working_directory: Path,
    history: ~[~str],
    output_redirects: ~[~str]
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            working_directory: Path::new(os::getcwd()),
            history: ~[],
            output_redirects: ~[]
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());
        // println!("PWD: {}", self.working_directory.display() );
        loop {
            print(self.cmd_prompt);
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();

            // Add this command to the history
            self.add_to_history(cmd_line.clone());

            // for &x in ins.iter() {
            //     println!("Elem {}", x);
            // }

            // Check to see if there are any output redirects
            // if cmd_line.contains(">") {
            //     let mut split : ~[&str] = cmd_line.split('>').collect();
            //     split.remove(0);
            //     let mut split2 = ~[];
            //     for x in split.iter() {
            //         split2.push(x.to_owned());
            //     }
            //     self.output_redirects = split2;
            // }

            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn get_output_redirects(&mut self, cmd_line: &str) -> ~[&str] {
        /*
        *   Get the output redirect filenames designated by >
        */
        let mut output_files : ~[&str] = ~[];
        if cmd_line.contains(">") {
            let mut files: ~[&str] = cmd_line.split('>').collect();
            files.remove(0);
            output_files = files.clone();
        }
        return output_files;
    }

    fn get_input_redirect(&mut self, cmd_line: &str) -> ~[&str] {
        let mut input_files : ~[&str] = ~[];
        if cmd_line.contains("<") {
            let mut files: ~[&str] = cmd_line.split('<').collect();
            files.remove(0);
            for elem in files.iter() {
                if elem.contains(">") {
                    let mut outs: ~[&str] = elem.split('>').collect();
                    let inputf : &str = outs[0];
                    input_files.push(inputf.trim());
                } else {
                    input_files.push(elem.trim());
                }
            }
        }
        input_files
    }

    fn get_pipes(&mut self, cmd_line: &str) -> ~[&str] {
        let mut pipes : ~[&str] = ~[];
        if cmd_line.contains("|") {
            let mut temp : ~[&str] = cmd_line.split('|').collect();
            temp.remove(0);
            pipes = temp.clone();
        }
        pipes
    }

    fn add_to_history(&mut self, cmd_line: ~str) {
        if cmd_line != ~"" {
            self.history.push(cmd_line);
        }
    }

    fn print_history(&mut self) {
        let mut count = 0;
        for x in self.history.iter() {
            println!("  {} {}",count, *x);
            count += 1;
        }
    }

    fn should_run_in_background(&mut self, cmd_line: &str) -> bool {
        // Check to see if we should be running in the background
        let mut run_in_background : bool = false;
        if cmd_line.contains("&") {
            run_in_background = true;
        }
        run_in_background
    }

    fn change_directory(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 0 {
            // Remove the "cd" argument
            argv.remove(0);
            if argv.len() > 0 {
                self.working_directory.push(argv.remove(0));
            } else {
                self.working_directory = os::homedir().unwrap()
            }
            // println!("Changing directory to: {}", self.working_directory.display());
            os::change_dir(&self.working_directory);
            // println!("CWD: {}", self.working_directory.display());
        }
    }

    fn run_cmdline_in_background(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        // Remove & from argument list
        let mut ind = -1;
        let mut count = 0;
        for x in argv.iter() {
            if *x == ~"&" {
                ind = count;
            }
            count += 1;
        }
        argv.remove(ind);

        // let (port, chan): (Port<int>, Chan<int>) = Chan::new();

        println("[1]");

        if( argv[0] == ~"cd" ) {
            if argv.len() > 0 {
                argv.remove(0);
                if argv.len() > 0 {
                    self.working_directory.push(argv.remove(0));
                } else {
                    self.working_directory = os::homedir().unwrap()
                }
                let working_dir = self.working_directory.clone();
                spawn(proc() {
                    os::change_dir(&working_dir);
                } );
            }
        } else if( argv[0] == ~"history" ) {
            let hist : ~[~str] = self.history.clone();

            spawn( proc() {
                let mut count = 0;
                for x in hist.iter() {
                    println!("  {} {}",count, *x);
                    count += 1;
                }});
        } else if argv.len() > 0 {
            let prog: ~str = argv.remove(0);
            let args: ~[~str] = argv.clone();
            // fn call(f: |~str, ~[~str]|) { |prog, args| println!("Program={}, args={}", prog, args); }
            // let closure = |prog, args| println!("Program={}, args={}", prog, args);

            spawn( proc() {

                fn cmd_exists(cmd_path: &str) -> bool {
                    let ret = run::process_output("which", [cmd_path.to_owned()]);
                    return ret.expect("exit code error.").status.success();
                }

                for x in args.iter() {
                    print(x.clone())
                }
                // println("");
                if cmd_exists(prog) {
                    run::process_status(prog, args);
                } else {
                    println!("{:s}: command not found", prog);
                }
                // chan.send(1);
            });
        }
        // println!("[{:d}] Done",port.recv());

        // println("Done Executing Task");
    }

    fn run_cmdline(&mut self, cmd_line: &str) {

        // Check to see if we should be running in the background
        let run_in_background : bool = self.should_run_in_background(cmd_line.clone());
        let output_redirects = self.get_output_redirects(cmd_line);
        let input_redirects : ~[&str] = self.get_input_redirect(cmd_line);

        if run_in_background {
            self.run_cmdline_in_background(cmd_line.clone());
        }
        else if input_redirects.len() > 0 {
            let temp_split : ~[&str] = cmd_line.split('<').collect();
            let cmd_line_sans_redirects = temp_split[0];
            let mut argv: ~[~str] =
                cmd_line_sans_redirects.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

            if argv.len() > 0 {
                let program: ~str = argv.remove(0);
                self.run_cmd_with_input_redirect(program, argv, input_redirects);
            }
        }
        else if output_redirects.len() > 0 {
            // Need to first create the output files if they do not exist
            for &file in output_redirects.iter() {
                // Trim the filename to get rid of weird whitespace bugs
                let path: Path = Path::new(file.trim());
                if !path.is_file() {
                    File::create(&path);
                }
            }

            // Get the program and args.. We do not want the things after > as that will cause errors
            let temp_split : ~[&str] = cmd_line.split('>').collect();
            let cmd_line_sans_redirects = temp_split[0];
            let mut argv: ~[~str] =
                cmd_line_sans_redirects.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

            if argv.len() > 0 {
                let program: ~str = argv.remove(0);
                self.run_cmd_with_output_redirect(program, argv, output_redirects);
            }
        }
        else {
            let mut argv: ~[~str] =
                cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

            if( argv[0] == ~"cd" ) {
                self.change_directory(cmd_line);
            } else if( argv[0] == ~"history" ) {
                self.print_history();
            } else if argv.len() > 0 {
                let program: ~str = argv.remove(0);
                self.run_cmd(program, argv);
            }
        }
    }

    fn run_cmd_with_output_redirect(&mut self, program: &str, argv: &[~str], output_files: ~[&str]) {
        if self.cmd_exists(program) {
            let write_to_file : &str = output_files[output_files.len()-1].trim();
            let f = match native::io::file::open(&write_to_file.to_c_str(), Open, Write) {
                Ok(f)   => f,
                Err(e)  => { fail!("Could not open output file") }
            };
            let file_descriptor = f.fd();

            let mut process = run::Process::new(program, argv, run::ProcessOptions {
                env: None,
                dir: None,
                in_fd: Some(unsafe { libc::dup(libc::STDIN_FILENO) }),
                out_fd: Some(file_descriptor),
                err_fd: Some(unsafe { libc::dup(libc::STDERR_FILENO) })
            });

            // process.finish();
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn run_cmd_with_input_redirect(&mut self, program: &str, argv: &[~str], input_files: ~[&str]) {
        if self.cmd_exists(program) {
            let read_from_file : &str = input_files[input_files.len()-1].trim();
            let f = match native::io::file::open(&read_from_file.to_c_str(), Open, Read) {
                Ok(f)   => f,
                Err(e)  => fail!("Problem opening file")
            };
            let file_descriptor = f.fd();

            let mut process = run::Process::new(program, argv, run::ProcessOptions {
                env: None,
                dir: None,
                in_fd: Some(file_descriptor),
                out_fd: Some(unsafe { libc::dup(libc::STDOUT_FILENO) }),
                err_fd: Some(unsafe { libc::dup(libc::STDERR_FILENO) })
            });
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn run_cmd(&mut self, program: &str, argv: &[~str]) {
        // println!("Program: {:s}", program);
        // print("Has args: ");

        // for x in argv.iter() {
        //     print(x.clone())
        // }

        // println("");
        if self.cmd_exists(program) {
            run::process_status(program, argv);
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn cmd_exists(&mut self, cmd_path: &str) -> bool {
        // println!("Checking if command {:s} exists", cmd_path);
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                                                Some(cmd_str) => {cmd_str.to_owned()},
                                                None => {~""}
                                              };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn handle_interrupt() {
    let mut l = Listener::new();
    l.register(Interrupt);

    let l_copy = l;
    spawn(proc() {
        loop {
            match l_copy.port.recv() {
                Interrupt => continue,
                _         => ()
            }
        }
    });
}

fn main() {
    handle_interrupt();

    let opt_cmd_line = get_cmdline_from_args();
    match opt_cmd_line {
        Some(cmd_line) => { println!("Command: {:s}", cmd_line); Shell::new("").run_cmdline(cmd_line) },
        None           => { Shell::new("gash > ").run() }
    }
}
<|endoftext|>


<commit_before>use engine::Game;
use gui::Console;
use gui::screens::{self, Screen, ScreenChange};
use util::FirstLast;
use util::units::Size;

#[derive(PartialEq)]
pub enum State {
    Running,
    Exited
}

pub struct GUI {
    pub console: Console,
    pub screens: Vec<Box<Screen>>,
    game: Game,
    state: State,
}

impl GUI {
    pub fn new(game: Game, console: Console) -> GUI {
        GUI {
            game: game,
            console: console,
            screens: vec![screens::MenuScreen::new()],
            state: State::Running,
        }
    }

    pub fn size(&self) -> Size {
        self.console.size()
    }

    pub fn run(&mut self) {
        while !self.console.window_closed() && !self.exited() {
            self.render();
            self.update();
            self.handle_input();
        }
    }

    fn handle_input(&mut self) {
        let outcome = self.screens.first().input(&mut self.game, &mut self.console);
        self.update_screens(outcome);
    }

    fn update(&mut self) {
        let outcome = self.screens.first().update(&mut self.game, &mut self.console);
        self.update_screens(outcome);
    }

    fn render(&mut self) {
        self.console.clear();
        self.screens.first().render(&mut self.game, &mut self.console);
        self.console.flush();
    }

    fn update_screens(&mut self, outcome: Option<ScreenChange>) {
        match outcome {
            Some(ScreenChange::AddScreen(screen)) => { self.screens.insert(0, screen) },
            Some(ScreenChange::RemoveScreen) => { self.screens.remove(0); }
            Some(ScreenChange::ExitGame) => { self.state = State::Exited },
            None => {}
        }
    }

    pub fn exited(&self) -> bool {
        self.state == State::Exited
    }
}
<commit_msg>Extend exited() method to cover window closing<commit_after>use engine::Game;
use gui::Console;
use gui::screens::{self, Screen, ScreenChange};
use util::FirstLast;
use util::units::Size;

#[derive(PartialEq)]
pub enum State {
    Running,
    Exited
}

pub struct GUI {
    pub console: Console,
    pub screens: Vec<Box<Screen>>,
    game: Game,
    state: State,
}

impl GUI {
    pub fn new(game: Game, console: Console) -> GUI {
        GUI {
            game: game,
            console: console,
            screens: vec![screens::MenuScreen::new()],
            state: State::Running,
        }
    }

    pub fn size(&self) -> Size {
        self.console.size()
    }

    pub fn run(&mut self) {
        while !self.exited() {
            self.render();
            self.update();
            self.handle_input();
        }
    }

    fn handle_input(&mut self) {
        let outcome = self.screens.first().input(&mut self.game, &mut self.console);
        self.update_screens(outcome);
    }

    fn update(&mut self) {
        let outcome = self.screens.first().update(&mut self.game, &mut self.console);
        self.update_screens(outcome);
    }

    fn render(&mut self) {
        self.console.clear();
        self.screens.first().render(&mut self.game, &mut self.console);
        self.console.flush();
    }

    fn update_screens(&mut self, outcome: Option<ScreenChange>) {
        match outcome {
            Some(ScreenChange::AddScreen(screen)) => { self.screens.insert(0, screen) },
            Some(ScreenChange::RemoveScreen) => { self.screens.remove(0); }
            Some(ScreenChange::ExitGame) => { self.state = State::Exited },
            None => {}
        }
    }

    pub fn exited(&self) -> bool {
        self.console.window_closed() || self.state == State::Exited
    }
}
<|endoftext|>


<commit_before>use getopts::Options;
use std::env::Args;

pub struct Arguments {
    pub program_name: String,
    pub has_month: bool,
    pub month: usize,
    pub has_year: bool,
    pub year: usize,
    pub date_only: bool,
    pub exit: bool,
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&*brief));
}

pub fn parse_args(arguments: Args) -> Arguments {
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("d", "date", "only print today's date");
    opts.optopt("y", "year", "only print the year's calendar", "YEAR");
    opts.optopt("m", "month", "only print the month's calendar", "MONTH");

    let matches = match opts.parse(arguments) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    let mut args = Arguments {
        program_name: "".to_string(),
        date_only: matches.opt_present("d"),
        has_month: matches.opt_present("m"),
        month: match matches.opt_str("m") {
            Some(m) => match m.parse::<usize>() {
                Ok(mm) => mm,
                Err(_) => 0,
            },
            None => 0,
        },
        has_year: matches.opt_present("y"),
        year: match matches.opt_str("y") {
            Some(y) => match y.parse::<usize>() {
                Ok(mm) => mm,
                Err(_) => 0,
            },
            None => 0,
        },
        exit: matches.opt_present("h"),
    };

    if args.exit {
        print_usage(&*args.program_name, opts);
    }

    args
}

<commit_msg>Remove unneeded immutability<commit_after>use getopts::Options;
use std::env::Args;

pub struct Arguments {
    pub program_name: String,
    pub has_month: bool,
    pub month: usize,
    pub has_year: bool,
    pub year: usize,
    pub date_only: bool,
    pub exit: bool,
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&*brief));
}

pub fn parse_args(arguments: Args) -> Arguments {
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("d", "date", "only print today's date");
    opts.optopt("y", "year", "only print the year's calendar", "YEAR");
    opts.optopt("m", "month", "only print the month's calendar", "MONTH");

    let matches = match opts.parse(arguments) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    let args = Arguments {
        program_name: "".to_string(),
        date_only: matches.opt_present("d"),
        has_month: matches.opt_present("m"),
        month: match matches.opt_str("m") {
            Some(m) => match m.parse::<usize>() {
                Ok(mm) => mm,
                Err(_) => 0,
            },
            None => 0,
        },
        has_year: matches.opt_present("y"),
        year: match matches.opt_str("y") {
            Some(y) => match y.parse::<usize>() {
                Ok(mm) => mm,
                Err(_) => 0,
            },
            None => 0,
        },
        exit: matches.opt_present("h"),
    };

    if args.exit {
        print_usage(&*args.program_name, opts);
    }

    args
}

<|endoftext|>


<commit_before>/*
 * Rust BareBones OS
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * unwind.rs
 * - Stack unwind (panic) handling
 *
 * == LICENCE ==
 * This code has been put into the public domain, there are no restrictions on
 * its use, and the author takes no liability.
 */

#[lang="panic_fmt"]
#[no_mangle]
pub fn rust_begin_unwind(args: ::core::fmt::Arguments, file: &str, line: usize) -> !
{
	// 'args' will print to the formatted string passed to panic!
	log!("file='{}', line={} :: {}", file, line, args);
	loop {}
}

#[lang="stack_exhausted"]
#[no_mangle]
pub fn __morestack() -> !
{
	loop {}
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
pub enum _Unwind_Reason_Code
{
	_URC_NO_REASON = 0,
	_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
	_URC_FATAL_PHASE2_ERROR = 2,
	_URC_FATAL_PHASE1_ERROR = 3,
	_URC_NORMAL_STOP = 4,
	_URC_END_OF_STACK = 5,
	_URC_HANDLER_FOUND = 6,
	_URC_INSTALL_CONTEXT = 7,
	_URC_CONTINUE_UNWIND = 8,
}

#[allow(non_camel_case_types)]
#[derive(Clone,Copy)]
pub struct _Unwind_Context;

#[allow(non_camel_case_types)]
pub type _Unwind_Action = u32;
static _UA_SEARCH_PHASE: _Unwind_Action = 1;

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
#[allow(raw_pointer_derive)]
pub struct _Unwind_Exception
{
	exception_class: u64,
	exception_cleanup: fn(_Unwind_Reason_Code,*const _Unwind_Exception),
	private: [u64; 2],
}

#[lang="eh_personality"]
#[no_mangle]
pub fn rust_eh_personality(
	_version: isize, _actions: _Unwind_Action, _exception_class: u64,
	_exception_object: &_Unwind_Exception, _context: &_Unwind_Context
	) -> _Unwind_Reason_Code
{
	loop{}
}

#[no_mangle]
#[allow(non_snake_case)]
pub fn _Unwind_Resume()
{
	loop{}
}

<commit_msg>Fixed bad signature for lang="panic_fmt", thanks @tsurai<commit_after>/*
 * Rust BareBones OS
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * unwind.rs
 * - Stack unwind (panic) handling
 *
 * == LICENCE ==
 * This code has been put into the public domain, there are no restrictions on
 * its use, and the author takes no liability.
 */

#[lang="panic_fmt"]
#[no_mangle]
pub extern "C" fn rust_begin_unwind(args: ::core::fmt::Arguments, file: &str, line: usize) -> !
{
	// 'args' will print to the formatted string passed to panic!
	log!("file='{}', line={} :: {}", file, line, args);
	loop {}
}

#[lang="stack_exhausted"]
#[no_mangle]
pub fn __morestack() -> !
{
	loop {}
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
pub enum _Unwind_Reason_Code
{
	_URC_NO_REASON = 0,
	_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
	_URC_FATAL_PHASE2_ERROR = 2,
	_URC_FATAL_PHASE1_ERROR = 3,
	_URC_NORMAL_STOP = 4,
	_URC_END_OF_STACK = 5,
	_URC_HANDLER_FOUND = 6,
	_URC_INSTALL_CONTEXT = 7,
	_URC_CONTINUE_UNWIND = 8,
}

#[allow(non_camel_case_types)]
#[derive(Clone,Copy)]
pub struct _Unwind_Context;

#[allow(non_camel_case_types)]
pub type _Unwind_Action = u32;
static _UA_SEARCH_PHASE: _Unwind_Action = 1;

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
#[allow(raw_pointer_derive)]
pub struct _Unwind_Exception
{
	exception_class: u64,
	exception_cleanup: fn(_Unwind_Reason_Code,*const _Unwind_Exception),
	private: [u64; 2],
}

#[lang="eh_personality"]
#[no_mangle]
pub fn rust_eh_personality(
	_version: isize, _actions: _Unwind_Action, _exception_class: u64,
	_exception_object: &_Unwind_Exception, _context: &_Unwind_Context
	) -> _Unwind_Reason_Code
{
	loop{}
}

#[no_mangle]
#[allow(non_snake_case)]
pub fn _Unwind_Resume()
{
	loop{}
}

<|endoftext|>


<commit_before>// Parse ANSI attr code
use std::borrow::Cow;
use std::default::Default;
use std::mem;

use tuikit::prelude::*;
use vte::Perform;

/// An ANSI Parser, will parse one line at a time.
///
/// It will cache the latest attribute used, that means if an attribute affect multiple
/// lines, the parser will recognize it.
pub struct ANSIParser {
    partial_str: String,
    last_attr: Attr,

    stripped: String,
    fragments: Vec<(Attr, Cow<'static, str>)>,
}

impl Default for ANSIParser {
    fn default() -> Self {
        ANSIParser {
            partial_str: String::new(),
            last_attr: Attr::default(),

            stripped: String::new(),
            fragments: Vec::new(),
        }
    }
}

impl Perform for ANSIParser {
    fn print(&mut self, ch: char) {
        self.partial_str.push(ch);
    }

    fn execute(&mut self, byte: u8) {
        match byte {
            // put back \0 \r \n \t
            0x00 | 0x0d | 0x0A | 0x09 => self.partial_str.push(byte as char),
            // ignore all others
            _ => trace!("AnsiParser:execute ignored {:?}", byte),
        }
    }

    fn hook(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool) {
        trace!("AnsiParser:hook ignored {:?}", params);
    }

    fn put(&mut self, byte: u8) {
        trace!("AnsiParser:put ignored {:?}", byte);
    }

    fn unhook(&mut self) {
        trace!("AnsiParser:unhook ignored");
    }

    fn osc_dispatch(&mut self, params: &[&[u8]]) {
        trace!("AnsiParser:osc ignored {:?}", params);
    }

    fn csi_dispatch(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool, mode: char) {
        // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
        // Only care about graphic modes, ignore all others

        if mode != 'm' {
            trace!("ignore: params: {:?}, mode: {:?}", params, mode);
            return;
        }

        let mut attr = self.last_attr;
        let mut iter = params.into_iter();

        while let Some(&code) = iter.next() {
            match code {
                0 => attr = Attr::default(),
                1 => attr.effect |= Effect::BOLD,
                2 => attr.effect |= !Effect::BOLD,
                4 => attr.effect |= Effect::UNDERLINE,
                5 => attr.effect |= Effect::BLINK,
                7 => attr.effect |= Effect::REVERSE,
                num if num >= 30 && num <= 37 => {
                    attr.fg = Color::AnsiValue((num - 30) as u8);
                }
                38 => match iter.next() {
                    Some(2) => {
                        // ESC[ 38;2;<r>;<g>;<b> m Select RGB foreground color
                        let or = iter.next();
                        let og = iter.next();
                        let ob = iter.next();
                        if ob.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }

                        let r = *or.unwrap() as u8;
                        let g = *og.unwrap() as u8;
                        let b = *ob.unwrap() as u8;

                        attr.fg = Color::Rgb(r, g, b);
                    }
                    Some(5) => {
                        // ESC[ 38;5;<n> m Select foreground color
                        let color = iter.next();
                        if color.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }
                        attr.fg = Color::AnsiValue(*color.unwrap() as u8);
                    }
                    _ => {
                        trace!("error on parsing CSI {:?} m", params);
                    }
                },
                39 => attr.fg = Color::Default,
                num if num >= 40 && num <= 47 => {
                    attr.bg = Color::AnsiValue((num - 40) as u8);
                }
                48 => match iter.next() {
                    Some(2) => {
                        // ESC[ 48;2;<r>;<g>;<b> m Select RGB background color
                        let or = iter.next();
                        let og = iter.next();
                        let ob = iter.next();
                        if ob.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }

                        let r = *or.unwrap() as u8;
                        let g = *og.unwrap() as u8;
                        let b = *ob.unwrap() as u8;

                        attr.bg = Color::Rgb(r, g, b);
                    }
                    Some(5) => {
                        // ESC[ 48;5;<n> m Select background color
                        let color = iter.next();
                        if color.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }
                        attr.bg = Color::AnsiValue(*color.unwrap() as u8);
                    }
                    _ => {
                        trace!("ignore CSI {:?} m", params);
                    }
                },
                49 => attr.bg = Color::Default,
                _ => {
                    trace!("ignore CSI {:?} m", params);
                }
            }
        }

        self.attr_change(attr);
    }

    fn esc_dispatch(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _byte: u8) {
        // ESC characters are replaced with \[
        self.partial_str.push('"');
        self.partial_str.push('[');
    }
}

impl ANSIParser {
    /// save the partial_str into fragments with current attr
    fn save_str(&mut self) {
        if self.partial_str.is_empty() {
            return;
        }

        let string = mem::replace(&mut self.partial_str, String::new());
        self.stripped.push_str(&string);
        self.fragments.push((self.last_attr, Cow::Owned(string)));
    }

    // accept a new attr
    fn attr_change(&mut self, new_attr: Attr) {
        if new_attr == self.last_attr {
            return;
        }

        self.save_str();
        self.last_attr = new_attr;
    }

    pub fn parse_ansi(&mut self, text: &str) -> AnsiString {
        let mut statemachine = vte::Parser::new();

        for byte in text.as_bytes() {
            statemachine.advance(self, *byte);
        }
        self.save_str();

        let stripped = mem::replace(&mut self.stripped, String::new());
        let fragments = mem::replace(&mut self.fragments, Vec::new());
        AnsiString::new(stripped, fragments)
    }
}

#[derive(Clone, Debug)]
/// A String that contains ANSI state (e.g. colors)
///
/// It is internally represented as Vec<(attr, string)>
pub struct AnsiString {
    stripped: Cow<'static, str>,
    fragments: Vec<(Attr, Cow<'static, str>)>,
}

impl AnsiString {
    pub fn new_empty() -> Self {
        Self {
            stripped: Cow::Owned(String::new()),
            fragments: Vec::new(),
        }
    }

    pub fn new_string(string: String) -> Self {
        let stripped: Cow<'static, str> = Cow::Owned(string);
        Self {
            stripped: stripped.clone(),
            fragments: vec![(Attr::default(), stripped.clone())],
        }
    }

    pub fn new(stripped: String, fragments: Vec<(Attr, Cow<'static, str>)>) -> Self {
        Self {
            stripped: Cow::Owned(stripped),
            fragments,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.fragments.is_empty()
    }

    pub fn into_inner(self) -> String {
        self.stripped.into()
    }

    pub fn iter(&self) -> AnsiStringIterator {
        AnsiStringIterator::new(&self.fragments)
    }

    pub fn has_attrs(&self) -> bool {
        self.fragments.len() > 1
    }

    pub fn from_str(raw: &str) -> AnsiString {
        ANSIParser::default().parse_ansi(raw)
    }

    pub fn get_stripped(&self) -> &str {
        &self.stripped
    }
}

/// An iterator over all the (char, attr) characters.
pub struct AnsiStringIterator<'a> {
    fragments: &'a [(Attr, Cow<'a, str>)],
    fragment_idx: usize,
    attr: Attr,
    chars_iter: Option<std::str::Chars<'a>>,
}

impl<'a> AnsiStringIterator<'a> {
    pub fn new(fragments: &'a [(Attr, Cow<'a, str>)]) -> Self {
        Self {
            fragments,
            fragment_idx: 0,
            attr: Attr::default(),
            chars_iter: None,
        }
    }
}

impl<'a> Iterator for AnsiStringIterator<'a> {
    type Item = (char, Attr);

    fn next(&mut self) -> Option<Self::Item> {
        let ch = self.chars_iter.as_mut().and_then(|iter| iter.next());
        if ch.is_some() {
            Some((ch.unwrap(), self.attr))
        } else if ch.is_none() && self.fragment_idx >= self.fragments.len() {
            None
        } else {
            // try next fragment
            let (attr, string) = &self.fragments[self.fragment_idx];
            self.attr = *attr;
            self.chars_iter.replace(string.chars());
            self.fragment_idx += 1;
            self.next()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_ansi_iterator() {
        let input = "\x1B[48;2;5;10;15m\x1B[38;2;70;130;180mhi\x1B[0m";
        let ansistring = ANSIParser::default().parse_ansi(input);
        let mut it = ansistring.iter();
        let attr = Attr {
            fg: Color::Rgb(70, 130, 180),
            bg: Color::Rgb(5, 10, 15),
            ..Attr::default()
        };

        assert_eq!(Some(('h', attr)), it.next());
        assert_eq!(Some(('i', attr)), it.next());
        assert_eq!(None, it.next());
    }

    #[test]
    fn test_normal_string() {
        let input = "ab";
        let ansistring = ANSIParser::default().parse_ansi(input);

        assert_eq!(false, ansistring.has_attrs());

        let mut it = ansistring.iter();
        assert_eq!(Some(('a', Attr::default())), it.next());
        assert_eq!(Some(('b', Attr::default())), it.next());
        assert_eq!(None, it.next());

        assert_eq!("ab", ansistring.into_inner())
    }

    #[test]
    fn test_multiple_attributes() {
        let input = "\x1B[1;31mhi";
        let ansistring = ANSIParser::default().parse_ansi(input);
        let mut it = ansistring.iter();
        let attr = Attr {
            fg: Color::AnsiValue(1),
            effect: Effect::BOLD,
            ..Attr::default()
        };

        assert_eq!(Some(('h', attr)), it.next());
        assert_eq!(Some(('i', attr)), it.next());
        assert_eq!(None, it.next());
    }
}
<commit_msg>enh(src/ansi.rs): Use pattern match to destruct Option wrapper.<commit_after>// Parse ANSI attr code
use std::borrow::Cow;
use std::default::Default;
use std::mem;

use tuikit::prelude::*;
use vte::Perform;

/// An ANSI Parser, will parse one line at a time.
///
/// It will cache the latest attribute used, that means if an attribute affect multiple
/// lines, the parser will recognize it.
pub struct ANSIParser {
    partial_str: String,
    last_attr: Attr,

    stripped: String,
    fragments: Vec<(Attr, Cow<'static, str>)>,
}

impl Default for ANSIParser {
    fn default() -> Self {
        ANSIParser {
            partial_str: String::new(),
            last_attr: Attr::default(),

            stripped: String::new(),
            fragments: Vec::new(),
        }
    }
}

impl Perform for ANSIParser {
    fn print(&mut self, ch: char) {
        self.partial_str.push(ch);
    }

    fn execute(&mut self, byte: u8) {
        match byte {
            // put back \0 \r \n \t
            0x00 | 0x0d | 0x0A | 0x09 => self.partial_str.push(byte as char),
            // ignore all others
            _ => trace!("AnsiParser:execute ignored {:?}", byte),
        }
    }

    fn hook(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool) {
        trace!("AnsiParser:hook ignored {:?}", params);
    }

    fn put(&mut self, byte: u8) {
        trace!("AnsiParser:put ignored {:?}", byte);
    }

    fn unhook(&mut self) {
        trace!("AnsiParser:unhook ignored");
    }

    fn osc_dispatch(&mut self, params: &[&[u8]]) {
        trace!("AnsiParser:osc ignored {:?}", params);
    }

    fn csi_dispatch(&mut self, params: &[i64], _intermediates: &[u8], _ignore: bool, mode: char) {
        // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
        // Only care about graphic modes, ignore all others

        if mode != 'm' {
            trace!("ignore: params: {:?}, mode: {:?}", params, mode);
            return;
        }

        let mut attr = self.last_attr;
        let mut iter = params.into_iter();

        while let Some(&code) = iter.next() {
            match code {
                0 => attr = Attr::default(),
                1 => attr.effect |= Effect::BOLD,
                2 => attr.effect |= !Effect::BOLD,
                4 => attr.effect |= Effect::UNDERLINE,
                5 => attr.effect |= Effect::BLINK,
                7 => attr.effect |= Effect::REVERSE,
                num if num >= 30 && num <= 37 => {
                    attr.fg = Color::AnsiValue((num - 30) as u8);
                }
                38 => match iter.next() {
                    Some(2) => {
                        // ESC[ 38;2;<r>;<g>;<b> m Select RGB foreground color
                        let or = iter.next();
                        let og = iter.next();
                        let ob = iter.next();
                        if ob.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }

                        let r = *or.unwrap() as u8;
                        let g = *og.unwrap() as u8;
                        let b = *ob.unwrap() as u8;

                        attr.fg = Color::Rgb(r, g, b);
                    }
                    Some(5) => {
                        // ESC[ 38;5;<n> m Select foreground color
                        let color = iter.next();
                        if color.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }
                        attr.fg = Color::AnsiValue(*color.unwrap() as u8);
                    }
                    _ => {
                        trace!("error on parsing CSI {:?} m", params);
                    }
                },
                39 => attr.fg = Color::Default,
                num if num >= 40 && num <= 47 => {
                    attr.bg = Color::AnsiValue((num - 40) as u8);
                }
                48 => match iter.next() {
                    Some(2) => {
                        // ESC[ 48;2;<r>;<g>;<b> m Select RGB background color
                        let or = iter.next();
                        let og = iter.next();
                        let ob = iter.next();
                        if ob.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }

                        let r = *or.unwrap() as u8;
                        let g = *og.unwrap() as u8;
                        let b = *ob.unwrap() as u8;

                        attr.bg = Color::Rgb(r, g, b);
                    }
                    Some(5) => {
                        // ESC[ 48;5;<n> m Select background color
                        let color = iter.next();
                        if color.is_none() {
                            trace!("ignore CSI {:?} m", params);
                            continue;
                        }
                        attr.bg = Color::AnsiValue(*color.unwrap() as u8);
                    }
                    _ => {
                        trace!("ignore CSI {:?} m", params);
                    }
                },
                49 => attr.bg = Color::Default,
                _ => {
                    trace!("ignore CSI {:?} m", params);
                }
            }
        }

        self.attr_change(attr);
    }

    fn esc_dispatch(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _byte: u8) {
        // ESC characters are replaced with \[
        self.partial_str.push('"');
        self.partial_str.push('[');
    }
}

impl ANSIParser {
    /// save the partial_str into fragments with current attr
    fn save_str(&mut self) {
        if self.partial_str.is_empty() {
            return;
        }

        let string = mem::replace(&mut self.partial_str, String::new());
        self.stripped.push_str(&string);
        self.fragments.push((self.last_attr, Cow::Owned(string)));
    }

    // accept a new attr
    fn attr_change(&mut self, new_attr: Attr) {
        if new_attr == self.last_attr {
            return;
        }

        self.save_str();
        self.last_attr = new_attr;
    }

    pub fn parse_ansi(&mut self, text: &str) -> AnsiString {
        let mut statemachine = vte::Parser::new();

        for byte in text.as_bytes() {
            statemachine.advance(self, *byte);
        }
        self.save_str();

        let stripped = mem::replace(&mut self.stripped, String::new());
        let fragments = mem::replace(&mut self.fragments, Vec::new());
        AnsiString::new(stripped, fragments)
    }
}

#[derive(Clone, Debug)]
/// A String that contains ANSI state (e.g. colors)
///
/// It is internally represented as Vec<(attr, string)>
pub struct AnsiString {
    stripped: Cow<'static, str>,
    fragments: Vec<(Attr, Cow<'static, str>)>,
}

impl AnsiString {
    pub fn new_empty() -> Self {
        Self {
            stripped: Cow::Owned(String::new()),
            fragments: Vec::new(),
        }
    }

    pub fn new_string(string: String) -> Self {
        let stripped: Cow<'static, str> = Cow::Owned(string);
        Self {
            stripped: stripped.clone(),
            fragments: vec![(Attr::default(), stripped.clone())],
        }
    }

    pub fn new(stripped: String, fragments: Vec<(Attr, Cow<'static, str>)>) -> Self {
        Self {
            stripped: Cow::Owned(stripped),
            fragments,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.fragments.is_empty()
    }

    pub fn into_inner(self) -> String {
        self.stripped.into()
    }

    pub fn iter(&self) -> AnsiStringIterator {
        AnsiStringIterator::new(&self.fragments)
    }

    pub fn has_attrs(&self) -> bool {
        self.fragments.len() > 1
    }

    pub fn from_str(raw: &str) -> AnsiString {
        ANSIParser::default().parse_ansi(raw)
    }

    pub fn get_stripped(&self) -> &str {
        &self.stripped
    }
}

/// An iterator over all the (char, attr) characters.
pub struct AnsiStringIterator<'a> {
    fragments: &'a [(Attr, Cow<'a, str>)],
    fragment_idx: usize,
    attr: Attr,
    chars_iter: Option<std::str::Chars<'a>>,
}

impl<'a> AnsiStringIterator<'a> {
    pub fn new(fragments: &'a [(Attr, Cow<'a, str>)]) -> Self {
        Self {
            fragments,
            fragment_idx: 0,
            attr: Attr::default(),
            chars_iter: None,
        }
    }
}

impl<'a> Iterator for AnsiStringIterator<'a> {
    type Item = (char, Attr);

    fn next(&mut self) -> Option<Self::Item> {
        let ch = self.chars_iter.as_mut().and_then(|iter| iter.next());
        match ch {
            Some(c) => Some((c, self.attr)),
            None => {
                if self.fragment_idx >= self.fragments.len() {
                    None
                } else {
                    // try next fragment
                    let (attr, string) = &self.fragments[self.fragment_idx];
                    self.attr = *attr;
                    self.chars_iter.replace(string.chars());
                    self.fragment_idx += 1;
                    self.next()
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_ansi_iterator() {
        let input = "\x1B[48;2;5;10;15m\x1B[38;2;70;130;180mhi\x1B[0m";
        let ansistring = ANSIParser::default().parse_ansi(input);
        let mut it = ansistring.iter();
        let attr = Attr {
            fg: Color::Rgb(70, 130, 180),
            bg: Color::Rgb(5, 10, 15),
            ..Attr::default()
        };

        assert_eq!(Some(('h', attr)), it.next());
        assert_eq!(Some(('i', attr)), it.next());
        assert_eq!(None, it.next());
    }

    #[test]
    fn test_normal_string() {
        let input = "ab";
        let ansistring = ANSIParser::default().parse_ansi(input);

        assert_eq!(false, ansistring.has_attrs());

        let mut it = ansistring.iter();
        assert_eq!(Some(('a', Attr::default())), it.next());
        assert_eq!(Some(('b', Attr::default())), it.next());
        assert_eq!(None, it.next());

        assert_eq!("ab", ansistring.into_inner())
    }

    #[test]
    fn test_multiple_attributes() {
        let input = "\x1B[1;31mhi";
        let ansistring = ANSIParser::default().parse_ansi(input);
        let mut it = ansistring.iter();
        let attr = Attr {
            fg: Color::AnsiValue(1),
            effect: Effect::BOLD,
            ..Attr::default()
        };

        assert_eq!(Some(('h', attr)), it.next());
        assert_eq!(Some(('i', attr)), it.next());
        assert_eq!(None, it.next());
    }
}
<|endoftext|>


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

use bindgen::ir::Type;

pub fn mangle_path(name: &str, generic_values: &[Type]) -> String {
    assert!(!name.contains("_"));

    let mut out = String::from(name);
    if !generic_values.is_empty() {
        out.push_str("_"); // <
        let mut should_close = false;
        for (i, ty) in generic_values.iter().enumerate() {
            if i != 0 {
                out.push_str("__"); // ,
            }
            if should_close {
                out.push_str("___"); // >
            }
            should_close = append_type(ty, &mut out, mangle_path);
        }
    }
    out
}

fn append_type(ty: &Type, out: &mut String, generic_handler: fn(&str, &[Type]) -> String) -> bool {
    match ty {
        &Type::Path(ref path) => {
            out.push_str(&generic_handler(&path.name, &path.generics));
            true
        }
        &Type::Primitive(ref primitive) => {
            out.push_str(primitive.to_repr_rust());
            false
        }
        &Type::ConstPtr(..) => {
            unimplemented!()
        }
        &Type::Ptr(..) => {
            unimplemented!()
        }
        &Type::Array(..) => {
            unimplemented!()
        }
        &Type::FuncPtr(..) => {
            unimplemented!()
        }
    }
}

#[test]
fn mangle_test() {
    assert_eq!(mangle_path("Foo",
                           &vec![Type::Primitive(PrimitiveType::Float)]),
               "Foo_f32");

    assert_eq!(mangle_path("Foo",
                           &vec![Type::Path("Bar".to_owned(),
                                            vec![Type::Primitive(PrimitiveType::Float)])
                           ]),
               "Foo_Bar_f32");

    assert_eq!(mangle_path("Vec", &[Type::Path("u8".into(), Vec::new())]), "Vec_u8");
    assert_eq!(mangle_path("Vec", &[Type::Path("Vec".into(), vec!(Type::Primitive(PrimitiveType::UInt8)))]),
               "Vec_Vec_u8");
    assert_eq!(mangle_path("Foo", &[Type::Path("Bar".into(), vec![Type::Path("T".into(), Vec::new()),]),
                                    Type::Path("E".into(), Vec::new())]),
               "Foo_Bar_T_____E");
}
<commit_msg>Clean up mangling<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bindgen::ir::Type;

pub fn mangle_path(name: &str, generic_values: &[Type]) -> String {
    internal_mangle_path(name, generic_values, true)
}

fn internal_mangle_path(name: &str, generic_values: &[Type], last_in_parent: bool) -> String {
    assert!(!name.contains("_"));

    if generic_values.is_empty() {
        return String::from(name);
    }

    let mut out = String::from(name);

    out.push_str("_"); // <
    for (i, ty) in generic_values.iter().enumerate() {
        if i != 0 {
            out.push_str("__"); // ,
        }

        let is_last = i == generic_values.len() - 1;
        match ty {
            &Type::Path(ref path) => {
                out.push_str(&internal_mangle_path(&path.name,
                                                   &path.generics,
                                                   last_in_parent && is_last));
            }
            &Type::Primitive(ref primitive) => {
                out.push_str(primitive.to_repr_rust());
            }
            &Type::ConstPtr(..) |
            &Type::Ptr(..) |
            &Type::Array(..)|
            &Type::FuncPtr(..) => {
                unimplemented!()
            }
        }

        // Skip writing the trailing '>' mangling when possible
        if is_last && !last_in_parent {
            out.push_str("___"); // >
        }
    }

    out
}

#[test]
fn mangle_test() {
    use bindgen::ir::{GenericPath, PrimitiveType};

    fn float() -> Type {
        Type::Primitive(PrimitiveType::Float)
    }

    fn path(path: &str) -> Type {
        Type::Path(GenericPath::new(path.to_owned(), Vec::new()))
    }

    fn generic_path(path: &str, generics: &[Type]) -> Type {
        Type::Path(GenericPath::new(path.to_owned(), generics.to_owned()))
    }

    /* Foo<f32> => Foo_f32 */
    assert_eq!(mangle_path("Foo", &vec![float()]),
               "Foo_f32");

    /* Foo<Bar<f32>> => Foo_Bar_f32 */
    assert_eq!(mangle_path("Foo", &vec![generic_path("Bar", &[float()])]),
               "Foo_Bar_f32");

    /* Foo<Bar> => Foo_Bar */
    assert_eq!(mangle_path("Foo", &[path("Bar")]),
               "Foo_Bar");

    /* Foo<Bar<T>> => Foo_Bar_T */
    assert_eq!(mangle_path("Foo", &[generic_path("Bar", &[path("T")])]),
               "Foo_Bar_T");

    // /* Foo<Bar<T>, E> => Foo_Bar_T_____E */
    assert_eq!(mangle_path("Foo", &[generic_path("Bar", &[path("T")]),
                                    path("E")]),
               "Foo_Bar_T_____E");

    // /* Foo<Bar<T>, Bar<E>> => Foo_Bar_T_____Bar_E */
    assert_eq!(mangle_path("Foo", &[generic_path("Bar", &[path("T")]),
                                    generic_path("Bar", &[path("E")])]),
               "Foo_Bar_T_____Bar_E");
}
<|endoftext|>