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

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! single_pass_renderpass {
    (
        $device:expr,
        attachments: { $($a:tt)* },
        pass: {
            color: [$($color_atch:ident),*],
            depth_stencil: {$($depth_atch:ident)*}$(,)*
            $(resolve: [$($resolve_atch:ident),*])*$(,)*
        }
    ) => (
        ordered_passes_renderpass!(
            $device,
            attachments: { $($a)* },
            passes: [
                {
                    color: [$($color_atch),*],
                    depth_stencil: {$($depth_atch)*},
                    input: [],
                    resolve: [$($resolve_atch),*]
                }
            ]
        )
    )
}

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! ordered_passes_renderpass {
    (
        $device:expr,
        attachments: {
            $(
                $atch_name:ident: {
                    load: $load:ident,
                    store: $store:ident,
                    format: $format:expr,
                    samples: $samples:expr,
                    $(initial_layout: $init_layout:expr,)*
                    $(final_layout: $final_layout:expr,)*
                }
            ),*
        },
        passes: [
            $(
                {
                    color: [$($color_atch:ident),*],
                    depth_stencil: {$($depth_atch:ident)*},
                    input: [$($input_atch:ident),*]$(,)*
                    $(resolve: [$($resolve_atch:ident),*])*$(,)*
                }
            ),*
        ]
    ) => ({
        use $crate::framebuffer::RenderPassDesc;

        mod scope {
            #![allow(non_camel_case_types)]
            #![allow(non_snake_case)]

            use $crate::format::ClearValue;
            use $crate::format::Format;
            use $crate::framebuffer::RenderPassDesc;
            use $crate::framebuffer::RenderPassDescClearValues;
            use $crate::framebuffer::LayoutAttachmentDescription;
            use $crate::framebuffer::LayoutPassDescription;
            use $crate::framebuffer::LayoutPassDependencyDescription;
            use $crate::image::ImageLayout;
            use $crate::sync::AccessFlagBits;
            use $crate::sync::PipelineStages;

            pub struct CustomRenderPassDesc {
                $(
                    pub $atch_name: (Format, u32),
                )*
            }

            #[allow(unsafe_code)]
            unsafe impl RenderPassDesc for CustomRenderPassDesc {
                #[inline]
                fn num_attachments(&self) -> usize {
                    num_attachments()
                }

                #[inline]
                fn attachment_desc(&self, id: usize) -> Option<LayoutAttachmentDescription> {
                    attachment(self, id)
                }

                #[inline]
                fn num_subpasses(&self) -> usize {
                    num_subpasses()
                }

                #[inline]
                fn subpass_desc(&self, id: usize) -> Option<LayoutPassDescription> {
                    subpass(id)
                }

                #[inline]
                fn num_dependencies(&self) -> usize {
                    num_dependencies()
                }

                #[inline]
                fn dependency_desc(&self, id: usize) -> Option<LayoutPassDependencyDescription> {
                    dependency(id)
                }
            }

            unsafe impl RenderPassDescClearValues<Vec<ClearValue>> for CustomRenderPassDesc {
                fn convert_clear_values(&self, values: Vec<ClearValue>) -> Box<Iterator<Item = ClearValue>> {
                    // FIXME: safety checks
                    Box::new(values.into_iter())
                }
            }

            #[inline]
            fn num_attachments() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $(let $atch_name = num; num += 1;)*
                num
            }

            #[inline]
            fn attachment(desc: &CustomRenderPassDesc, id: usize) -> Option<LayoutAttachmentDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]

                let mut num = 0;

                $({
                    if id == num {
                        let (initial_layout, final_layout) = attachment_layouts(num);

                        return Some($crate::framebuffer::LayoutAttachmentDescription {
                            format: desc.$atch_name.0,
                            samples: desc.$atch_name.1,
                            load: $crate::framebuffer::LoadOp::$load,
                            store: $crate::framebuffer::StoreOp::$store,
                            stencil_load: $crate::framebuffer::LoadOp::$load,
                            stencil_store: $crate::framebuffer::StoreOp::$store,
                            initial_layout: initial_layout,
                            final_layout: final_layout,
                        });
                    }

                    num += 1;
                })*

                None
            }

            #[inline]
            fn num_subpasses() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $($(let $color_atch = num;)* num += 1;)*
                num
            }

            #[inline]
            fn subpass(id: usize) -> Option<LayoutPassDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut cur_pass_num = 0;

                $({
                    if id == cur_pass_num {
                        let mut depth = None;
                        $(
                            depth = Some(($depth_atch, ImageLayout::DepthStencilAttachmentOptimal));
                        )*

                        let mut desc = LayoutPassDescription {
                            color_attachments: vec![
                                $(
                                    ($color_atch, ImageLayout::ColorAttachmentOptimal)
                                ),*
                            ],
                            depth_stencil: depth,
                            input_attachments: vec![
                                $(
                                    ($input_atch, ImageLayout::ShaderReadOnlyOptimal)
                                ),*
                            ],
                            resolve_attachments: vec![
                                $($(
                                    ($resolve_atch, ImageLayout::TransferDstOptimal)
                                ),*)*
                            ],
                            preserve_attachments: (0 .. attachment_num).filter(|&a| {
                                $(if a == $color_atch { return false; })*
                                $(if a == $depth_atch { return false; })*
                                $(if a == $input_atch { return false; })*
                                $($(if a == $resolve_atch { return false; })*)*
                                true
                            }).collect()
                        };

                        assert!(desc.resolve_attachments.is_empty() ||
                                desc.resolve_attachments.len() == desc.color_attachments.len());
                        return Some(desc);
                    }

                    cur_pass_num += 1;
                })*

                None
            }

            #[inline]
            fn num_dependencies() -> usize {
                num_subpasses().saturating_sub(1)
            }

            #[inline]
            fn dependency(id: usize) -> Option<LayoutPassDependencyDescription> {
                let num_passes = num_subpasses();

                if id + 1 >= num_passes {
                    return None;
                }

                Some(LayoutPassDependencyDescription {
                    source_subpass: id,
                    destination_subpass: id + 1,
                    source_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    destination_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    source_access: AccessFlagBits::all(),         // TODO: correct values
                    destination_access: AccessFlagBits::all(),         // TODO: correct values
                    by_region: true,            // TODO: correct values
                })
            }

            /// Returns the initial and final layout of an attachment, given its num.
            ///
            /// The value always correspond to the first and last usages of an attachment.
            fn attachment_layouts(num: usize) -> (ImageLayout, ImageLayout) {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut initial_layout = None;
                let mut final_layout = None;

                $({
                    $(
                        if $depth_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                        }
                    )*

                    $(
                        if $color_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ColorAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::ColorAttachmentOptimal);
                        }
                    )*

                    $($(
                        if $resolve_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::TransferDstOptimal);
                            }
                            final_layout = Some(ImageLayout::TransferDstOptimal);
                        }
                    )*)*

                    $(
                        if $input_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                            }
                            final_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                        }
                    )*
                })*

                $(if $atch_name == num {
                    // If the clear OP is Clear or DontCare, default to the Undefined layout.
                    if initial_layout == Some(ImageLayout::DepthStencilAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::ColorAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::TransferDstOptimal)
                    {
                        if $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::Clear ||
                            $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::DontCare
                        {
                            initial_layout = Some(ImageLayout::Undefined);
                        }
                    }

                    $(initial_layout = Some($init_layout);)*
                    $(final_layout = Some($final_layout);)*
                })*

                (initial_layout.unwrap(), final_layout.unwrap())
            }
        }

        scope::CustomRenderPassDesc {
            $(
                $atch_name: ($format, $samples),
            )*
        }.build_render_pass($device)
    });
}
<commit_msg>Another fix for the render pass macro with resolve (#834)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! single_pass_renderpass {
    (
        $device:expr,
        attachments: { $($a:tt)* },
        pass: {
            color: [$($color_atch:ident),*],
            depth_stencil: {$($depth_atch:ident)*}$(,)*
            $(resolve: [$($resolve_atch:ident),*])*$(,)*
        }
    ) => (
        ordered_passes_renderpass!(
            $device,
            attachments: { $($a)* },
            passes: [
                {
                    color: [$($color_atch),*],
                    depth_stencil: {$($depth_atch)*},
                    input: [],
                    resolve: [$($($resolve_atch),*)*]
                }
            ]
        )
    )
}

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! ordered_passes_renderpass {
    (
        $device:expr,
        attachments: {
            $(
                $atch_name:ident: {
                    load: $load:ident,
                    store: $store:ident,
                    format: $format:expr,
                    samples: $samples:expr,
                    $(initial_layout: $init_layout:expr,)*
                    $(final_layout: $final_layout:expr,)*
                }
            ),*
        },
        passes: [
            $(
                {
                    color: [$($color_atch:ident),*],
                    depth_stencil: {$($depth_atch:ident)*},
                    input: [$($input_atch:ident),*]$(,)*
                    $(resolve: [$($resolve_atch:ident),*])*$(,)*
                }
            ),*
        ]
    ) => ({
        use $crate::framebuffer::RenderPassDesc;

        mod scope {
            #![allow(non_camel_case_types)]
            #![allow(non_snake_case)]

            use $crate::format::ClearValue;
            use $crate::format::Format;
            use $crate::framebuffer::RenderPassDesc;
            use $crate::framebuffer::RenderPassDescClearValues;
            use $crate::framebuffer::LayoutAttachmentDescription;
            use $crate::framebuffer::LayoutPassDescription;
            use $crate::framebuffer::LayoutPassDependencyDescription;
            use $crate::image::ImageLayout;
            use $crate::sync::AccessFlagBits;
            use $crate::sync::PipelineStages;

            pub struct CustomRenderPassDesc {
                $(
                    pub $atch_name: (Format, u32),
                )*
            }

            #[allow(unsafe_code)]
            unsafe impl RenderPassDesc for CustomRenderPassDesc {
                #[inline]
                fn num_attachments(&self) -> usize {
                    num_attachments()
                }

                #[inline]
                fn attachment_desc(&self, id: usize) -> Option<LayoutAttachmentDescription> {
                    attachment(self, id)
                }

                #[inline]
                fn num_subpasses(&self) -> usize {
                    num_subpasses()
                }

                #[inline]
                fn subpass_desc(&self, id: usize) -> Option<LayoutPassDescription> {
                    subpass(id)
                }

                #[inline]
                fn num_dependencies(&self) -> usize {
                    num_dependencies()
                }

                #[inline]
                fn dependency_desc(&self, id: usize) -> Option<LayoutPassDependencyDescription> {
                    dependency(id)
                }
            }

            unsafe impl RenderPassDescClearValues<Vec<ClearValue>> for CustomRenderPassDesc {
                fn convert_clear_values(&self, values: Vec<ClearValue>) -> Box<Iterator<Item = ClearValue>> {
                    // FIXME: safety checks
                    Box::new(values.into_iter())
                }
            }

            #[inline]
            fn num_attachments() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $(let $atch_name = num; num += 1;)*
                num
            }

            #[inline]
            fn attachment(desc: &CustomRenderPassDesc, id: usize) -> Option<LayoutAttachmentDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]

                let mut num = 0;

                $({
                    if id == num {
                        let (initial_layout, final_layout) = attachment_layouts(num);

                        return Some($crate::framebuffer::LayoutAttachmentDescription {
                            format: desc.$atch_name.0,
                            samples: desc.$atch_name.1,
                            load: $crate::framebuffer::LoadOp::$load,
                            store: $crate::framebuffer::StoreOp::$store,
                            stencil_load: $crate::framebuffer::LoadOp::$load,
                            stencil_store: $crate::framebuffer::StoreOp::$store,
                            initial_layout: initial_layout,
                            final_layout: final_layout,
                        });
                    }

                    num += 1;
                })*

                None
            }

            #[inline]
            fn num_subpasses() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $($(let $color_atch = num;)* num += 1;)*
                num
            }

            #[inline]
            fn subpass(id: usize) -> Option<LayoutPassDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut cur_pass_num = 0;

                $({
                    if id == cur_pass_num {
                        let mut depth = None;
                        $(
                            depth = Some(($depth_atch, ImageLayout::DepthStencilAttachmentOptimal));
                        )*

                        let mut desc = LayoutPassDescription {
                            color_attachments: vec![
                                $(
                                    ($color_atch, ImageLayout::ColorAttachmentOptimal)
                                ),*
                            ],
                            depth_stencil: depth,
                            input_attachments: vec![
                                $(
                                    ($input_atch, ImageLayout::ShaderReadOnlyOptimal)
                                ),*
                            ],
                            resolve_attachments: vec![
                                $($(
                                    ($resolve_atch, ImageLayout::TransferDstOptimal)
                                ),*)*
                            ],
                            preserve_attachments: (0 .. attachment_num).filter(|&a| {
                                $(if a == $color_atch { return false; })*
                                $(if a == $depth_atch { return false; })*
                                $(if a == $input_atch { return false; })*
                                $($(if a == $resolve_atch { return false; })*)*
                                true
                            }).collect()
                        };

                        assert!(desc.resolve_attachments.is_empty() ||
                                desc.resolve_attachments.len() == desc.color_attachments.len());
                        return Some(desc);
                    }

                    cur_pass_num += 1;
                })*

                None
            }

            #[inline]
            fn num_dependencies() -> usize {
                num_subpasses().saturating_sub(1)
            }

            #[inline]
            fn dependency(id: usize) -> Option<LayoutPassDependencyDescription> {
                let num_passes = num_subpasses();

                if id + 1 >= num_passes {
                    return None;
                }

                Some(LayoutPassDependencyDescription {
                    source_subpass: id,
                    destination_subpass: id + 1,
                    source_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    destination_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    source_access: AccessFlagBits::all(),         // TODO: correct values
                    destination_access: AccessFlagBits::all(),         // TODO: correct values
                    by_region: true,            // TODO: correct values
                })
            }

            /// Returns the initial and final layout of an attachment, given its num.
            ///
            /// The value always correspond to the first and last usages of an attachment.
            fn attachment_layouts(num: usize) -> (ImageLayout, ImageLayout) {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut initial_layout = None;
                let mut final_layout = None;

                $({
                    $(
                        if $depth_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                        }
                    )*

                    $(
                        if $color_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ColorAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::ColorAttachmentOptimal);
                        }
                    )*

                    $($(
                        if $resolve_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::TransferDstOptimal);
                            }
                            final_layout = Some(ImageLayout::TransferDstOptimal);
                        }
                    )*)*

                    $(
                        if $input_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                            }
                            final_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                        }
                    )*
                })*

                $(if $atch_name == num {
                    // If the clear OP is Clear or DontCare, default to the Undefined layout.
                    if initial_layout == Some(ImageLayout::DepthStencilAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::ColorAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::TransferDstOptimal)
                    {
                        if $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::Clear ||
                            $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::DontCare
                        {
                            initial_layout = Some(ImageLayout::Undefined);
                        }
                    }

                    $(initial_layout = Some($init_layout);)*
                    $(final_layout = Some($final_layout);)*
                })*

                (initial_layout.unwrap(), final_layout.unwrap())
            }
        }

        scope::CustomRenderPassDesc {
            $(
                $atch_name: ($format, $samples),
            )*
        }.build_render_pass($device)
    });
}

#[cfg(test)]
mod tests {
    use format::Format;

    #[test]
    fn single_pass_resolve() {
        let (device, _) = gfx_dev_and_queue!();
        let _ = single_pass_renderpass!(device.clone(),
            attachments: {
                a: {
                    load: Clear,
                    store: DontCare,
                    format: Format::R8G8B8A8Unorm,
                    samples: 4,
                },
                b: {
                    load: DontCare,
                    store: Store,
                    format: Format::R8G8B8A8Unorm,
                    samples: 1,
                }
            },
            pass: {
                color: [a],
                depth_stencil: {},
                resolve: [b],
            }
        ).unwrap();
    }
}
<|endoftext|>


<commit_before>use core::prelude::*;
use core::mem::size_of;
use collections::Vec;

use paging;

#[repr(packed)]
struct RSDP {
    signature: [u8; 8],
    _checksum: u8,
    _oemid: [u8; 6],
    _revision: u8,
    rsdt_address: u32,
}

#[repr(packed)]
pub struct Header {
    signature: [u8; 4],
    length: u32,
    _revision: u8,
    _checksum: u8,
    _oemid: [u8; 6],
    _oem_table_id: [u8; 8],
    _oem_revision: u32,
    _creatorid: u32,
    _creator_revision: u32,
}

#[repr(packed)]
struct RSDT {
    header: Header,
    _table_ptr: u32,
}

static mut RSDT_CACHE: Option<&'static RSDT> = None;

unsafe fn checksum(start: *const u8, length: isize) -> bool {
    let mut sum = 0u32;
    for i in 0..length {
        sum += *start.offset(i) as u32;
    }
    (sum & 0xff) == 0
}

fn validate_rsdp(candidate: &RSDP) -> bool {
    let sig: &[u8] = &(*candidate).signature;
    if sig != b"RSD PTR " {
        return false
    }
    else {
        unsafe {
            checksum(candidate as *const RSDP as *const u8, size_of::<RSDP>() as isize)
        }
    }
}

fn find_rsdp() -> Option<&'static RSDP> {
    let high_mem_iter = (0xE0_000..0x100_000).step_by(0x10);
    let ebda_address =
        unsafe {
            *(0x40E as *const u16) as usize * 0x10
        };
    let ebda_iter = (ebda_address..ebda_address + 1024).step_by(0x10);
    for addr in high_mem_iter.chain(ebda_iter) {
        let candidate = unsafe {
            &*(addr as *const RSDP)
        };
        if validate_rsdp(candidate) {
            return Some(candidate)
        }
    }
    None
}

unsafe fn load_rsdt() -> &'static RSDT {
    match RSDT_CACHE {
        Some(x) => x,
        None => {
            let rsdp = find_rsdp().unwrap();
            paging::identity_map(rsdp.rsdt_address as usize);
            let maybe_rsdt = rsdp.rsdt_address as *const RSDT;
            if checksum(maybe_rsdt as *const u8, (*maybe_rsdt).header.length as isize) {
                RSDT_CACHE = Some(&*maybe_rsdt);
            }
            &*maybe_rsdt
        }
    }
}

pub fn find_table(name: &[u8; 4]) -> Option<&'static Header> {
    unsafe {
        let rsdt = load_rsdt();
        let rsdt_start = rsdt as *const RSDT as usize;
        let mut addr = rsdt_start + size_of::<Header>();
        let stop = rsdt_start + (rsdt.header.length as usize);
        while addr < stop {
            let header_loc = *(addr as *const u32);
            paging::identity_map(header_loc as usize);
            let header = header_loc as *const Header;
            if (*header).signature == *name {
                return Some(&*header);
            }
            addr += 4;
        }
        None
    }
}

#[repr(packed)]
struct MADTEntry {
    kind: u8,
    length: u8,
    _f0: u8,
    f1: u8,
    f2: u32,
    _f3: u32,
}

pub fn processor_list() -> Vec<u8> {
    let mut ret = Vec::new();
    let table = find_table(b"APIC").unwrap();
    let mut addr = table as *const Header as usize;
    let stop = table.length as usize + addr;
    addr += size_of::<Header>() + 0x8;
    while addr < stop {
        let entry = unsafe { &*(addr as *const MADTEntry) };
        addr += entry.length as usize;
        match entry.kind {
            0 => {
                if entry.f2 & 1 == 1 {
                    ret.push(entry.f1);
                }
            },
            _ => (),
        }
    }
    ret
}
<commit_msg>Remove unneeded stars<commit_after>use core::prelude::*;
use core::mem::size_of;
use collections::Vec;

use paging;

#[repr(packed)]
struct RSDP {
    signature: [u8; 8],
    _checksum: u8,
    _oemid: [u8; 6],
    _revision: u8,
    rsdt_address: u32,
}

#[repr(packed)]
pub struct Header {
    signature: [u8; 4],
    length: u32,
    _revision: u8,
    _checksum: u8,
    _oemid: [u8; 6],
    _oem_table_id: [u8; 8],
    _oem_revision: u32,
    _creatorid: u32,
    _creator_revision: u32,
}

#[repr(packed)]
struct RSDT {
    header: Header,
    _table_ptr: u32,
}

static mut RSDT_CACHE: Option<&'static RSDT> = None;

unsafe fn checksum(start: *const u8, length: isize) -> bool {
    let mut sum = 0u32;
    for i in 0..length {
        sum += *start.offset(i) as u32;
    }
    (sum & 0xff) == 0
}

fn validate_rsdp(candidate: &RSDP) -> bool {
    let sig: &[u8] = &candidate.signature;
    if sig != b"RSD PTR " {
        return false
    }
    else {
        unsafe {
            checksum(candidate as *const RSDP as *const u8, size_of::<RSDP>() as isize)
        }
    }
}

fn find_rsdp() -> Option<&'static RSDP> {
    let high_mem_iter = (0xE0_000..0x100_000).step_by(0x10);
    let ebda_address =
        unsafe {
            *(0x40E as *const u16) as usize * 0x10
        };
    let ebda_iter = (ebda_address..ebda_address + 1024).step_by(0x10);
    for addr in high_mem_iter.chain(ebda_iter) {
        let candidate = unsafe {
            &*(addr as *const RSDP)
        };
        if validate_rsdp(candidate) {
            return Some(candidate)
        }
    }
    None
}

unsafe fn load_rsdt() -> &'static RSDT {
    match RSDT_CACHE {
        Some(x) => x,
        None => {
            let rsdp = find_rsdp().unwrap();
            paging::identity_map(rsdp.rsdt_address as usize);
            let maybe_rsdt = rsdp.rsdt_address as *const RSDT;
            if checksum(maybe_rsdt as *const u8, (*maybe_rsdt).header.length as isize) {
                RSDT_CACHE = Some(&*maybe_rsdt);
            }
            &*maybe_rsdt
        }
    }
}

pub fn find_table(name: &[u8; 4]) -> Option<&'static Header> {
    unsafe {
        let rsdt = load_rsdt();
        let rsdt_start = rsdt as *const RSDT as usize;
        let mut addr = rsdt_start + size_of::<Header>();
        let stop = rsdt_start + (rsdt.header.length as usize);
        while addr < stop {
            let header_loc = *(addr as *const u32);
            paging::identity_map(header_loc as usize);
            let header = header_loc as *const Header;
            if (*header).signature == *name {
                return Some(&*header);
            }
            addr += 4;
        }
        None
    }
}

#[repr(packed)]
struct MADTEntry {
    kind: u8,
    length: u8,
    _f0: u8,
    f1: u8,
    f2: u32,
    _f3: u32,
}

pub fn processor_list() -> Vec<u8> {
    let mut ret = Vec::new();
    let table = find_table(b"APIC").unwrap();
    let mut addr = table as *const Header as usize;
    let stop = table.length as usize + addr;
    addr += size_of::<Header>() + 0x8;
    while addr < stop {
        let entry = unsafe { &*(addr as *const MADTEntry) };
        addr += entry.length as usize;
        match entry.kind {
            0 => {
                if entry.f2 & 1 == 1 {
                    ret.push(entry.f1);
                }
            },
            _ => (),
        }
    }
    ret
}
<|endoftext|>


<commit_before>use std::fmt;

use common;
use byteorder::{BigEndian, ByteOrder};
use super::Field;
use std::str;

/// Size (in bytes) of the buffer of a int field.
const INT_TERM_LEN: usize = 4 + 8;

/// Term represents the value that the token can take.
///
/// It actually wraps a `Vec<u8>`.
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct Term(Vec<u8>);

impl Term {

    /// Set the content of the term.
    pub fn set_content(&mut self, content: &[u8]) {
        assert!(content.len() >= 4);
        self.0.resize(content.len(), 0u8);
        (&mut self.0[..]).clone_from_slice(content);
    }

    /// Returns the field id.
    fn field_id(&self,) -> u32 {
        BigEndian::read_u32(&self.0[..4])
    }

    /// Returns the field.
    pub fn field(&self,) -> Field {
        Field(self.field_id())
    }

    /// Returns the field.
    pub fn set_field(&mut self, field: Field) {
        if self.0.len() < 4 {
            self.0.resize(4, 0u8);
        }
        BigEndian::write_u32(&mut self.0[0..4], field.0);
    }

    /// Builds a term given a field, and a u64-value
    ///
    /// Assuming the term has a field id of 1, and a u64 value of 3234,
    /// the Term will have 8 bytes.
    ///
    /// The first four byte are dedicated to storing the field id as a u64.
    /// The 4 following bytes are encoding the u64 value.
    pub fn from_field_u64(field: Field, val: u64) -> Term {
        let mut term = Term(vec![0u8; INT_TERM_LEN]);
        term.set_field(field);
        term.set_u64(val);
        term
    }

    /// Sets a u64 value in the term.
    ///
    /// U64 are serialized using (8-byte) BigEndian
    /// representation.
    /// The use of BigEndian has the benefit of preserving
    /// the natural order of the values.
    pub fn set_u64(&mut self, val: u64) {
        self.0.resize(INT_TERM_LEN, 0u8);
        BigEndian::write_u64(&mut self.0[4..], val);
    }

    /// Builds a term given a field, and a u64-value
    ///
    /// Assuming the term has a field id of 1, and a u64 value of 3234,
    /// the Term will have 8 bytes.
    ///
    /// The first four byte are dedicated to storing the field id as a u64.
    /// The 4 following bytes are encoding the u64 value.
    pub fn from_field_i64(field: Field, val: i64) -> Term {
        let val_u64: u64 = common::i64_to_u64(val);
        Term::from_field_u64(field, val_u64)
    }

    /// Builds a term given a field, and a string value
    ///
    /// Assuming the term has a field id of 2, and a text value of "abc",
    /// the Term will have 4 bytes.
    /// The first byte is 2, and the three following bytes are the utf-8
    /// representation of "abc".
    pub fn from_field_text(field: Field, text: &str) -> Term {
        let buffer = Vec::with_capacity(4 + text.len());
        let mut term = Term(buffer);
        term.set_field(field);
        term.set_text(text);
        term
    }

    /// Creates a new Term with an empty buffer,
    /// but with a given capacity.
    ///
    /// It is declared unsafe, as the term content
    /// is not initialized, and a call to `.field()`
    /// would panic.
    pub(crate) unsafe fn with_capacity(num_bytes: usize) -> Term {
        Term(Vec::with_capacity(num_bytes))
    }

    /// Assume the term is a u64 field.
    ///
    /// Panics if the term is not a u64 field.
    pub fn get_u64(&self) -> u64 {
        BigEndian::read_u64(&self.0[4..])
    }

    /// Builds a term from its byte representation.
    ///
    /// If you want to build a field for a given `str`,
    /// you want to use `from_field_text`.
    pub fn from_bytes(data: &[u8]) -> Term {
        Term(Vec::from(data))
    }

    /// Returns the serialized value of the term.
    /// (this does not include the field.)
    ///
    /// If the term is a string, its value is utf-8 encoded.
    /// If the term is a u64, its value is encoded according
    /// to `byteorder::LittleEndian`.
    pub fn value(&self) -> &[u8] {
        &self.0[4..]
    }

    /// Returns the text associated with the term.
    ///
    /// # Panics
    /// If the value is not valid utf-8. This may happen
    /// if the index is corrupted or if you try to
    /// call this method on a non-string type.
    pub fn text(&self) -> &str {
        str::from_utf8(self.value()).expect("Term does not contain valid utf-8.")
    }

    /// Set the texts only, keeping the field untouched.
    pub fn set_text(&mut self, text: &str) {
        self.0.resize(4, 0u8);
        self.0.extend(text.as_bytes());
    }

    /// Returns the underlying `&[u8]`
    pub fn as_slice(&self,)->&[u8] {
        &self.0
    }
}

impl AsRef<[u8]> for Term {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl fmt::Debug for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Term({:?})", &self.0[..])
    }
}

#[cfg(test)]
mod tests {

    use schema::*;

    #[test]
    pub fn test_term() {
        let mut schema_builder = SchemaBuilder::default();
        schema_builder.add_text_field("text", STRING);
        let title_field = schema_builder.add_text_field("title", STRING);
        let count_field = schema_builder.add_text_field("count", STRING);
        {
            let term = Term::from_field_text(title_field, "test");
            assert_eq!(term.field(), title_field);
            assert_eq!(&term.as_slice()[0..4], &[0u8,0u8,0u8,1u8]);
            assert_eq!(&term.as_slice()[4..], "test".as_bytes());
        }
        {
            let term = Term::from_field_u64(count_field, 983u64);
            assert_eq!(term.field(), count_field);
            assert_eq!(&term.as_slice()[0..4], &[0u8, 0u8, 0u8, 2u8]);
            assert_eq!(term.as_slice().len(), 4 + 8);
            assert_eq!(term.as_slice()[4], 0u8);
            assert_eq!(term.as_slice()[5], 0u8);
            assert_eq!(term.as_slice()[6], 0u8);
            assert_eq!(term.as_slice()[7], 0u8);
            assert_eq!(term.as_slice()[8], 0u8);
            assert_eq!(term.as_slice()[9], 0u8);
            assert_eq!(term.as_slice()[10], (933u64 / 256u64) as u8);
            assert_eq!(term.as_slice()[11], (983u64 % 256u64) as u8);
        }

    }
}<commit_msg>NOBUG Hiding methods making it possible to build a incorrect Term.<commit_after>use std::fmt;

use common;
use byteorder::{BigEndian, ByteOrder};
use super::Field;
use std::str;

/// Size (in bytes) of the buffer of a int field.
const INT_TERM_LEN: usize = 4 + 8;

/// Term represents the value that the token can take.
///
/// It actually wraps a `Vec<u8>`.
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct Term(Vec<u8>);

impl Term {

    /// Set the content of the term.
    pub(crate) fn set_content(&mut self, content: &[u8]) {
        assert!(content.len() >= 4);
        self.0.resize(content.len(), 0u8);
        (&mut self.0[..]).clone_from_slice(content);
    }

    /// Returns the field id.
    fn field_id(&self,) -> u32 {
        BigEndian::read_u32(&self.0[..4])
    }

    /// Returns the field.
    pub fn field(&self,) -> Field {
        Field(self.field_id())
    }

    /// Returns the field.
    pub fn set_field(&mut self, field: Field) {
        if self.0.len() < 4 {
            self.0.resize(4, 0u8);
        }
        BigEndian::write_u32(&mut self.0[0..4], field.0);
    }

    /// Builds a term given a field, and a u64-value
    ///
    /// Assuming the term has a field id of 1, and a u64 value of 3234,
    /// the Term will have 8 bytes.
    ///
    /// The first four byte are dedicated to storing the field id as a u64.
    /// The 4 following bytes are encoding the u64 value.
    pub fn from_field_u64(field: Field, val: u64) -> Term {
        let mut term = Term(vec![0u8; INT_TERM_LEN]);
        term.set_field(field);
        term.set_u64(val);
        term
    }

    /// Sets a u64 value in the term.
    ///
    /// U64 are serialized using (8-byte) BigEndian
    /// representation.
    /// The use of BigEndian has the benefit of preserving
    /// the natural order of the values.
    pub fn set_u64(&mut self, val: u64) {
        self.0.resize(INT_TERM_LEN, 0u8);
        BigEndian::write_u64(&mut self.0[4..], val);
    }

    /// Builds a term given a field, and a u64-value
    ///
    /// Assuming the term has a field id of 1, and a u64 value of 3234,
    /// the Term will have 8 bytes.
    ///
    /// The first four byte are dedicated to storing the field id as a u64.
    /// The 4 following bytes are encoding the u64 value.
    pub fn from_field_i64(field: Field, val: i64) -> Term {
        let val_u64: u64 = common::i64_to_u64(val);
        Term::from_field_u64(field, val_u64)
    }

    /// Builds a term given a field, and a string value
    ///
    /// Assuming the term has a field id of 2, and a text value of "abc",
    /// the Term will have 4 bytes.
    /// The first byte is 2, and the three following bytes are the utf-8
    /// representation of "abc".
    pub fn from_field_text(field: Field, text: &str) -> Term {
        let buffer = Vec::with_capacity(4 + text.len());
        let mut term = Term(buffer);
        term.set_field(field);
        term.set_text(text);
        term
    }

    /// Creates a new Term with an empty buffer,
    /// but with a given capacity.
    ///
    /// It is declared unsafe, as the term content
    /// is not initialized, and a call to `.field()`
    /// would panic.
    pub(crate) unsafe fn with_capacity(num_bytes: usize) -> Term {
        Term(Vec::with_capacity(num_bytes))
    }

    /// Assume the term is a u64 field.
    ///
    /// Panics if the term is not a u64 field.
    pub fn get_u64(&self) -> u64 {
        BigEndian::read_u64(&self.0[4..])
    }

    /// Builds a term from its byte representation.
    ///
    /// If you want to build a field for a given `str`,
    /// you want to use `from_field_text`.
    pub(crate) fn from_bytes(data: &[u8]) -> Term {
        Term(Vec::from(data))
    }

    /// Returns the serialized value of the term.
    /// (this does not include the field.)
    ///
    /// If the term is a string, its value is utf-8 encoded.
    /// If the term is a u64, its value is encoded according
    /// to `byteorder::LittleEndian`.
    pub fn value(&self) -> &[u8] {
        &self.0[4..]
    }

    /// Returns the text associated with the term.
    ///
    /// # Panics
    /// If the value is not valid utf-8. This may happen
    /// if the index is corrupted or if you try to
    /// call this method on a non-string type.
    pub fn text(&self) -> &str {
        str::from_utf8(self.value()).expect("Term does not contain valid utf-8.")
    }

    /// Set the texts only, keeping the field untouched.
    pub fn set_text(&mut self, text: &str) {
        self.0.resize(4, 0u8);
        self.0.extend(text.as_bytes());
    }

    /// Returns the underlying `&[u8]`
    pub fn as_slice(&self,)->&[u8] {
        &self.0
    }
}

impl AsRef<[u8]> for Term {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl fmt::Debug for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Term({:?})", &self.0[..])
    }
}

#[cfg(test)]
mod tests {

    use schema::*;

    #[test]
    pub fn test_term() {
        let mut schema_builder = SchemaBuilder::default();
        schema_builder.add_text_field("text", STRING);
        let title_field = schema_builder.add_text_field("title", STRING);
        let count_field = schema_builder.add_text_field("count", STRING);
        {
            let term = Term::from_field_text(title_field, "test");
            assert_eq!(term.field(), title_field);
            assert_eq!(&term.as_slice()[0..4], &[0u8,0u8,0u8,1u8]);
            assert_eq!(&term.as_slice()[4..], "test".as_bytes());
        }
        {
            let term = Term::from_field_u64(count_field, 983u64);
            assert_eq!(term.field(), count_field);
            assert_eq!(&term.as_slice()[0..4], &[0u8, 0u8, 0u8, 2u8]);
            assert_eq!(term.as_slice().len(), 4 + 8);
            assert_eq!(term.as_slice()[4], 0u8);
            assert_eq!(term.as_slice()[5], 0u8);
            assert_eq!(term.as_slice()[6], 0u8);
            assert_eq!(term.as_slice()[7], 0u8);
            assert_eq!(term.as_slice()[8], 0u8);
            assert_eq!(term.as_slice()[9], 0u8);
            assert_eq!(term.as_slice()[10], (933u64 / 256u64) as u8);
            assert_eq!(term.as_slice()[11], (983u64 % 256u64) as u8);
        }

    }
}<|endoftext|>


<commit_before>extern crate rustbox;
use rustbox::*;
use screen::common::*;
use utility::string::*;
use model::ShowItem;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show { rustbox: &rustbox }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        print_header(&self.rustbox,
                     self.rustbox.width(),
                     &format!("{} - {}", item.title, title));
        print_body(&self.rustbox,
                   self.body_width(),
                   2,
                   self.body_height(),
                   &item);
    }
    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }
}

fn print_header(rustbox: &rustbox::RustBox, width: usize, text: &str) {
    let title_len = jks_len(text);
    let padding = (if width >= title_len {
        width - title_len
    } else {
        0
    }) / 2;

    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    clearline(&rustbox, width, 0, 0);
    rustbox.print(padding,
                  0,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  text);
    rustbox.print(0,
                  1,
                  rustbox::RB_BOLD,
                  Color::Yellow,
                  Color::Black,
                  &header_bottom);
}

fn print_body(rustbox: &rustbox::RustBox,
              width: usize,
              offset_y: usize,
              rows: usize,
              item: &ShowItem) {

    let mut y = offset_y;
    let replier_max_width = 14;

    let separator_width = if rustbox.width() >= 4 {
        rustbox.width() - 4
    } else {
        0
    };
    let separator_padding_width = if rustbox.width() > separator_width {
        rustbox.width() - separator_width
    } else {
        0
    } / 2;

    let separator_padding = (0..separator_padding_width).map(|_| " ").collect::<Vec<_>>().join("");

    let separator_bottom = make_separator_bottom(separator_width, &separator_padding);

    for (i, reply) in item.replies.iter().take(rows).enumerate() {
        let contents: Vec<&str> = reply.content.split("\n").collect();

        let mut m = 0;

        for (j, content) in contents.iter().enumerate() {
            rustbox.print(0,
                          j + y,
                          rustbox::RB_NORMAL,
                          Color::White,
                          Color::Black,
                          &format!(" {}", content));
            m += 1;
        }
        let replier_name = reply.username.clone();
        let separator_top = make_separator_top(separator_width,
                                               &separator_padding,
                                               replier_max_width,
                                               &replier_name);

        rustbox.print(0,
                      m + y,
                      rustbox::RB_NORMAL,
                      Color::White,
                      Color::Black,
                      &separator_top);
        m += 1;

        rustbox.print(0,
                      m + y,
                      rustbox::RB_NORMAL,
                      Color::White,
                      Color::Black,
                      &separator_bottom);
        m += 1;
        y += m;
    }
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = (0..replier_name_left_spacing_width)
                                        .map(|_| "─")
                                        .collect::<Vec<_>>()
                                        .join("");

    let replier_name_right_spacing = (0..replier_name_right_spacing_width)
                                         .map(|_| "─")
                                         .collect::<Vec<_>>()
                                         .join("");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "╮");
    return separator_replier;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_top_middle_width = if separator_width > separator_replier_width {
        separator_width - separator_replier_width
    } else {
        0
    };
    let separator_top_middle = (0..separator_top_middle_width)
                                   .map(|_| " ")
                                   .collect::<Vec<_>>()
                                   .join("");

    let separator_top = format!("{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_padding);
    return separator_top;
}
fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = (0..separator_bottom_middle_width)
                                      .map(|_| "─")
                                      .collect::<Vec<_>>()
                                      .join("");
    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}
<commit_msg>show - add publish date time to separator<commit_after>extern crate rustbox;
use rustbox::*;
use screen::common::*;
use utility::string::*;
use model::ShowItem;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show { rustbox: &rustbox }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        print_header(&self.rustbox,
                     self.rustbox.width(),
                     &format!("{} - {}", item.title, title));
        print_body(&self.rustbox,
                   self.body_width(),
                   2,
                   self.body_height(),
                   &item);
    }
    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }
}

fn print_header(rustbox: &rustbox::RustBox, width: usize, text: &str) {
    let title_len = jks_len(text);
    let padding = (if width >= title_len {
        width - title_len
    } else {
        0
    }) / 2;

    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    clearline(&rustbox, width, 0, 0);
    rustbox.print(padding,
                  0,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  text);
    rustbox.print(0,
                  1,
                  rustbox::RB_BOLD,
                  Color::Yellow,
                  Color::Black,
                  &header_bottom);
}

fn print_body(rustbox: &rustbox::RustBox,
              width: usize,
              offset_y: usize,
              rows: usize,
              item: &ShowItem) {

    let mut y = offset_y;
    let replier_max_width = 14;
    let time_max_width = 17;

    let separator_width = if rustbox.width() >= 4 {
        rustbox.width() - 4
    } else {
        0
    };
    let separator_padding_width = if rustbox.width() > separator_width {
        rustbox.width() - separator_width
    } else {
        0
    } / 2;

    let separator_padding = (0..separator_padding_width).map(|_| " ").collect::<Vec<_>>().join("");

    let separator_bottom = make_separator_bottom(separator_width, &separator_padding);

    for (i, reply) in item.replies.iter().take(rows).enumerate() {
        let contents: Vec<&str> = reply.content.split("\n").collect();

        let mut m = 0;

        for (j, content) in contents.iter().enumerate() {
            rustbox.print(0,
                          j + y,
                          rustbox::RB_NORMAL,
                          Color::White,
                          Color::Black,
                          &format!(" {}", content));
            m += 1;
        }
        let replier_name = reply.username.clone();
        let time = reply.published_at.clone();
        let separator_top = make_separator_top(separator_width,
                                               &separator_padding,
                                               replier_max_width,
                                               &replier_name,
                                               time_max_width,
                                               &time);

        rustbox.print(0,
                      m + y,
                      rustbox::RB_NORMAL,
                      Color::White,
                      Color::Black,
                      &separator_top);
        m += 1;

        rustbox.print(0,
                      m + y,
                      rustbox::RB_NORMAL,
                      Color::White,
                      Color::Black,
                      &separator_bottom);
        m += 1;
        y += m;
    }
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = (0..replier_name_left_spacing_width)
                                        .map(|_| "─")
                                        .collect::<Vec<_>>()
                                        .join("");

    let replier_name_right_spacing = (0..replier_name_right_spacing_width)
                                         .map(|_| "─")
                                         .collect::<Vec<_>>()
                                         .join("");

    let separator_replier = format!("{}{}{}{}{}",
                                    "",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "╮");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = (0..time_left_spacing_width)
                                .map(|_| "─")
                                .collect::<Vec<_>>()
                                .join("");

    let time_right_spacing = (0..time_right_spacing_width)
                                 .map(|_| "─")
                                 .collect::<Vec<_>>()
                                 .join("");

    let separator_time = format!("{}{}{}{}{}",
                                 "╭",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = (0..separator_top_middle_width)
                                   .map(|_| " ")
                                   .collect::<Vec<_>>()
                                   .join("");

    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_time,
                                separator_replier,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = (0..separator_bottom_middle_width)
                                      .map(|_| "─")
                                      .collect::<Vec<_>>()
                                      .join("");
    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::{self, Display, Formatter};
use std::net::SocketAddr;
use std::boxed::FnBox;
use std::time::Instant;
use std::sync::{Arc, RwLock};

use mio::Token;
use futures::{Async, Future, Poll, Stream};
use futures::stream::{self, Once};
use grpc::{ChannelBuilder, Environment, WriteFlags};
use kvproto::raft_serverpb::SnapshotChunk;
use kvproto::raft_serverpb::RaftMessage;
use kvproto::tikvpb_grpc::TikvClient;

use raftstore::store::{SnapEntry, SnapKey, SnapManager, Snapshot};
use util::threadpool::{DefaultContext, ThreadPool, ThreadPoolBuilder};
use util::worker::Runnable;
use util::buf::PipeBuffer;
use util::collections::{HashMap, HashMapEntry as Entry};
use util::HandyRwLock;

use super::metrics::*;
use super::{Error, Result};
use super::transport::RaftStoreRouter;

pub type Callback = Box<FnBox(Result<()>) + Send>;

const DEFAULT_SENDER_POOL_SIZE: usize = 3;

/// `Task` that `Runner` can handle.
///
/// `Register` register a pending snapshot file with token;
/// `Write` write data to snapshot file;
/// `Close` save the snapshot file;
/// `Discard` discard all the unsaved changes made to snapshot file;
/// `SendTo` send the snapshot file to specified address.
pub enum Task {
    Register(Token, RaftMessage),
    Write(Token, PipeBuffer),
    Close(Token),
    Discard(Token),
    SendTo {
        addr: SocketAddr,
        msg: RaftMessage,
        cb: Callback,
    },
}

impl Display for Task {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Task::Register(token, ref meta) => write!(f, "Register {:?} token: {:?}", meta, token),
            Task::Write(token, _) => write!(f, "Write snap for {:?}", token),
            Task::Close(token) => write!(f, "Close file {:?}", token),
            Task::Discard(token) => write!(f, "Discard file {:?}", token),
            Task::SendTo {
                ref addr, ref msg, ..
            } => write!(f, "SendTo Snap[to: {}, snap: {:?}]", addr, msg),
        }
    }
}

struct SnapChunk {
    snap: Arc<RwLock<Box<Snapshot>>>,
    remain_bytes: usize,
}

const SNAP_CHUNK_LEN: usize = 1024 * 1024;

impl Stream for SnapChunk {
    type Item = (SnapshotChunk, WriteFlags);
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Error> {
        let mut buf = match self.remain_bytes {
            0 => return Ok(Async::Ready(None)),
            n if n > SNAP_CHUNK_LEN => vec![0; SNAP_CHUNK_LEN],
            n => vec![0; n],
        };
        match self.snap.wl().read_exact(buf.as_mut_slice()) {
            Ok(_) => {
                self.remain_bytes -= buf.len();
                let mut chunk = SnapshotChunk::new();
                chunk.set_data(buf);
                Ok(Async::Ready(Some((chunk, WriteFlags::default()))))
            }
            Err(e) => Err(box_err!("failed to read snapshot chunk: {}", e)),
        }
    }
}

/// Send the snapshot to specified address.
///
/// It will first send the normal raft snapshot message and then send the snapshot file.
fn send_snap(
    env: Arc<Environment>,
    mgr: SnapManager,
    addr: SocketAddr,
    msg: RaftMessage,
) -> Result<()> {
    assert!(msg.get_message().has_snapshot());
    let timer = Instant::now();

    let send_timer = SEND_SNAP_HISTOGRAM.start_coarse_timer();

    let key = {
        let snap = msg.get_message().get_snapshot();
        try!(SnapKey::from_snap(snap))
    };
    mgr.register(key.clone(), SnapEntry::Sending);
    defer!({
        mgr.deregister(&key, &SnapEntry::Sending);
    });
    let s = box_try!(mgr.get_snapshot_for_sending(&key));
    if !s.exists() {
        return Err(box_err!("missing snap file: {:?}", s.path()));
    }
    let total_size = try!(s.total_size());

    // snapshot file has been validated when created, so no need to validate again.
    let s = Arc::new(RwLock::new(s));

    let chunks = {
        let snap_chunk = SnapChunk {
            snap: s.clone(),
            remain_bytes: total_size as usize,
        };
        let first: Once<(SnapshotChunk, _), Error> = stream::once({
            let mut chunk = SnapshotChunk::new();
            chunk.set_message(msg);
            Ok((chunk, WriteFlags::default()))
        });
        first.chain(snap_chunk)
    };

    let channel = ChannelBuilder::new(env).connect(&format!("{}", addr));
    let client = TikvClient::new(channel);
    let (sink, receiver) = client.snapshot();
    let send = chunks.forward(sink);
    let res = send.and_then(|_| receiver.map_err(Error::from))
        .and_then(|_| {
            info!(
                "[region {}] sent snapshot {} [size: {}, dur: {:?}]",
                key.region_id,
                key,
                total_size,
                timer.elapsed()
            );
            s.wl().delete();
            Ok(())
        })
        .wait()
        .map_err(Error::from);

    send_timer.observe_duration();
    res
}

pub struct Runner<R: RaftStoreRouter + 'static> {
    env: Arc<Environment>,
    snap_mgr: SnapManager,
    files: HashMap<Token, (Box<Snapshot>, RaftMessage)>,
    pool: ThreadPool<DefaultContext>,
    raft_router: R,
}

impl<R: RaftStoreRouter + 'static> Runner<R> {
    pub fn new(env: Arc<Environment>, snap_mgr: SnapManager, r: R) -> Runner<R> {
        Runner {
            env: env,
            snap_mgr: snap_mgr,
            files: map![],
            pool: ThreadPoolBuilder::with_default_factory(thd_name!("snap sender"))
                .thread_count(DEFAULT_SENDER_POOL_SIZE)
                .build(),
            raft_router: r,
        }
    }
}

impl<R: RaftStoreRouter + 'static> Runnable<Task> for Runner<R> {
    fn run(&mut self, task: Task) {
        match task {
            Task::Register(token, meta) => {
                SNAP_TASK_COUNTER.with_label_values(&["register"]).inc();
                let mgr = self.snap_mgr.clone();
                let key = match SnapKey::from_snap(meta.get_message().get_snapshot()) {
                    Ok(k) => k,
                    Err(e) => {
                        error!("failed to create snap key for token {:?}: {:?}", token, e);
                        return;
                    }
                };
                match mgr.get_snapshot_for_receiving(
                    &key,
                    meta.get_message().get_snapshot().get_data(),
                ) {
                    Ok(snap) => {
                        if snap.exists() {
                            info!(
                                "snapshot file {} already exists, skip receiving.",
                                snap.path()
                            );
                            if let Err(e) = self.raft_router.send_raft_msg(meta) {
                                error!("send snapshot for key {} token {:?}: {:?}", key, token, e);
                            }
                            return;
                        }
                        debug!("begin to receive snap {:?}", meta);
                        mgr.register(key, SnapEntry::Receiving);
                        self.files.insert(token, (snap, meta));
                    }
                    Err(e) => {
                        error!(
                            "failed to create snapshot file for token {:?}: {:?}",
                            token,
                            e
                        );
                        return;
                    }
                }
            }
            Task::Write(token, mut data) => {
                SNAP_TASK_COUNTER.with_label_values(&["write"]).inc();
                match self.files.entry(token) {
                    Entry::Occupied(mut e) => {
                        if let Err(err) = data.write_all_to(&mut e.get_mut().0) {
                            error!(
                                "failed to write data to snapshot file {} for token {:?}: {:?}",
                                e.get_mut().0.path(),
                                token,
                                err
                            );
                            let (_, msg) = e.remove();
                            let key = SnapKey::from_snap(msg.get_message().get_snapshot()).unwrap();
                            self.snap_mgr.deregister(&key, &SnapEntry::Receiving);
                        }
                    }
                    Entry::Vacant(_) => error!("invalid snap token {:?}", token),
                }
            }
            Task::Close(token) => {
                SNAP_TASK_COUNTER.with_label_values(&["close"]).inc();
                match self.files.remove(&token) {
                    Some((mut snap, msg)) => {
                        let key = SnapKey::from_snap(msg.get_message().get_snapshot()).unwrap();
                        info!("saving snapshot to {}", snap.path());
                        defer!({
                            self.snap_mgr.deregister(&key, &SnapEntry::Receiving);
                        });
                        if let Err(e) = snap.save() {
                            error!(
                                "failed to save snapshot file {} for token {:?}: {:?}",
                                snap.path(),
                                token,
                                e
                            );
                            return;
                        }
                        if let Err(e) = self.raft_router.send_raft_msg(msg) {
                            error!("send snapshot for token {:?} err {:?}", token, e);
                        }
                    }
                    None => error!("invalid snap token {:?}", token),
                }
            }
            Task::Discard(token) => {
                SNAP_TASK_COUNTER.with_label_values(&["discard"]).inc();
                if let Some((_, msg)) = self.files.remove(&token) {
                    debug!("discard snapshot: {:?}", msg);
                    // because token is inserted, following can't panic.
                    let key = SnapKey::from_snap(msg.get_message().get_snapshot()).unwrap();
                    self.snap_mgr.deregister(&key, &SnapEntry::Receiving);
                }
            }
            Task::SendTo { addr, msg, cb } => {
                SNAP_TASK_COUNTER.with_label_values(&["send"]).inc();
                let env = self.env.clone();
                let mgr = self.snap_mgr.clone();
                self.pool.execute(move |_| {
                    let res = send_snap(env, mgr, addr, msg);
                    if res.is_err() {
                        error!("failed to send snap to {}: {:?}", addr, res);
                    }
                    cb(res)
                });
            }
        }
    }
}
<commit_msg>grpc: set buffer hint for snapshot calls. (#2350)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::{self, Display, Formatter};
use std::net::SocketAddr;
use std::boxed::FnBox;
use std::time::Instant;
use std::sync::{Arc, RwLock};

use mio::Token;
use futures::{Async, Future, Poll, Stream};
use futures::stream::{self, Once};
use grpc::{ChannelBuilder, Environment, WriteFlags};
use kvproto::raft_serverpb::SnapshotChunk;
use kvproto::raft_serverpb::RaftMessage;
use kvproto::tikvpb_grpc::TikvClient;

use raftstore::store::{SnapEntry, SnapKey, SnapManager, Snapshot};
use util::threadpool::{DefaultContext, ThreadPool, ThreadPoolBuilder};
use util::worker::Runnable;
use util::buf::PipeBuffer;
use util::collections::{HashMap, HashMapEntry as Entry};
use util::HandyRwLock;

use super::metrics::*;
use super::{Error, Result};
use super::transport::RaftStoreRouter;

pub type Callback = Box<FnBox(Result<()>) + Send>;

const DEFAULT_SENDER_POOL_SIZE: usize = 3;

/// `Task` that `Runner` can handle.
///
/// `Register` register a pending snapshot file with token;
/// `Write` write data to snapshot file;
/// `Close` save the snapshot file;
/// `Discard` discard all the unsaved changes made to snapshot file;
/// `SendTo` send the snapshot file to specified address.
pub enum Task {
    Register(Token, RaftMessage),
    Write(Token, PipeBuffer),
    Close(Token),
    Discard(Token),
    SendTo {
        addr: SocketAddr,
        msg: RaftMessage,
        cb: Callback,
    },
}

impl Display for Task {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Task::Register(token, ref meta) => write!(f, "Register {:?} token: {:?}", meta, token),
            Task::Write(token, _) => write!(f, "Write snap for {:?}", token),
            Task::Close(token) => write!(f, "Close file {:?}", token),
            Task::Discard(token) => write!(f, "Discard file {:?}", token),
            Task::SendTo {
                ref addr, ref msg, ..
            } => write!(f, "SendTo Snap[to: {}, snap: {:?}]", addr, msg),
        }
    }
}

struct SnapChunk {
    snap: Arc<RwLock<Box<Snapshot>>>,
    remain_bytes: usize,
}

const SNAP_CHUNK_LEN: usize = 1024 * 1024;

impl Stream for SnapChunk {
    type Item = (SnapshotChunk, WriteFlags);
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Error> {
        let mut buf = match self.remain_bytes {
            0 => return Ok(Async::Ready(None)),
            n if n > SNAP_CHUNK_LEN => vec![0; SNAP_CHUNK_LEN],
            n => vec![0; n],
        };
        match self.snap.wl().read_exact(buf.as_mut_slice()) {
            Ok(_) => {
                self.remain_bytes -= buf.len();
                let mut chunk = SnapshotChunk::new();
                chunk.set_data(buf);
                Ok(Async::Ready(Some((
                    chunk,
                    WriteFlags::default().buffer_hint(self.remain_bytes > 0),
                ))))
            }
            Err(e) => Err(box_err!("failed to read snapshot chunk: {}", e)),
        }
    }
}

/// Send the snapshot to specified address.
///
/// It will first send the normal raft snapshot message and then send the snapshot file.
fn send_snap(
    env: Arc<Environment>,
    mgr: SnapManager,
    addr: SocketAddr,
    msg: RaftMessage,
) -> Result<()> {
    assert!(msg.get_message().has_snapshot());
    let timer = Instant::now();

    let send_timer = SEND_SNAP_HISTOGRAM.start_coarse_timer();

    let key = {
        let snap = msg.get_message().get_snapshot();
        try!(SnapKey::from_snap(snap))
    };
    mgr.register(key.clone(), SnapEntry::Sending);
    defer!({
        mgr.deregister(&key, &SnapEntry::Sending);
    });
    let s = box_try!(mgr.get_snapshot_for_sending(&key));
    if !s.exists() {
        return Err(box_err!("missing snap file: {:?}", s.path()));
    }
    let total_size = try!(s.total_size());

    // snapshot file has been validated when created, so no need to validate again.
    let s = Arc::new(RwLock::new(s));

    let chunks = {
        let snap_chunk = SnapChunk {
            snap: s.clone(),
            remain_bytes: total_size as usize,
        };
        let first: Once<(SnapshotChunk, _), Error> = stream::once({
            let mut chunk = SnapshotChunk::new();
            chunk.set_message(msg);
            Ok((chunk, WriteFlags::default()))
        });
        first.chain(snap_chunk)
    };

    let channel = ChannelBuilder::new(env).connect(&format!("{}", addr));
    let client = TikvClient::new(channel);
    let (sink, receiver) = client.snapshot();
    let send = chunks.forward(sink);
    let res = send.and_then(|_| receiver.map_err(Error::from))
        .and_then(|_| {
            info!(
                "[region {}] sent snapshot {} [size: {}, dur: {:?}]",
                key.region_id,
                key,
                total_size,
                timer.elapsed()
            );
            s.wl().delete();
            Ok(())
        })
        .wait()
        .map_err(Error::from);

    send_timer.observe_duration();
    res
}

pub struct Runner<R: RaftStoreRouter + 'static> {
    env: Arc<Environment>,
    snap_mgr: SnapManager,
    files: HashMap<Token, (Box<Snapshot>, RaftMessage)>,
    pool: ThreadPool<DefaultContext>,
    raft_router: R,
}

impl<R: RaftStoreRouter + 'static> Runner<R> {
    pub fn new(env: Arc<Environment>, snap_mgr: SnapManager, r: R) -> Runner<R> {
        Runner {
            env: env,
            snap_mgr: snap_mgr,
            files: map![],
            pool: ThreadPoolBuilder::with_default_factory(thd_name!("snap sender"))
                .thread_count(DEFAULT_SENDER_POOL_SIZE)
                .build(),
            raft_router: r,
        }
    }
}

impl<R: RaftStoreRouter + 'static> Runnable<Task> for Runner<R> {
    fn run(&mut self, task: Task) {
        match task {
            Task::Register(token, meta) => {
                SNAP_TASK_COUNTER.with_label_values(&["register"]).inc();
                let mgr = self.snap_mgr.clone();
                let key = match SnapKey::from_snap(meta.get_message().get_snapshot()) {
                    Ok(k) => k,
                    Err(e) => {
                        error!("failed to create snap key for token {:?}: {:?}", token, e);
                        return;
                    }
                };
                match mgr.get_snapshot_for_receiving(
                    &key,
                    meta.get_message().get_snapshot().get_data(),
                ) {
                    Ok(snap) => {
                        if snap.exists() {
                            info!(
                                "snapshot file {} already exists, skip receiving.",
                                snap.path()
                            );
                            if let Err(e) = self.raft_router.send_raft_msg(meta) {
                                error!("send snapshot for key {} token {:?}: {:?}", key, token, e);
                            }
                            return;
                        }
                        debug!("begin to receive snap {:?}", meta);
                        mgr.register(key, SnapEntry::Receiving);
                        self.files.insert(token, (snap, meta));
                    }
                    Err(e) => {
                        error!(
                            "failed to create snapshot file for token {:?}: {:?}",
                            token,
                            e
                        );
                        return;
                    }
                }
            }
            Task::Write(token, mut data) => {
                SNAP_TASK_COUNTER.with_label_values(&["write"]).inc();
                match self.files.entry(token) {
                    Entry::Occupied(mut e) => {
                        if let Err(err) = data.write_all_to(&mut e.get_mut().0) {
                            error!(
                                "failed to write data to snapshot file {} for token {:?}: {:?}",
                                e.get_mut().0.path(),
                                token,
                                err
                            );
                            let (_, msg) = e.remove();
                            let key = SnapKey::from_snap(msg.get_message().get_snapshot()).unwrap();
                            self.snap_mgr.deregister(&key, &SnapEntry::Receiving);
                        }
                    }
                    Entry::Vacant(_) => error!("invalid snap token {:?}", token),
                }
            }
            Task::Close(token) => {
                SNAP_TASK_COUNTER.with_label_values(&["close"]).inc();
                match self.files.remove(&token) {
                    Some((mut snap, msg)) => {
                        let key = SnapKey::from_snap(msg.get_message().get_snapshot()).unwrap();
                        info!("saving snapshot to {}", snap.path());
                        defer!({
                            self.snap_mgr.deregister(&key, &SnapEntry::Receiving);
                        });
                        if let Err(e) = snap.save() {
                            error!(
                                "failed to save snapshot file {} for token {:?}: {:?}",
                                snap.path(),
                                token,
                                e
                            );
                            return;
                        }
                        if let Err(e) = self.raft_router.send_raft_msg(msg) {
                            error!("send snapshot for token {:?} err {:?}", token, e);
                        }
                    }
                    None => error!("invalid snap token {:?}", token),
                }
            }
            Task::Discard(token) => {
                SNAP_TASK_COUNTER.with_label_values(&["discard"]).inc();
                if let Some((_, msg)) = self.files.remove(&token) {
                    debug!("discard snapshot: {:?}", msg);
                    // because token is inserted, following can't panic.
                    let key = SnapKey::from_snap(msg.get_message().get_snapshot()).unwrap();
                    self.snap_mgr.deregister(&key, &SnapEntry::Receiving);
                }
            }
            Task::SendTo { addr, msg, cb } => {
                SNAP_TASK_COUNTER.with_label_values(&["send"]).inc();
                let env = self.env.clone();
                let mgr = self.snap_mgr.clone();
                self.pool.execute(move |_| {
                    let res = send_snap(env, mgr, addr, msg);
                    if res.is_err() {
                        error!("failed to send snap to {}: {:?}", addr, res);
                    }
                    cb(res)
                });
            }
        }
    }
}
<|endoftext|>