<commit_before>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use ref_into::{RefInto, MutInto};
use types::{Color, Viewport, Capability, GLError};
use buffer::{BufferBinder, ContextBufferExt, BufferBinderRef, BufferBinderMut};
use framebuffer::{FramebufferBinder, ContextFramebufferExt};
use program::{ProgramBinder, ContextProgramExt, ProgramAttrib};
use renderbuffer::{RenderbufferBinder, ContextRenderbufferExt};
use shader::ContextShaderExt;
use texture::ContextTextureExt;
use texture_units::TextureUnits;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub type ContextRef<'a> = ContextOf<&'a BufferBinder,
                                    &'a FramebufferBinder,
                                    &'a ProgramBinder,
                                    &'a RenderbufferBinder,
                                    &'a TextureUnits>;

pub type ContextMut<'a> = ContextOf<&'a mut BufferBinder,
                                    &'a mut FramebufferBinder,
                                    &'a mut ProgramBinder,
                                    &'a mut RenderbufferBinder,
                                    &'a mut TextureUnits>;

pub type ContextSubRef<'a> = ContextOf<BufferBinderRef<'a>,
                                       &'a FramebufferBinder,
                                       &'a ProgramBinder,
                                       &'a RenderbufferBinder,
                                       &'a TextureUnits>;

pub type ContextSubMut<'a> = ContextOf<BufferBinderMut<'a>,
                                       &'a mut FramebufferBinder,
                                       &'a mut ProgramBinder,
                                       &'a mut RenderbufferBinder,
                                       &'a mut TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn ref_into<'a,
                    BI = &'a B,
                    FI = &'a F,
                    PI = &'a P,
                    RI = &'a R,
                    TI = &'a T>
                   (&'a self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: RefInto<'a, BI>,
              F: RefInto<'a, FI>,
              P: RefInto<'a, PI>,
              R: RefInto<'a, RI>,
              T: RefInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }

    pub fn mut_into<'a,
                    BI = &'a mut B,
                    FI = &'a mut F,
                    PI = &'a mut P,
                    RI = &'a mut R,
                    TI = &'a mut T>
                   (&'a mut self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: MutInto<'a, BI>,
              F: MutInto<'a, FI>,
              P: MutInto<'a, PI>,
              R: MutInto<'a, RI>,
              T: MutInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }

    pub fn split_buffers(self)
        -> (B, ContextOf<(), F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: (),
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn join_buffers<JB>(self, buffers: JB)
        -> ContextOf<JB, F, P, R, T>
    {
        ContextOf {
            buffers: buffers,
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn map_buffers<FN, BO>(self, f: FN) -> ContextOf<BO, F, P, R, T>
        where FN: FnOnce(B) -> BO
    {
        ContextOf {
            buffers: f(self.buffers),
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn split_framebuffer(self)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: (),
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_program(self)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: (),
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_renderbuffer(self)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: (),
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_tex_units(self)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: ()
            }
        )
    }
}

pub trait ContextExt {
    fn clear_color(&mut self, color: Color);
    fn enable(&mut self, cap: Capability);
    fn disable(&mut self, cap: Capability);
    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib);
    fn viewport(&self, viewport: Viewport);
}

impl ContextExt for ContextOf {
    fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }
}

pub mod ext {
    pub use ContextExt;
    pub use ContextBufferExt;
    pub use ContextFramebufferExt;
    pub use ContextProgramExt;
    pub use ContextRenderbufferExt;
    pub use ContextShaderExt;
    pub use ContextTextureExt;
}

pub trait AContext: ContextExt +
                    ContextBufferExt +
                    ContextFramebufferExt +
                    ContextProgramExt +
                    ContextRenderbufferExt +
                    ContextShaderExt +
                    ContextTextureExt
{

}

impl<C> AContext for C
    where C: ContextExt +
             ContextBufferExt +
             ContextFramebufferExt +
             ContextProgramExt +
             ContextRenderbufferExt +
             ContextShaderExt +
             ContextTextureExt
{

}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a mut ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<commit_msg>Replace `AContext` generic impl with concrete impl for `ContextOf<...>`<commit_after>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use ref_into::{RefInto, MutInto};
use types::{Color, Viewport, Capability, GLError};
use buffer::{BufferBinder, ContextBufferExt, BufferBinderRef, BufferBinderMut};
use framebuffer::{FramebufferBinder, ContextFramebufferExt};
use program::{ProgramBinder, ContextProgramExt, ProgramAttrib};
use renderbuffer::{RenderbufferBinder, ContextRenderbufferExt};
use shader::ContextShaderExt;
use texture::ContextTextureExt;
use texture_units::TextureUnits;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub type ContextRef<'a> = ContextOf<&'a BufferBinder,
                                    &'a FramebufferBinder,
                                    &'a ProgramBinder,
                                    &'a RenderbufferBinder,
                                    &'a TextureUnits>;

pub type ContextMut<'a> = ContextOf<&'a mut BufferBinder,
                                    &'a mut FramebufferBinder,
                                    &'a mut ProgramBinder,
                                    &'a mut RenderbufferBinder,
                                    &'a mut TextureUnits>;

pub type ContextSubRef<'a> = ContextOf<BufferBinderRef<'a>,
                                       &'a FramebufferBinder,
                                       &'a ProgramBinder,
                                       &'a RenderbufferBinder,
                                       &'a TextureUnits>;

pub type ContextSubMut<'a> = ContextOf<BufferBinderMut<'a>,
                                       &'a mut FramebufferBinder,
                                       &'a mut ProgramBinder,
                                       &'a mut RenderbufferBinder,
                                       &'a mut TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn ref_into<'a,
                    BI = &'a B,
                    FI = &'a F,
                    PI = &'a P,
                    RI = &'a R,
                    TI = &'a T>
                   (&'a self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: RefInto<'a, BI>,
              F: RefInto<'a, FI>,
              P: RefInto<'a, PI>,
              R: RefInto<'a, RI>,
              T: RefInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }

    pub fn mut_into<'a,
                    BI = &'a mut B,
                    FI = &'a mut F,
                    PI = &'a mut P,
                    RI = &'a mut R,
                    TI = &'a mut T>
                   (&'a mut self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: MutInto<'a, BI>,
              F: MutInto<'a, FI>,
              P: MutInto<'a, PI>,
              R: MutInto<'a, RI>,
              T: MutInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }

    pub fn split_buffers(self)
        -> (B, ContextOf<(), F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: (),
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn join_buffers<JB>(self, buffers: JB)
        -> ContextOf<JB, F, P, R, T>
    {
        ContextOf {
            buffers: buffers,
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn map_buffers<FN, BO>(self, f: FN) -> ContextOf<BO, F, P, R, T>
        where FN: FnOnce(B) -> BO
    {
        ContextOf {
            buffers: f(self.buffers),
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn split_framebuffer(self)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: (),
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_program(self)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: (),
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_renderbuffer(self)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: (),
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_tex_units(self)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: ()
            }
        )
    }
}

pub trait ContextExt {
    fn clear_color(&mut self, color: Color);
    fn enable(&mut self, cap: Capability);
    fn disable(&mut self, cap: Capability);
    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib);
    fn viewport(&self, viewport: Viewport);
}

impl ContextExt for ContextOf {
    fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }
}

pub mod ext {
    pub use ContextExt;
    pub use ContextBufferExt;
    pub use ContextFramebufferExt;
    pub use ContextProgramExt;
    pub use ContextRenderbufferExt;
    pub use ContextShaderExt;
    pub use ContextTextureExt;
}

pub trait AContext: ContextExt +
                    ContextBufferExt +
                    ContextFramebufferExt +
                    ContextProgramExt +
                    ContextRenderbufferExt +
                    ContextShaderExt +
                    ContextTextureExt
{

}

impl AContext for ContextOf {

}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a mut ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<|endoftext|>


<commit_before>extern crate ndarray;

use ndarray_ext::NdArray;
use std::collections::hash_map::HashMap;
use tensor::Tensor;

#[derive(Clone)]
/// What is necessary to run computation graphs.
///
/// `Context` object is used to:
///
/// * create shared variable tensors
/// * create constant tensors
/// * run computation graphs actually
///
/// When a computation graph is evaluated, all the variables/constants in the graph
/// must be generated in the same context; otherwise will panic.
///
/// ```
/// extern crate ndarray;
/// extern crate autograd as ag;
///
/// // new Context
/// let mut ctx = ag::Context::new();
///
/// let ref x = ag::placeholder(&[2]);
/// // make shared variable in the context
/// let ref v = ag::variable(ndarray::arr1(&[2., 2.]), &mut ctx);
/// let ref b = ag::ones(&[2]);
/// let ref z = x + v + b;
///
/// // fills placeholder
/// ctx.feed_input(x, ndarray::arr1(&[1., 1.]));
///
/// // eval
/// assert_eq!(z.eval(&mut ctx).as_slice().unwrap(), &[4., 4.]);
/// assert!(ctx.variables.contains_key(v));
/// assert_eq!(ctx.variables.get(v).unwrap(), &ndarray::arr1(&[2., 2.]).into_dyn());
/// ```
pub struct Context {
    /// Variables generated in this context.
    /// Each array can be obtained by using corresponding `Tensor` object.
    pub variables: HashMap<Tensor, NdArray>,

    #[doc(hidden)]
    // Evaluation results of tensors in this context are stored in this map.
    // Each output are cleared after evaluation.
    pub outputs: HashMap<Tensor, Result<NdArray, ::OpComputeErrorStatus>>,
}

impl Context {
    /// Creates new context object.
    pub fn new() -> Context
    {
        Context { variables: HashMap::new(), outputs: HashMap::new() }
    }

    /// Returns all variables in this context.
    pub fn list_vars(&self) -> Vec<&Tensor>
    {
        self.variables.keys().collect::<Vec<_>>()
    }

    /// Skips `arr`'s shape checking.
    pub fn feed_input_unchecked<T>(&mut self, placeholder: &Tensor, arr: ndarray::Array<f32, T>)
    where
        T: ndarray::Dimension,
    {
        if "PH" != placeholder.op.name() {
            panic!(
                "Don't call `feed_input_unchecked` with non placeholder, got: {}",
                placeholder.op.name()
            )
        }
        self.outputs.insert(placeholder.clone(), Ok(arr.into_dyn()));
    }

    // TODO: Input's shape checking for dynamic placeholder.
    pub fn feed_input<T>(&mut self, placeholder: &Tensor, arr: ndarray::Array<f32, T>)
    where
        T: ndarray::Dimension,
    {
        if "PH" != placeholder.op.name() {
            panic!(
                "Don't call `feed_input` with non placeholder, got: {}",
                placeholder.op.name()
            )
        }
        // check arr's shape
        if let Some(ref inner) = placeholder.shape {
            // unwrap is safe (guaranteed by ops::placeholder's implementation)
            assert_eq!(
                inner.eval(self).as_slice().unwrap(),
                arr.shape()
                    .iter()
                    .map(|&a| a as f32)
                    .collect::<Vec<_>>()
                    .as_slice()
            )
        }
        self.outputs.insert(placeholder.clone(), Ok(arr.into_dyn()));
    }

    /// Same as [autograd::variable](../ops/fn.variable.html).
    pub fn variable<T>(&mut self, arr: ndarray::Array<f32, T>) -> Tensor
    where
        T: ndarray::Dimension,
    {
        ::ops::variable(arr, self)
    }

    /// Same as [autograd::constant](../ops/fn.constant.html).
    pub fn constant<T>(&mut self, arr: ndarray::Array<f32, T>) -> Tensor
    where
        T: ndarray::Dimension,
    {
        ::ops::constant(arr, self)
    }
}
<commit_msg>Remove context.rs<commit_after><|endoftext|>


<commit_before>#![macro_use]
use super::Core;
macro_rules! ir_dx {
	($e:ident) => (($e.ir >> 9 & 7) as usize);
}
macro_rules! ir_dy {
	($e:ident) => (($e.ir & 7) as usize);
}
macro_rules! ir_ax {
	($e:ident) => (8+($e.ir >> 9 & 7) as usize);
}
macro_rules! ir_ay {
	($e:ident) => (8+($e.ir & 7) as usize);
}
macro_rules! dx {
	($e:ident) => ($e.dar[ir_dx!($e)]);
}
macro_rules! dy {
	($e:ident) => ($e.dar[ir_dy!($e)]);
}
macro_rules! ax {
	($e:ident) => ($e.dar[ir_ax!($e)]);
}
macro_rules! ay {
	($e:ident) => ($e.dar[ir_ay!($e)]);
}
macro_rules! mask_out_above_8 {
	($e:expr) => ($e & 0xff)
}
macro_rules! mask_out_below_8 {
	($e:expr) => ($e & !0xff)
}
macro_rules! low_nibble {
	($e:expr) => ($e & 0x0f);
}
macro_rules! high_nibble {
	($e:expr) => ($e & 0xf0);
}
macro_rules! true1 {
	($e:expr) => (if $e {1} else {0})
}
macro_rules! not1 {
	($e:expr) => (true1!($e == 0))
}

pub mod fake {
	use super::super::Core;

	pub fn set_d0(core: &mut Core) {
		core.dar[0] = 0xabcd;
	}

	pub fn set_d1(core: &mut Core) {
		core.dar[1] = 0xbcde;
	}

	pub fn set_dx(core: &mut Core) {
		dx!(core) = 0xcdef;
	}

	use super::super::InstructionSet;
	use super::illegal;
	const SET_DX_0: usize = 0b0100_0000_0000_0000;

	pub fn instruction_set() -> InstructionSet {
		// Covers all possible IR values (64k entries)
		let mut handler: InstructionSet = Vec::with_capacity(0x10000);
		for _ in 0..0x10000 { handler.push(illegal); }
		handler[0xA] = set_d0;
		handler[0xB] = set_d1;
		for i in 0..8 {
			let opcode = SET_DX_0 | (i << 9);
			// println!("{:x}", opcode);
			handler[opcode] = set_dx;
		}
		handler
	}
}

pub fn illegal(core: &mut Core) {
	panic!("Illegal instruction {:04x} at {:08x}", core.ir, core.pc-2);
}

use std::num::Wrapping;

pub fn abcd_8_common(core: &mut Core, dst: u32, src: u32) -> u32 {
	let mut res = low_nibble!(src) + low_nibble!(dst) + core.x_flag_as_1();

	// m68ki_cpu.v_flag = ~res;
	core.v_flag = !res;

	// if(res > 9)
	//  res += 6;
	if res > 9 {
		res += 6;
	}
	// res += ((src) & 0xf0) + ((dst) & 0xf0);
	res += high_nibble!(src) + high_nibble!(dst);
	// m68ki_cpu.x_flag = m68ki_cpu.c_flag = (res > 0x99) << 8;
	core.c_flag = true1!(res > 0x99) << 8;
	core.x_flag = core.c_flag;

	if core.c_flag > 0 {
		res = (Wrapping(res) - Wrapping(0xa0)).0;
	}

	// m68ki_cpu.v_flag &= res;
	// m68ki_cpu.n_flag = (res);
	core.v_flag &= res;
	core.n_flag = res;

	// res = ((res) & 0xff);
	// m68ki_cpu.not_z_flag |= res;
	res = mask_out_above_8!(res);
	core.not_z_flag |= res;
	res
}

// All instructions are ported from https://github.com/kstenerud/Musashi
// First real instruction
pub fn abcd_8_rr(core: &mut Core) {
	// unsigned int* r_dst = &(m68ki_cpu.dar[(m68ki_cpu.ir >> 9) & 7]);
	// unsigned int src = (m68ki_cpu.dar[m68ki_cpu.ir & 7]);
	// unsigned int dst = *r_dst;
	// unsigned int res = ((src) & 0x0f) + ((dst) & 0x0f) + ((m68ki_cpu.x_flag>>8)&1);
	let dst = dx!(core);
	let src = dy!(core);
	let res = abcd_8_common(core, dst, src);
	// *r_dst = ((*r_dst) & ~0xff) | res;
	dx!(core) = mask_out_below_8!(dst) | res;
}

use ram::{AddressBus, SUPERVISOR_DATA};
fn ea_predecrement_8(core: &mut Core, reg_ndx: usize) -> u32 {
	// pre-decrement
	core.dar[reg_ndx] -= match reg_ndx {
		15 => 2, // A7 is kept even
		 _ => 1
	};
	core.dar[reg_ndx]
}
fn ea_ay_pd_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	ea_predecrement_8(core, reg_ndx)
}
fn ea_ax_pd_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ax!(core);
	ea_predecrement_8(core, reg_ndx)
}
fn oper_ay_pd_8(core: &mut Core) -> u32 {
	let ea = ea_ay_pd_8(core);
	core.mem.read_byte(SUPERVISOR_DATA, ea)
}
fn oper_ax_pd_8(core: &mut Core) -> (u32, u32) {
	let ea = ea_ax_pd_8(core);
	(core.mem.read_byte(SUPERVISOR_DATA, ea), ea)
}

// Second real instruction
pub fn abcd_8_mm(core: &mut Core) {
	// unsigned int src = OPER_AY_PD_8();
	let src = oper_ay_pd_8(core);
	// unsigned int ea = (--((m68ki_cpu.dar+8)[(m68ki_cpu.ir >> 9) & 7]));
	// unsigned int dst = m68ki_read_8_fc (ea, m68ki_cpu.s_flag | m68ki_address_space);
	let (dst, ea) = oper_ax_pd_8(core);

	let res = abcd_8_common(core, dst, src);

	// m68ki_write_8_fc (ea, m68ki_cpu.s_flag | 1, res);		*/
	core.mem.write_byte(SUPERVISOR_DATA, ea, res);
}

use super::Handler;
#[allow(dead_code)]
struct OpcodeHandler {
	mask: u32,
	matching: u32,
	name: String,
	handler: Handler
}

use super::InstructionSet;
macro_rules! op_entry {
    ($mask:expr, $matching:expr, $handler:ident) => (OpcodeHandler { mask: $mask, matching: $matching, handler: $handler, name: stringify!($handler).to_string() })
}
pub const MASK_OUT_X_Y: u32 = 0b1111000111111000; // masks out X and Y register bits (????xxx??????yyy)
pub const OP_ABCD_8_RR: u32 = 0xc100;
pub const OP_ABCD_8_MM: u32 = 0xc108;
pub fn instruction_set() -> InstructionSet {
	// Covers all possible IR values (64k entries)
	let mut handler: InstructionSet = Vec::with_capacity(0x10000);
	for _ in 0..0x10000 { handler.push(illegal); }
	//let handler = [illegal].iter().cycle().take(0x10000).collect::<InstructionSet>();
	// (0..0x10000).map(|_| illegal).collect::<InstructionSet>();
	// the optable contains opcode mask, matching mask and the corresponding handler + name
	let optable = vec![
		op_entry!(MASK_OUT_X_Y, OP_ABCD_8_RR, abcd_8_rr),
		op_entry!(MASK_OUT_X_Y, OP_ABCD_8_MM, abcd_8_mm),
	];
	for op in optable {
		for opcode in 0..0x10000 {
			if (opcode & op.mask) == op.matching {
				// println!("{:16b}: {}", opcode, op.name);
				handler[opcode as usize] = op.handler;
			}
		}
	}
	handler
}

#[cfg(test)]
mod tests {
	use super::super::Core;
	use super::{oper_ax_pd_8, oper_ay_pd_8};
	use ram::{AddressBus, SUPERVISOR_DATA};

	#[test]
	fn low_nibble() {
		assert_eq!(0x0a, low_nibble!(0xba));
	}
	#[test]
	fn high_nibble() {
		assert_eq!(0xb0, high_nibble!(0xba));
	}
	#[test]
	fn mask_out_below_8() {
		assert_eq!(0x2bcdef00, mask_out_below_8!(0x2bcdef73));
	}
	#[test]
	fn mask_out_above_8() {
		assert_eq!(0xf1, mask_out_above_8!(0x2bcdeff1));
	}
	#[test]
	fn dx_and_dy() {
		let mut core = Core::new(0x40);
		core.dar[0] = 0x00;
		core.dar[1] = 0x11;
		core.dar[2] = 0x22;
		core.dar[3] = 0x33;
		core.dar[4] = 0x44;
		core.dar[5] = 0x55;
		core.dar[6] = 0x66;
		core.dar[7] = 0x77;

		core.ir = 0b1111_1001_1111_1010; // X=4, Y=2
		assert_eq!(0x22, dy!(core));
		assert_eq!(0x44, dx!(core));

		core.ir = 0b1111_1011_1111_1110; // X=5, Y=6
		assert_eq!(0x66, dy!(core));
		assert_eq!(0x55, dx!(core));
	}
	#[test]
	fn predecrement_ax() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			let addr: u32 = 0x200 + 4*i;
			core.dar[8+i as usize] = addr;
			// write just before where A0-A7 points
			let adjustment = if i == 7 {2} else {1};
			core.mem.write_byte(SUPERVISOR_DATA, addr - adjustment, 0x11*i);
		}
		core.ir = 0b1111_1001_1111_1010; // X=4, Y=2
		let core = &mut core;

		assert_eq!(512+4*4, core.dar[8+4]);
		let (dst, ea) = oper_ax_pd_8(core);
		assert_eq!(0x44, dst);
		assert_eq!(512+4*4-1, core.dar[8+4]);
		assert_eq!(512+4*4-1, ea);

		core.ir = 0b1111_1111_1111_1111; // X=7, Y=7
		assert_eq!(512+4*7, core.dar[8+7]);
		let (dst, ea) = oper_ax_pd_8(core);
		assert_eq!(0x77, dst);
		// A7 is kept even
		assert_eq!(512+4*7-2, core.dar[8+7]);
		assert_eq!(512+4*7-2, ea);
	}
	#[test]
	fn predecrement_ay() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			let addr: u32 = 0x200 + 4*i;
			core.dar[8+i as usize] = addr;
			// write just before where A0-A7 points
			let adjustment = if i == 7 {2} else {1};
			core.mem.write_byte(SUPERVISOR_DATA, addr - adjustment, 0x11*i);
		}

		core.ir = 0b1111_1001_1111_1010; // X=4, Y=2
		let core = &mut core;
		assert_eq!(512+4*2, core.dar[8+2]);
		assert_eq!(0x22, oper_ay_pd_8(core));
		assert_eq!(512+4*2-1, core.dar[8+2]);

		core.ir = 0b1111_1011_1111_1111; // X=5, Y=7
		assert_eq!(512+4*7, core.dar[8+7]);
		assert_eq!(0x77, oper_ay_pd_8(core));
		// A7 is kept even
		assert_eq!(512+4*7-2, core.dar[8+7]);
	}
}
<commit_msg>Use current address space for oper_a?_pd_8<commit_after>#![macro_use]
use super::Core;
macro_rules! ir_dx {
	($e:ident) => (($e.ir >> 9 & 7) as usize);
}
macro_rules! ir_dy {
	($e:ident) => (($e.ir & 7) as usize);
}
macro_rules! ir_ax {
	($e:ident) => (8+($e.ir >> 9 & 7) as usize);
}
macro_rules! ir_ay {
	($e:ident) => (8+($e.ir & 7) as usize);
}
macro_rules! dx {
	($e:ident) => ($e.dar[ir_dx!($e)]);
}
macro_rules! dy {
	($e:ident) => ($e.dar[ir_dy!($e)]);
}
macro_rules! ax {
	($e:ident) => ($e.dar[ir_ax!($e)]);
}
macro_rules! ay {
	($e:ident) => ($e.dar[ir_ay!($e)]);
}
macro_rules! mask_out_above_8 {
	($e:expr) => ($e & 0xff)
}
macro_rules! mask_out_below_8 {
	($e:expr) => ($e & !0xff)
}
macro_rules! low_nibble {
	($e:expr) => ($e & 0x0f);
}
macro_rules! high_nibble {
	($e:expr) => ($e & 0xf0);
}
macro_rules! true1 {
	($e:expr) => (if $e {1} else {0})
}
macro_rules! not1 {
	($e:expr) => (true1!($e == 0))
}

pub mod fake {
	use super::super::Core;

	pub fn set_d0(core: &mut Core) {
		core.dar[0] = 0xabcd;
	}

	pub fn set_d1(core: &mut Core) {
		core.dar[1] = 0xbcde;
	}

	pub fn set_dx(core: &mut Core) {
		dx!(core) = 0xcdef;
	}

	use super::super::InstructionSet;
	use super::illegal;
	const SET_DX_0: usize = 0b0100_0000_0000_0000;

	pub fn instruction_set() -> InstructionSet {
		// Covers all possible IR values (64k entries)
		let mut handler: InstructionSet = Vec::with_capacity(0x10000);
		for _ in 0..0x10000 { handler.push(illegal); }
		handler[0xA] = set_d0;
		handler[0xB] = set_d1;
		for i in 0..8 {
			let opcode = SET_DX_0 | (i << 9);
			// println!("{:x}", opcode);
			handler[opcode] = set_dx;
		}
		handler
	}
}

pub fn illegal(core: &mut Core) {
	panic!("Illegal instruction {:04x} at {:08x}", core.ir, core.pc-2);
}

use std::num::Wrapping;

pub fn abcd_8_common(core: &mut Core, dst: u32, src: u32) -> u32 {
	let mut res = low_nibble!(src) + low_nibble!(dst) + core.x_flag_as_1();

	// m68ki_cpu.v_flag = ~res;
	core.v_flag = !res;

	// if(res > 9)
	//  res += 6;
	if res > 9 {
		res += 6;
	}
	// res += ((src) & 0xf0) + ((dst) & 0xf0);
	res += high_nibble!(src) + high_nibble!(dst);
	// m68ki_cpu.x_flag = m68ki_cpu.c_flag = (res > 0x99) << 8;
	core.c_flag = true1!(res > 0x99) << 8;
	core.x_flag = core.c_flag;

	if core.c_flag > 0 {
		res = (Wrapping(res) - Wrapping(0xa0)).0;
	}

	// m68ki_cpu.v_flag &= res;
	// m68ki_cpu.n_flag = (res);
	core.v_flag &= res;
	core.n_flag = res;

	// res = ((res) & 0xff);
	// m68ki_cpu.not_z_flag |= res;
	res = mask_out_above_8!(res);
	core.not_z_flag |= res;
	res
}

// All instructions are ported from https://github.com/kstenerud/Musashi
// First real instruction
pub fn abcd_8_rr(core: &mut Core) {
	// unsigned int* r_dst = &(m68ki_cpu.dar[(m68ki_cpu.ir >> 9) & 7]);
	// unsigned int src = (m68ki_cpu.dar[m68ki_cpu.ir & 7]);
	// unsigned int dst = *r_dst;
	// unsigned int res = ((src) & 0x0f) + ((dst) & 0x0f) + ((m68ki_cpu.x_flag>>8)&1);
	let dst = dx!(core);
	let src = dy!(core);
	let res = abcd_8_common(core, dst, src);
	// *r_dst = ((*r_dst) & ~0xff) | res;
	dx!(core) = mask_out_below_8!(dst) | res;
}

use ram::{AddressBus, SUPERVISOR_DATA, USER_DATA};
fn ea_predecrement_8(core: &mut Core, reg_ndx: usize) -> u32 {
	// pre-decrement
	core.dar[reg_ndx] -= match reg_ndx {
		15 => 2, // A7 is kept even
		 _ => 1
	};
	core.dar[reg_ndx]
}
fn ea_ay_pd_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	ea_predecrement_8(core, reg_ndx)
}
fn ea_ax_pd_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ax!(core);
	ea_predecrement_8(core, reg_ndx)
}
fn oper_ay_pd_8(core: &mut Core) -> u32 {
	let ea = ea_ay_pd_8(core);
	let address_space = if core.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
	core.mem.read_byte(address_space, ea)
}
fn oper_ax_pd_8(core: &mut Core) -> (u32, u32) {
	let ea = ea_ax_pd_8(core);
	let address_space = if core.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
	(core.mem.read_byte(address_space, ea), ea)
}

// Second real instruction
pub fn abcd_8_mm(core: &mut Core) {
	// unsigned int src = OPER_AY_PD_8();
	let src = oper_ay_pd_8(core);
	// unsigned int ea = (--((m68ki_cpu.dar+8)[(m68ki_cpu.ir >> 9) & 7]));
	// unsigned int dst = m68ki_read_8_fc (ea, m68ki_cpu.s_flag | m68ki_address_space);
	let (dst, ea) = oper_ax_pd_8(core);

	let res = abcd_8_common(core, dst, src);

	// m68ki_write_8_fc (ea, m68ki_cpu.s_flag | 1, res);		*/
	core.mem.write_byte(SUPERVISOR_DATA, ea, res);
}

use super::Handler;
#[allow(dead_code)]
struct OpcodeHandler {
	mask: u32,
	matching: u32,
	name: String,
	handler: Handler
}

use super::InstructionSet;
macro_rules! op_entry {
    ($mask:expr, $matching:expr, $handler:ident) => (OpcodeHandler { mask: $mask, matching: $matching, handler: $handler, name: stringify!($handler).to_string() })
}
pub const MASK_OUT_X_Y: u32 = 0b1111000111111000; // masks out X and Y register bits (????xxx??????yyy)
pub const OP_ABCD_8_RR: u32 = 0xc100;
pub const OP_ABCD_8_MM: u32 = 0xc108;
pub fn instruction_set() -> InstructionSet {
	// Covers all possible IR values (64k entries)
	let mut handler: InstructionSet = Vec::with_capacity(0x10000);
	for _ in 0..0x10000 { handler.push(illegal); }
	//let handler = [illegal].iter().cycle().take(0x10000).collect::<InstructionSet>();
	// (0..0x10000).map(|_| illegal).collect::<InstructionSet>();
	// the optable contains opcode mask, matching mask and the corresponding handler + name
	let optable = vec![
		op_entry!(MASK_OUT_X_Y, OP_ABCD_8_RR, abcd_8_rr),
		op_entry!(MASK_OUT_X_Y, OP_ABCD_8_MM, abcd_8_mm),
	];
	for op in optable {
		for opcode in 0..0x10000 {
			if (opcode & op.mask) == op.matching {
				// println!("{:16b}: {}", opcode, op.name);
				handler[opcode as usize] = op.handler;
			}
		}
	}
	handler
}

#[cfg(test)]
mod tests {
	use super::super::Core;
	use super::{oper_ax_pd_8, oper_ay_pd_8};
	use ram::{AddressBus, SUPERVISOR_DATA};

	#[test]
	fn low_nibble() {
		assert_eq!(0x0a, low_nibble!(0xba));
	}
	#[test]
	fn high_nibble() {
		assert_eq!(0xb0, high_nibble!(0xba));
	}
	#[test]
	fn mask_out_below_8() {
		assert_eq!(0x2bcdef00, mask_out_below_8!(0x2bcdef73));
	}
	#[test]
	fn mask_out_above_8() {
		assert_eq!(0xf1, mask_out_above_8!(0x2bcdeff1));
	}
	#[test]
	fn dx_and_dy() {
		let mut core = Core::new(0x40);
		core.dar[0] = 0x00;
		core.dar[1] = 0x11;
		core.dar[2] = 0x22;
		core.dar[3] = 0x33;
		core.dar[4] = 0x44;
		core.dar[5] = 0x55;
		core.dar[6] = 0x66;
		core.dar[7] = 0x77;

		core.ir = 0b1111_1001_1111_1010; // X=4, Y=2
		assert_eq!(0x22, dy!(core));
		assert_eq!(0x44, dx!(core));

		core.ir = 0b1111_1011_1111_1110; // X=5, Y=6
		assert_eq!(0x66, dy!(core));
		assert_eq!(0x55, dx!(core));
	}
	#[test]
	fn predecrement_ax() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			let addr: u32 = 0x200 + 4*i;
			core.dar[8+i as usize] = addr;
			// write just before where A0-A7 points
			let adjustment = if i == 7 {2} else {1};
			core.mem.write_byte(SUPERVISOR_DATA, addr - adjustment, 0x11*i);
		}
		core.ir = 0b1111_1001_1111_1010; // X=4, Y=2
		let core = &mut core;

		assert_eq!(512+4*4, core.dar[8+4]);
		let (dst, ea) = oper_ax_pd_8(core);
		assert_eq!(0x44, dst);
		assert_eq!(512+4*4-1, core.dar[8+4]);
		assert_eq!(512+4*4-1, ea);

		core.ir = 0b1111_1111_1111_1111; // X=7, Y=7
		assert_eq!(512+4*7, core.dar[8+7]);
		let (dst, ea) = oper_ax_pd_8(core);
		assert_eq!(0x77, dst);
		// A7 is kept even
		assert_eq!(512+4*7-2, core.dar[8+7]);
		assert_eq!(512+4*7-2, ea);
	}
	#[test]
	fn predecrement_ay() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			let addr: u32 = 0x200 + 4*i;
			core.dar[8+i as usize] = addr;
			// write just before where A0-A7 points
			let adjustment = if i == 7 {2} else {1};
			core.mem.write_byte(SUPERVISOR_DATA, addr - adjustment, 0x11*i);
		}

		core.ir = 0b1111_1001_1111_1010; // X=4, Y=2
		let core = &mut core;
		assert_eq!(512+4*2, core.dar[8+2]);
		assert_eq!(0x22, oper_ay_pd_8(core));
		assert_eq!(512+4*2-1, core.dar[8+2]);

		core.ir = 0b1111_1011_1111_1111; // X=5, Y=7
		assert_eq!(512+4*7, core.dar[8+7]);
		assert_eq!(0x77, oper_ay_pd_8(core));
		// A7 is kept even
		assert_eq!(512+4*7-2, core.dar[8+7]);
	}
}
<|endoftext|>


<commit_before>use std::{collections::VecDeque, io::SeekFrom};

use log::{debug, error, trace};
use nom::{self, Err, IResult, Needed, Offset};

use av_data::{packet::Packet, params::*, rational::Rational64, timeinfo::TimeInfo};
use av_format::{
    buffer::Buffered,
    common::GlobalInfo,
    demuxer::{Demuxer, Descr, Descriptor, Event},
    error::*,
    stream::Stream,
};

use crate::{
    ebml::{self, custom_error, ebml_header, EBMLHeader},
    elements::{
        segment, segment_element, simple_block, Cluster, Info, SeekHead, SegmentElement,
        TrackEntry, Tracks,
    },
};

#[derive(Debug, Clone, Default)]
pub struct MkvDemuxer {
    pub header: Option<EBMLHeader>,
    pub seek_head: Option<SeekHead>,
    pub info: Option<Info>,
    pub tracks: Option<Tracks>,
    pub queue: VecDeque<Event>,
    pub blockstream: Vec<u8>,
    pub params: Option<DemuxerParams>,
}

#[derive(Debug, Clone, Default)]
pub struct DemuxerParams {
    pub track_numbers: Option<Vec<u64>>,
}

impl MkvDemuxer {
    pub fn new() -> MkvDemuxer {
        MkvDemuxer {
            header: None,
            seek_head: None,
            info: None,
            tracks: None,
            queue: VecDeque::new(),
            blockstream: Vec::new(),
            params: None,
        }
    }

    pub fn with_params(params: DemuxerParams) -> MkvDemuxer {
        MkvDemuxer {
            params: Some(params),
            ..Default::default()
        }
    }

    pub fn parse_until_tracks<'a>(
        &mut self,
        original_input: &'a [u8],
    ) -> IResult<&'a [u8], (), ebml::Error<'a>> {
        let (i1, header) = ebml_header(original_input)?;

        self.header = Some(header);

        let (mut input, _) = segment(i1)?;

        self.seek_head = None;
        self.info = None;
        self.tracks = None;

        loop {
            if self.seek_head.is_some() && self.info.is_some() && self.tracks.is_some() {
                return Ok((input, ()));
            }

            let (i3, element) = segment_element(input)?;

            match element {
                SegmentElement::SeekHead(s) => {
                    trace!("got seek head: {:#?}", s);
                    if self.seek_head.is_some() {
                        return Err(Err::Error(custom_error(input, 1)));
                    } else {
                        self.seek_head = Some(s);
                    }
                }
                SegmentElement::Info(i) => {
                    trace!("got info: {:#?}", i);
                    if self.info.is_some() {
                        return Err(Err::Error(custom_error(input, 1)));
                    } else {
                        self.info = Some(i);
                    }
                }
                SegmentElement::Tracks(t) => {
                    trace!("got tracks: {:#?}", t);
                    if self.tracks.is_some() {
                        return Err(Err::Error(custom_error(input, 1)));
                    } else {
                        let mut t = t;

                        // Only keep tracks we're interested in
                        if let Some(params) = &self.params {
                            if let Some(track_numbers) = &params.track_numbers {
                                t.tracks = t
                                    .tracks
                                    .into_iter()
                                    .filter(|tr| track_numbers.contains(&tr.track_number))
                                    .collect();
                            }
                        };

                        self.tracks = Some(t);
                    }
                }
                el => {
                    debug!("got element: {:#?}", el);
                }
            }

            input = i3;
        }
    }
}

impl Demuxer for MkvDemuxer {
    fn read_headers(&mut self, buf: &Box<dyn Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom> {
        match self.parse_until_tracks(buf.data()) {
            Ok((i, _)) => {
                info.duration = self
                    .info
                    .as_ref()
                    .and_then(|info| info.duration)
                    .map(|d| d as u64);
                if let Some(ref mut t) = self.tracks {
                    for tr in t.tracks.iter_mut() {
                        info.add_stream(track_to_stream(self.info.as_ref().unwrap(), tr));
                        tr.stream_index = info.streams.last().unwrap().index;
                    }
                }
                Ok(SeekFrom::Current(buf.data().offset(i) as i64))
            }
            Err(Err::Incomplete(needed)) => {
                let sz = match needed {
                    Needed::Size(size) => buf.data().len() + usize::from(size),
                    _ => 1024,
                };
                Err(Error::MoreDataNeeded(sz))
            }
            e => {
                error!("{:?}", e);
                Err(Error::InvalidData)
            }
        }
    }

    fn read_event(&mut self, buf: &Box<dyn Buffered>) -> Result<(SeekFrom, Event)> {
        if let Some(event) = self.queue.pop_front() {
            Ok((SeekFrom::Current(0), event))
        } else {
            match segment_element(buf.data()) {
                Ok((i, element)) => {
                    let seek = SeekFrom::Current(buf.data().offset(i) as i64);
                    match element {
                        SegmentElement::Cluster(c) => {
                            //self.clusters.push(c);
                            debug!("got cluster element at timecode: {}", c.timecode);
                            let mut packets = c.generate_packets(self.tracks.as_ref().unwrap());
                            self.queue.extend(packets.drain(..));
                            if let Some(event) = self.queue.pop_front() {
                                return Ok((seek, event));
                            }
                        }
                        _el => {}
                    }
                    Ok((seek, Event::MoreDataNeeded(0)))
                }
                Err(Err::Incomplete(Needed::Size(size))) => {
                    Err(Error::MoreDataNeeded(usize::from(size)))
                }
                e => {
                    error!("{:?}", e);
                    Err(Error::InvalidData)
                }
            }
        }
    }
}

fn track_entry_codec_id(t: &TrackEntry) -> Option<String> {
    // TODO: Support V_QUICKTIME and V_MS/VFW/FOURCC
    match t.codec_id.as_ref() {
        "A_OPUS" => Some("opus".to_owned()),
        "A_VORBIS" => Some("vorbis".to_owned()),
        "V_AV1" => Some("av1".to_owned()),
        "V_VP8" => Some("vp8".to_owned()),
        "V_VP9" => Some("vp9".to_owned()),
        _ => None,
    }
}

fn track_entry_video_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref video) = t.video {
        let v = VideoInfo {
            width: video.pixel_width as usize,
            height: video.pixel_height as usize,
            // TODO parse Colour and/or CodecPrivate to extract the format
            format: None,
        };
        Some(MediaKind::Video(v))
    } else {
        None
    }
}

fn track_entry_audio_kind(t: &TrackEntry) -> Option<MediaKind> {
    use av_data::audiosample::*;
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref audio) = t.audio {
        let rate = if let Some(r) = audio.output_sampling_frequency {
            r
        } else {
            audio.sampling_frequency
        };
        // TODO: complete it
        let map = if audio.channel_positions.is_none() {
            Some(ChannelMap::default_map(audio.channels as usize))
        } else {
            unimplemented!("Convert matroska map to rust-av map")
        };
        let a = AudioInfo {
            rate: rate as usize,
            map,
            format: None,
        };
        Some(MediaKind::Audio(a))
    } else {
        None
    }
}

fn track_entry_media_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Use an enum for the track type
    match t.track_type {
        0x1 => track_entry_video_kind(t),
        0x2 => track_entry_audio_kind(t),
        _ => None,
    }
}

// TODO: make sure the timecode_scale isn't 0
pub fn track_to_stream(info: &Info, t: &TrackEntry) -> Stream {
    let num = if let Some(ts) = t.track_timecode_scale {
        (ts * info.timecode_scale as f64) as i64
    } else {
        info.timecode_scale as i64
    };

    Stream {
        id: t.track_uid as isize,
        index: t.stream_index,
        start: None,
        duration: t.default_duration,
        timebase: Rational64::new(num, 1000 * 1000 * 1000),
        // TODO: Extend CodecParams and fill it with the remaining information
        params: CodecParams {
            extradata: t.codec_private.clone(),
            bit_rate: 0,
            delay: t.codec_delay.unwrap_or(0) as usize,
            convergence_window: t.seek_pre_roll.unwrap_or(0) as usize,
            codec_id: track_entry_codec_id(t),
            kind: track_entry_media_kind(t),
        },
        user_private: None,
    }
}

impl<'a> Cluster<'a> {
    pub fn generate_packets(&self, tracks: &Tracks) -> Vec<Event> {
        let mut v = Vec::new();

        for block_data in self.simple_block.iter() {
            if let Ok((i, block)) = simple_block(block_data) {
                debug!("parsing simple block: {:?}", block);
                if let Some(index) = tracks.lookup(block.track_number) {
                    let packet = Packet {
                        data: i.into(),
                        t: TimeInfo {
                            pts: Some(i64::from(block.timecode)),
                            dts: None,
                            duration: None,
                            timebase: None,
                            user_private: None,
                        },
                        pos: None,
                        stream_index: index as isize,
                        is_key: block.keyframe,
                        is_corrupted: false,
                    };

                    v.push(Event::NewPacket(packet));
                }
            } else {
                error!("error parsing simple block");
            }
        }

        v
    }
}

struct Des {
    d: Descr,
}

impl Descriptor for Des {
    fn create(&self) -> Box<dyn Demuxer> {
        Box::new(MkvDemuxer::new())
    }
    fn describe(&self) -> &Descr {
        &self.d
    }
    fn probe(&self, data: &[u8]) -> u8 {
        match ebml_header(&data[..100]) {
            Ok(_) => 100,
            _ => 0,
        }
    }
}

pub const MKV_DESC: &dyn Descriptor = &Des {
    d: Descr {
        name: "matroska",
        demuxer: "mkv",
        description: "Nom-based Matroska demuxer",
        extensions: &["mkv", "webm", "mka"],
        mime: &["video/x-matroska", "audio/x-matroska"],
    },
};

#[cfg(test)]
#[allow(non_upper_case_globals)]
mod tests {
    use std::io::Cursor;

    use log::info;
    use nom::Offset;

    use av_format::{buffer::*, demuxer::Context};

    use super::*;

    const webm: &[u8] = include_bytes!("../assets/bbb-vp9-opus.webm");

    #[test]
    fn parse_headers() {
        let mut demuxer = MkvDemuxer::new();

        let res = demuxer.parse_until_tracks(webm);
        info!("got parsing res: {:?}", res);
        match res {
            Ok((i, _)) => {
                info!("offset: {} bytes", webm.offset(i));
            }
            e => {
                info!("could not parse: {:?}", e);
            }
        }

        info!("demuxer: {:#?}", demuxer);
    }

    #[test]
    fn parse_headers_incremental_buffer() {
        let mut demuxer = MkvDemuxer::new();

        for n in 100..2000 {
            let res = demuxer.parse_until_tracks(&webm[0..n]);
            match res {
                Ok(_) => info!("Size {} ok", n),
                Err(Err::Incomplete(needed)) => info!("Incomplete {} needs {:?}", n, needed),
                Err(e) => {
                    panic!("Error at size {}: {:?}", n, e);
                }
            }
        }
    }

    #[test]
    fn context() {
        let mut context = Context::new(
            Box::new(MkvDemuxer::new()),
            Box::new(AccReader::new(Cursor::new(webm))),
        );
        info!("read headers: {:?}", context.read_headers().unwrap());
        info!("streams: {:?}", context.info.streams);
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
    }
}
<commit_msg>Simplify demuxer code<commit_after>use std::{collections::VecDeque, io::SeekFrom};

use log::{debug, error, trace};
use nom::{self, Err, IResult, Needed, Offset};

use av_data::{packet::Packet, params::*, rational::Rational64, timeinfo::TimeInfo};
use av_format::{
    buffer::Buffered,
    common::GlobalInfo,
    demuxer::{Demuxer, Descr, Descriptor, Event},
    error::*,
    stream::Stream,
};

use crate::{
    ebml::{self, custom_error, ebml_header, EBMLHeader},
    elements::{
        segment, segment_element, simple_block, Cluster, Info, SeekHead, SegmentElement,
        TrackEntry, Tracks,
    },
};

#[derive(Debug, Clone, Default)]
pub struct MkvDemuxer {
    pub header: Option<EBMLHeader>,
    pub seek_head: Option<SeekHead>,
    pub info: Option<Info>,
    pub tracks: Option<Tracks>,
    pub queue: VecDeque<Event>,
    pub blockstream: Vec<u8>,
    pub params: Option<DemuxerParams>,
}

#[derive(Debug, Clone, Default)]
pub struct DemuxerParams {
    pub track_numbers: Option<Vec<u64>>,
}

impl MkvDemuxer {
    pub fn new() -> MkvDemuxer {
        MkvDemuxer {
            header: None,
            seek_head: None,
            info: None,
            tracks: None,
            queue: VecDeque::new(),
            blockstream: Vec::new(),
            params: None,
        }
    }

    pub fn with_params(params: DemuxerParams) -> MkvDemuxer {
        MkvDemuxer {
            params: Some(params),
            ..Default::default()
        }
    }

    pub fn parse_until_tracks<'a>(
        &mut self,
        original_input: &'a [u8],
    ) -> IResult<&'a [u8], (), ebml::Error<'a>> {
        let (i1, header) = ebml_header(original_input)?;

        self.header = Some(header);

        let (mut input, _) = segment(i1)?;

        self.seek_head = None;
        self.info = None;
        self.tracks = None;

        loop {
            if self.seek_head.is_some() && self.info.is_some() && self.tracks.is_some() {
                return Ok((input, ()));
            }

            let (i3, element) = segment_element(input)?;

            match element {
                SegmentElement::SeekHead(s) => {
                    trace!("got seek head: {:#?}", s);
                    self.seek_head = if self.seek_head.is_none() {
                        Some(s)
                    } else {
                        return Err(Err::Error(custom_error(input, 1)));
                    };
                }
                SegmentElement::Info(i) => {
                    trace!("got info: {:#?}", i);
                    self.info = if self.info.is_none() {
                        Some(i)
                    } else {
                        return Err(Err::Error(custom_error(input, 1)));
                    };
                }
                SegmentElement::Tracks(t) => {
                    trace!("got tracks: {:#?}", t);
                    self.tracks = if self.tracks.is_none() {
                        // Only keep tracks we're interested in
                        if let Some(params) = &self.params {
                            if let Some(track_numbers) = &params.track_numbers {
                                let mut t = t;
                                t.tracks = t
                                    .tracks
                                    .into_iter()
                                    .filter(|tr| track_numbers.contains(&tr.track_number))
                                    .collect();
                                Some(t)
                            } else {
                                None
                            }
                        } else {
                            None
                        }
                    } else {
                        return Err(Err::Error(custom_error(input, 1)));
                    };
                }
                el => {
                    debug!("got element: {:#?}", el);
                }
            }

            input = i3;
        }
    }
}

impl Demuxer for MkvDemuxer {
    fn read_headers(&mut self, buf: &Box<dyn Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom> {
        match self.parse_until_tracks(buf.data()) {
            Ok((i, _)) => {
                info.duration = self
                    .info
                    .as_ref()
                    .and_then(|info| info.duration)
                    .map(|d| d as u64);
                if let Some(ref mut t) = self.tracks {
                    for tr in t.tracks.iter_mut() {
                        info.add_stream(track_to_stream(self.info.as_ref().unwrap(), tr));
                        tr.stream_index = info.streams.last().unwrap().index;
                    }
                }
                Ok(SeekFrom::Current(buf.data().offset(i) as i64))
            }
            Err(Err::Incomplete(needed)) => {
                let sz = match needed {
                    Needed::Size(size) => buf.data().len() + usize::from(size),
                    _ => 1024,
                };
                Err(Error::MoreDataNeeded(sz))
            }
            e => {
                error!("{:?}", e);
                Err(Error::InvalidData)
            }
        }
    }

    fn read_event(&mut self, buf: &Box<dyn Buffered>) -> Result<(SeekFrom, Event)> {
        if let Some(event) = self.queue.pop_front() {
            Ok((SeekFrom::Current(0), event))
        } else {
            match segment_element(buf.data()) {
                Ok((i, element)) => {
                    let seek = SeekFrom::Current(buf.data().offset(i) as i64);
                    match element {
                        SegmentElement::Cluster(c) => {
                            //self.clusters.push(c);
                            debug!("got cluster element at timecode: {}", c.timecode);
                            let mut packets = c.generate_packets(self.tracks.as_ref().unwrap());
                            self.queue.extend(packets.drain(..));
                            if let Some(event) = self.queue.pop_front() {
                                return Ok((seek, event));
                            }
                        }
                        _el => {}
                    }
                    Ok((seek, Event::MoreDataNeeded(0)))
                }
                Err(Err::Incomplete(Needed::Size(size))) => {
                    Err(Error::MoreDataNeeded(usize::from(size)))
                }
                e => {
                    error!("{:?}", e);
                    Err(Error::InvalidData)
                }
            }
        }
    }
}

fn track_entry_codec_id(t: &TrackEntry) -> Option<String> {
    // TODO: Support V_QUICKTIME and V_MS/VFW/FOURCC
    match t.codec_id.as_ref() {
        "A_OPUS" => Some("opus".to_owned()),
        "A_VORBIS" => Some("vorbis".to_owned()),
        "V_AV1" => Some("av1".to_owned()),
        "V_VP8" => Some("vp8".to_owned()),
        "V_VP9" => Some("vp9".to_owned()),
        _ => None,
    }
}

fn track_entry_video_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref video) = t.video {
        let v = VideoInfo {
            width: video.pixel_width as usize,
            height: video.pixel_height as usize,
            // TODO parse Colour and/or CodecPrivate to extract the format
            format: None,
        };
        Some(MediaKind::Video(v))
    } else {
        None
    }
}

fn track_entry_audio_kind(t: &TrackEntry) -> Option<MediaKind> {
    use av_data::audiosample::*;
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref audio) = t.audio {
        let rate = if let Some(r) = audio.output_sampling_frequency {
            r
        } else {
            audio.sampling_frequency
        };
        // TODO: complete it
        let map = if audio.channel_positions.is_none() {
            Some(ChannelMap::default_map(audio.channels as usize))
        } else {
            unimplemented!("Convert matroska map to rust-av map")
        };
        let a = AudioInfo {
            rate: rate as usize,
            map,
            format: None,
        };
        Some(MediaKind::Audio(a))
    } else {
        None
    }
}

fn track_entry_media_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Use an enum for the track type
    match t.track_type {
        0x1 => track_entry_video_kind(t),
        0x2 => track_entry_audio_kind(t),
        _ => None,
    }
}

// TODO: make sure the timecode_scale isn't 0
pub fn track_to_stream(info: &Info, t: &TrackEntry) -> Stream {
    let num = if let Some(ts) = t.track_timecode_scale {
        (ts * info.timecode_scale as f64) as i64
    } else {
        info.timecode_scale as i64
    };

    Stream {
        id: t.track_uid as isize,
        index: t.stream_index,
        start: None,
        duration: t.default_duration,
        timebase: Rational64::new(num, 1000 * 1000 * 1000),
        // TODO: Extend CodecParams and fill it with the remaining information
        params: CodecParams {
            extradata: t.codec_private.clone(),
            bit_rate: 0,
            delay: t.codec_delay.unwrap_or(0) as usize,
            convergence_window: t.seek_pre_roll.unwrap_or(0) as usize,
            codec_id: track_entry_codec_id(t),
            kind: track_entry_media_kind(t),
        },
        user_private: None,
    }
}

impl<'a> Cluster<'a> {
    pub fn generate_packets(&self, tracks: &Tracks) -> Vec<Event> {
        let mut v = Vec::new();

        for block_data in self.simple_block.iter() {
            if let Ok((i, block)) = simple_block(block_data) {
                debug!("parsing simple block: {:?}", block);
                if let Some(index) = tracks.lookup(block.track_number) {
                    let packet = Packet {
                        data: i.into(),
                        t: TimeInfo {
                            pts: Some(i64::from(block.timecode)),
                            dts: None,
                            duration: None,
                            timebase: None,
                            user_private: None,
                        },
                        pos: None,
                        stream_index: index as isize,
                        is_key: block.keyframe,
                        is_corrupted: false,
                    };

                    v.push(Event::NewPacket(packet));
                }
            } else {
                error!("error parsing simple block");
            }
        }

        v
    }
}

struct Des {
    d: Descr,
}

impl Descriptor for Des {
    fn create(&self) -> Box<dyn Demuxer> {
        Box::new(MkvDemuxer::new())
    }
    fn describe(&self) -> &Descr {
        &self.d
    }
    fn probe(&self, data: &[u8]) -> u8 {
        match ebml_header(&data[..100]) {
            Ok(_) => 100,
            _ => 0,
        }
    }
}

pub const MKV_DESC: &dyn Descriptor = &Des {
    d: Descr {
        name: "matroska",
        demuxer: "mkv",
        description: "Nom-based Matroska demuxer",
        extensions: &["mkv", "webm", "mka"],
        mime: &["video/x-matroska", "audio/x-matroska"],
    },
};

#[cfg(test)]
#[allow(non_upper_case_globals)]
mod tests {
    use std::io::Cursor;

    use log::info;
    use nom::Offset;

    use av_format::{buffer::*, demuxer::Context};

    use super::*;

    const webm: &[u8] = include_bytes!("../assets/bbb-vp9-opus.webm");

    #[test]
    fn parse_headers() {
        let mut demuxer = MkvDemuxer::new();

        let res = demuxer.parse_until_tracks(webm);
        info!("got parsing res: {:?}", res);
        match res {
            Ok((i, _)) => {
                info!("offset: {} bytes", webm.offset(i));
            }
            e => {
                info!("could not parse: {:?}", e);
            }
        }

        info!("demuxer: {:#?}", demuxer);
    }

    #[test]
    fn parse_headers_incremental_buffer() {
        let mut demuxer = MkvDemuxer::new();

        for n in 100..2000 {
            let res = demuxer.parse_until_tracks(&webm[0..n]);
            match res {
                Ok(_) => info!("Size {} ok", n),
                Err(Err::Incomplete(needed)) => info!("Incomplete {} needs {:?}", n, needed),
                Err(e) => {
                    panic!("Error at size {}: {:?}", n, e);
                }
            }
        }
    }

    #[test]
    fn context() {
        let mut context = Context::new(
            Box::new(MkvDemuxer::new()),
            Box::new(AccReader::new(Cursor::new(webm))),
        );
        info!("read headers: {:?}", context.read_headers().unwrap());
        info!("streams: {:?}", context.info.streams);
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
        info!("event: {:?}", context.read_event().unwrap());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer-coding Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Result as IoResult;
use std::io::Write;

/// Splits the incoming data into HTTP chunks.
pub struct Encoder<W> where W: Write {
    // where to send the result
    output: W,

    // size of each chunk
    chunks_size: usize,

    // data waiting to be sent is stored here
    buffer: Vec<u8>,
}

impl<W> Encoder<W> where W: Write {
    pub fn new(output: W) -> Encoder<W> {
        Encoder::new_with_chunks_size(output, 8192)
    }

    pub fn new_with_chunks_size(output: W, chunks: usize) -> Encoder<W> {
        Encoder {
            output: output,
            chunks_size: chunks,
            buffer: Vec::with_capacity(0),
        }
    }
}

fn send<W>(output: &mut W, data: &[u8]) -> IoResult<()> where W: Write {
    try!(write!(output, "{:x}\r\n", data.len()));
    try!(output.write_all(data));
    try!(write!(output, "\r\n"));
    Ok(())
}

impl<W> Write for Encoder<W> where W: Write {
    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
        try!(self.buffer.write_all(buf));

        while self.buffer.len() >= self.chunks_size {
            let rest = {
                let (to_send, rest) = self.buffer.split_at_mut(self.chunks_size);
                try!(send(&mut self.output, to_send));
                rest.to_vec()
            };
            self.buffer = rest;
        }

        Ok(buf.len())
    }

    fn flush(&mut self) -> IoResult<()> {
        if self.buffer.len() == 0 {
            return Ok(());
        }

        try!(send(&mut self.output, &self.buffer));
        self.buffer.clear();
        Ok(())
    }
}

impl<W> Drop for Encoder<W> where W: Write {
    fn drop(&mut self) {
        self.flush().ok();
        send(&mut self.output, &[]).ok();
    }
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;
    use std::str::from_utf8;
    use super::Encoder;

    #[test]
    fn test() {
        let mut source = io::Cursor::new("hello world".to_string().into_bytes());
        let mut dest: Vec<u8> = vec![];

        {
            let mut encoder = Encoder::new_with_chunks_size(dest.by_ref(), 5);
            io::copy(&mut source, &mut encoder).unwrap();
        }

        let output = from_utf8(&dest).unwrap();

        assert_eq!(output, "5\r\nhello\r\n5\r\n worl\r\n1\r\nd\r\n0\r\n\r\n");
    }
}
<commit_msg>Shorten constructor name<commit_after>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer-coding Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Result as IoResult;
use std::io::Write;

/// Splits the incoming data into HTTP chunks.
pub struct Encoder<W> where W: Write {
    // where to send the result
    output: W,

    // size of each chunk
    chunks_size: usize,

    // data waiting to be sent is stored here
    buffer: Vec<u8>,
}

impl<W> Encoder<W> where W: Write {
    pub fn new(output: W) -> Encoder<W> {
        Encoder::with_chunks_size(output, 8192)
    }

    pub fn with_chunks_size(output: W, chunks: usize) -> Encoder<W> {
        Encoder {
            output: output,
            chunks_size: chunks,
            buffer: Vec::with_capacity(0),
        }
    }
}

fn send<W>(output: &mut W, data: &[u8]) -> IoResult<()> where W: Write {
    try!(write!(output, "{:x}\r\n", data.len()));
    try!(output.write_all(data));
    try!(write!(output, "\r\n"));
    Ok(())
}

impl<W> Write for Encoder<W> where W: Write {
    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
        try!(self.buffer.write_all(buf));

        while self.buffer.len() >= self.chunks_size {
            let rest = {
                let (to_send, rest) = self.buffer.split_at_mut(self.chunks_size);
                try!(send(&mut self.output, to_send));
                rest.to_vec()
            };
            self.buffer = rest;
        }

        Ok(buf.len())
    }

    fn flush(&mut self) -> IoResult<()> {
        if self.buffer.len() == 0 {
            return Ok(());
        }

        try!(send(&mut self.output, &self.buffer));
        self.buffer.clear();
        Ok(())
    }
}

impl<W> Drop for Encoder<W> where W: Write {
    fn drop(&mut self) {
        self.flush().ok();
        send(&mut self.output, &[]).ok();
    }
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;
    use std::str::from_utf8;
    use super::Encoder;

    #[test]
    fn test() {
        let mut source = io::Cursor::new("hello world".to_string().into_bytes());
        let mut dest: Vec<u8> = vec![];

        {
            let mut encoder = Encoder::with_chunks_size(dest.by_ref(), 5);
            io::copy(&mut source, &mut encoder).unwrap();
        }

        let output = from_utf8(&dest).unwrap();

        assert_eq!(output, "5\r\nhello\r\n5\r\n worl\r\n1\r\nd\r\n0\r\n\r\n");
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Result as IoResult;
use std::io::Write;

/// Splits the incoming data into HTTP chunks.
///
/// # Example
///
/// ```
/// use chunked_transfer::Encoder;
/// use std::io::Write;
///
/// let mut decoded = "hello world";
/// let mut encoded: Vec<u8> = vec![];
///
/// {
///     let mut encoder = Encoder::with_chunks_size(&mut encoded, 5);
///     encoder.write_all(decoded.as_bytes());
/// }
///
/// assert_eq!(encoded, b"5\r\nhello\r\n5\r\n worl\r\n1\r\nd\r\n0\r\n\r\n");
/// ```
pub struct Encoder<W>
where
    W: Write,
{
    // where to send the result
    output: W,

    // size of each chunk
    chunks_size: usize,

    // data waiting to be sent is stored here
    // This will always be at least 6 bytes long. The first 6 bytes
    // are reserved for the chunk size and \r\n.
    buffer: Vec<u8>,

    // Flushes the internal buffer after each write. This might be useful
    // if data should be sent immediately to downstream consumers
    flush_after_write: bool,
}

const MAX_CHUNK_SIZE: usize = std::u32::MAX as usize;
const HEADER_SIZE: usize = 6;

impl<W> Encoder<W>
where
    W: Write,
{
    pub fn new(output: W) -> Encoder<W> {
        Encoder::with_chunks_size(output, 8192)
    }

    pub fn with_chunks_size(output: W, chunks: usize) -> Encoder<W> {
        let chunks_size = chunks.min(MAX_CHUNK_SIZE);
        let mut encoder = Encoder {
            output,
            chunks_size,
            buffer: Vec::with_capacity(0),
            flush_after_write: false,
        };
        encoder.reset_buffer();
        encoder
    }

    pub fn with_flush_after_write(output: W) -> Encoder<W> {
        let mut encoder = Encoder {
            output,
            chunks_size: 8192,
            buffer: Vec::with_capacity(0),
            flush_after_write: true,
        };
        encoder.reset_buffer();
        encoder
    }

    fn reset_buffer(&mut self) {
        // Reset buffer
        self.buffer.clear();
        // Arbitrary bytes to take up space. Should never be seen in output.
        let placeholder: [u8; HEADER_SIZE] = *b"DACE\r\n";
        self.buffer.extend_from_slice(&placeholder);
    }

    fn buffer_empty(&self) -> bool {
        self.buffer.len() == HEADER_SIZE
    }

    fn send(&mut self) -> IoResult<()> {
        // Never send an empty buffer, because that would be interpreted
        // as the end of the stream, which we indicate explicitly on drop.
        if self.buffer_empty() {
            return Ok(());
        }
        // Prepend the length and \r\n to the buffer.
        let prelude = format!("{:x}\r\n", self.buffer.len() - HEADER_SIZE);
        let prelude = prelude.as_bytes();
        if prelude.len() > HEADER_SIZE {
            // This should never happen because MAX_CHUNK_SIZE of u32::MAX
            // can always be encoded in 4 hex bytes.
            panic!("invariant failed: prelude longer than HEADER_SIZE");
        }
        // Copy the prelude into the buffer. For small chunks, this won't necessarily
        // take up all the space that was reserved for the prelude.
        let offset = HEADER_SIZE - prelude.len();
        self.buffer[offset..HEADER_SIZE].clone_from_slice(&prelude);

        // Append the chunk-finishing \r\n to the buffer.
        self.buffer.write_all(b"\r\n")?;

        self.output.write_all(&self.buffer[offset..])?;
        self.reset_buffer();

        Ok(())
    }
}

impl<W> Write for Encoder<W>
where
    W: Write,
{
    fn write(&mut self, data: &[u8]) -> IoResult<usize> {
        let buffer_size = self.buffer.len() - HEADER_SIZE;
        let remaining_buffer_space = self.chunks_size - buffer_size;
        let bytes_to_buffer = std::cmp::min(remaining_buffer_space, data.len());
        self.buffer.extend_from_slice(&data[0..bytes_to_buffer]);
        let more_to_write: bool = bytes_to_buffer < data.len();
        if self.flush_after_write || more_to_write {
            self.send()?;
        }

        // If we didn't write the whole thing, keep working on it.
        if more_to_write {
            self.write_all(&data[bytes_to_buffer..])?;
        }
        Ok(data.len())
    }

    fn flush(&mut self) -> IoResult<()> {
        self.send()
    }
}

impl<W> Drop for Encoder<W>
where
    W: Write,
{
    fn drop(&mut self) {
        self.flush().ok();
        write!(self.output, "0\r\n\r\n").ok();
    }
}

#[cfg(test)]
mod test {
    use super::Encoder;
    use std::io;
    use std::io::Write;
    use std::str::from_utf8;

    #[test]
    fn test() {
        let mut source = io::Cursor::new("hello world".to_string().into_bytes());
        let mut dest: Vec<u8> = vec![];

        {
            let mut encoder = Encoder::with_chunks_size(dest.by_ref(), 5);
            io::copy(&mut source, &mut encoder).unwrap();
            assert!(!encoder.buffer_empty());
        }

        let output = from_utf8(&dest).unwrap();

        assert_eq!(output, "5\r\nhello\r\n5\r\n worl\r\n1\r\nd\r\n0\r\n\r\n");
    }
    #[test]
    fn flush_after_write() {
        let mut source = io::Cursor::new("hello world".to_string().into_bytes());
        let mut dest: Vec<u8> = vec![];

        {
            let mut encoder = Encoder::with_flush_after_write(dest.by_ref());
            io::copy(&mut source, &mut encoder).unwrap();
            // The internal buffer has been flushed.
            assert!(encoder.buffer_empty());
        }

        let output = from_utf8(&dest).unwrap();

        assert_eq!(output, "b\r\nhello world\r\n0\r\n\r\n");
    }
}
<commit_msg>Prefix boolean method with is_<commit_after>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Result as IoResult;
use std::io::Write;

/// Splits the incoming data into HTTP chunks.
///
/// # Example
///
/// ```
/// use chunked_transfer::Encoder;
/// use std::io::Write;
///
/// let mut decoded = "hello world";
/// let mut encoded: Vec<u8> = vec![];
///
/// {
///     let mut encoder = Encoder::with_chunks_size(&mut encoded, 5);
///     encoder.write_all(decoded.as_bytes());
/// }
///
/// assert_eq!(encoded, b"5\r\nhello\r\n5\r\n worl\r\n1\r\nd\r\n0\r\n\r\n");
/// ```
pub struct Encoder<W>
where
    W: Write,
{
    // where to send the result
    output: W,

    // size of each chunk
    chunks_size: usize,

    // data waiting to be sent is stored here
    // This will always be at least 6 bytes long. The first 6 bytes
    // are reserved for the chunk size and \r\n.
    buffer: Vec<u8>,

    // Flushes the internal buffer after each write. This might be useful
    // if data should be sent immediately to downstream consumers
    flush_after_write: bool,
}

const MAX_CHUNK_SIZE: usize = std::u32::MAX as usize;
const HEADER_SIZE: usize = 6;

impl<W> Encoder<W>
where
    W: Write,
{
    pub fn new(output: W) -> Encoder<W> {
        Encoder::with_chunks_size(output, 8192)
    }

    pub fn with_chunks_size(output: W, chunks: usize) -> Encoder<W> {
        let chunks_size = chunks.min(MAX_CHUNK_SIZE);
        let mut encoder = Encoder {
            output,
            chunks_size,
            buffer: Vec::with_capacity(0),
            flush_after_write: false,
        };
        encoder.reset_buffer();
        encoder
    }

    pub fn with_flush_after_write(output: W) -> Encoder<W> {
        let mut encoder = Encoder {
            output,
            chunks_size: 8192,
            buffer: Vec::with_capacity(0),
            flush_after_write: true,
        };
        encoder.reset_buffer();
        encoder
    }

    fn reset_buffer(&mut self) {
        // Reset buffer
        self.buffer.clear();
        // Arbitrary bytes to take up space. Should never be seen in output.
        let placeholder: [u8; HEADER_SIZE] = *b"DACE\r\n";
        self.buffer.extend_from_slice(&placeholder);
    }

    fn is_buffer_empty(&self) -> bool {
        self.buffer.len() == HEADER_SIZE
    }

    fn send(&mut self) -> IoResult<()> {
        // Never send an empty buffer, because that would be interpreted
        // as the end of the stream, which we indicate explicitly on drop.
        if self.is_buffer_empty() {
            return Ok(());
        }
        // Prepend the length and \r\n to the buffer.
        let prelude = format!("{:x}\r\n", self.buffer.len() - HEADER_SIZE);
        let prelude = prelude.as_bytes();
        if prelude.len() > HEADER_SIZE {
            // This should never happen because MAX_CHUNK_SIZE of u32::MAX
            // can always be encoded in 4 hex bytes.
            panic!("invariant failed: prelude longer than HEADER_SIZE");
        }
        // Copy the prelude into the buffer. For small chunks, this won't necessarily
        // take up all the space that was reserved for the prelude.
        let offset = HEADER_SIZE - prelude.len();
        self.buffer[offset..HEADER_SIZE].clone_from_slice(&prelude);

        // Append the chunk-finishing \r\n to the buffer.
        self.buffer.write_all(b"\r\n")?;

        self.output.write_all(&self.buffer[offset..])?;
        self.reset_buffer();

        Ok(())
    }
}

impl<W> Write for Encoder<W>
where
    W: Write,
{
    fn write(&mut self, data: &[u8]) -> IoResult<usize> {
        let buffer_size = self.buffer.len() - HEADER_SIZE;
        let remaining_buffer_space = self.chunks_size - buffer_size;
        let bytes_to_buffer = std::cmp::min(remaining_buffer_space, data.len());
        self.buffer.extend_from_slice(&data[0..bytes_to_buffer]);
        let more_to_write: bool = bytes_to_buffer < data.len();
        if self.flush_after_write || more_to_write {
            self.send()?;
        }

        // If we didn't write the whole thing, keep working on it.
        if more_to_write {
            self.write_all(&data[bytes_to_buffer..])?;
        }
        Ok(data.len())
    }

    fn flush(&mut self) -> IoResult<()> {
        self.send()
    }
}

impl<W> Drop for Encoder<W>
where
    W: Write,
{
    fn drop(&mut self) {
        self.flush().ok();
        write!(self.output, "0\r\n\r\n").ok();
    }
}

#[cfg(test)]
mod test {
    use super::Encoder;
    use std::io;
    use std::io::Write;
    use std::str::from_utf8;

    #[test]
    fn test() {
        let mut source = io::Cursor::new("hello world".to_string().into_bytes());
        let mut dest: Vec<u8> = vec![];

        {
            let mut encoder = Encoder::with_chunks_size(dest.by_ref(), 5);
            io::copy(&mut source, &mut encoder).unwrap();
            assert!(!encoder.is_buffer_empty());
        }

        let output = from_utf8(&dest).unwrap();

        assert_eq!(output, "5\r\nhello\r\n5\r\n worl\r\n1\r\nd\r\n0\r\n\r\n");
    }
    #[test]
    fn flush_after_write() {
        let mut source = io::Cursor::new("hello world".to_string().into_bytes());
        let mut dest: Vec<u8> = vec![];

        {
            let mut encoder = Encoder::with_flush_after_write(dest.by_ref());
            io::copy(&mut source, &mut encoder).unwrap();
            // The internal buffer has been flushed.
            assert!(encoder.is_buffer_empty());
        }

        let output = from_utf8(&dest).unwrap();

        assert_eq!(output, "b\r\nhello world\r\n0\r\n\r\n");
    }
}
<|endoftext|>