<commit_before>use crate::error::Error;
use crate::instance::{Instance, RunResult, State, TerminationDetails};
use crate::val::Val;
use crate::vmctx::{Vmctx, VmctxInternal};
use std::any::Any;
use std::future::Future;
use std::pin::Pin;

/// This is the same type defined by the `futures` library, but we don't need the rest of the
/// library for this purpose.
type LocalBoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;

/// A unique type that wraps a boxed future with a boxed return value.
///
/// Type and lifetime guarantees are maintained by `Vmctx::block_on` and `Instance::run_async`. The
/// user never sees this type.
struct YieldedFuture(LocalBoxFuture<'static, ResumeVal>);

/// A unique type for a boxed return value. The user never sees this type.
struct ResumeVal(Box<dyn Any + 'static>);

impl Vmctx {
    /// Block on the result of an `async` computation from an instance run by `Instance::run_async`.
    ///
    /// Lucet hostcalls are synchronous `extern "C" fn` functions called from WebAssembly. In that
    /// context, we cannot use `.await` directly because the hostcall is not `async`. While we could
    /// block on an executor using `futures::executor::block_on` or
    /// `tokio::runtime::Runtime::block_on`, that has two drawbacks:
    ///
    /// - If the Lucet instance was originally invoked from an async context, trying to block on the
    ///   same runtime will fail if the executor cannot be nested (all executors we know of have this
    ///   restriction).
    ///
    /// - The current OS thread would be blocked on the result of the computation, rather than being
    ///   able to run other async tasks while awaiting. This means an application will need more
    ///   threads than otherwise would be necessary.
    ///
    /// This `block_on` operator instead yields yields a future back to a loop that runs in
    /// `Instance::run_async`, which `.await`s on it and then resumes the instance with the
    /// result. The future runs on the same runtime that invoked `run_async`, avoiding problems of
    /// nesting, and allowing the current OS thread to continue performing other async work.
    ///
    /// Note that this method may only be used if `Instance::run_async` was used to run the VM,
    /// otherwise it will terminate the instance with `TerminationDetails::AwaitNeedsAsync`.
    pub fn block_on<'a, R>(&'a self, f: impl Future<Output = R> + 'a) -> R
    where
        R: Any + 'static,
    {
        // Die if we aren't in Instance::run_async
        match self.instance().state {
            State::Running { async_context } => {
                if !async_context {
                    panic!(TerminationDetails::AwaitNeedsAsync)
                }
            }
            _ => unreachable!("Access to vmctx implies instance is Running"),
        }
        // Wrap the Output of `f` as a boxed ResumeVal. Then, box the entire
        // async computation.
        let f = Box::pin(async move { ResumeVal(Box::new(f.await)) });
        // Change the lifetime of the async computation from `'a` to `'static.
        // We need to lie about this lifetime so that `YieldedFuture` may impl
        // `Any` and be passed through the yield. `Instance::run_async`
        // rehydrates this lifetime to be at most as long as the Vmctx's `'a`.
        let f = unsafe {
            std::mem::transmute::<LocalBoxFuture<'a, ResumeVal>, LocalBoxFuture<'static, ResumeVal>>(
                f,
            )
        };
        // Wrap the computation in `YieldedFuture` so that
        // `Instance::run_async` can catch and run it.  We will get the
        // `ResumeVal` we applied to `f` above.
        let ResumeVal(v) = self.yield_val_expecting_val(YieldedFuture(f));
        // We may now downcast and unbox the returned Box<dyn Any> into an `R`
        // again.
        *v.downcast().expect("run_async broke invariant")
    }
}

impl Instance {
    /// Run a WebAssembly function with arguments in the guest context at the given entrypoint.
    ///
    /// This method is similar to `Instance::run()`, but allows the Wasm program to invoke hostcalls
    /// that use `Vmctx::block_on` and provides the trampoline that `.await`s those futures on
    /// behalf of the guest.
    ///
    /// # Blocking thread
    ///
    /// The `wrap_blocking` argument is a function that is called with a closure that runs the Wasm
    /// program. Since Wasm may execute for an arbitrarily long time without `await`ing, we need to
    /// make sure that it runs on a thread that is allowed to block.
    ///
    /// This argument is designed with [`tokio::task::block_in_place`][tokio] in mind. The odd type
    /// is a concession to the fact that we don't have rank 2 types in Rust, and so must fall back
    /// to trait objects in order to be able to take an argument that is itself a function that
    /// takes a closure.
    ///
    /// In order to provide an appropriate function, you may have to wrap the library function in
    /// another closure so that the types are compatible. For example:
    ///
    /// ```no_run
    /// # async fn f() {
    /// # let instance: lucet_runtime_internals::instance::InstanceHandle = unimplemented!();
    /// fn block_in_place<F, R>(f: F) -> R
    /// where
    ///     F: FnOnce() -> R,
    /// {
    ///     // ...
    ///     # f()
    /// }
    ///
    /// instance.run_async("entrypoint", &[], |f| block_in_place(f)).await.unwrap();
    /// # }
    /// ```
    ///
    /// [tokio]: https://docs.rs/tokio/0.2.21/tokio/task/fn.block_in_place.html
    pub async fn run_async<'a, F>(
        &'a mut self,
        entrypoint: &'a str,
        args: &'a [Val],
        wrap_blocking: F,
    ) -> Result<RunResult, Error>
    where
        F: Fn(&mut (dyn FnMut() -> Result<RunResult, Error>)) -> Result<RunResult, Error>,
    {
        if self.is_yielded() {
            return Err(Error::Unsupported(
                "cannot run_async a yielded instance".to_owned(),
            ));
        }

        // Store the ResumeVal here when we get it.
        let mut resume_val: Option<ResumeVal> = None;
        let ret = loop {
            // Run the WebAssembly program
            let run = wrap_blocking(&mut || {
                if self.is_yielded() {
                    // A previous iteration of the loop stored the ResumeVal in
                    // `resume_val`, send it back to the guest ctx and continue
                    // running:
                    self.resume_with_val_impl(
                        resume_val
                            .take()
                            .expect("is_yielded implies resume_value is some"),
                        true,
                    )
                } else {
                    // This is the first iteration, call the entrypoint:
                    let func = self.module.get_export_func(entrypoint)?;
                    self.run_func(func, args, true)
                }
            });
            match run {
                Ok(run_result) => {
                    // Check if the yield came from Vmctx::block_on:
                    if run_result.has_yielded::<YieldedFuture>() {
                        let YieldedFuture(future) = *run_result
                            .yielded()
                            .unwrap()
                            .downcast::<YieldedFuture>()
                            .unwrap();
                        // Rehydrate the lifetime from `'static` to `'a`, which
                        // is morally the same lifetime as was passed into
                        // `Vmctx::block_on`.
                        let future = future as LocalBoxFuture<'a, ResumeVal>;
                        // await on the computation. Store its result in
                        // `resume_val`.
                        resume_val = Some(future.await);
                        // Now we want to `Instance::resume_with_val` and start
                        // this cycle over.
                        continue;
                    } else {
                        // Any other result of the run is returned to this
                        // function's caller.
                        break Ok(run_result);
                    }
                }
                _ => {
                    // Any other result of the run is returned to this
                    // function's caller.
                    break run;
                }
            }
        };

        // Return the result of the run.
        return ret;
    }
}
<commit_msg>instance::block_on: reject all yields except for block_on<commit_after>use crate::error::Error;
use crate::instance::{Instance, RunResult, State, TerminationDetails};
use crate::val::{UntypedRetVal, Val};
use crate::vmctx::{Vmctx, VmctxInternal};
use std::any::Any;
use std::future::Future;
use std::pin::Pin;

/// This is the same type defined by the `futures` library, but we don't need the rest of the
/// library for this purpose.
type LocalBoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;

/// A unique type that wraps a boxed future with a boxed return value.
///
/// Type and lifetime guarantees are maintained by `Vmctx::block_on` and `Instance::run_async`. The
/// user never sees this type.
struct YieldedFuture(LocalBoxFuture<'static, ResumeVal>);

/// A unique type for a boxed return value. The user never sees this type.
struct ResumeVal(Box<dyn Any + Send + 'static>);

impl Vmctx {
    /// Block on the result of an `async` computation from an instance run by `Instance::run_async`.
    ///
    /// Lucet hostcalls are synchronous `extern "C" fn` functions called from WebAssembly. In that
    /// context, we cannot use `.await` directly because the hostcall is not `async`. While we could
    /// block on an executor using `futures::executor::block_on` or
    /// `tokio::runtime::Runtime::block_on`, that has two drawbacks:
    ///
    /// - If the Lucet instance was originally invoked from an async context, trying to block on the
    ///   same runtime will fail if the executor cannot be nested (all executors we know of have this
    ///   restriction).
    ///
    /// - The current OS thread would be blocked on the result of the computation, rather than being
    ///   able to run other async tasks while awaiting. This means an application will need more
    ///   threads than otherwise would be necessary.
    ///
    /// This `block_on` operator instead yields yields a future back to a loop that runs in
    /// `Instance::run_async`, which `.await`s on it and then resumes the instance with the
    /// result. The future runs on the same runtime that invoked `run_async`, avoiding problems of
    /// nesting, and allowing the current OS thread to continue performing other async work.
    ///
    /// Note that this method may only be used if `Instance::run_async` was used to run the VM,
    /// otherwise it will terminate the instance with `TerminationDetails::AwaitNeedsAsync`.
    pub fn block_on<'a, R>(&'a self, f: impl Future<Output = R> + 'a) -> R
    where
        R: Any + Send + 'static,
    {
        // Die if we aren't in Instance::run_async
        match self.instance().state {
            State::Running { async_context } => {
                if !async_context {
                    panic!(TerminationDetails::AwaitNeedsAsync)
                }
            }
            _ => unreachable!("Access to vmctx implies instance is Running"),
        }
        // Wrap the Output of `f` as a boxed ResumeVal. Then, box the entire
        // async computation.
        let f = Box::pin(async move { ResumeVal(Box::new(f.await)) });
        // Change the lifetime of the async computation from `'a` to `'static.
        // We need to lie about this lifetime so that `YieldedFuture` may impl
        // `Any` and be passed through the yield. `Instance::run_async`
        // rehydrates this lifetime to be at most as long as the Vmctx's `'a`.
        let f = unsafe {
            std::mem::transmute::<LocalBoxFuture<'a, ResumeVal>, LocalBoxFuture<'static, ResumeVal>>(
                f,
            )
        };
        // Wrap the computation in `YieldedFuture` so that
        // `Instance::run_async` can catch and run it.  We will get the
        // `ResumeVal` we applied to `f` above.
        let ResumeVal(v) = self.yield_val_expecting_val(YieldedFuture(f));
        // We may now downcast and unbox the returned Box<dyn Any> into an `R`
        // again.
        *v.downcast().expect("run_async broke invariant")
    }
}

impl Instance {
    /// Run a WebAssembly function with arguments in the guest context at the given entrypoint.
    ///
    /// This method is similar to `Instance::run()`, but allows the Wasm program to invoke hostcalls
    /// that use `Vmctx::block_on` and provides the trampoline that `.await`s those futures on
    /// behalf of the guest.
    ///
    /// # Blocking thread
    ///
    /// The `wrap_blocking` argument is a function that is called with a closure that runs the Wasm
    /// program. Since Wasm may execute for an arbitrarily long time without `await`ing, we need to
    /// make sure that it runs on a thread that is allowed to block.
    ///
    /// This argument is designed with [`tokio::task::block_in_place`][tokio] in mind. The odd type
    /// is a concession to the fact that we don't have rank 2 types in Rust, and so must fall back
    /// to trait objects in order to be able to take an argument that is itself a function that
    /// takes a closure.
    ///
    /// In order to provide an appropriate function, you may have to wrap the library function in
    /// another closure so that the types are compatible. For example:
    ///
    /// ```no_run
    /// # async fn f() {
    /// # let instance: lucet_runtime_internals::instance::InstanceHandle = unimplemented!();
    /// fn block_in_place<F, R>(f: F) -> R
    /// where
    ///     F: FnOnce() -> R,
    /// {
    ///     // ...
    ///     # f()
    /// }
    ///
    /// instance.run_async("entrypoint", &[], |f| block_in_place(f)).await.unwrap();
    /// # }
    /// ```
    ///
    /// [tokio]: https://docs.rs/tokio/0.2.21/tokio/task/fn.block_in_place.html
    pub async fn run_async<'a, F>(
        &'a mut self,
        entrypoint: &'a str,
        args: &'a [Val],
        wrap_blocking: F,
    ) -> Result<UntypedRetVal, Error>
    where
        F: Fn(&mut (dyn FnMut() -> Result<RunResult, Error>)) -> Result<RunResult, Error>,
    {
        if self.is_yielded() {
            return Err(Error::Unsupported(
                "cannot run_async a yielded instance".to_owned(),
            ));
        }

        // Store the ResumeVal here when we get it.
        let mut resume_val: Option<ResumeVal> = None;
        loop {
            // Run the WebAssembly program
            let run_result = wrap_blocking(&mut || {
                if self.is_yielded() {
                    // A previous iteration of the loop stored the ResumeVal in
                    // `resume_val`, send it back to the guest ctx and continue
                    // running:
                    self.resume_with_val_impl(
                        resume_val
                            .take()
                            .expect("is_yielded implies resume_value is some"),
                        true,
                    )
                } else {
                    // This is the first iteration, call the entrypoint:
                    let func = self.module.get_export_func(entrypoint)?;
                    self.run_func(func, args, true)
                }
            })?;
            match run_result {
                RunResult::Returned(rval) => {
                    // Finished running, return UntypedReturnValue
                    return Ok(rval);
                }
                RunResult::Yielded(yval) => {
                    // Check if the yield came from Vmctx::block_on:
                    if yval.is::<YieldedFuture>() {
                        let YieldedFuture(future) = *yval.downcast::<YieldedFuture>().unwrap();
                        // Rehydrate the lifetime from `'static` to `'a`, which
                        // is morally the same lifetime as was passed into
                        // `Vmctx::block_on`.
                        let future = future as LocalBoxFuture<'a, ResumeVal>;
                        // await on the computation. Store its result in
                        // `resume_val`.
                        resume_val = Some(future.await);
                        // Now we want to `Instance::resume_with_val` and start
                        // this cycle over.
                        continue;
                    } else {
                        // Any other yielded value is not supported - die with an error.
                        return Err(Error::Unsupported(
                            "cannot yield anything besides a future in Instance::run_async"
                                .to_owned(),
                        ));
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;
use nom::{
    IResult,
    InputLength,
    digit,
    alphanumeric,
    eol,
    space
};

use std::str;
use std::str::FromStr;

pub struct ObjParser {
}

impl ObjParser {
    pub fn new() -> Self {
        ObjParser{}
    }
}

pub enum ObjLine {
    ObjectName(String),
    MaterialLibrary(String),
    Vertex(f32, f32, f32),
    Normal(f32, f32, f32),
    Face(u32,u32,u32),
    TextureCoord(f32,f32),
    Comment(String),
}

named!(end_of_line, alt!(eof!() | eol));

named!(unsigned_float <f32>, map_res!(
  map_res!(
    recognize!(
      alt!(
        delimited!(digit, tag!("."), opt!(digit)) |
        delimited!(opt!(digit), tag!("."), digit)
      )
    ),
    str::from_utf8
  ),
  FromStr::from_str
));

named!(float <f32>, map!(
   pair!(
     opt!(alt!(tag!("+") | tag!("-"))),
     unsigned_float
   ),
   |(sign, value): (Option<&[u8]>, f32)| {
     sign.and_then(|s| if s[0] == ('-' as u8) { Some(-1f32) } else { None }).unwrap_or(1f32) * value
   }
));

named!(comment, delimited!(tag!("#"), take_until!("\n"), tag!("\n")) );
named!(object_line, delimited!(tag!("o"), take_until!("\n"), tag!("\n")) );
named!(mtllib_line, delimited!(tag!("mtllib"), take_until!("\n"), tag!("\n")) );
named!(float_triple< &[u8], (f32,f32,f32) >, ws!(tuple!(float, float, float)));
named!(float_pair< &[u8], (f32,f32) >, ws!(tuple!(float, float)));
named!(vertex_line< &[u8], (&[u8], f32,f32,f32) >, ws!(tuple!(tag!("v"), float, float, float)));
named!(normal_line< &[u8], (&[u8], f32,f32,f32) >, ws!(tuple!(tag!("vn"), float, float, float)));
named!(texcoord_line< &[u8], (&[u8], f32,f32) >, ws!(tuple!(tag!("vt"), float, float)));
named!(face_line< &[u8], (&[u8], Vec<u32> ) >, tuple!(tag!("f"), separated_list!(space,digit) ));

#[cfg(test)]
mod tests {

    use super::*;

    #[test] fn can_parse_face_index_singular() {
        // TODO: actually read the obj spec, what is each number? vert/tex/norm?
        // face indices are 1 based, not zero based
        let something = float("1/11/4".as_bytes());
        assert_eq!(something, IResult::Done(&b""[..], -0.00005));
    }

    #[test] fn can_parse_texcoord_line() {
        let vline = "vt -1.000000 -1.000000 \r\n".as_bytes();
        let v = texcoord_line(vline);
        let (a,b) = v.unwrap();
        assert_eq!(b, ("vt".as_bytes(), -1.0, -1.0));
    }

    #[test] fn can_parse_normal_line() {
        let vline = "vn -1.000000 -1.000000 1.000000  \r\n".as_bytes();
        let v = normal_line(vline);
        let (a,b) = v.unwrap();
        assert_eq!(b, ("vn".as_bytes(), -1.0, -1.0, 1.0));
    }

    #[test]
    #[should_panic]
    fn can_parse_vertex_line_2() {
        let vline = "vZZ -1.000000 -1.000000 1.000000 \r\n".as_bytes();
        let v = vertex_line(vline);
        let (a,b) = v.unwrap();
        assert_eq!(b, ("v".as_bytes(), -1.0, -1.0, 1.0));
    }

    #[test]
    fn can_parse_vertex_line() {
        let vline = "v -1.000000 -1.000000 1.000000 \r\n".as_bytes();
        let v = vertex_line(vline);
        let (a,b) = v.unwrap();
        assert_eq!(b, ("v".as_bytes(), -1.0, -1.0, 1.0));
    }

    #[test]
    fn can_parse_float_pair() {
        let ff = float_pair("     -1.000001 7742.9 ".as_bytes());
        let (a,b) = ff.unwrap();
        assert_eq!(b, (-1.000001, 7742.9));
    }

    #[test]
    fn can_parse_float_triple() {
        let fff = float_triple("    0.95  -1.000001 42.9 ".as_bytes());
        let (a,b) = fff.unwrap();
        assert_eq!(b, (0.95, -1.000001, 42.9));
    }

    #[test] fn can_parse_comments() {
        let cmt = comment("# a comment exists here \n".as_bytes());
        let (a,b) = cmt.unwrap();
        assert_eq!(str::from_utf8(b).unwrap(), " a comment exists here ");
    }

    #[test] fn can_parse_signed_floats() {
        let something = float("-0.00005".as_bytes());
        assert_eq!(something, IResult::Done(&b""[..], -0.00005));
    }

    const CUBE_MODEL: &'static str = "
# Blender v3.78 (sub 0) OBJ File: 'untitled.blend'
# www.blender.org
mtllib cube.mtl
o Cube_Cube.001
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 1.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
usemtl None
s off
f 2/1/1 3/2/1 1/3/1
f 4/4/2 7/5/2 3/6/2
f 8/7/3 5/8/3 7/9/3
f 6/10/4 1/11/4 5/12/4
f 7/13/5 1/11/5 3/6/5
f 4/4/6 6/14/6 8/15/6
f 2/1/1 4/16/1 3/6/1
f 4/4/2 8/17/2 7/9/2
f 8/7/3 6/14/3 5/12/3
f 6/10/4 2/18/4 1/3/4
f 7/13/5 5/8/5 1/3/5
f 4/4/6 2/18/6 6/19/6
";

}<commit_msg>next up, tying together the parser<commit_after>#[macro_use]
extern crate nom;
use nom::{
    IResult,
    digit,
    eol
};

use std::str;
use std::str::FromStr;

pub struct ObjParser {
}

impl ObjParser {
    pub fn new() -> Self {
        ObjParser{}
    }
}

named!(whitespace, eat_separator!(&b" \t"[..]));
macro_rules! sp (
   ($i:expr, $($args:tt)*) => (
     {
       sep!($i, whitespace, $($args)*)
     }
   )
 );

named!(slashes, eat_separator!(&b"/"[..]));
macro_rules! slash_sep (
   ($i:expr, $($args:tt)*) => (
     {
       sep!($i, slashes, $($args)*)
     }
   )
 );

named!(end_of_line, alt!(eof!() | eol));

named!(unsigned_float <f32>, map_res!(
  map_res!(
    recognize!(
      alt!(
        delimited!(digit, tag!("."), opt!(digit)) |
        delimited!(opt!(digit), tag!("."), digit)
      )
    ),
    str::from_utf8
  ),
  FromStr::from_str
));

named!(float <f32>, map!(
   pair!(
     opt!(alt!(tag!("+") | tag!("-"))),
     unsigned_float
   ),
   |(sign, value): (Option<&[u8]>, f32)| {
     sign.and_then(|s| if s[0] == ('-' as u8) { Some(-1f32) } else { None }).unwrap_or(1f32) * value
   }
));

named!(uint <u32>, map_res!(map_res!( recognize!( digit ), str::from_utf8 ), FromStr::from_str));

named!( comment, delimited!(
    tag!("#"),
    take_until!("\n"),
    end_of_line
));
named!( object_line, delimited!( tag!("o"), take_until!("\n"), end_of_line) );
named!( mtllib_line, delimited!(tag!("mtllib"), take_until!("\n"), end_of_line) );
named!( usemtl_line, delimited!(tag!("usemtl"), take_until!("\n"), end_of_line) );
named!( s_line, delimited!(tag!("s"), take_until!("\n"), end_of_line) );

named!( float_triple< &[u8], (f32,f32,f32) >, sp!(tuple!(float, float, float)));
named!( float_pair< &[u8], (f32,f32) >,  sp!(tuple!(float, float)));

named!( vertex_line< &[u8], (f32,f32,f32) >, sp!(
    delimited!(
        tag!("v"),
        float_triple,
        end_of_line
    )
));

named!( normal_line< &[u8], (f32,f32,f32) >, sp!(
    delimited!(
        tag!("vn"),
        float_triple,
        end_of_line
    )
));
named!( texcoord_line< &[u8], (f32,f32) >,   sp!(
    delimited!(
        tag!("vt"),
        float_pair,
        end_of_line
    )
));

named!( face_triple< &[u8], (u32, Option<u32>, Option<u32>) >,
    sp!(slash_sep!(
        tuple!(uint, opt!(uint), opt!(uint))
    ))
);

named!( face_line< &[u8], ((u32, Option<u32>, Option<u32>), (u32, Option<u32>, Option<u32>), (u32, Option<u32>, Option<u32>)) >, sp!(
    delimited!(
        tag!("f"),
        tuple!(face_triple, face_triple, face_triple),
        end_of_line
    )
));

pub enum ObjLine {
    Comment(&'a str),
    ObjectName(&'a str),
    MaterialLibrary(&'a str),
    SLine(&'a str),
    Vertex(f32, f32, f32),
    Normal(f32, f32, f32),
    Face(u32,u32,u32),
    TextureCoord(f32,f32),
}

// TODO: tie parsers together into a single one that can chunk through a file

#[cfg(test)]
mod tests {

    use super::*;

    #[test] fn can_parse_face_triple() {
        let ff = face_triple(" 1/11/4".as_bytes());
        let (_,b) = ff.unwrap();
        assert_eq!(b, (1, Some(11), Some(4)) );
    }

    #[test] fn can_parse_face_line() {
        let ff = face_line("f 1/11/4  1/11/4 1/11/4  \n".as_bytes());
        let (_,b) = ff.unwrap();
        assert_eq!(b,
            (
                (1, Some(11), Some(4)),
                (1, Some(11), Some(4)),
                (1, Some(11), Some(4))
            )
        );
    }

    #[test] fn can_parse_signed_floats() {
        let something = float("-0.00005".as_bytes());
        assert_eq!(something, IResult::Done(&b""[..], -0.00005));
    }

    #[test]
    fn can_parse_float_pair() {
        let ff = float_pair("     -1.000001 7742.9 ".as_bytes());
        let (_,b) = ff.unwrap();
        assert_eq!(b, (-1.000001, 7742.9));
    }

    #[test]
    fn can_parse_float_triple() {
        let fff = float_triple("    0.95  -1.000001 42.9 ".as_bytes());
        let (_,b) = fff.unwrap();
        assert_eq!(b, (0.95, -1.000001, 42.9));
    }

    #[test] fn can_parse_texcoord_line() {
        let vline = "vt -1.000000 -1.000000 \r\n".as_bytes();
        let v = texcoord_line(vline);
        let (_a,b) = v.unwrap();
        assert_eq!(b, (-1.0, -1.0));
    }

    #[test] fn can_parse_normal_line() {
        let vline = "vn -1.000000 -1.000000 1.000000  \r\n".as_bytes();
        let v = normal_line(vline);
        let (_,b) = v.unwrap();
        assert_eq!(b, (-1.0, -1.0, 1.0));
    }

    #[test]
    #[should_panic]
    fn can_parse_vertex_line_2() {
        let vline = "vZZ -1.000000 -1.000000 1.000000 \r\n".as_bytes();
        let v = vertex_line(vline);
        let (_,b) = v.unwrap();
        assert_eq!(b, (-1.0, -1.0, 1.0));
    }

    #[test]
    fn can_parse_vertex_line() {
        let vline = "v -1.000000 -1.000000 1.000000 \r\n".as_bytes();
        let v = vertex_line(vline);
        let (_,b) = v.unwrap();
        assert_eq!(b, (-1.0, -1.0, 1.0));
    }

    #[test] fn can_parse_object_line() {
        let cmt = object_line("o someobject.999asdf.7 \n".as_bytes());
        let (_,b) = cmt.unwrap();
        assert_eq!(str::from_utf8(b).unwrap(), " someobject.999asdf.7 ");
    }

    #[test] fn can_parse_mtllib_line() {
        let cmt = mtllib_line("mtllib somelib \n".as_bytes());
        let (_,b) = cmt.unwrap();
        assert_eq!(str::from_utf8(b).unwrap(), " somelib ");
    }

    #[test] fn can_parse_usemtl_line() {
        let cmt = usemtl_line("usemtl SomeMaterial\n".as_bytes());
        let (_,b) = cmt.unwrap();
        assert_eq!(str::from_utf8(b).unwrap(), " SomeMaterial");
    }

    #[test] fn can_parse_s_line() {
        let cmt = s_line("s off\n".as_bytes());
        let (_,b) = cmt.unwrap();
        assert_eq!(str::from_utf8(b).unwrap(), " off");
    }

    #[test] fn can_parse_comments() {
        let cmt = comment("# a comment exists here \n".as_bytes());
        let (_,b) = cmt.unwrap();
        assert_eq!(str::from_utf8(b).unwrap(), " a comment exists here ");
    }

    const CUBE_MODEL: &'static str = "
# Blender v3.78 (sub 0) OBJ File: 'untitled.blend'
# www.blender.org
mtllib cube.mtl
o Cube_Cube.001
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 1.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
usemtl None
s off
f 2/1/1 3/2/1 1/3/1
f 4/4/2 7/5/2 3/6/2
f 8/7/3 5/8/3 7/9/3
f 6/10/4 1/11/4 5/12/4
f 7/13/5 1/11/5 3/6/5
f 4/4/6 6/14/6 8/15/6
f 2/1/1 4/16/1 3/6/1
f 4/4/2 8/17/2 7/9/2
f 8/7/3 6/14/3 5/12/3
f 6/10/4 2/18/4 1/3/4
f 7/13/5 5/8/5 1/3/5
f 4/4/6 2/18/6 6/19/6
";

}<|endoftext|>


<commit_before>use rustc_serialize::json::Json;
use rustc_serialize::json;
use matrix::json as mjson;
use std::fmt;

#[derive(Clone, Debug)]
pub struct UserID {
    pub nickname: String,
    pub homeserver: String
}

impl UserID {
    pub fn from_str(s: &str) -> Self {
        let parts: Vec<&str> = s.split(":").collect();
        UserID {
            nickname: parts[0][1..].to_string(),
            homeserver: parts[1].to_string()
        }
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct RoomID {
    pub id: String,
    pub homeserver: String
}

impl fmt::Display for RoomID {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "!{}:{}", self.id, self.homeserver)
    }
}

impl RoomID {
    pub fn from_str(s: &str) -> Self {
        let parts: Vec<&str> = s.split(":").collect();
        RoomID {
            id: parts[0][1..].to_string(),
            homeserver: parts[1].to_string()
        }
    }
}

#[derive(Debug)]
pub enum MembershipAction {
    Join
}

impl MembershipAction {
    pub fn from_str(s: &str) -> Self {
        match s {
            "join" => MembershipAction::Join,
            _ => panic!("unknown membership action {:?}", s)
        }
    }
}

#[derive(Debug)]
pub enum RoomEvent {
    CanonicalAlias(String),
    JoinRules(String),
    Membership(UserID, MembershipAction),
    HistoryVisibility(String),
    Create,
    Aliases,
    Message(UserID, String),
    PowerLevels
}

#[derive(Debug)]
pub struct TypingEvent {
    pub users: Vec<UserID>,
    pub room: RoomID,
}

#[derive(Debug)]
pub enum EventData {
    Room(RoomID, RoomEvent),
    Typing(TypingEvent),
    Presence(PresenceEvent)
}

impl EventData {
    pub fn type_str(&self) -> &'static str {
        match self {
            &EventData::Room(_, RoomEvent::Message(_, _)) => {
                "m.room.message"
            },
            _ => unreachable!()
        }
    }

    pub fn to_json(&self) -> json::Json {
        let mut ret = json::Object::new();
        match self {
            &EventData::Room(ref id, ref evt) => {
                match evt {
                    &RoomEvent::Message(ref sender, ref text) => {
                        ret.insert("msgtype".to_string(), json::Json::String("m.text".to_string()));
                        ret.insert("body".to_string(), json::Json::String(text.clone()));
                    },
                    _ => unreachable!()
                }
            },
            _ => unreachable!()
        }
        json::Json::Object(ret)
    }
}

#[derive(Debug)]
pub struct Event {
    pub id: Option<String>,
    pub data: EventData
}

#[derive(Debug)]
pub struct PresenceEvent {
    pub presence: String,
    pub user: UserID
}

impl Event {
    pub fn from_json(json: &Json) -> Self {
        let tokens: Vec<&str> = mjson::string(json, "type").trim().split(".").collect();
        assert!(tokens[0] == "m");
        let id = match json.as_object().unwrap().get("event_id") {
            Some(i) => Some(i.as_string().unwrap().to_string()),
            None => None
        };
        Event {
            id: id,
            data: match tokens[1] {
                "room" =>
                    Self::from_room_json(tokens[2], json),
                "typing" =>
                    EventData::Typing(TypingEvent {
                        users: vec![],
                        room: RoomID::from_str(mjson::string(json, "room_id"))
                    }),
                "presence" =>
                    EventData::Presence(PresenceEvent{
                        presence: mjson::string(json, "content.presence").to_string(),
                        user: UserID::from_str(mjson::string(json, "content.user_id"))
                    }),
                e => panic!("Unknown event {:?}!\nRaw JSON: {:?}", e, json)
            }
        }
    }

    fn from_room_json(event_type: &str, json: &Json) -> EventData {
        EventData::Room(
            RoomID::from_str(mjson::string(json, "room_id")),
            match event_type {
                "canonical_alias" =>
                    RoomEvent::CanonicalAlias(mjson::string(json, "content.alias").to_string()),
                "join_rules" =>
                    RoomEvent::JoinRules(mjson::string(json, "content.join_rule").to_string()),
                "member" =>
                    RoomEvent::Membership(UserID::from_str(mjson::string(json, "user_id")), MembershipAction::from_str(mjson::string(json, "content.membership"))),
                "history_visibility" =>
                    RoomEvent::HistoryVisibility(mjson::string(json, "content.history_visibility").to_string()),
                "create" =>
                    RoomEvent::Create,
                "aliases" =>
                    RoomEvent::Aliases,
                "power_levels" =>
                    RoomEvent::PowerLevels,
                "message" =>
                    RoomEvent::Message(UserID::from_str(mjson::string(json, "user_id")), mjson::string(json, "content.body").to_string()),
                e => panic!("Unknown room event {:?}: {:?}", e, json)
            }
        )
    }
}
<commit_msg>matrix: events: implement an eventid type<commit_after>use rustc_serialize::json::Json;
use rustc_serialize::json;
use matrix::json as mjson;
use std::fmt;

#[derive(Clone, Debug, PartialEq)]
pub struct EventID {
    pub id: String,
    pub homeserver: String
}

impl EventID {
    pub fn from_str(s: &str) -> Self {
        let parts: Vec<&str> = s.split(":").collect();
        EventID {
            id: parts[0][1..].to_string(),
            homeserver: parts[1].to_string()
        }
    }
}

#[derive(Clone, Debug)]
pub struct UserID {
    pub nickname: String,
    pub homeserver: String
}

impl UserID {
    pub fn from_str(s: &str) -> Self {
        let parts: Vec<&str> = s.split(":").collect();
        UserID {
            nickname: parts[0][1..].to_string(),
            homeserver: parts[1].to_string()
        }
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct RoomID {
    pub id: String,
    pub homeserver: String
}

impl fmt::Display for RoomID {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "!{}:{}", self.id, self.homeserver)
    }
}

impl RoomID {
    pub fn from_str(s: &str) -> Self {
        let parts: Vec<&str> = s.split(":").collect();
        RoomID {
            id: parts[0][1..].to_string(),
            homeserver: parts[1].to_string()
        }
    }
}

#[derive(Debug)]
pub enum MembershipAction {
    Join
}

impl MembershipAction {
    pub fn from_str(s: &str) -> Self {
        match s {
            "join" => MembershipAction::Join,
            _ => panic!("unknown membership action {:?}", s)
        }
    }
}

#[derive(Debug)]
pub enum RoomEvent {
    CanonicalAlias(String),
    JoinRules(String),
    Membership(UserID, MembershipAction),
    HistoryVisibility(String),
    Create,
    Aliases,
    Message(UserID, String),
    PowerLevels
}

#[derive(Debug)]
pub struct TypingEvent {
    pub users: Vec<UserID>,
    pub room: RoomID,
}

#[derive(Debug)]
pub enum EventData {
    Room(RoomID, RoomEvent),
    Typing(TypingEvent),
    Presence(PresenceEvent)
}

impl EventData {
    pub fn type_str(&self) -> &'static str {
        match self {
            &EventData::Room(_, RoomEvent::Message(_, _)) => {
                "m.room.message"
            },
            _ => unreachable!()
        }
    }

    pub fn to_json(&self) -> json::Json {
        let mut ret = json::Object::new();
        match self {
            &EventData::Room(ref id, ref evt) => {
                match evt {
                    &RoomEvent::Message(ref sender, ref text) => {
                        ret.insert("msgtype".to_string(), json::Json::String("m.text".to_string()));
                        ret.insert("body".to_string(), json::Json::String(text.clone()));
                    },
                    _ => unreachable!()
                }
            },
            _ => unreachable!()
        }
        json::Json::Object(ret)
    }
}

#[derive(Debug)]
pub struct Event {
    pub id: Option<EventID>,
    pub data: EventData
}

#[derive(Debug)]
pub struct PresenceEvent {
    pub presence: String,
    pub user: UserID
}

impl Event {
    pub fn from_json(json: &Json) -> Self {
        let tokens: Vec<&str> = mjson::string(json, "type").trim().split(".").collect();
        assert!(tokens[0] == "m");
        let id = match json.as_object().unwrap().get("event_id") {
            Some(i) => Some(EventID::from_str(i.as_string().unwrap())),
            None => None
        };
        Event {
            id: id,
            data: match tokens[1] {
                "room" =>
                    Self::from_room_json(tokens[2], json),
                "typing" =>
                    EventData::Typing(TypingEvent {
                        users: vec![],
                        room: RoomID::from_str(mjson::string(json, "room_id"))
                    }),
                "presence" =>
                    EventData::Presence(PresenceEvent{
                        presence: mjson::string(json, "content.presence").to_string(),
                        user: UserID::from_str(mjson::string(json, "content.user_id"))
                    }),
                e => panic!("Unknown event {:?}!\nRaw JSON: {:?}", e, json)
            }
        }
    }

    fn from_room_json(event_type: &str, json: &Json) -> EventData {
        EventData::Room(
            RoomID::from_str(mjson::string(json, "room_id")),
            match event_type {
                "canonical_alias" =>
                    RoomEvent::CanonicalAlias(mjson::string(json, "content.alias").to_string()),
                "join_rules" =>
                    RoomEvent::JoinRules(mjson::string(json, "content.join_rule").to_string()),
                "member" =>
                    RoomEvent::Membership(UserID::from_str(mjson::string(json, "user_id")), MembershipAction::from_str(mjson::string(json, "content.membership"))),
                "history_visibility" =>
                    RoomEvent::HistoryVisibility(mjson::string(json, "content.history_visibility").to_string()),
                "create" =>
                    RoomEvent::Create,
                "aliases" =>
                    RoomEvent::Aliases,
                "power_levels" =>
                    RoomEvent::PowerLevels,
                "message" =>
                    RoomEvent::Message(UserID::from_str(mjson::string(json, "user_id")), mjson::string(json, "content.body").to_string()),
                e => panic!("Unknown room event {:?}: {:?}", e, json)
            }
        )
    }
}
<|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 dom::audiobuffer::{AudioBuffer, MAX_SAMPLE_RATE, MIN_SAMPLE_RATE};
use dom::audionode::MAX_CHANNEL_COUNT;
use dom::baseaudiocontext::{BaseAudioContext, BaseAudioContextOptions};
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextBinding::BaseAudioContextMethods;
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding;
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding::OfflineAudioContextMethods;
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding::OfflineAudioContextOptions;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::num::Finite;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::root::DomRoot;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::offlineaudiocompletionevent::OfflineAudioCompletionEvent;
use dom::promise::Promise;
use dom::window::Window;
use dom_struct::dom_struct;
use servo_media::audio::context::OfflineAudioContextOptions as ServoMediaOfflineAudioContextOptions;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread::Builder;
use task_source::{TaskSource, TaskSourceName};

#[dom_struct]
pub struct OfflineAudioContext {
    context: BaseAudioContext,
    channel_count: u32,
    length: u32,
    rendering_started: Cell<bool>,
    #[ignore_malloc_size_of = "promises are hard"]
    pending_rendering_promise: DomRefCell<Option<Rc<Promise>>>,
}

impl OfflineAudioContext {
    #[allow(unrooted_must_root)]
    fn new_inherited(channel_count: u32, length: u32, sample_rate: f32) -> OfflineAudioContext {
        let options = ServoMediaOfflineAudioContextOptions {
            channels: channel_count as u8,
            length: length as usize,
            sample_rate,
        };
        let context =
            BaseAudioContext::new_inherited(BaseAudioContextOptions::OfflineAudioContext(options));
        OfflineAudioContext {
            context,
            channel_count,
            length,
            rendering_started: Cell::new(false),
            pending_rendering_promise: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    fn new(
        window: &Window,
        channel_count: u32,
        length: u32,
        sample_rate: f32,
    ) -> Fallible<DomRoot<OfflineAudioContext>> {
        if channel_count > MAX_CHANNEL_COUNT ||
            channel_count <= 0 ||
            length <= 0 ||
            sample_rate < MIN_SAMPLE_RATE ||
            sample_rate > MAX_SAMPLE_RATE
        {
            return Err(Error::NotSupported);
        }
        let context = OfflineAudioContext::new_inherited(channel_count, length, sample_rate);
        Ok(reflect_dom_object(
            Box::new(context),
            window,
            OfflineAudioContextBinding::Wrap,
        ))
    }

    pub fn Constructor(
        window: &Window,
        options: &OfflineAudioContextOptions,
    ) -> Fallible<DomRoot<OfflineAudioContext>> {
        OfflineAudioContext::new(
            window,
            options.numberOfChannels,
            options.length,
            *options.sampleRate,
        )
    }

    pub fn Constructor_(
        window: &Window,
        number_of_channels: u32,
        length: u32,
        sample_rate: Finite<f32>,
    ) -> Fallible<DomRoot<OfflineAudioContext>> {
        OfflineAudioContext::new(window, number_of_channels, length, *sample_rate)
    }
}

impl OfflineAudioContextMethods for OfflineAudioContext {
    // https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-oncomplete
    event_handler!(complete, GetOncomplete, SetOncomplete);

    // https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-length
    fn Length(&self) -> u32 {
        self.length
    }

    // https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-startrendering
    #[allow(unrooted_must_root)]
    fn StartRendering(&self) -> Rc<Promise> {
        let promise = Promise::new(&self.global());
        if self.rendering_started.get() {
            promise.reject_error(Error::InvalidState);
            return promise;
        }
        self.rendering_started.set(true);

        *self.pending_rendering_promise.borrow_mut() = Some(promise.clone());

        let processed_audio = Arc::new(Mutex::new(Vec::new()));
        let processed_audio_ = processed_audio.clone();
        let (sender, receiver) = mpsc::channel();
        let sender = Mutex::new(sender);
        self.context
            .audio_context_impl()
            .set_eos_callback(Box::new(move |buffer| {
                processed_audio_
                    .lock()
                    .unwrap()
                    .extend_from_slice((*buffer).as_ref());
                let _ = sender.lock().unwrap().send(());
            }));

        let this = Trusted::new(self);
        let global = self.global();
        let window = global.as_window();
        let task_source = window.dom_manipulation_task_source();
        let canceller = window.task_canceller(TaskSourceName::DOMManipulation);
        Builder::new()
            .name("OfflineAudioContextResolver".to_owned())
            .spawn(move || {
                let _ = receiver.recv();
                let _ = task_source.queue_with_canceller(
                    task!(resolve: move || {
                        let this = this.root();
                        let processed_audio = processed_audio.lock().unwrap();
                        let processed_audio: Vec<_> = processed_audio
                            .chunks(this.length as usize)
                            .map(|channel| channel.to_vec())
                            .collect();
                        let buffer = AudioBuffer::new(
                            &this.global().as_window(),
                            this.channel_count,
                            this.length,
                            *this.context.SampleRate(),
                            Some(processed_audio.as_slice()));
                        (*this.pending_rendering_promise.borrow_mut()).take().unwrap().resolve_native(&buffer);
                        let global = &this.global();
                        let window = global.as_window();
                        let event = OfflineAudioCompletionEvent::new(&window,
                                                                     atom!("complete"),
                                                                     EventBubbles::DoesNotBubble,
                                                                     EventCancelable::NotCancelable,
                                                                     &buffer);
                        event.upcast::<Event>().fire(this.upcast());
                    }),
                    &canceller,
                );
            })
            .unwrap();

        if self.context.audio_context_impl().resume().is_err() {
            promise.reject_error(Error::Type("Could not start offline rendering".to_owned()));
        }

        promise
    }
}
<commit_msg>Handle case when OfflineAudioContext doesn't receive all the buffers<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 dom::audiobuffer::{AudioBuffer, MAX_SAMPLE_RATE, MIN_SAMPLE_RATE};
use dom::audionode::MAX_CHANNEL_COUNT;
use dom::baseaudiocontext::{BaseAudioContext, BaseAudioContextOptions};
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextBinding::BaseAudioContextMethods;
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding;
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding::OfflineAudioContextMethods;
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding::OfflineAudioContextOptions;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::num::Finite;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::root::DomRoot;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::offlineaudiocompletionevent::OfflineAudioCompletionEvent;
use dom::promise::Promise;
use dom::window::Window;
use dom_struct::dom_struct;
use servo_media::audio::context::OfflineAudioContextOptions as ServoMediaOfflineAudioContextOptions;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread::Builder;
use task_source::{TaskSource, TaskSourceName};

#[dom_struct]
pub struct OfflineAudioContext {
    context: BaseAudioContext,
    channel_count: u32,
    length: u32,
    rendering_started: Cell<bool>,
    #[ignore_malloc_size_of = "promises are hard"]
    pending_rendering_promise: DomRefCell<Option<Rc<Promise>>>,
}

impl OfflineAudioContext {
    #[allow(unrooted_must_root)]
    fn new_inherited(channel_count: u32, length: u32, sample_rate: f32) -> OfflineAudioContext {
        let options = ServoMediaOfflineAudioContextOptions {
            channels: channel_count as u8,
            length: length as usize,
            sample_rate,
        };
        let context =
            BaseAudioContext::new_inherited(BaseAudioContextOptions::OfflineAudioContext(options));
        OfflineAudioContext {
            context,
            channel_count,
            length,
            rendering_started: Cell::new(false),
            pending_rendering_promise: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    fn new(
        window: &Window,
        channel_count: u32,
        length: u32,
        sample_rate: f32,
    ) -> Fallible<DomRoot<OfflineAudioContext>> {
        if channel_count > MAX_CHANNEL_COUNT ||
            channel_count <= 0 ||
            length <= 0 ||
            sample_rate < MIN_SAMPLE_RATE ||
            sample_rate > MAX_SAMPLE_RATE
        {
            return Err(Error::NotSupported);
        }
        let context = OfflineAudioContext::new_inherited(channel_count, length, sample_rate);
        Ok(reflect_dom_object(
            Box::new(context),
            window,
            OfflineAudioContextBinding::Wrap,
        ))
    }

    pub fn Constructor(
        window: &Window,
        options: &OfflineAudioContextOptions,
    ) -> Fallible<DomRoot<OfflineAudioContext>> {
        OfflineAudioContext::new(
            window,
            options.numberOfChannels,
            options.length,
            *options.sampleRate,
        )
    }

    pub fn Constructor_(
        window: &Window,
        number_of_channels: u32,
        length: u32,
        sample_rate: Finite<f32>,
    ) -> Fallible<DomRoot<OfflineAudioContext>> {
        OfflineAudioContext::new(window, number_of_channels, length, *sample_rate)
    }
}

impl OfflineAudioContextMethods for OfflineAudioContext {
    // https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-oncomplete
    event_handler!(complete, GetOncomplete, SetOncomplete);

    // https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-length
    fn Length(&self) -> u32 {
        self.length
    }

    // https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-startrendering
    #[allow(unrooted_must_root)]
    fn StartRendering(&self) -> Rc<Promise> {
        let promise = Promise::new(&self.global());
        if self.rendering_started.get() {
            promise.reject_error(Error::InvalidState);
            return promise;
        }
        self.rendering_started.set(true);

        *self.pending_rendering_promise.borrow_mut() = Some(promise.clone());

        let processed_audio = Arc::new(Mutex::new(Vec::new()));
        let processed_audio_ = processed_audio.clone();
        let (sender, receiver) = mpsc::channel();
        let sender = Mutex::new(sender);
        self.context
            .audio_context_impl()
            .set_eos_callback(Box::new(move |buffer| {
                processed_audio_
                    .lock()
                    .unwrap()
                    .extend_from_slice((*buffer).as_ref());
                let _ = sender.lock().unwrap().send(());
            }));

        let this = Trusted::new(self);
        let global = self.global();
        let window = global.as_window();
        let task_source = window.dom_manipulation_task_source();
        let canceller = window.task_canceller(TaskSourceName::DOMManipulation);
        Builder::new()
            .name("OfflineAudioContextResolver".to_owned())
            .spawn(move || {
                let _ = receiver.recv();
                let _ = task_source.queue_with_canceller(
                    task!(resolve: move || {
                        let this = this.root();
                        let processed_audio = processed_audio.lock().unwrap();
                        let mut processed_audio: Vec<_> = processed_audio
                            .chunks(this.length as usize)
                            .map(|channel| channel.to_vec())
                            .collect();
                        // it can end up being empty if the task failed
                        if processed_audio.len() != this.length as usize {
                            processed_audio.resize(this.length as usize, Vec::new())
                        }
                        let buffer = AudioBuffer::new(
                            &this.global().as_window(),
                            this.channel_count,
                            this.length,
                            *this.context.SampleRate(),
                            Some(processed_audio.as_slice()));
                        (*this.pending_rendering_promise.borrow_mut()).take().unwrap().resolve_native(&buffer);
                        let global = &this.global();
                        let window = global.as_window();
                        let event = OfflineAudioCompletionEvent::new(&window,
                                                                     atom!("complete"),
                                                                     EventBubbles::DoesNotBubble,
                                                                     EventCancelable::NotCancelable,
                                                                     &buffer);
                        event.upcast::<Event>().fire(this.upcast());
                    }),
                    &canceller,
                );
            })
            .unwrap();

        if self.context.audio_context_impl().resume().is_err() {
            promise.reject_error(Error::Type("Could not start offline rendering".to_owned()));
        }

        promise
    }
}
<|endoftext|>


<commit_before>use actix_rt;
use chrono::Local;
use qapro_rs::qaconnector::clickhouse::ckclient;
use qapro_rs::qaconnector::clickhouse::ckclient::DataConnector;
use qapro_rs::qaenv::localenv::CONFIG;
use qapro_rs::qalog::log4::init_log4;
use qapro_rs::qaprotocol::mifi::qafastkline::QAKlineBase;
use qapro_rs::qautil::tradedate::get_n_day_before_date9;
use serde_json::Value;
use std::collections::HashMap;
#[actix_rt::main]
async fn main() {
    let c = ckclient::QACKClient::init();

    let codelist = ["600010.XSHG", "300002.XSHE"];
    let mut hisdata = c
        .get_stock_adv(Vec::from(codelist), "2021-07-11", "2021-12-22", "day")
        .await
        .unwrap();

    println!("qadatastruct {}", hisdata.data);
    hisdata.save_cache();
}
<commit_msg>#cache save/load<commit_after>use actix_rt;
use chrono::Local;
use qapro_rs::qaconnector::clickhouse::ckclient;
use qapro_rs::qaconnector::clickhouse::ckclient::DataConnector;
use qapro_rs::qadatastruct::stockday::QADataStruct_StockDay;
use qapro_rs::qaenv::localenv::CONFIG;
use qapro_rs::qalog::log4::init_log4;
use qapro_rs::qaprotocol::mifi::qafastkline::QAKlineBase;
use qapro_rs::qautil::tradedate::get_n_day_before_date9;
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::format;

#[actix_rt::main]
async fn main() {
    let c = ckclient::QACKClient::init();

    let codelist = ["600010.XSHG", "300002.XSHE"];
    let mut hisdata = c
        .get_stock_adv(Vec::from(codelist), "2021-07-11", "2021-12-22", "day")
        .await
        .unwrap();

    println!("qadatastruct {}", hisdata.data);
    hisdata.save_cache();

    let cache_file = format!("{}stockday.parquet", &CONFIG.DataPath.cache);

    let data = QADataStruct_StockDay::new_from_parquet(cache_file.as_str());

    println!("load cache file {:#?}", data.data);
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use bindgen::builder;
use cmake::Config;
use subprocess::Exec;

use std::{
    env,
    fs::{copy, create_dir_all},
    path::{Path, PathBuf},
};

const MYSQL_DIR: &str = "mysql-8.0.19";

const LIBSTRINGS: &str = "libstrings.a";
const LIBWRAPPER: &str = "libwrapper.a";

/// Returns mysql source path.
fn download_mysql(out_dir: &Path) {
    const URL: &str = "https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.19.tar.gz";
    {
        Exec::cmd("curl").arg("-s").arg("-L").arg(URL)
            | Exec::cmd("tar").arg("-xz").arg("-C").arg(out_dir)
    }
    .join()
    .unwrap();
}

fn make_libstrings(mysql_src: &Path, libdir: &Path, out_dir: &Path) {
    println!("cargo:warning=Building libstrings.a");
    let dst = Config::new(mysql_src)
        .define("FORCE_INSOURCE_BUILD", "1")
        .define("DOWNLOAD_BOOST", "1")
        .define("WITH_BOOST", out_dir)
        .build_target("strings")
        .build();

    copy(
        dst.join("build")
            .join("archive_output_directory")
            .join(LIBSTRINGS),
        Path::new(libdir).join(LIBSTRINGS),
    )
    .unwrap();
}

fn make_libwrapper(mysql_src: &Path, libdir: &Path, out_dir: &Path) {
    println!("cargo:warning=Building libwrapper.a");
    cc::Build::new()
        .file("wrapper.cc")
        .cpp(true)
        .include(out_dir.join("build").join("include"))
        .include(mysql_src.join("include"))
        .flag("-std=c++14")
        .cargo_metadata(false)
        .compile("wrapper");
    copy(out_dir.join(LIBWRAPPER), Path::new(libdir).join(LIBWRAPPER)).unwrap();
}

#[allow(dead_code)]
fn gen_bindings(mysql_src: &Path, out_dir: &Path) {
    let builder = builder()
        .header("wrapper.hh")
        .clang_arg(format!(
            "-I{}",
            out_dir.join("build").join("include").display()
        ))
        .clang_arg(format!("-I{}", mysql_src.join("include").display()))
        .clang_arg("-std=c++14")
        .clang_arg("-stdlib=libc++")
        .whitelist_recursively(true)
        .whitelist_type("decimal_t")
        .whitelist_function("c_string2decimal")
        .whitelist_function("c_decimal2string")
        .whitelist_function("c_decimal2bin")
        .whitelist_function("c_bin2decimal")
        .whitelist_function("c_decimal_bin_size")
        .derive_debug(false)
        .use_core()
        .generate_comments(false);
    let bindings = builder
        .generate()
        .expect("unable to generate bindings for libstrings");
    bindings
        .write_to_file(
            Path::new("src")
                .join("binlog")
                .join("decimal")
                .join("test")
                .join("libstrings_bindings.rs"),
        )
        .expect("Couldn't write bindings!");
}

fn main() {
    if cfg!(all(feature = "test", unix)) {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
        let mysql_src = out_dir.join(MYSQL_DIR);
        let libdir = Path::new("lib")
            .join(Path::new(&env::var("CARGO_CFG_TARGET_OS").unwrap()))
            .join(env::var("CARGO_CFG_TARGET_ARCH").unwrap());

        create_dir_all(&libdir).unwrap();

        if !libdir.join(LIBSTRINGS).exists() || !libdir.join(LIBWRAPPER).exists() {
            if !mysql_src.exists() {
                println!("cargo:warning=Downloading MySql source distribution");
                download_mysql(&out_dir);
            }
            make_libstrings(&mysql_src, &libdir, &out_dir);
            make_libwrapper(&mysql_src, &libdir, &out_dir);
            // uncomment if mysql-server version bumped
            // gen_bindings(&mysql_src, &out_dir);
        }

        println!("cargo:rustc-link-search=native={}", libdir.display());
        println!("cargo:rustc-link-lib=wrapper");
        println!("cargo:rustc-link-lib=strings");
    }
}
<commit_msg>Fix deprecation warnings<commit_after>// Copyright (c) 2020 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use bindgen::builder;
use cmake::Config;
use subprocess::Exec;

use std::{
    env,
    fs::{copy, create_dir_all},
    path::{Path, PathBuf},
};

const MYSQL_DIR: &str = "mysql-8.0.19";

const LIBSTRINGS: &str = "libstrings.a";
const LIBWRAPPER: &str = "libwrapper.a";

/// Returns mysql source path.
fn download_mysql(out_dir: &Path) {
    const URL: &str = "https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.19.tar.gz";
    {
        Exec::cmd("curl").arg("-s").arg("-L").arg(URL)
            | Exec::cmd("tar").arg("-xz").arg("-C").arg(out_dir)
    }
    .join()
    .unwrap();
}

fn make_libstrings(mysql_src: &Path, libdir: &Path, out_dir: &Path) {
    println!("cargo:warning=Building libstrings.a");
    let dst = Config::new(mysql_src)
        .define("FORCE_INSOURCE_BUILD", "1")
        .define("DOWNLOAD_BOOST", "1")
        .define("WITH_BOOST", out_dir)
        .build_target("strings")
        .build();

    copy(
        dst.join("build")
            .join("archive_output_directory")
            .join(LIBSTRINGS),
        Path::new(libdir).join(LIBSTRINGS),
    )
    .unwrap();
}

fn make_libwrapper(mysql_src: &Path, libdir: &Path, out_dir: &Path) {
    println!("cargo:warning=Building libwrapper.a");
    cc::Build::new()
        .file("wrapper.cc")
        .cpp(true)
        .include(out_dir.join("build").join("include"))
        .include(mysql_src.join("include"))
        .flag("-std=c++14")
        .cargo_metadata(false)
        .compile("wrapper");
    copy(out_dir.join(LIBWRAPPER), Path::new(libdir).join(LIBWRAPPER)).unwrap();
}

#[allow(dead_code)]
fn gen_bindings(mysql_src: &Path, out_dir: &Path) {
    let builder = builder()
        .header("wrapper.hh")
        .clang_arg(format!(
            "-I{}",
            out_dir.join("build").join("include").display()
        ))
        .clang_arg(format!("-I{}", mysql_src.join("include").display()))
        .clang_arg("-std=c++14")
        .clang_arg("-stdlib=libc++")
        .allowlist_recursively(true)
        .allowlist_type("decimal_t")
        .allowlist_function("c_string2decimal")
        .allowlist_function("c_decimal2string")
        .allowlist_function("c_decimal2bin")
        .allowlist_function("c_bin2decimal")
        .allowlist_function("c_decimal_bin_size")
        .derive_debug(false)
        .use_core()
        .generate_comments(false);
    let bindings = builder
        .generate()
        .expect("unable to generate bindings for libstrings");
    bindings
        .write_to_file(
            Path::new("src")
                .join("binlog")
                .join("decimal")
                .join("test")
                .join("libstrings_bindings.rs"),
        )
        .expect("Couldn't write bindings!");
}

fn main() {
    if cfg!(all(feature = "test", unix)) {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
        let mysql_src = out_dir.join(MYSQL_DIR);
        let libdir = Path::new("lib")
            .join(Path::new(&env::var("CARGO_CFG_TARGET_OS").unwrap()))
            .join(env::var("CARGO_CFG_TARGET_ARCH").unwrap());

        create_dir_all(&libdir).unwrap();

        if !libdir.join(LIBSTRINGS).exists() || !libdir.join(LIBWRAPPER).exists() {
            if !mysql_src.exists() {
                println!("cargo:warning=Downloading MySql source distribution");
                download_mysql(&out_dir);
            }
            make_libstrings(&mysql_src, &libdir, &out_dir);
            make_libwrapper(&mysql_src, &libdir, &out_dir);
            // uncomment if mysql-server version bumped
            // gen_bindings(&mysql_src, &out_dir);
        }

        println!("cargo:rustc-link-search=native={}", libdir.display());
        println!("cargo:rustc-link-lib=wrapper");
        println!("cargo:rustc-link-lib=strings");
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::Path;

fn main() {
    cc::Build::new()
        .file("src/cxx.cc")
        .cpp(true)
        .cpp_link_stdlib(None) // linked via link-cplusplus crate
        .flag_if_supported(cxxbridge_flags::STD)
        .compile("cxxbridge05");
    println!("cargo:rerun-if-changed=src/cxx.cc");
    println!("cargo:rerun-if-changed=include/cxx.h");
    println!("cargo:rustc-cfg=built_with_cargo");
    if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") {
        let cxx_h = Path::new(&manifest_dir).join("include").join("cxx.h");
        println!("cargo:HEADER={}", cxx_h.to_string_lossy());
    }
}
<commit_msg>Warn on unsupported rust toolchain version<commit_after>use std::env;
use std::path::Path;
use std::process::Command;

fn main() {
    cc::Build::new()
        .file("src/cxx.cc")
        .cpp(true)
        .cpp_link_stdlib(None) // linked via link-cplusplus crate
        .flag_if_supported(cxxbridge_flags::STD)
        .compile("cxxbridge05");

    println!("cargo:rerun-if-changed=src/cxx.cc");
    println!("cargo:rerun-if-changed=include/cxx.h");
    println!("cargo:rustc-cfg=built_with_cargo");

    if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") {
        let cxx_h = Path::new(&manifest_dir).join("include").join("cxx.h");
        println!("cargo:HEADER={}", cxx_h.to_string_lossy());
    }

    if let Some(rustc) = rustc_version() {
        if rustc.minor < 43 {
            println!("cargo:warning=The cxx crate requires a rustc version 1.43.0 or newer.");
            println!(
                "cargo:warning=You appear to be building with: {}",
                rustc.version,
            );
        }
    }
}

struct RustVersion {
    version: String,
    minor: u32,
}

fn rustc_version() -> Option<RustVersion> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = String::from_utf8(output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    let minor = pieces.next()?.parse().ok()?;
    Some(RustVersion { version, minor })
}
<|endoftext|>


<commit_before>extern crate cmake;

fn add_squirrel_defines(cmake_cfg: &mut cmake::Config) {
	cmake_cfg.define("INSTALL_LIB_DIR", ".");
    if cfg!(feature = "use_double") {
        cmake_cfg.define("SQUSEDOUBLE", "");
    }
    if cfg!(feature = "use_unicode") {
        cmake_cfg.define("SQUNICODE", "");
    }
}

fn export_squirrel(path: std::path::PathBuf) {
    println!("cargo:rustc-link-search=native={}", path.display());
    println!("cargo:rustc-link-lib=static=squirrel_static");
}

fn main() {
    let mut cmake_cfg = cmake::Config::new("squirrel");
    add_squirrel_defines(&mut cmake_cfg);
    let path = cmake_cfg.build();
    export_squirrel(path);
}
<commit_msg>Fix stdc++ linkage on linux<commit_after>extern crate cmake;

fn add_squirrel_defines(cmake_cfg: &mut cmake::Config) {
	cmake_cfg.define("INSTALL_LIB_DIR", ".");
    if cfg!(feature = "use_double") {
        cmake_cfg.define("SQUSEDOUBLE", "");
    }
    if cfg!(feature = "use_unicode") {
        cmake_cfg.define("SQUNICODE", "");
    }
}

fn export_squirrel(path: std::path::PathBuf) {
    println!("cargo:rustc-link-search=native={}", path.display());
    println!("cargo:rustc-link-lib=static=squirrel_static");
    println!("cargo:rustc-flags=-l dylib=stdc++");
}

fn main() {
    let mut cmake_cfg = cmake::Config::new("squirrel");
    add_squirrel_defines(&mut cmake_cfg);
    let path = cmake_cfg.build();
    export_squirrel(path);
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(io)]
#![feature(os)]
#![feature(path)]

use std::old_io::{Command, File, Truncate, Write};
use std::os;

fn main()
{
    let dir = Path::new(os::getenv("OUT_DIR").unwrap());
    let out_file = dir.join("codegen");
    Command::new("gcc").arg("src/codegen/type_size.c")
                       .arg("-o")
                       .arg(out_file.as_vec())
                       .status()
                       .unwrap();
    let code = Command::new(out_file.as_vec()).output().unwrap();
    if !code.status.success() {
        panic!("{}", String::from_utf8_lossy(code.error.as_slice()));
    }
    let mut f = File::open_mode(&Path::new("src/type_size.rs"), Truncate, Write);
    f.write_all(code.output.as_slice()).unwrap();
}
<commit_msg>Update build script with the new path module<commit_after>#![feature(core)]
#![feature(io)]
#![feature(env)]
#![feature(path)]

use std::old_io::{Command, File, Truncate, Write};
use std::path;
use std::env;

fn main()
{
    let dir_var = env::var("OUT_DIR").unwrap();
    let dir = path::Path::new(&dir_var);
    let out_file = dir.join("codegen");
    Command::new("gcc").arg("src/codegen/type_size.c")
                       .arg("-o")
                       .arg(out_file.to_str().unwrap())
                       .status()
                       .unwrap();
    let code = Command::new(out_file.to_str().unwrap()).output().unwrap();
    if !code.status.success() {
        panic!("{}", String::from_utf8_lossy(code.error.as_slice()));
    }
    let mut f = File::open_mode(&Path::new("src/type_size.rs"), Truncate, Write);
    f.write_all(code.output.as_slice()).unwrap();
}
<|endoftext|>


<commit_before>// build.rs

use std::process::Command;
use std::env;
use std::path::Path;

fn main() {
//    let out_dir = env::var("OUT_DIR").unwrap();
//  println!("cargo:rustc-link-search=native={}", out_dir);
	println!("cargo:rustc-link-search=native=lib");
    // println!("cargo:rustc-link-lib=static=hello");
}<commit_msg>Remove unused imports<commit_after>// build.rs

fn main() {
//    let out_dir = env::var("OUT_DIR").unwrap();
//  println!("cargo:rustc-link-search=native={}", out_dir);
	println!("cargo:rustc-link-search=native=lib");
    // println!("cargo:rustc-link-lib=static=hello");
}<|endoftext|>


<commit_before>use std::io;
use std::io::File;
use std::io::fs;
use std::io::fs::PathExtensions;

fn main() {
    let dst = Path::new(concat!(env!("HOME"), "/.xr3wm/config.rs"));
    if !dst.exists() {
      fs::mkdir(&dst.dir_path(), io::USER_RWX);
      let mut f = File::create(&dst).unwrap();
      f.write_str(
"pub fn get_config() -> Config {
  Default::default()
}").unwrap();
    }
}<commit_msg>fixed lifetime in auto generated config<commit_after>use std::io;
use std::io::File;
use std::io::fs;
use std::io::fs::PathExtensions;

fn main() {
    let dst = Path::new(concat!(env!("HOME"), "/.xr3wm/config.rs"));
    if !dst.exists() {
      fs::mkdir(&dst.dir_path(), io::USER_RWX);
      let mut f = File::create(&dst).unwrap();
      f.write_str(
"pub fn get_config<'a>() -> Config<'a> {
  Default::default()
}").unwrap();
    }
}<|endoftext|>