<commit_before>use std::mem;
use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::ContextOf;
use framebuffer::FramebufferBinding;
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, BufferBinderOf, BufferBinding,
             ArrayBufferBinder, ElementArrayBufferBinder,
             ArrayBufferBinding, ElementArrayBufferBinding};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    pub fn enable<V, B, F, P, R, T>(&self, gl: &ContextOf<B, F, P, R, T>)
        -> Result<(), AttribError>
        where V: VertexData
    {
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V: VertexData>(&self, gl_buffer: &ArrayBufferBinding)
        -> Result<(), AttribError>
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                gl_buffer.vertex_attrib_pointer(
                    program_attrib,
                    vertex_attrib.ty.components,
                    vertex_attrib.ty.data,
                    vertex_attrib.ty.normalize,
                    vertex_attrib.stride,
                    vertex_attrib.offset
                );
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn bind<BA, BE, F, P, R, T>(&mut self,
                                    gl: ContextOf<BufferBinderOf<BA, BE>,
                                                  F,
                                                  P,
                                                  R,
                                                  T>)
        -> Result<(), VertexBindError>
        where BA: BorrowMut<ArrayBufferBinder>
    {
        match self.attrib_binder {
            Some(ref binder) => {
                let (mut ba_binder, mut gl) = gl.split_array_buffer();
                let mut ba_binder = ba_binder.borrow_mut();
                let gl_buffer = ba_binder.bind(&mut self.buffer);
                try!(binder.enable::<V, _, _, _, _, _>(&mut gl));
                try!(binder.bind::<V>(&gl_buffer));
                Ok(())
            },
            None => { Err(VertexBindError::NoAttributeBindings) }
        }
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    vbo: &'a mut VertexBuffer<T>
}

impl<'a, T: VertexData + 'a> VertexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        self.vbo.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }
}

impl<'a> FramebufferBinding<'a> {
    pub fn draw_arrays_range_vbo<V>(&mut self,
                                    gl_vbo: &VertexBufferBinding<V>,
                                    mode: DrawingMode,
                                    start: u32,
                                    length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= gl_vbo.vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    pub fn draw_arrays_vbo<V>(&mut self,
                              gl_vbo: &VertexBufferBinding<V>,
                              mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   gl_vbo.vbo.count);
        }
    }

    pub fn draw_n_elements_buffered_vbo<V, I>(&mut self,
                                              gl_vbo: &VertexBufferBinding<V>,
                                              gl_ibo: &IndexBufferBinding<I>,
                                              mode: DrawingMode,
                                              length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= gl_ibo.ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    pub fn draw_elements_buffered_vbo<V, I>(&mut self,
                                            gl_vbo: &VertexBufferBinding<V>,
                                            gl_ibo: &IndexBufferBinding<I>,
                                            mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          gl_ibo.ibo.count,
                                          I::index_datum_type());
        }
    }

    pub fn draw_n_elements_vbo<V, I>(&mut self,
                                     gl_vbo: &VertexBufferBinding<'a, V>,
                                     mode: DrawingMode,
                                     count: usize,
                                     indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    pub fn draw_elements_vbo<V, I>(&mut self,
                                   gl_vbo: &VertexBufferBinding<'a, V>,
                                   mode: DrawingMode,
                                   indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

// TODO: Refactor to work with less restrictive generic params
impl<BA, BE, F, P, R, T> ContextOf<BufferBinderOf<BA, BE>, F, P, R, T> {
    pub fn bind_vertex_buffer<'a, V>(mut self, vbo: &'a mut VertexBuffer<V>)
        -> (
            VertexBufferBinding<V>,
            ContextOf<BufferBinderOf<(), BE>, F, P, R, T>
        )
        where V: VertexData,
              BA: BorrowMut<ArrayBufferBinder>
    {
        // NOTE: The mem::transmute here unsafely extends the borrow of
        //       ibo.buffer_mut()
        // TODO: Find a safe(r) way to do this
        {
            let gl = self.borrowed_mut().map_buffers(|b| b.borrowed_mut());

            vbo.bind(gl).unwrap();
        }
        let (mut ba_binder, gl) = self.split_array_buffer();
        let mut ba_binder = ba_binder.borrow_mut();
        let buffer = unsafe { mem::transmute(vbo.buffer_mut() as *mut Buffer) };
        let gl_array_buffer = ba_binder.bind(buffer);
        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                vbo: vbo
            },
            gl
        )
    }

    pub fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (
            IndexBufferBinding<I>,
            ContextOf<BufferBinderOf<BA, ()>, F, P, R, T>
        )
        where I: IndexDatum + 'a,
              BE: BorrowMut<ElementArrayBufferBinder> + 'a
    {
        // NOTE: The mem::transmute here unsafely extends the borrow of
        //       ibo.buffer_mut()
        // TODO: Find a safe(r) way to do this
        let (mut be_binder, gl) = self.split_element_array_buffer();
        let be_binder = be_binder.borrow_mut();
        let buffer = unsafe { mem::transmute(ibo.buffer_mut() as *mut Buffer) };
        let gl_be = be_binder.bind(buffer);
        (IndexBufferBinding::new(gl_be, ibo), gl)
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    ibo: &'a mut IndexBuffer<T>
}

impl<'a, T: IndexDatum + 'a> IndexBufferBinding<'a, T> {
    pub fn new(gl_buffer: ElementArrayBufferBinding<'a>,
               ibo: &'a mut IndexBuffer<T>)
        -> Self
    {
        IndexBufferBinding {
            gl_buffer: gl_buffer,
            ibo: ibo
        }
    }

    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: IndexData
    {
        self.ibo.count = data.len();
        self.gl_buffer.buffer_bytes(data.index_bytes(), usage);
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<commit_msg>Use literal initializer instead of `new()` for `IndexBufferBinding`<commit_after>use std::mem;
use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::ContextOf;
use framebuffer::FramebufferBinding;
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, BufferBinderOf, BufferBinding,
             ArrayBufferBinder, ElementArrayBufferBinder,
             ArrayBufferBinding, ElementArrayBufferBinding};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    pub fn enable<V, B, F, P, R, T>(&self, gl: &ContextOf<B, F, P, R, T>)
        -> Result<(), AttribError>
        where V: VertexData
    {
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V: VertexData>(&self, gl_buffer: &ArrayBufferBinding)
        -> Result<(), AttribError>
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                gl_buffer.vertex_attrib_pointer(
                    program_attrib,
                    vertex_attrib.ty.components,
                    vertex_attrib.ty.data,
                    vertex_attrib.ty.normalize,
                    vertex_attrib.stride,
                    vertex_attrib.offset
                );
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn bind<BA, BE, F, P, R, T>(&mut self,
                                    gl: ContextOf<BufferBinderOf<BA, BE>,
                                                  F,
                                                  P,
                                                  R,
                                                  T>)
        -> Result<(), VertexBindError>
        where BA: BorrowMut<ArrayBufferBinder>
    {
        match self.attrib_binder {
            Some(ref binder) => {
                let (mut ba_binder, mut gl) = gl.split_array_buffer();
                let mut ba_binder = ba_binder.borrow_mut();
                let gl_buffer = ba_binder.bind(&mut self.buffer);
                try!(binder.enable::<V, _, _, _, _, _>(&mut gl));
                try!(binder.bind::<V>(&gl_buffer));
                Ok(())
            },
            None => { Err(VertexBindError::NoAttributeBindings) }
        }
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    vbo: &'a mut VertexBuffer<T>
}

impl<'a, T: VertexData + 'a> VertexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        self.vbo.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }
}

impl<'a> FramebufferBinding<'a> {
    pub fn draw_arrays_range_vbo<V>(&mut self,
                                    gl_vbo: &VertexBufferBinding<V>,
                                    mode: DrawingMode,
                                    start: u32,
                                    length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= gl_vbo.vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    pub fn draw_arrays_vbo<V>(&mut self,
                              gl_vbo: &VertexBufferBinding<V>,
                              mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   gl_vbo.vbo.count);
        }
    }

    pub fn draw_n_elements_buffered_vbo<V, I>(&mut self,
                                              gl_vbo: &VertexBufferBinding<V>,
                                              gl_ibo: &IndexBufferBinding<I>,
                                              mode: DrawingMode,
                                              length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= gl_ibo.ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    pub fn draw_elements_buffered_vbo<V, I>(&mut self,
                                            gl_vbo: &VertexBufferBinding<V>,
                                            gl_ibo: &IndexBufferBinding<I>,
                                            mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          gl_ibo.ibo.count,
                                          I::index_datum_type());
        }
    }

    pub fn draw_n_elements_vbo<V, I>(&mut self,
                                     gl_vbo: &VertexBufferBinding<'a, V>,
                                     mode: DrawingMode,
                                     count: usize,
                                     indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    pub fn draw_elements_vbo<V, I>(&mut self,
                                   gl_vbo: &VertexBufferBinding<'a, V>,
                                   mode: DrawingMode,
                                   indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

// TODO: Refactor to work with less restrictive generic params
impl<BA, BE, F, P, R, T> ContextOf<BufferBinderOf<BA, BE>, F, P, R, T> {
    pub fn bind_vertex_buffer<'a, V>(mut self, vbo: &'a mut VertexBuffer<V>)
        -> (
            VertexBufferBinding<V>,
            ContextOf<BufferBinderOf<(), BE>, F, P, R, T>
        )
        where V: VertexData,
              BA: BorrowMut<ArrayBufferBinder>
    {
        // NOTE: The mem::transmute here unsafely extends the borrow of
        //       ibo.buffer_mut()
        // TODO: Find a safe(r) way to do this
        {
            let gl = self.borrowed_mut().map_buffers(|b| b.borrowed_mut());

            vbo.bind(gl).unwrap();
        }
        let (mut ba_binder, gl) = self.split_array_buffer();
        let mut ba_binder = ba_binder.borrow_mut();
        let buffer = unsafe { mem::transmute(vbo.buffer_mut() as *mut Buffer) };
        let gl_array_buffer = ba_binder.bind(buffer);
        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                vbo: vbo
            },
            gl
        )
    }

    pub fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (
            IndexBufferBinding<I>,
            ContextOf<BufferBinderOf<BA, ()>, F, P, R, T>
        )
        where I: IndexDatum + 'a,
              BE: BorrowMut<ElementArrayBufferBinder> + 'a
    {
        // NOTE: The mem::transmute here unsafely extends the borrow of
        //       ibo.buffer_mut()
        // TODO: Find a safe(r) way to do this
        let (mut be_binder, gl) = self.split_element_array_buffer();
        let be_binder = be_binder.borrow_mut();
        let buffer = unsafe { mem::transmute(ibo.buffer_mut() as *mut Buffer) };
        let gl_be = be_binder.bind(buffer);
        (
            IndexBufferBinding {
                gl_buffer: gl_be,
                ibo: ibo
            },
            gl
        )
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    ibo: &'a mut IndexBuffer<T>
}

impl<'a, T: IndexDatum + 'a> IndexBufferBinding<'a, T> {
    pub fn new(gl_buffer: ElementArrayBufferBinding<'a>,
               ibo: &'a mut IndexBuffer<T>)
        -> Self
    {
        IndexBufferBinding {
            gl_buffer: gl_buffer,
            ibo: ibo
        }
    }

    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: IndexData
    {
        self.ibo.count = data.len();
        self.gl_buffer.buffer_bytes(data.index_bytes(), usage);
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<|endoftext|>


<commit_before>use std::time::Duration;

use chrono::prelude::*;

use errors::*;
use text::{Attributes, Text};

pub struct Clock {
    update_interval: Duration,
    attr: Attributes,
}

impl Clock {
    pub fn new(attr: Attributes) -> Clock {
        Clock {
            update_interval: Duration::from_secs(1),
            attr: attr,
        }
    }

    fn tick(&self) -> Result<Vec<Text>> {
        let current_time = Local::now().format("%Y-%m-%d %a %I:%M:%S %p").to_string();
        Ok(vec![
            Text {
                attr: self.attr.clone(),
                text: current_time,
                stretch: false,
            },
        ])
    }
}

timer_widget!(Clock, update_interval, tick);
<commit_msg>Don't show seconds in Clock widget.<commit_after>use std::time::Duration;

use chrono::prelude::*;

use errors::*;
use text::{Attributes, Text};

pub struct Clock {
    update_interval: Duration,
    attr: Attributes,
}

impl Clock {
    pub fn new(attr: Attributes) -> Clock {
        Clock {
            update_interval: Duration::from_secs(1),
            attr: attr,
        }
    }

    fn tick(&self) -> Result<Vec<Text>> {
        let current_time = Local::now().format("%Y-%m-%d %a %I:%M %p").to_string();
        Ok(vec![
            Text {
                attr: self.attr.clone(),
                text: current_time,
                stretch: false,
            },
        ])
    }
}

timer_widget!(Clock, update_interval, tick);
<|endoftext|>


<commit_before>//! Defines a series of convenience modifiers for editing Responses
//!
//! Modifiers can be used to edit Responses through the owning
//! method `set` or the mutating `set_mut`, both of which are
//! defined in the `Set` trait.
//!
//! Instead of having a combinatorial explosion of Response methods
//! and constructors, this provides a series of modifiers that
//! can be used through the `Set` trait.
//!
//! For instance, instead of `Response::redirect` constructing a
//! redirect response, we provide a `Redirect` modifier, so you
//! can just do:
//!
//! ```
//! # use iron::prelude::*;
//! # use iron::status;
//! # use iron::modifiers::Redirect;
//! # use iron::Url;
//!
//! let url = Url::parse("http://doc.rust-lang.org").unwrap();
//! Response::with((status::Found, Redirect(url)));
//! ```
//!
//! This is more extensible as it allows you to combine
//! arbitrary modifiers without having a massive number of
//! Response constructors.
//!
//! For more information about the modifier system, see
//! [rust-modifier](https://github.com/reem/rust-modifier).

use std::old_io::{File, MemReader};
use std::path::Path;

use modifier::Modifier;

use hyper::mime::Mime;

use {status, headers, Response, Url};

impl Modifier<Response> for Mime {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentType(self))
    }
}

impl Modifier<Response> for Box<Reader + Send> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.body = Some(self);
    }
}

impl Modifier<Response> for String {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.into_bytes().modify(res);
    }
}

impl Modifier<Response> for Vec<u8> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentLength(self.len() as u64));
        res.body = Some(Box::new(MemReader::new(self)) as Box<Reader + Send>);
    }
}

impl<'a> Modifier<Response> for &'a str {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_string().modify(res);
    }
}

impl<'a> Modifier<Response> for &'a [u8] {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_vec().modify(res);
    }
}

impl Modifier<Response> for File {
    #[inline]
    fn modify(self, res: &mut Response) {
        // Also set the content type.
        // self.path().extension_str()
        //     .and_then(get_content_type)
        //     .and_then(|ct| { res.set_mut(ct) });
        res.body = Some(Box::new(self) as Box<Reader + Send>);
    }
}

impl Modifier<Response> for Path {
    /// Set the body to the contents of the File at this path.
    ///
    /// ## Panics
    ///
    /// Panics if there is no file at the passed-in Path.
    fn modify(self, res: &mut Response) {
        File::open(&self)
            .ok().expect(format!("No such file: {}", self.display()).as_slice())
            .modify(res);
    }
}

impl Modifier<Response> for status::Status {
    fn modify(self, res: &mut Response) {
        res.status = Some(self);
    }
}

/// A modifier for creating redirect responses.
pub struct Redirect(pub Url);

impl Modifier<Response> for Redirect {
    fn modify(self, res: &mut Response) {
        let Redirect(url) = self;
        res.headers.set(headers::Location(url.to_string()));
    }
}

<commit_msg>Update modifiers.rs<commit_after>//! Defines a series of convenience modifiers for editing Responses
//!
//! Modifiers can be used to edit Responses through the owning
//! method `set` or the mutating `set_mut`, both of which are
//! defined in the `Set` trait.
//!
//! Instead of having a combinatorial explosion of Response methods
//! and constructors, this provides a series of modifiers that
//! can be used through the `Set` trait.
//!
//! For instance, instead of `Response::redirect` constructing a
//! redirect response, we provide a `Redirect` modifier, so you
//! can just do:
//!
//! ```
//! # use iron::prelude::*;
//! # use iron::status;
//! # use iron::modifiers::Redirect;
//! # use iron::Url;
//!
//! let url = Url::parse("http://doc.rust-lang.org").unwrap();
//! Response::with((status::Found, Redirect(url)));
//! ```
//!
//! This is more extensible as it allows you to combine
//! arbitrary modifiers without having a massive number of
//! Response constructors.
//!
//! For more information about the modifier system, see
//! [rust-modifier](https://github.com/reem/rust-modifier).

use std::old_io::{File, MemReader};
use std::old_path::Path;

use modifier::Modifier;

use hyper::mime::Mime;

use {status, headers, Response, Url};

impl Modifier<Response> for Mime {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentType(self))
    }
}

impl Modifier<Response> for Box<Reader + Send> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.body = Some(self);
    }
}

impl Modifier<Response> for String {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.into_bytes().modify(res);
    }
}

impl Modifier<Response> for Vec<u8> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentLength(self.len() as u64));
        res.body = Some(Box::new(MemReader::new(self)) as Box<Reader + Send>);
    }
}

impl<'a> Modifier<Response> for &'a str {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_string().modify(res);
    }
}

impl<'a> Modifier<Response> for &'a [u8] {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_vec().modify(res);
    }
}

impl Modifier<Response> for File {
    #[inline]
    fn modify(self, res: &mut Response) {
        // Also set the content type.
        // self.path().extension_str()
        //     .and_then(get_content_type)
        //     .and_then(|ct| { res.set_mut(ct) });
        res.body = Some(Box::new(self) as Box<Reader + Send>);
    }
}

impl Modifier<Response> for Path {
    /// Set the body to the contents of the File at this path.
    ///
    /// ## Panics
    ///
    /// Panics if there is no file at the passed-in Path.
    fn modify(self, res: &mut Response) {
        File::open(&self)
            .ok().expect(format!("No such file: {}", self.display()).as_slice())
            .modify(res);
    }
}

impl Modifier<Response> for status::Status {
    fn modify(self, res: &mut Response) {
        res.status = Some(self);
    }
}

/// A modifier for creating redirect responses.
pub struct Redirect(pub Url);

impl Modifier<Response> for Redirect {
    fn modify(self, res: &mut Response) {
        let Redirect(url) = self;
        res.headers.set(headers::Location(url.to_string()));
    }
}

<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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
//
//     https://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.

mod cbor;
pub mod config;
pub mod fetcher;
mod header_integrity;
pub mod headers;
pub mod http;
pub mod http_cache;
mod http_parser;
mod id_headers;
mod link;
mod mice;
mod ocsp;
pub mod signature;
mod structured_header;
mod sxg;
mod utils;
#[cfg(feature = "wasm")]
mod wasm_worker;

use anyhow::{Error, Result};
use config::Config;
use fetcher::Fetcher;
use headers::{AcceptFilter, Headers};
use http::{HeaderFields, HttpResponse};
use http_cache::HttpCache;
use serde::Serialize;
use url::Url;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;

#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct SxgWorker {
    config: Config,
}

#[derive(Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", tag = "kind")]
pub enum PresetContent {
    Direct(HttpResponse),
    ToBeSigned {
        url: String,
        payload: HttpResponse,
        fallback: HttpResponse,
    },
}

impl SxgWorker {
    pub fn new(config_yaml: &str, cert_pem: &str, issuer_pem: &str) -> Result<SxgWorker> {
        let config = Config::new(config_yaml, cert_pem, issuer_pem)?;
        Ok(SxgWorker { config })
    }
    // TODO: Make OCSP status as an internal state of SxgWorker, so that
    // SxgWorker is able to fetch OCSP. This will need a definition of a
    // Fetcher trait. Both js and rust callers need to implement this trait.
    pub fn create_cert_cbor(&self, ocsp_der: &[u8]) -> Vec<u8> {
        use cbor::DataItem;
        let cert_cbor = DataItem::Array(vec![
            DataItem::TextString("📜⛓"),
            DataItem::Map(vec![
                (
                    DataItem::TextString("cert"),
                    DataItem::ByteString(&self.config.cert_der),
                ),
                (DataItem::TextString("ocsp"), DataItem::ByteString(ocsp_der)),
            ]),
            DataItem::Map(vec![(
                DataItem::TextString("cert"),
                DataItem::ByteString(&self.config.issuer_der),
            )]),
        ]);
        cert_cbor.serialize()
    }
    pub fn cert_basename(&self) -> String {
        base64::encode_config(&self.config.cert_sha256, base64::URL_SAFE_NO_PAD)
    }
    pub async fn create_signed_exchange<S: signature::Signer, F: Fetcher, C: HttpCache>(
        &self,
        params: CreateSignedExchangeParams<'_, S, F, C>,
    ) -> Result<HttpResponse> {
        let CreateSignedExchangeParams {
            fallback_url,
            cert_origin,
            now,
            payload_body,
            payload_headers,
            signer,
            status_code,
            subresource_fetcher,
            header_integrity_cache,
        } = params;
        let fallback_base = Url::parse(fallback_url)
            .map_err(|e| Error::new(e).context("Failed to parse fallback URL"))?;
        let cert_base = Url::parse(cert_origin)
            .map_err(|e| Error::new(e).context("Failed to parse cert origin"))?;
        let (signed_headers, payload_body) = utils::signed_headers_and_payload(
            &fallback_base,
            status_code,
            &payload_headers,
            payload_body,
            subresource_fetcher,
            header_integrity_cache,
            &self.config.strip_response_headers,
        )
        .await?;
        let cert_url = cert_base
            .join(&format!(
                "{}{}",
                &self.config.cert_url_dirname,
                &self.cert_basename()
            ))
            .map_err(|e| Error::new(e).context("Failed to parse cert_url_dirname"))?;
        let validity_url = fallback_base
            .join(&format!(
                "{}{}",
                &self.config.validity_url_dirname, "validity"
            ))
            .map_err(|e| Error::new(e).context("Failed to parse validity_url_dirname"))?;
        let signature = signature::Signature::new(signature::SignatureParams {
            cert_url: cert_url.as_str(),
            cert_sha256: &self.config.cert_sha256,
            date: now,
            expires: now + payload_headers.signature_duration()?,
            headers: &signed_headers,
            id: "sig",
            request_url: fallback_url,
            signer,
            validity_url: validity_url.as_str(),
        })
        .await;

        let signature = signature.map_err(|e| e.context("Failed to create signature."))?;
        let sxg_body = sxg::build(
            fallback_url,
            &signature.serialize(),
            &signed_headers,
            &payload_body,
        )
        .map_err(|e| e.context("Failed to create SXG."))?;
        Ok(HttpResponse {
            body: sxg_body,
            headers: vec![
                (
                    "content-type".into(),
                    "application/signed-exchange;v=b3".into(),
                ),
                ("x-content-type-options".into(), "nosniff".into()),
            ],
            status: 200,
        })
    }
    fn create_validity(&self) -> Vec<u8> {
        let validity = cbor::DataItem::Map(vec![]);
        validity.serialize()
    }
    pub async fn fetch_ocsp_from_ca<F: fetcher::Fetcher>(&self, fetcher: F) -> Vec<u8> {
        let result =
            ocsp::fetch_from_ca(&self.config.cert_der, &self.config.issuer_der, fetcher).await;
        // TODO: Remove panic
        result.unwrap()
    }
    pub fn serve_preset_content(&self, req_url: &str, ocsp_der: &[u8]) -> Option<PresetContent> {
        let req_url = url::Url::parse(req_url).ok()?;
        let path = req_url.path();
        if let Some(basename) = path.strip_prefix(&self.config.reserved_path) {
            match basename {
                "test.html" => Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/html"))],
                    status: 200,
                    body: include_bytes!("./static/test.html").to_vec(),
                })),
                "prefetch.html" => Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/html"))],
                    status: 200,
                    body: include_bytes!("./static/prefetch.html").to_vec(),
                })),
                "fallback.html" => Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/html"))],
                    status: 200,
                    body: include_bytes!("./static/fallback.html").to_vec(),
                })),
                "test.sxg" => {
                    let mut fallback_url = req_url;
                    fallback_url
                        .set_path(&fallback_url.path().replace("test.sxg", "fallback.html"));
                    Some(PresetContent::ToBeSigned {
                        url: fallback_url.to_string(),
                        payload: HttpResponse {
                            headers: vec![(
                                String::from("content-type"),
                                String::from("text/html"),
                            )],
                            status: 200,
                            body: include_bytes!("./static/success.html").to_vec(),
                        },
                        fallback: HttpResponse {
                            headers: vec![(
                                String::from("content-type"),
                                String::from("text/html"),
                            )],
                            status: 200,
                            body: include_bytes!("./static/fallback.html").to_vec(),
                        },
                    })
                }
                _ => None,
            }
        } else if let Some(cert_name) = path.strip_prefix(&self.config.cert_url_dirname) {
            if cert_name == self.cert_basename() {
                Some(PresetContent::Direct(HttpResponse {
                    body: self.create_cert_cbor(ocsp_der),
                    headers: vec![(
                        String::from("content-type"),
                        String::from("application/cert-chain+cbor"),
                    )],
                    status: 200,
                }))
            } else {
                Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/plain"))],
                    status: 404,
                    body: format!("Unknown path {}", req_url).into_bytes(),
                }))
            }
        } else if let Some(validity_name) = path.strip_prefix(&self.config.validity_url_dirname) {
            if validity_name == "validity" {
                Some(PresetContent::Direct(HttpResponse {
                    body: self.create_validity(),
                    headers: vec![(
                        String::from("content-type"),
                        String::from("application/cbor"),
                    )],
                    status: 200,
                }))
            } else {
                Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/plain"))],
                    status: 404,
                    body: format!("Unknown path {}", req_url).into_bytes(),
                }))
            }
        } else {
            None
        }
    }
    /// Checks `fields` as request headers from browser,
    /// and returns the request headers to be sent to backend server.
    pub fn transform_request_headers(
        &self,
        fields: HeaderFields,
        accept_filter: AcceptFilter,
    ) -> Result<HeaderFields> {
        let headers = Headers::new(fields, &self.config.strip_request_headers);
        headers.forward_to_origin_server(accept_filter, &self.config.forward_request_headers)
    }
    /// Checks `fields` as response headers from backend server,
    /// and returns the reqsponse headers to be sent to browser.
    pub fn transform_payload_headers(&self, fields: HeaderFields) -> Result<Headers> {
        let headers = Headers::new(fields, &self.config.strip_response_headers);
        headers.validate_as_sxg_payload()?;
        Ok(headers)
    }
    #[cfg(feature = "rust_signer")]
    pub fn create_rust_signer(&self) -> Result<signature::rust_signer::RustSigner> {
        let private_key_der = base64::decode(
            self.config
                .private_key_base64
                .as_ref()
                .ok_or_else(|| Error::msg("Config private_key_base64 is not set"))?,
        )?;
        signature::rust_signer::RustSigner::new(&private_key_der)
            .map_err(|e| e.context("Failed to call RustSigner::new()."))
    }
    pub fn should_respond_debug_info(&self) -> bool {
        self.config.respond_debug_info
    }
    /// Replaces the host name to be the html_host in the config.
    // TODO: implement get_fallback_url_and_cert_origin, so that Cloudflare Worker can use it.
    pub fn get_fallback_url(&self, original_url: &Url) -> Result<Url> {
        let mut fallback = original_url.clone();
        if let Some(html_host) = &self.config.html_host {
            if !html_host.is_empty() {
                fallback.set_host(Some(html_host)).map_err(Error::new)?;
            }
        }
        Ok(fallback)
    }
}

pub struct CreateSignedExchangeParams<'a, S: signature::Signer, F: Fetcher, C: HttpCache> {
    pub fallback_url: &'a str,
    pub cert_origin: &'a str,
    pub now: std::time::SystemTime,
    pub payload_body: &'a [u8],
    pub payload_headers: headers::Headers,
    pub signer: S,
    pub status_code: u16,
    pub subresource_fetcher: F,
    pub header_integrity_cache: C,
}

#[cfg(test)]
mod lib_tests {
    use super::*;
    use utils::tests as util;
    fn new_worker() -> SxgWorker {
        let yaml = r#"
cert_url_dirname: ".well-known/sxg-certs/"
forward_request_headers:
  - "cf-IPCOUNTRY"
  - "USER-agent"
html_host: my_domain.com
reserved_path: ".sxg"
respond_debug_info: false
strip_request_headers: ["Forwarded"]
strip_response_headers: ["Set-Cookie", "STRICT-TRANSPORT-SECURITY"]
validity_url_dirname: "//.well-known/sxg-validity"
        "#;
        SxgWorker::new(yaml, util::SELF_SIGNED_CERT_PEM, util::SELF_SIGNED_CERT_PEM).unwrap()
    }
    #[test]
    fn cert_basename() {
        assert_eq!(new_worker().cert_basename(), util::SELF_SIGNED_CERT_SHA256);
    }
    #[test]
    fn serve_preset_content() {
        let worker = new_worker();
        assert_eq!(
            worker.serve_preset_content("https://my_domain.com/unknown", &[]),
            None
        );
        assert!(matches!(
            worker.serve_preset_content("https://my_domain.com/.sxg/test.html", &[]),
            Some(PresetContent::Direct(HttpResponse { status: 200, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content("https://my_domain.com/.sxg/test.sxg", &[]),
            Some(PresetContent::ToBeSigned { .. })
        ));
        assert!(matches!(
            worker.serve_preset_content(
                &format!(
                    "https://my_domain.com/.well-known/sxg-certs/{}",
                    util::SELF_SIGNED_CERT_SHA256
                ),
                &[]
            ),
            Some(PresetContent::Direct(HttpResponse { status: 200, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content("https://my_domain.com/.well-known/sxg-certs/unknown", &[]),
            Some(PresetContent::Direct(HttpResponse { status: 404, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content(
                "https://my_domain.com/.well-known/sxg-validity/validity",
                &[]
            ),
            Some(PresetContent::Direct(HttpResponse { status: 200, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content(
                "https://my_domain.com/.well-known/sxg-validity/unknown",
                &[]
            ),
            Some(PresetContent::Direct(HttpResponse { status: 404, .. }))
        ));
    }
}
<commit_msg>Backdate `date` but not `expires` by an hour. (#75)<commit_after>// Copyright 2021 Google LLC
//
// 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
//
//     https://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.

mod cbor;
pub mod config;
pub mod fetcher;
mod header_integrity;
pub mod headers;
pub mod http;
pub mod http_cache;
mod http_parser;
mod id_headers;
mod link;
mod mice;
mod ocsp;
pub mod signature;
mod structured_header;
mod sxg;
mod utils;
#[cfg(feature = "wasm")]
mod wasm_worker;

use anyhow::{anyhow, Error, Result};
use config::Config;
use fetcher::Fetcher;
use headers::{AcceptFilter, Headers};
use http::{HeaderFields, HttpResponse};
use http_cache::HttpCache;
use serde::Serialize;
use std::time::Duration;
use url::Url;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;

#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct SxgWorker {
    config: Config,
}

#[derive(Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", tag = "kind")]
pub enum PresetContent {
    Direct(HttpResponse),
    ToBeSigned {
        url: String,
        payload: HttpResponse,
        fallback: HttpResponse,
    },
}

impl SxgWorker {
    pub fn new(config_yaml: &str, cert_pem: &str, issuer_pem: &str) -> Result<SxgWorker> {
        let config = Config::new(config_yaml, cert_pem, issuer_pem)?;
        Ok(SxgWorker { config })
    }
    // TODO: Make OCSP status as an internal state of SxgWorker, so that
    // SxgWorker is able to fetch OCSP. This will need a definition of a
    // Fetcher trait. Both js and rust callers need to implement this trait.
    pub fn create_cert_cbor(&self, ocsp_der: &[u8]) -> Vec<u8> {
        use cbor::DataItem;
        let cert_cbor = DataItem::Array(vec![
            DataItem::TextString("📜⛓"),
            DataItem::Map(vec![
                (
                    DataItem::TextString("cert"),
                    DataItem::ByteString(&self.config.cert_der),
                ),
                (DataItem::TextString("ocsp"), DataItem::ByteString(ocsp_der)),
            ]),
            DataItem::Map(vec![(
                DataItem::TextString("cert"),
                DataItem::ByteString(&self.config.issuer_der),
            )]),
        ]);
        cert_cbor.serialize()
    }
    pub fn cert_basename(&self) -> String {
        base64::encode_config(&self.config.cert_sha256, base64::URL_SAFE_NO_PAD)
    }
    pub async fn create_signed_exchange<S: signature::Signer, F: Fetcher, C: HttpCache>(
        &self,
        params: CreateSignedExchangeParams<'_, S, F, C>,
    ) -> Result<HttpResponse> {
        let CreateSignedExchangeParams {
            fallback_url,
            cert_origin,
            now,
            payload_body,
            payload_headers,
            signer,
            status_code,
            subresource_fetcher,
            header_integrity_cache,
        } = params;
        let fallback_base = Url::parse(fallback_url)
            .map_err(|e| Error::new(e).context("Failed to parse fallback URL"))?;
        let cert_base = Url::parse(cert_origin)
            .map_err(|e| Error::new(e).context("Failed to parse cert origin"))?;
        let (signed_headers, payload_body) = utils::signed_headers_and_payload(
            &fallback_base,
            status_code,
            &payload_headers,
            payload_body,
            subresource_fetcher,
            header_integrity_cache,
            &self.config.strip_response_headers,
        )
        .await?;
        let cert_url = cert_base
            .join(&format!(
                "{}{}",
                &self.config.cert_url_dirname,
                &self.cert_basename()
            ))
            .map_err(|e| Error::new(e).context("Failed to parse cert_url_dirname"))?;
        let validity_url = fallback_base
            .join(&format!(
                "{}{}",
                &self.config.validity_url_dirname, "validity"
            ))
            .map_err(|e| Error::new(e).context("Failed to parse validity_url_dirname"))?;
        // To avoid issues with clock skew, backdate the start time by an hour. Don't backdate the
        // expiration because it goes against the origin's cache-control header. (e.g. For max-age
        // <1h, an SXG would be instantly invalid; this would be confusing.)
        let expires = now
            .checked_add(payload_headers.signature_duration()?)
            .ok_or_else(|| anyhow!("Failed to construct expires"))?;
        const ONE_HOUR: Duration = Duration::from_secs(60 * 60);
        let date = now
            .checked_sub(ONE_HOUR)
            .ok_or_else(|| anyhow!("Failed to construct date"))?;
        let signature = signature::Signature::new(signature::SignatureParams {
            cert_url: cert_url.as_str(),
            cert_sha256: &self.config.cert_sha256,
            date,
            expires,
            headers: &signed_headers,
            id: "sig",
            request_url: fallback_url,
            signer,
            validity_url: validity_url.as_str(),
        })
        .await;

        let signature = signature.map_err(|e| e.context("Failed to create signature."))?;
        let sxg_body = sxg::build(
            fallback_url,
            &signature.serialize(),
            &signed_headers,
            &payload_body,
        )
        .map_err(|e| e.context("Failed to create SXG."))?;
        Ok(HttpResponse {
            body: sxg_body,
            headers: vec![
                (
                    "content-type".into(),
                    "application/signed-exchange;v=b3".into(),
                ),
                ("x-content-type-options".into(), "nosniff".into()),
            ],
            status: 200,
        })
    }
    fn create_validity(&self) -> Vec<u8> {
        let validity = cbor::DataItem::Map(vec![]);
        validity.serialize()
    }
    pub async fn fetch_ocsp_from_ca<F: fetcher::Fetcher>(&self, fetcher: F) -> Vec<u8> {
        let result =
            ocsp::fetch_from_ca(&self.config.cert_der, &self.config.issuer_der, fetcher).await;
        // TODO: Remove panic
        result.unwrap()
    }
    pub fn serve_preset_content(&self, req_url: &str, ocsp_der: &[u8]) -> Option<PresetContent> {
        let req_url = url::Url::parse(req_url).ok()?;
        let path = req_url.path();
        if let Some(basename) = path.strip_prefix(&self.config.reserved_path) {
            match basename {
                "test.html" => Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/html"))],
                    status: 200,
                    body: include_bytes!("./static/test.html").to_vec(),
                })),
                "prefetch.html" => Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/html"))],
                    status: 200,
                    body: include_bytes!("./static/prefetch.html").to_vec(),
                })),
                "fallback.html" => Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/html"))],
                    status: 200,
                    body: include_bytes!("./static/fallback.html").to_vec(),
                })),
                "test.sxg" => {
                    let mut fallback_url = req_url;
                    fallback_url
                        .set_path(&fallback_url.path().replace("test.sxg", "fallback.html"));
                    Some(PresetContent::ToBeSigned {
                        url: fallback_url.to_string(),
                        payload: HttpResponse {
                            headers: vec![(
                                String::from("content-type"),
                                String::from("text/html"),
                            )],
                            status: 200,
                            body: include_bytes!("./static/success.html").to_vec(),
                        },
                        fallback: HttpResponse {
                            headers: vec![(
                                String::from("content-type"),
                                String::from("text/html"),
                            )],
                            status: 200,
                            body: include_bytes!("./static/fallback.html").to_vec(),
                        },
                    })
                }
                _ => None,
            }
        } else if let Some(cert_name) = path.strip_prefix(&self.config.cert_url_dirname) {
            if cert_name == self.cert_basename() {
                Some(PresetContent::Direct(HttpResponse {
                    body: self.create_cert_cbor(ocsp_der),
                    headers: vec![(
                        String::from("content-type"),
                        String::from("application/cert-chain+cbor"),
                    )],
                    status: 200,
                }))
            } else {
                Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/plain"))],
                    status: 404,
                    body: format!("Unknown path {}", req_url).into_bytes(),
                }))
            }
        } else if let Some(validity_name) = path.strip_prefix(&self.config.validity_url_dirname) {
            if validity_name == "validity" {
                Some(PresetContent::Direct(HttpResponse {
                    body: self.create_validity(),
                    headers: vec![(
                        String::from("content-type"),
                        String::from("application/cbor"),
                    )],
                    status: 200,
                }))
            } else {
                Some(PresetContent::Direct(HttpResponse {
                    headers: vec![(String::from("content-type"), String::from("text/plain"))],
                    status: 404,
                    body: format!("Unknown path {}", req_url).into_bytes(),
                }))
            }
        } else {
            None
        }
    }
    /// Checks `fields` as request headers from browser,
    /// and returns the request headers to be sent to backend server.
    pub fn transform_request_headers(
        &self,
        fields: HeaderFields,
        accept_filter: AcceptFilter,
    ) -> Result<HeaderFields> {
        let headers = Headers::new(fields, &self.config.strip_request_headers);
        headers.forward_to_origin_server(accept_filter, &self.config.forward_request_headers)
    }
    /// Checks `fields` as response headers from backend server,
    /// and returns the reqsponse headers to be sent to browser.
    pub fn transform_payload_headers(&self, fields: HeaderFields) -> Result<Headers> {
        let headers = Headers::new(fields, &self.config.strip_response_headers);
        headers.validate_as_sxg_payload()?;
        Ok(headers)
    }
    #[cfg(feature = "rust_signer")]
    pub fn create_rust_signer(&self) -> Result<signature::rust_signer::RustSigner> {
        let private_key_der = base64::decode(
            self.config
                .private_key_base64
                .as_ref()
                .ok_or_else(|| Error::msg("Config private_key_base64 is not set"))?,
        )?;
        signature::rust_signer::RustSigner::new(&private_key_der)
            .map_err(|e| e.context("Failed to call RustSigner::new()."))
    }
    pub fn should_respond_debug_info(&self) -> bool {
        self.config.respond_debug_info
    }
    /// Replaces the host name to be the html_host in the config.
    // TODO: implement get_fallback_url_and_cert_origin, so that Cloudflare Worker can use it.
    pub fn get_fallback_url(&self, original_url: &Url) -> Result<Url> {
        let mut fallback = original_url.clone();
        if let Some(html_host) = &self.config.html_host {
            if !html_host.is_empty() {
                fallback.set_host(Some(html_host)).map_err(Error::new)?;
            }
        }
        Ok(fallback)
    }
}

pub struct CreateSignedExchangeParams<'a, S: signature::Signer, F: Fetcher, C: HttpCache> {
    pub fallback_url: &'a str,
    pub cert_origin: &'a str,
    pub now: std::time::SystemTime,
    pub payload_body: &'a [u8],
    pub payload_headers: headers::Headers,
    pub signer: S,
    pub status_code: u16,
    pub subresource_fetcher: F,
    pub header_integrity_cache: C,
}

#[cfg(test)]
mod lib_tests {
    use super::*;
    use utils::tests as util;
    fn new_worker() -> SxgWorker {
        let yaml = r#"
cert_url_dirname: ".well-known/sxg-certs/"
forward_request_headers:
  - "cf-IPCOUNTRY"
  - "USER-agent"
html_host: my_domain.com
reserved_path: ".sxg"
respond_debug_info: false
strip_request_headers: ["Forwarded"]
strip_response_headers: ["Set-Cookie", "STRICT-TRANSPORT-SECURITY"]
validity_url_dirname: "//.well-known/sxg-validity"
        "#;
        SxgWorker::new(yaml, util::SELF_SIGNED_CERT_PEM, util::SELF_SIGNED_CERT_PEM).unwrap()
    }
    #[test]
    fn cert_basename() {
        assert_eq!(new_worker().cert_basename(), util::SELF_SIGNED_CERT_SHA256);
    }
    #[test]
    fn serve_preset_content() {
        let worker = new_worker();
        assert_eq!(
            worker.serve_preset_content("https://my_domain.com/unknown", &[]),
            None
        );
        assert!(matches!(
            worker.serve_preset_content("https://my_domain.com/.sxg/test.html", &[]),
            Some(PresetContent::Direct(HttpResponse { status: 200, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content("https://my_domain.com/.sxg/test.sxg", &[]),
            Some(PresetContent::ToBeSigned { .. })
        ));
        assert!(matches!(
            worker.serve_preset_content(
                &format!(
                    "https://my_domain.com/.well-known/sxg-certs/{}",
                    util::SELF_SIGNED_CERT_SHA256
                ),
                &[]
            ),
            Some(PresetContent::Direct(HttpResponse { status: 200, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content("https://my_domain.com/.well-known/sxg-certs/unknown", &[]),
            Some(PresetContent::Direct(HttpResponse { status: 404, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content(
                "https://my_domain.com/.well-known/sxg-validity/validity",
                &[]
            ),
            Some(PresetContent::Direct(HttpResponse { status: 200, .. }))
        ));
        assert!(matches!(
            worker.serve_preset_content(
                "https://my_domain.com/.well-known/sxg-validity/unknown",
                &[]
            ),
            Some(PresetContent::Direct(HttpResponse { status: 404, .. }))
        ));
    }
}
<|endoftext|>


<commit_before>pub mod ffi;

use std::result;
use std::mem;

use libc::c_long;

pub enum HttpStatus {
   Continue,
   SwitchingProtocols,
   Processing,
   Ok,
   Created,
   Accepted,
   NoContent,
   PartialContent,
   SpecialResponse,
   MovedPermanently,
   MovedTemporarily,
   SeeOther,
   NotModified,
   TemporaryRedirect,
   BadRequest,
   Unauthorized,
   Forbidden,
   NotFound,
   NotAllowed,
   RequestTimeOut,
   Conflict,
   LengthRequired,
   PreconditionFailed,
   RequestUriTooLarge,
   RequestEntityTooLarge,
   UnsupportedMediaType,
   RangeNotSatisfiable,
   Close,
   RequestHeaderTooLarge,
   CertError,
   NoCert,
   ToHttps,
   ClientClosedRequest,
   InternalServerError,
   NotImplement,
   BadGateway,
   ServiceUnavailable,
   GatewayTimeOut,
   InsufficientStorage,
}

impl HttpStatus {
   pub fn new(rc: ffi::ngx_int_t) -> Self {
      match rc {
         ffi::NGX_HTTP_CONTINUE => {
            HttpStatus::Continue }
         ffi::NGX_HTTP_SWITCHING_PROTOCOLS => {
            HttpStatus::SwitchingProtocols }
         ffi::NGX_HTTP_PROCESSING => {
            HttpStatus::Processing }
         ffi::NGX_HTTP_OK => {
            HttpStatus::Ok }
         ffi::NGX_HTTP_CREATED => {
            HttpStatus::Created }
         ffi::NGX_HTTP_ACCEPTED => {
            HttpStatus::Accepted }
         ffi::NGX_HTTP_NO_CONTENT => {
            HttpStatus::NoContent }
         ffi::NGX_HTTP_PARTIAL_CONTENT => {
            HttpStatus::PartialContent }
         ffi::NGX_HTTP_SPECIAL_RESPONSE => {
            HttpStatus::SpecialResponse }
         ffi::NGX_HTTP_MOVED_PERMANENTLY => {
            HttpStatus::MovedPermanently }
         ffi::NGX_HTTP_MOVED_TEMPORARILY => {
            HttpStatus::MovedTemporarily }
         ffi::NGX_HTTP_SEE_OTHER => {
            HttpStatus::SeeOther }
         ffi::NGX_HTTP_NOT_MODIFIED => {
            HttpStatus::NotModified }
         ffi::NGX_HTTP_TEMPORARY_REDIRECT => {
            HttpStatus::TemporaryRedirect }
         ffi::NGX_HTTP_BAD_REQUEST => {
            HttpStatus::BadRequest }
         ffi::NGX_HTTP_UNAUTHORIZED => {
            HttpStatus::Unauthorized }
         ffi::NGX_HTTP_FORBIDDEN => {
            HttpStatus::Forbidden }
         ffi::NGX_HTTP_NOT_FOUND => {
            HttpStatus::NotFound }
         ffi::NGX_HTTP_NOT_ALLOWED => {
            HttpStatus::NotAllowed }
         ffi::NGX_HTTP_REQUEST_TIME_OUT => {
            HttpStatus::RequestTimeOut }
         ffi::NGX_HTTP_CONFLICT => {
            HttpStatus::Conflict }
         ffi::NGX_HTTP_LENGTH_REQUIRED => {
            HttpStatus::LengthRequired }
         ffi::NGX_HTTP_PRECONDITION_FAILED => {
            HttpStatus::PreconditionFailed }
         ffi::NGX_HTTP_REQUEST_URI_TOO_LARGE => {
            HttpStatus::RequestUriTooLarge }
         ffi::NGX_HTTP_REQUEST_ENTITY_TOO_LARGE => {
            HttpStatus::RequestEntityTooLarge }
         ffi::NGX_HTTP_UNSUPPORTED_MEDIA_TYPE => {
            HttpStatus::UnsupportedMediaType }
         ffi::NGX_HTTP_RANGE_NOT_SATISFIABLE => {
            HttpStatus::RangeNotSatisfiable }
         ffi::NGX_HTTP_CLOSE => {
            HttpStatus::Close }
         ffi::NGX_HTTP_REQUEST_HEADER_TOO_LARGE => {
            HttpStatus::RequestHeaderTooLarge }
         ffi::NGX_HTTPS_CERT_ERROR => {
            HttpStatus::CertError }
         ffi::NGX_HTTPS_NO_CERT => {
            HttpStatus::NoCert }
         ffi::NGX_HTTP_TO_HTTPS => {
            HttpStatus::ToHttps }
         ffi::NGX_HTTP_CLIENT_CLOSED_REQUEST => {
            HttpStatus::ClientClosedRequest }
         ffi::NGX_HTTP_INTERNAL_SERVER_ERROR => {
            HttpStatus::InternalServerError }
         ffi::NGX_HTTP_NOT_IMPLEMENTED => {
            HttpStatus::NotImplement }
         ffi::NGX_HTTP_BAD_GATEWAY => {
            HttpStatus::BadGateway }
         ffi::NGX_HTTP_SERVICE_UNAVAILABLE => {
            HttpStatus::ServiceUnavailable }
         ffi::NGX_HTTP_GATEWAY_TIME_OUT => {
            HttpStatus::GatewayTimeOut }
         ffi::NGX_HTTP_INSUFFICIENT_STORAGE => {
            HttpStatus::InsufficientStorage }
         _ => {
            HttpStatus::InternalServerError }
      }
   }

   pub fn rc(&self) -> ffi::ngx_int_t {
      match *self {
         HttpStatus::Continue => {
            ffi::NGX_HTTP_CONTINUE }
         HttpStatus::SwitchingProtocols => {
            ffi::NGX_HTTP_SWITCHING_PROTOCOLS }
         HttpStatus::Processing => {
            ffi::NGX_HTTP_PROCESSING }
         HttpStatus::Ok => {
            ffi::NGX_HTTP_OK }
         HttpStatus::Created => {
            ffi::NGX_HTTP_CREATED }
         HttpStatus::Accepted => {
            ffi::NGX_HTTP_ACCEPTED }
         HttpStatus::NoContent => {
            ffi::NGX_HTTP_NO_CONTENT }
         HttpStatus::PartialContent => {
            ffi::NGX_HTTP_PARTIAL_CONTENT }
         HttpStatus::SpecialResponse => {
            ffi::NGX_HTTP_SPECIAL_RESPONSE }
         HttpStatus::MovedPermanently => {
            ffi::NGX_HTTP_MOVED_PERMANENTLY }
         HttpStatus::MovedTemporarily => {
            ffi::NGX_HTTP_MOVED_TEMPORARILY }
         HttpStatus::SeeOther => {
            ffi::NGX_HTTP_SEE_OTHER }
         HttpStatus::NotModified => {
            ffi::NGX_HTTP_NOT_MODIFIED }
         HttpStatus::TemporaryRedirect => {
            ffi::NGX_HTTP_TEMPORARY_REDIRECT }
         HttpStatus::BadRequest => {
            ffi::NGX_HTTP_BAD_REQUEST }
         HttpStatus::Unauthorized => {
            ffi::NGX_HTTP_UNAUTHORIZED }
         HttpStatus::Forbidden => {
            ffi::NGX_HTTP_FORBIDDEN }
         HttpStatus::NotFound => {
            ffi::NGX_HTTP_NOT_FOUND }
         HttpStatus::NotAllowed => {
            ffi::NGX_HTTP_NOT_ALLOWED }
         HttpStatus::RequestTimeOut => {
            ffi::NGX_HTTP_REQUEST_TIME_OUT }
         HttpStatus::InternalServerError => {
            ffi::NGX_HTTP_INTERNAL_SERVER_ERROR }
         HttpStatus::Conflict => {
            ffi::NGX_HTTP_CONFLICT }
         HttpStatus::LengthRequired => {
            ffi::NGX_HTTP_LENGTH_REQUIRED }
         HttpStatus::PreconditionFailed => {
            ffi::NGX_HTTP_PRECONDITION_FAILED }
         HttpStatus::RequestUriTooLarge => {
            ffi::NGX_HTTP_REQUEST_URI_TOO_LARGE }
         HttpStatus::RequestEntityTooLarge => {
            ffi::NGX_HTTP_REQUEST_ENTITY_TOO_LARGE }
         HttpStatus::UnsupportedMediaType => {
            ffi::NGX_HTTP_UNSUPPORTED_MEDIA_TYPE }
         HttpStatus::RangeNotSatisfiable => {
            ffi::NGX_HTTP_RANGE_NOT_SATISFIABLE }
         HttpStatus::Close => {
            ffi::NGX_HTTP_CLOSE }
         HttpStatus::RequestHeaderTooLarge => {
            ffi::NGX_HTTP_REQUEST_HEADER_TOO_LARGE }
         HttpStatus::CertError => {
            ffi::NGX_HTTPS_CERT_ERROR }
         HttpStatus::NoCert => {
            ffi::NGX_HTTPS_NO_CERT }
         HttpStatus::ToHttps => {
            ffi::NGX_HTTP_TO_HTTPS }
         HttpStatus::ClientClosedRequest => {
            ffi::NGX_HTTP_CLIENT_CLOSED_REQUEST }
         HttpStatus::NotImplement => {
            ffi::NGX_HTTP_NOT_IMPLEMENTED }
         HttpStatus::BadGateway => {
            ffi::NGX_HTTP_BAD_GATEWAY }
         HttpStatus::ServiceUnavailable => {
            ffi::NGX_HTTP_SERVICE_UNAVAILABLE }
         HttpStatus::GatewayTimeOut => {
            ffi::NGX_HTTP_GATEWAY_TIME_OUT }
         HttpStatus::InsufficientStorage => {
            ffi::NGX_HTTP_INSUFFICIENT_STORAGE }
      }
   }
}

pub enum Status {
   Ok,
   Error,
   Again,
   Busy,
   Done,
   Declined,
   Abort,
   Http(HttpStatus),
}

impl Status {
   pub fn new(rc: ffi::ngx_int_t) -> Self {
      match rc {
         ffi::NGX_OK => {
            Status::Ok }
         ffi::NGX_ERROR => {
            Status::Error }
         ffi::NGX_AGAIN => {
            Status::Again }
         ffi::NGX_BUSY => {
            Status::Busy }
         ffi::NGX_DONE => {
            Status::Done }
         ffi::NGX_DECLINED => {
            Status::Declined }
         ffi::NGX_ABORT => {
            Status::Abort }
         http_rc if rc > 0 => {
            Status::Http(HttpStatus::new(http_rc)) }
         _ => {
            Status::Error }
      }
   }

   pub fn rc(&self) -> ffi::ngx_int_t {
      match *self {
         Status::Ok => {
            ffi::NGX_OK }
         Status::Error => {
            ffi::NGX_ERROR }
         Status::Again => {
            ffi::NGX_AGAIN }
         Status::Busy => {
            ffi::NGX_BUSY }
         Status::Done => {
            ffi::NGX_DONE }
         Status::Declined => {
            ffi::NGX_DECLINED }
         Status::Abort => {
            ffi::NGX_ABORT }
         Status::Http(ref http_status) => {
            http_status.rc() }
      }
   }
}

pub struct Connection {
   raw: *mut ffi::ngx_connection_t,
}

impl Connection {
   pub fn new(raw: *mut ffi::ngx_connection_t) -> Self {
      Connection {
         raw: raw
      }
   }

   pub fn log(&self) -> Option<Log> {
      let raw = unsafe { (*self.raw).log };

      if raw.is_null() {
         None
      } else {
         Some(
            Log::new(raw)
         )
      }
   }
}

pub struct Log {
   pub raw: *mut ffi::ngx_log_t,
}

impl Log {
   pub fn new(raw: *mut ffi::ngx_log_t) -> Self {
      Log {
         raw: raw
      }
   }
}

pub struct HttpRequest {
   raw: *mut ffi::ngx_http_request_t,
}

impl HttpRequest {
   pub fn new(raw: *mut ffi::ngx_http_request_t) -> Self {
      HttpRequest {
         raw: raw
      }
   }

   pub fn connection(&self) -> Option<Connection> {
      let raw = unsafe { (*self.raw).connection };

      if raw.is_null() {
         None
      } else {
         Some(
            Connection::new(raw)
         )
      }
   }

   pub fn headers_out(&self) -> HttpHeadersOut {
      let raw: *mut ffi::ngx_http_headers_out_t = unsafe { &mut (*self.raw).headers_out };

      HttpHeadersOut::new(raw)
   }

   pub fn http_send_header(&self) -> Status {
      let rc = unsafe { ffi::ngx_http_send_header(self.raw) };
      Status::new(rc)
   }

   pub fn pool(&self) -> Option<Pool> {
      let raw = unsafe { (*self.raw).pool };

      if raw.is_null() {
         None
      } else {
         Some(
            Pool::new(raw)
         )
      }
   }
}

pub struct HttpHeadersOut {
   raw: *mut ffi::ngx_http_headers_out_t,
}

impl HttpHeadersOut {
   pub fn new(raw: *mut ffi::ngx_http_headers_out_t) -> Self {
      HttpHeadersOut {
         raw: raw
      }
   }

   pub fn set_status(&mut self, status: ffi::ngx_uint_t) {
      unsafe {
         (*self.raw).status = status;
      };
   }

   pub fn set_content_length_n(&mut self, content_length_n: ffi::off_t) {
      unsafe {
         (*self.raw).content_length_n = content_length_n;
      };
   }
}

pub struct Pool {
   raw: *mut ffi::ngx_pool_t,
}

impl Pool {
   pub fn new(raw: *mut ffi::ngx_pool_t) -> Self {
      Pool {
         raw: raw
      }
   }

   #[inline]
   pub fn palloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_palloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn pnalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pnalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn pcalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pcalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

}
<commit_msg>Renaming the pool alloc methods with 'raw_' prefix to indicate returning/accepting raw pointers.<commit_after>pub mod ffi;

use std::result;
use std::mem;

use libc::c_long;

pub enum HttpStatus {
   Continue,
   SwitchingProtocols,
   Processing,
   Ok,
   Created,
   Accepted,
   NoContent,
   PartialContent,
   SpecialResponse,
   MovedPermanently,
   MovedTemporarily,
   SeeOther,
   NotModified,
   TemporaryRedirect,
   BadRequest,
   Unauthorized,
   Forbidden,
   NotFound,
   NotAllowed,
   RequestTimeOut,
   Conflict,
   LengthRequired,
   PreconditionFailed,
   RequestUriTooLarge,
   RequestEntityTooLarge,
   UnsupportedMediaType,
   RangeNotSatisfiable,
   Close,
   RequestHeaderTooLarge,
   CertError,
   NoCert,
   ToHttps,
   ClientClosedRequest,
   InternalServerError,
   NotImplement,
   BadGateway,
   ServiceUnavailable,
   GatewayTimeOut,
   InsufficientStorage,
}

impl HttpStatus {
   pub fn new(rc: ffi::ngx_int_t) -> Self {
      match rc {
         ffi::NGX_HTTP_CONTINUE => {
            HttpStatus::Continue }
         ffi::NGX_HTTP_SWITCHING_PROTOCOLS => {
            HttpStatus::SwitchingProtocols }
         ffi::NGX_HTTP_PROCESSING => {
            HttpStatus::Processing }
         ffi::NGX_HTTP_OK => {
            HttpStatus::Ok }
         ffi::NGX_HTTP_CREATED => {
            HttpStatus::Created }
         ffi::NGX_HTTP_ACCEPTED => {
            HttpStatus::Accepted }
         ffi::NGX_HTTP_NO_CONTENT => {
            HttpStatus::NoContent }
         ffi::NGX_HTTP_PARTIAL_CONTENT => {
            HttpStatus::PartialContent }
         ffi::NGX_HTTP_SPECIAL_RESPONSE => {
            HttpStatus::SpecialResponse }
         ffi::NGX_HTTP_MOVED_PERMANENTLY => {
            HttpStatus::MovedPermanently }
         ffi::NGX_HTTP_MOVED_TEMPORARILY => {
            HttpStatus::MovedTemporarily }
         ffi::NGX_HTTP_SEE_OTHER => {
            HttpStatus::SeeOther }
         ffi::NGX_HTTP_NOT_MODIFIED => {
            HttpStatus::NotModified }
         ffi::NGX_HTTP_TEMPORARY_REDIRECT => {
            HttpStatus::TemporaryRedirect }
         ffi::NGX_HTTP_BAD_REQUEST => {
            HttpStatus::BadRequest }
         ffi::NGX_HTTP_UNAUTHORIZED => {
            HttpStatus::Unauthorized }
         ffi::NGX_HTTP_FORBIDDEN => {
            HttpStatus::Forbidden }
         ffi::NGX_HTTP_NOT_FOUND => {
            HttpStatus::NotFound }
         ffi::NGX_HTTP_NOT_ALLOWED => {
            HttpStatus::NotAllowed }
         ffi::NGX_HTTP_REQUEST_TIME_OUT => {
            HttpStatus::RequestTimeOut }
         ffi::NGX_HTTP_CONFLICT => {
            HttpStatus::Conflict }
         ffi::NGX_HTTP_LENGTH_REQUIRED => {
            HttpStatus::LengthRequired }
         ffi::NGX_HTTP_PRECONDITION_FAILED => {
            HttpStatus::PreconditionFailed }
         ffi::NGX_HTTP_REQUEST_URI_TOO_LARGE => {
            HttpStatus::RequestUriTooLarge }
         ffi::NGX_HTTP_REQUEST_ENTITY_TOO_LARGE => {
            HttpStatus::RequestEntityTooLarge }
         ffi::NGX_HTTP_UNSUPPORTED_MEDIA_TYPE => {
            HttpStatus::UnsupportedMediaType }
         ffi::NGX_HTTP_RANGE_NOT_SATISFIABLE => {
            HttpStatus::RangeNotSatisfiable }
         ffi::NGX_HTTP_CLOSE => {
            HttpStatus::Close }
         ffi::NGX_HTTP_REQUEST_HEADER_TOO_LARGE => {
            HttpStatus::RequestHeaderTooLarge }
         ffi::NGX_HTTPS_CERT_ERROR => {
            HttpStatus::CertError }
         ffi::NGX_HTTPS_NO_CERT => {
            HttpStatus::NoCert }
         ffi::NGX_HTTP_TO_HTTPS => {
            HttpStatus::ToHttps }
         ffi::NGX_HTTP_CLIENT_CLOSED_REQUEST => {
            HttpStatus::ClientClosedRequest }
         ffi::NGX_HTTP_INTERNAL_SERVER_ERROR => {
            HttpStatus::InternalServerError }
         ffi::NGX_HTTP_NOT_IMPLEMENTED => {
            HttpStatus::NotImplement }
         ffi::NGX_HTTP_BAD_GATEWAY => {
            HttpStatus::BadGateway }
         ffi::NGX_HTTP_SERVICE_UNAVAILABLE => {
            HttpStatus::ServiceUnavailable }
         ffi::NGX_HTTP_GATEWAY_TIME_OUT => {
            HttpStatus::GatewayTimeOut }
         ffi::NGX_HTTP_INSUFFICIENT_STORAGE => {
            HttpStatus::InsufficientStorage }
         _ => {
            HttpStatus::InternalServerError }
      }
   }

   pub fn rc(&self) -> ffi::ngx_int_t {
      match *self {
         HttpStatus::Continue => {
            ffi::NGX_HTTP_CONTINUE }
         HttpStatus::SwitchingProtocols => {
            ffi::NGX_HTTP_SWITCHING_PROTOCOLS }
         HttpStatus::Processing => {
            ffi::NGX_HTTP_PROCESSING }
         HttpStatus::Ok => {
            ffi::NGX_HTTP_OK }
         HttpStatus::Created => {
            ffi::NGX_HTTP_CREATED }
         HttpStatus::Accepted => {
            ffi::NGX_HTTP_ACCEPTED }
         HttpStatus::NoContent => {
            ffi::NGX_HTTP_NO_CONTENT }
         HttpStatus::PartialContent => {
            ffi::NGX_HTTP_PARTIAL_CONTENT }
         HttpStatus::SpecialResponse => {
            ffi::NGX_HTTP_SPECIAL_RESPONSE }
         HttpStatus::MovedPermanently => {
            ffi::NGX_HTTP_MOVED_PERMANENTLY }
         HttpStatus::MovedTemporarily => {
            ffi::NGX_HTTP_MOVED_TEMPORARILY }
         HttpStatus::SeeOther => {
            ffi::NGX_HTTP_SEE_OTHER }
         HttpStatus::NotModified => {
            ffi::NGX_HTTP_NOT_MODIFIED }
         HttpStatus::TemporaryRedirect => {
            ffi::NGX_HTTP_TEMPORARY_REDIRECT }
         HttpStatus::BadRequest => {
            ffi::NGX_HTTP_BAD_REQUEST }
         HttpStatus::Unauthorized => {
            ffi::NGX_HTTP_UNAUTHORIZED }
         HttpStatus::Forbidden => {
            ffi::NGX_HTTP_FORBIDDEN }
         HttpStatus::NotFound => {
            ffi::NGX_HTTP_NOT_FOUND }
         HttpStatus::NotAllowed => {
            ffi::NGX_HTTP_NOT_ALLOWED }
         HttpStatus::RequestTimeOut => {
            ffi::NGX_HTTP_REQUEST_TIME_OUT }
         HttpStatus::InternalServerError => {
            ffi::NGX_HTTP_INTERNAL_SERVER_ERROR }
         HttpStatus::Conflict => {
            ffi::NGX_HTTP_CONFLICT }
         HttpStatus::LengthRequired => {
            ffi::NGX_HTTP_LENGTH_REQUIRED }
         HttpStatus::PreconditionFailed => {
            ffi::NGX_HTTP_PRECONDITION_FAILED }
         HttpStatus::RequestUriTooLarge => {
            ffi::NGX_HTTP_REQUEST_URI_TOO_LARGE }
         HttpStatus::RequestEntityTooLarge => {
            ffi::NGX_HTTP_REQUEST_ENTITY_TOO_LARGE }
         HttpStatus::UnsupportedMediaType => {
            ffi::NGX_HTTP_UNSUPPORTED_MEDIA_TYPE }
         HttpStatus::RangeNotSatisfiable => {
            ffi::NGX_HTTP_RANGE_NOT_SATISFIABLE }
         HttpStatus::Close => {
            ffi::NGX_HTTP_CLOSE }
         HttpStatus::RequestHeaderTooLarge => {
            ffi::NGX_HTTP_REQUEST_HEADER_TOO_LARGE }
         HttpStatus::CertError => {
            ffi::NGX_HTTPS_CERT_ERROR }
         HttpStatus::NoCert => {
            ffi::NGX_HTTPS_NO_CERT }
         HttpStatus::ToHttps => {
            ffi::NGX_HTTP_TO_HTTPS }
         HttpStatus::ClientClosedRequest => {
            ffi::NGX_HTTP_CLIENT_CLOSED_REQUEST }
         HttpStatus::NotImplement => {
            ffi::NGX_HTTP_NOT_IMPLEMENTED }
         HttpStatus::BadGateway => {
            ffi::NGX_HTTP_BAD_GATEWAY }
         HttpStatus::ServiceUnavailable => {
            ffi::NGX_HTTP_SERVICE_UNAVAILABLE }
         HttpStatus::GatewayTimeOut => {
            ffi::NGX_HTTP_GATEWAY_TIME_OUT }
         HttpStatus::InsufficientStorage => {
            ffi::NGX_HTTP_INSUFFICIENT_STORAGE }
      }
   }
}

pub enum Status {
   Ok,
   Error,
   Again,
   Busy,
   Done,
   Declined,
   Abort,
   Http(HttpStatus),
}

impl Status {
   pub fn new(rc: ffi::ngx_int_t) -> Self {
      match rc {
         ffi::NGX_OK => {
            Status::Ok }
         ffi::NGX_ERROR => {
            Status::Error }
         ffi::NGX_AGAIN => {
            Status::Again }
         ffi::NGX_BUSY => {
            Status::Busy }
         ffi::NGX_DONE => {
            Status::Done }
         ffi::NGX_DECLINED => {
            Status::Declined }
         ffi::NGX_ABORT => {
            Status::Abort }
         http_rc if rc > 0 => {
            Status::Http(HttpStatus::new(http_rc)) }
         _ => {
            Status::Error }
      }
   }

   pub fn rc(&self) -> ffi::ngx_int_t {
      match *self {
         Status::Ok => {
            ffi::NGX_OK }
         Status::Error => {
            ffi::NGX_ERROR }
         Status::Again => {
            ffi::NGX_AGAIN }
         Status::Busy => {
            ffi::NGX_BUSY }
         Status::Done => {
            ffi::NGX_DONE }
         Status::Declined => {
            ffi::NGX_DECLINED }
         Status::Abort => {
            ffi::NGX_ABORT }
         Status::Http(ref http_status) => {
            http_status.rc() }
      }
   }
}

pub struct Connection {
   raw: *mut ffi::ngx_connection_t,
}

impl Connection {
   pub fn new(raw: *mut ffi::ngx_connection_t) -> Self {
      Connection {
         raw: raw
      }
   }

   pub fn log(&self) -> Option<Log> {
      let raw = unsafe { (*self.raw).log };

      if raw.is_null() {
         None
      } else {
         Some(
            Log::new(raw)
         )
      }
   }
}

pub struct Log {
   pub raw: *mut ffi::ngx_log_t,
}

impl Log {
   pub fn new(raw: *mut ffi::ngx_log_t) -> Self {
      Log {
         raw: raw
      }
   }
}

pub struct HttpRequest {
   raw: *mut ffi::ngx_http_request_t,
}

impl HttpRequest {
   pub fn new(raw: *mut ffi::ngx_http_request_t) -> Self {
      HttpRequest {
         raw: raw
      }
   }

   pub fn connection(&self) -> Option<Connection> {
      let raw = unsafe { (*self.raw).connection };

      if raw.is_null() {
         None
      } else {
         Some(
            Connection::new(raw)
         )
      }
   }

   pub fn headers_out(&self) -> HttpHeadersOut {
      let raw: *mut ffi::ngx_http_headers_out_t = unsafe { &mut (*self.raw).headers_out };

      HttpHeadersOut::new(raw)
   }

   pub fn http_send_header(&self) -> Status {
      let rc = unsafe { ffi::ngx_http_send_header(self.raw) };
      Status::new(rc)
   }

   pub fn pool(&self) -> Option<Pool> {
      let raw = unsafe { (*self.raw).pool };

      if raw.is_null() {
         None
      } else {
         Some(
            Pool::new(raw)
         )
      }
   }
}

pub struct HttpHeadersOut {
   raw: *mut ffi::ngx_http_headers_out_t,
}

impl HttpHeadersOut {
   pub fn new(raw: *mut ffi::ngx_http_headers_out_t) -> Self {
      HttpHeadersOut {
         raw: raw
      }
   }

   pub fn set_status(&mut self, status: ffi::ngx_uint_t) {
      unsafe {
         (*self.raw).status = status;
      };
   }

   pub fn set_content_length_n(&mut self, content_length_n: ffi::off_t) {
      unsafe {
         (*self.raw).content_length_n = content_length_n;
      };
   }
}

pub struct Pool {
   raw: *mut ffi::ngx_pool_t,
}

impl Pool {
   pub fn new(raw: *mut ffi::ngx_pool_t) -> Self {
      Pool {
         raw: raw
      }
   }

   #[inline]
   pub fn raw_palloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_palloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn raw_pnalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pnalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn raw_pcalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pcalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

}
<|endoftext|>


<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Represents an address in the guest's memory space.

use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use std::ops::{BitAnd, BitOr};

/// Represents an Address in the guest's memory.
#[derive(Clone, Copy, Debug)]
pub struct GuestAddress(pub usize);

impl GuestAddress {
    /// Returns the offset from this address to the given base address.
    ///
    /// # Examples
    ///
    /// ```
    /// # use sys_util::GuestAddress;
    ///   let base = GuestAddress(0x100);
    ///   let addr = GuestAddress(0x150);
    ///   assert_eq!(addr.offset_from(base), 0x50usize);
    /// ```
    pub fn offset_from(&self, base: GuestAddress) -> usize {
        self.0 - base.0
    }

    /// Returns the address as a usize offset from 0x0.
    /// Use this when a raw number is needed to pass to the kernel.
    pub fn offset(&self) -> usize {
        self.0
    }

    /// Returns the result of the add or None if there is overflow.
    pub fn checked_add(&self, other: usize) -> Option<GuestAddress> {
        self.0.checked_add(other).map(GuestAddress)
    }

    /// Returns the result of the base address + the size.
    /// Only use this when `offset` is guaranteed not to overflow.
    pub fn unchecked_add(&self, offset: usize) -> GuestAddress {
        GuestAddress(self.0 + offset)
    }

    /// Returns the result of the subtraction of None if there is underflow.
    pub fn checked_sub(&self, other: usize) -> Option<GuestAddress> {
        self.0.checked_sub(other).map(GuestAddress)
    }

    /// Returns the bitwise and of the address with the given mask.
    pub fn mask(&self, mask: u64) -> GuestAddress {
        GuestAddress(self.0 & mask as usize)
    }
}

impl BitAnd<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitand(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 & other as usize)
    }
}

impl BitOr<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitor(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 | other as usize)
    }
}

impl PartialEq for GuestAddress {
    fn eq(&self, other: &GuestAddress) -> bool {
        self.0 == other.0
    }
}
impl Eq for GuestAddress {}

impl Ord for GuestAddress {
    fn cmp(&self, other: &GuestAddress) -> Ordering {
        self.0.cmp(&other.0)
    }
}

impl PartialOrd for GuestAddress {
    fn partial_cmp(&self, other: &GuestAddress) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

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

    #[test]
    fn equals() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x300);
        let c = GuestAddress(0x301);
        assert_eq!(a, b);
        assert_eq!(b, a);
        assert_ne!(a, c);
        assert_ne!(c, a);
    }

    #[test]
    fn cmp() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x301);
        assert!(a < b);
        assert!(b > a);
        assert!(!(a < a));
    }

    #[test]
    fn mask() {
        let a = GuestAddress(0x5050);
        assert_eq!(GuestAddress(0x5000), a & 0xff00u64);
        assert_eq!(GuestAddress(0x5055), a | 0x0005u64);
    }

    #[test]
    fn add_sub() {
        let a = GuestAddress(0x50);
        let b = GuestAddress(0x60);
        assert_eq!(Some(GuestAddress(0xb0)), a.checked_add(0x60));
        assert_eq!(0x10, b.offset_from(a));
    }

    #[test]
    fn checked_add_overflow() {
        let a = GuestAddress(0xffffffffffffff55);
        assert_eq!(Some(GuestAddress(0xffffffffffffff57)), a.checked_add(2));
        assert!(a.checked_add(0xf0).is_none());
    }
}
<commit_msg>sys_util: guest_address: add tests to cover offset(), mask() and checked_sub()<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Represents an address in the guest's memory space.

use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use std::ops::{BitAnd, BitOr};

/// Represents an Address in the guest's memory.
#[derive(Clone, Copy, Debug)]
pub struct GuestAddress(pub usize);

impl GuestAddress {
    /// Returns the offset from this address to the given base address.
    ///
    /// # Examples
    ///
    /// ```
    /// # use sys_util::GuestAddress;
    ///   let base = GuestAddress(0x100);
    ///   let addr = GuestAddress(0x150);
    ///   assert_eq!(addr.offset_from(base), 0x50usize);
    /// ```
    pub fn offset_from(&self, base: GuestAddress) -> usize {
        self.0 - base.0
    }

    /// Returns the address as a usize offset from 0x0.
    /// Use this when a raw number is needed to pass to the kernel.
    pub fn offset(&self) -> usize {
        self.0
    }

    /// Returns the result of the add or None if there is overflow.
    pub fn checked_add(&self, other: usize) -> Option<GuestAddress> {
        self.0.checked_add(other).map(GuestAddress)
    }

    /// Returns the result of the base address + the size.
    /// Only use this when `offset` is guaranteed not to overflow.
    pub fn unchecked_add(&self, offset: usize) -> GuestAddress {
        GuestAddress(self.0 + offset)
    }

    /// Returns the result of the subtraction of None if there is underflow.
    pub fn checked_sub(&self, other: usize) -> Option<GuestAddress> {
        self.0.checked_sub(other).map(GuestAddress)
    }

    /// Returns the bitwise and of the address with the given mask.
    pub fn mask(&self, mask: u64) -> GuestAddress {
        GuestAddress(self.0 & mask as usize)
    }
}

impl BitAnd<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitand(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 & other as usize)
    }
}

impl BitOr<u64> for GuestAddress {
    type Output = GuestAddress;

    fn bitor(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 | other as usize)
    }
}

impl PartialEq for GuestAddress {
    fn eq(&self, other: &GuestAddress) -> bool {
        self.0 == other.0
    }
}
impl Eq for GuestAddress {}

impl Ord for GuestAddress {
    fn cmp(&self, other: &GuestAddress) -> Ordering {
        self.0.cmp(&other.0)
    }
}

impl PartialOrd for GuestAddress {
    fn partial_cmp(&self, other: &GuestAddress) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

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

    #[test]
    fn equals() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x300);
        let c = GuestAddress(0x301);
        assert_eq!(a, GuestAddress(a.offset()));
        assert_eq!(a, b);
        assert_eq!(b, a);
        assert_ne!(a, c);
        assert_ne!(c, a);
    }

    #[test]
    fn cmp() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x301);
        assert!(a < b);
        assert!(b > a);
        assert!(!(a < a));
    }

    #[test]
    fn mask() {
        let a = GuestAddress(0x5050);
        assert_eq!(GuestAddress(0x5000), a & 0xff00u64);
        assert_eq!(GuestAddress(0x5000), a.mask(0xff00u64));
        assert_eq!(GuestAddress(0x5055), a | 0x0005u64);
    }

    #[test]
    fn add_sub() {
        let a = GuestAddress(0x50);
        let b = GuestAddress(0x60);
        assert_eq!(Some(GuestAddress(0xb0)), a.checked_add(0x60));
        assert_eq!(0x10, b.offset_from(a));
    }

    #[test]
    fn checked_add_overflow() {
        let a = GuestAddress(0xffffffffffffff55);
        assert_eq!(Some(GuestAddress(0xffffffffffffff57)), a.checked_add(2));
        assert!(a.checked_add(0xf0).is_none());
    }

    #[test]
    fn checked_sub_underflow() {
        let a = GuestAddress(0xff);
        assert_eq!(Some(GuestAddress(0x0f)), a.checked_sub(0xf0));
        assert!(a.checked_sub(0xffff).is_none());
    }
}
<|endoftext|>