<commit_before>use std::mem;
use std::slice;
use gl;

pub trait Image2d {
    fn width(&self) -> usize;
    fn height(&self) -> usize;
    fn format(&self) -> ImageFormat;
    fn textel_bytes(&self) -> &[u8];
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Pixel {
    r: u8,
    g: u8,
    b: u8,
    a: u8
}

impl Pixel {
    pub fn r_g_b_a(r: u8, g: u8, b: u8, a: u8) -> Self {
        Pixel { r: r, g: g, b: b, a: a }
    }

    pub fn r_g_b(r: u8, g: u8, b: u8) -> Self {
        Pixel::r_g_b_a(r, g, b, 0xFF)
    }

    pub fn rgb(rgb: u32) -> Self {
        Pixel::rgb_a(rgb, 0xFF)
    }

    pub fn argb(argb: u32) -> Self {
        let a = (argb & 0xFF000000) >> 24;
        Pixel::rgb_a(argb, a as u8)
    }

    pub fn rgba(rgba: u32) -> Self {
        let r = (rgba & 0xFF000000) >> 24;
        let g = (rgba & 0x00FF0000) >> 16;
        let b = (rgba & 0x0000FF00) >> 8;
        let a =  rgba & 0x000000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a as u8)
    }

    pub fn rgb_a(rgb: u32, a: u8) -> Self {
        let r = (rgb & 0xFF0000) >> 16;
        let g = (rgb & 0x00FF00) >> 8;
        let b =  rgb & 0x0000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a)
    }
}

pub struct Pixels {
    width: usize,
    height: usize,
    pixels: Vec<Pixel>
}

impl Pixels {
    pub fn new(width: usize, height: usize) -> Self {
        Pixels {
            width: width,
            height: height,
            pixels: vec![Pixel::rgb(0x0000FF); width * height]
        }
    }
}

impl Image2d for Pixels {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn format(&self) -> ImageFormat {
        ImageFormat::rgba8()
    }

    fn textel_bytes(&self) -> &[u8] {
        let len = self.pixels.len() * mem::size_of::<Pixel>();
        unsafe {
            slice::from_raw_parts(mem::transmute(&self.pixels[0]), len)
        }
    }
}

gl_enum! {
    pub gl_enum TextelType {
        UnsignedByte as UNSIGNED_BYTE_TEXTEL = gl::UNSIGNED_BYTE,
        UnsignedShort565 as UNSIGNED_SHORT_5_6_5 = gl::UNSIGNED_SHORT_5_6_5,
        UnsignedShort4444 as UNSIGNED_SHORT_4_4_4_4 =
            gl::UNSIGNED_SHORT_4_4_4_4,
        UnsignedShort5551 as UNSIGNED_SHORT_5_5_5_1 =
            gl::UNSIGNED_SHORT_5_5_5_1
    }
}

gl_enum! {
    pub gl_enum TextelFormat {
        Alpha as ALPHA = gl::ALPHA,
        RGB as RGB = gl::RGB,
        RGBA as RGBA = gl::RGBA
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ImageFormat {
    pub textel_type: TextelType,
    pub textel_format: TextelFormat
}

impl ImageFormat {
    fn rgba8() -> Self {
        ImageFormat {
            textel_type: TextelType::UnsignedByte,
            textel_format: TextelFormat::RGBA
        }
    }
}
<commit_msg>Impl indexing ops for `Pixels`<commit_after>use std::ops;
use std::mem;
use std::slice;
use gl;

pub trait Image2d {
    fn width(&self) -> usize;
    fn height(&self) -> usize;
    fn format(&self) -> ImageFormat;
    fn textel_bytes(&self) -> &[u8];
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Pixel {
    r: u8,
    g: u8,
    b: u8,
    a: u8
}

impl Pixel {
    pub fn r_g_b_a(r: u8, g: u8, b: u8, a: u8) -> Self {
        Pixel { r: r, g: g, b: b, a: a }
    }

    pub fn r_g_b(r: u8, g: u8, b: u8) -> Self {
        Pixel::r_g_b_a(r, g, b, 0xFF)
    }

    pub fn rgb(rgb: u32) -> Self {
        Pixel::rgb_a(rgb, 0xFF)
    }

    pub fn argb(argb: u32) -> Self {
        let a = (argb & 0xFF000000) >> 24;
        Pixel::rgb_a(argb, a as u8)
    }

    pub fn rgba(rgba: u32) -> Self {
        let r = (rgba & 0xFF000000) >> 24;
        let g = (rgba & 0x00FF0000) >> 16;
        let b = (rgba & 0x0000FF00) >> 8;
        let a =  rgba & 0x000000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a as u8)
    }

    pub fn rgb_a(rgb: u32, a: u8) -> Self {
        let r = (rgb & 0xFF0000) >> 16;
        let g = (rgb & 0x00FF00) >> 8;
        let b =  rgb & 0x0000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a)
    }
}

pub struct Pixels {
    width: usize,
    height: usize,
    pixels: Vec<Pixel>
}

impl Pixels {
    pub fn new(width: usize, height: usize) -> Self {
        Pixels {
            width: width,
            height: height,
            pixels: vec![Pixel::rgb(0x0000FF); width * height]
        }
    }
}

impl ops::Index<usize> for Pixels {
    type Output = [Pixel];

    fn index(&self, row: usize) -> &[Pixel] {
        debug_assert!(row < self.height);

        &self.pixels[(row*self.width)..((row+1)*self.width)]
    }
}

impl ops::IndexMut<usize> for Pixels {
    fn index_mut(&mut self, row: usize) -> &mut [Pixel] {
        debug_assert!(row < self.height);

        &mut self.pixels[(row*self.width)..((row+1)*self.width)]
    }
}

impl Image2d for Pixels {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn format(&self) -> ImageFormat {
        ImageFormat::rgba8()
    }

    fn textel_bytes(&self) -> &[u8] {
        let len = self.pixels.len() * mem::size_of::<Pixel>();
        unsafe {
            slice::from_raw_parts(mem::transmute(&self.pixels[0]), len)
        }
    }
}

gl_enum! {
    pub gl_enum TextelType {
        UnsignedByte as UNSIGNED_BYTE_TEXTEL = gl::UNSIGNED_BYTE,
        UnsignedShort565 as UNSIGNED_SHORT_5_6_5 = gl::UNSIGNED_SHORT_5_6_5,
        UnsignedShort4444 as UNSIGNED_SHORT_4_4_4_4 =
            gl::UNSIGNED_SHORT_4_4_4_4,
        UnsignedShort5551 as UNSIGNED_SHORT_5_5_5_1 =
            gl::UNSIGNED_SHORT_5_5_5_1
    }
}

gl_enum! {
    pub gl_enum TextelFormat {
        Alpha as ALPHA = gl::ALPHA,
        RGB as RGB = gl::RGB,
        RGBA as RGBA = gl::RGBA
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ImageFormat {
    pub textel_type: TextelType,
    pub textel_format: TextelFormat
}

impl ImageFormat {
    fn rgba8() -> Self {
        ImageFormat {
            textel_type: TextelType::UnsignedByte,
            textel_format: TextelFormat::RGBA
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

/// Escape characters <, >, and &
pub fn escape_html(input: &str) -> String {
    let mut output = String::new();
    for c in input.chars() {
        match c {
            '<' => output.push_str("<"),
            '>' => output.push_str(">"),
            '&' => output.push_str("&"),
            _ => output.push(c),
        }
    }

    output
}

/// Escape characters for tex file
pub fn escape_tex(input: &str) -> String {
    let mut output = String::new();
    for c in input.chars() {
        match c {
            '&' => output.push_str(r"\&"),
            '%' => output.push_str(r"\%"),
            '$' => output.push_str(r"\$"),
            '#' => output.push_str(r"\#"),
            '_' => output.push_str(r"\_"),
            '{' => output.push_str(r"\{"),
            '}' => output.push_str(r"\}"),
            '~' => output.push_str(r"\textasciitilde " ),
            '^' => output.push_str(r"\textasciicircum "),
            '\\' => output.push_str(r"\textbackslash "),
            _  => output.push(c)
        }
    }

    output
}
<commit_msg>ecape dashes in latex so -- is not transformed to –<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

/// Escape characters <, >, and &
pub fn escape_html(input: &str) -> String {
    let mut output = String::new();
    for c in input.chars() {
        match c {
            '<' => output.push_str("<"),
            '>' => output.push_str(">"),
            '&' => output.push_str("&"),
            _ => output.push(c),
        }
    }

    output
}

/// Escape characters for tex file
pub fn escape_tex(input: &str) -> String {
    let mut output = String::new();
    for c in input.chars() {
        match c {
            '-' => output.push_str(r"-{}"),
            '&' => output.push_str(r"\&"),
            '%' => output.push_str(r"\%"),
            '$' => output.push_str(r"\$"),
            '#' => output.push_str(r"\#"),
            '_' => output.push_str(r"\_"),
            '{' => output.push_str(r"\{"),
            '}' => output.push_str(r"\}"),
            '~' => output.push_str(r"\textasciitilde " ),
            '^' => output.push_str(r"\textasciicircum "),
            '\\' => output.push_str(r"\textbackslash "),
            _  => output.push(c)
        }
    }

    output
}
<|endoftext|>


<commit_before>macro_rules! deref {
    ($name:ident::$field:tt => $target:ty) => (itemize! {
        impl ::std::ops::Deref for $name {
            type Target = $target;

            #[inline]
            fn deref(&self) -> &Self::Target {
                &self.$field
            }
        }
    });
    (mut $name:ident::$field:tt => $target:ty) => (itemize! {
        impl ::std::ops::DerefMut for $name {
            #[inline]
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.$field
            }
        }
    });
}

macro_rules! itemize(($($blob:item)*) => ($($blob)*));

macro_rules! getters {
    () => ();
    ($name:ident: $kind:ty, $($tail:tt)*) => (
        #[inline(always)]
        pub fn $name(&self) -> $kind {
            self.$name
        }
        getters! { $($tail)* }
    );
    (ref $name:ident: $kind:ty, $($tail:tt)*) => (
        #[inline(always)]
        pub fn $name(&self) -> &$kind {
            &self.$name
        }
        getters! { $($tail)* }
    );
}

macro_rules! ok(
    ($result:expr) => (match $result {
        Ok(result) => result,
        Err(error) => raise!(error),
    });
);

macro_rules! order {
    ($name:ident($field:tt) ascending) => (order! { $name($field) Less < Greater });
    ($name:ident($field:tt) descending) => (order! { $name($field) Greater < Less });
    ($name:ident($field:tt) $less:ident < $greater:ident) => (itemize! {
        impl ::std::cmp::Eq for $name {
        }

        impl ::std::cmp::Ord for $name {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.$field < other.$field {
                    ::std::cmp::Ordering::$less
                } else if self.$field > other.$field {
                    ::std::cmp::Ordering::$greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }

        impl ::std::cmp::PartialEq for $name {
            #[inline]
            fn eq(&self, other: &Self) -> bool {
                self.$field == other.$field
            }
        }

        impl ::std::cmp::PartialOrd for $name {
            #[inline]
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                Some(self.cmp(other))
            }
        }
    });
}

macro_rules! path(
    ($config:ident, $destination:expr) => ({
        let path = some!($config.get::<String>("path"), "the path to {} is missing", $destination);
        let mut path = ::std::path::PathBuf::from(path);
        if path.is_relative() {
            if let Some(root) = $config.get::<String>("root") {
                path = ::std::path::Path::new(root).join(path);
            }
        }
        if ::std::fs::metadata(&path).is_err() {
            raise!("the file {:?} does not exist", &path);
        }
        path
    });
);

macro_rules! raise(
    ($message:expr) => (return Err(::Error::new($message)));
    ($($arg:tt)*) => (return Err(::Error::new(format!($($arg)*))));
);

macro_rules! some(
    ($option:expr) => (match $option {
        Some(value) => value,
        _ => raise!("encountered a logic error"),
    });
    ($option:expr, $($arg:tt)+) => (match $option {
        Some(value) => value,
        _ => raise!($($arg)*),
    });
);
<commit_msg>Reorder the macros<commit_after>macro_rules! deref {
    ($name:ident::$field:tt => $target:ty) => (itemize! {
        impl ::std::ops::Deref for $name {
            type Target = $target;

            #[inline]
            fn deref(&self) -> &Self::Target {
                &self.$field
            }
        }
    });
    (mut $name:ident::$field:tt => $target:ty) => (itemize! {
        impl ::std::ops::DerefMut for $name {
            #[inline]
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.$field
            }
        }
    });
}

macro_rules! getters {
    () => ();
    ($name:ident: $kind:ty, $($tail:tt)*) => (
        #[inline(always)]
        pub fn $name(&self) -> $kind {
            self.$name
        }
        getters! { $($tail)* }
    );
    (ref $name:ident: $kind:ty, $($tail:tt)*) => (
        #[inline(always)]
        pub fn $name(&self) -> &$kind {
            &self.$name
        }
        getters! { $($tail)* }
    );
}

macro_rules! itemize(($($blob:item)*) => ($($blob)*));

macro_rules! ok(
    ($result:expr) => (match $result {
        Ok(result) => result,
        Err(error) => raise!(error),
    });
);

macro_rules! order {
    ($name:ident($field:tt) ascending) => (order! { $name($field) Less < Greater });
    ($name:ident($field:tt) descending) => (order! { $name($field) Greater < Less });
    ($name:ident($field:tt) $less:ident < $greater:ident) => (itemize! {
        impl ::std::cmp::Eq for $name {
        }

        impl ::std::cmp::Ord for $name {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.$field < other.$field {
                    ::std::cmp::Ordering::$less
                } else if self.$field > other.$field {
                    ::std::cmp::Ordering::$greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }

        impl ::std::cmp::PartialEq for $name {
            #[inline]
            fn eq(&self, other: &Self) -> bool {
                self.$field == other.$field
            }
        }

        impl ::std::cmp::PartialOrd for $name {
            #[inline]
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                Some(self.cmp(other))
            }
        }
    });
}

macro_rules! path(
    ($config:ident, $destination:expr) => ({
        let path = some!($config.get::<String>("path"), "the path to {} is missing", $destination);
        let mut path = ::std::path::PathBuf::from(path);
        if path.is_relative() {
            if let Some(root) = $config.get::<String>("root") {
                path = ::std::path::Path::new(root).join(path);
            }
        }
        if ::std::fs::metadata(&path).is_err() {
            raise!("the file {:?} does not exist", &path);
        }
        path
    });
);

macro_rules! raise(
    ($message:expr) => (return Err(::Error::new($message)));
    ($($arg:tt)*) => (return Err(::Error::new(format!($($arg)*))));
);

macro_rules! some(
    ($option:expr) => (match $option {
        Some(value) => value,
        _ => raise!("encountered a logic error"),
    });
    ($option:expr, $($arg:tt)+) => (match $option {
        Some(value) => value,
        _ => raise!($($arg)*),
    });
);
<|endoftext|>


<commit_before>use byteorder::{ReadBytesExt, LittleEndian};
use rwinstructs::timestamp::{WinTimestamp};
use rwinstructs::guid::{Guid};
use rshellitems::shelllist::{ShellList};
use lnkpkg::locationinfo::{LocationInfo};
use lnkpkg::datablocks::{ExtraDataBlocks};
use lnkpkg::flags::{DataFlags,FileFlags};
use lnkpkg::flags;
use lnkpkg::errors::{LnkError};
use lnkpkg::utils;
use std::io::Read;
use std::io::{Seek,SeekFrom};
use std::io::Cursor;

#[derive(Serialize,Debug)]
// 76 bytes long,
pub struct ShellLinkHeader {
    #[serde(skip_serializing)]
    _offset: u64,
    pub header_size: u32,
    pub guid: Guid,
    pub data_flags: DataFlags,
    pub file_flags: FileFlags,
    pub created: WinTimestamp,
    pub accessed: WinTimestamp,
    pub modified: WinTimestamp,
    pub file_size: u32,
    pub icon_offset: i32,
    pub window_flag: u32,
    pub hot_key: u16,
    #[serde(skip_serializing)]
    pub unknown1: u16,
    #[serde(skip_serializing)]
    pub unknown2: u32,
    #[serde(skip_serializing)]
    pub unknown3: u32
}
impl ShellLinkHeader {
    pub fn new<Rs: Read+Seek>(mut reader: Rs) -> Result<ShellLinkHeader,LnkError> {
        let _offset = reader.seek(SeekFrom::Current(0))?;
        let header_size = reader.read_u32::<LittleEndian>()?;
        let mut guid = Guid([0; 16]);
        reader.read_exact(&mut guid.0)?;

        let data_flags = DataFlags::from_bits_truncate(
            reader.read_u32::<LittleEndian>()?
        );

        let file_flags = FileFlags::from_bits_truncate(
            reader.read_u32::<LittleEndian>()?
        );
        let created = WinTimestamp(reader.read_u64::<LittleEndian>()?);
        let accessed = WinTimestamp(reader.read_u64::<LittleEndian>()?);
        let modified = WinTimestamp(reader.read_u64::<LittleEndian>()?);
        let file_size = reader.read_u32::<LittleEndian>()?;
        let icon_offset = reader.read_i32::<LittleEndian>()?;
        let window_flag = reader.read_u32::<LittleEndian>()?;
        let hot_key = reader.read_u16::<LittleEndian>()?;
        let unknown1 = reader.read_u16::<LittleEndian>()?;
        let unknown2 = reader.read_u32::<LittleEndian>()?;
        let unknown3 = reader.read_u32::<LittleEndian>()?;

        Ok(
            ShellLinkHeader {
                _offset: _offset,
                header_size: header_size,
                guid: guid,
                data_flags: data_flags,
                file_flags: file_flags,
                created: created,
                accessed: accessed,
                modified: modified,
                file_size: file_size,
                icon_offset: icon_offset,
                window_flag: window_flag,
                hot_key: hot_key,
                unknown1: unknown1,
                unknown2: unknown2,
                unknown3: unknown3
            }
        )
    }

    pub fn get_data_flags(&self) -> DataFlags {
        self.data_flags
    }
}

#[derive(Serialize,Debug)]
pub struct TargetIdList {
    #[serde(skip_serializing)]
    _offset: u64,
    pub list_size: u16,
    pub shell_items: ShellList
}

impl TargetIdList{
    pub fn new<Rs: Read+Seek>(mut reader: Rs) -> Result<TargetIdList, LnkError> {
        let _offset = reader.seek(SeekFrom::Current(0))?;
        let list_size = reader.read_u16::<LittleEndian>()?;
        let mut buffer = vec![0; list_size as usize];
        reader.read_exact(&mut buffer)?;

        let shell_items = ShellList::new(
            Cursor::new(buffer)
        )?;

        Ok(
            TargetIdList {
                _offset: _offset,
                list_size: list_size,
                shell_items: shell_items
            }
        )
    }

    pub fn get_size(&self) -> u16 {
        self.list_size
    }
}

#[derive(Serialize,Debug)]
pub struct DataStrings {
    pub description: Option<String>,
    pub relative_path: Option<String>,
    pub working_directory: Option<String>,
    pub command_line_args: Option<String>,
    pub icon_location: Option<String>
}
impl DataStrings {
    pub fn new<R: Read>(mut reader: R, data_flags: DataFlags) -> Result<DataStrings,LnkError> {
        let unicode_flag = data_flags.contains(flags::IS_UNICODE);

        let mut description = None;
        if data_flags.contains(flags::HAS_NAME) {
            if unicode_flag {
                description = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                description = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut relative_path = None;
        if data_flags.contains(flags::HAS_RELATIVE_PATH) {
            if unicode_flag {
                relative_path = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                relative_path = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut working_directory = None;
        if data_flags.contains(flags::HAS_WORKING_DIR) {
            if unicode_flag {
                working_directory = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                working_directory = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut command_line_args = None;
        if data_flags.contains(flags::HAS_ARGUMENTS) {
            if unicode_flag {
                command_line_args = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                command_line_args = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut icon_location = None;
        if data_flags.contains(flags::HAS_ICON_LOCATION) {
            if unicode_flag {
                icon_location = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                icon_location = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        Ok(
            DataStrings {
                description: description,
                relative_path: relative_path,
                working_directory: working_directory,
                command_line_args: command_line_args,
                icon_location: icon_location
            }
        )
    }
}

#[derive(Serialize,Debug)]
pub struct Lnk {
    pub header: ShellLinkHeader,
    pub target_list: Option<TargetIdList>,
    pub location_info: Option<LocationInfo>,
    pub data_strings: DataStrings,
    pub extra_data: ExtraDataBlocks
}

impl Lnk {
    pub fn new<Rs: Read+Seek>(mut reader: Rs) -> Result<Lnk,LnkError> {
        let header = ShellLinkHeader::new(&mut reader)?;
        let header_flags = header.get_data_flags();

        let mut target_list = None;
        if header_flags.contains(flags::HAS_TARGET_ID_LIST) {
            target_list = Some(TargetIdList::new(&mut reader)?);
        }

        let mut location_info = None;
        if header_flags.contains(flags::HAS_LINK_INFO) {
            location_info = Some(LocationInfo::new(&mut reader)?);
        }

        let data_strings = DataStrings::new(
            &mut reader,
            header_flags
        )?;

        let extra_data = ExtraDataBlocks::new(&mut reader)?;

        Ok(
            Lnk {
                header: header,
                target_list: target_list,
                location_info: location_info,
                data_strings: data_strings,
                extra_data: extra_data
            }
        )
    }
}
<commit_msg>cleanup<commit_after>use byteorder::{ReadBytesExt, LittleEndian};
use rwinstructs::timestamp::{WinTimestamp};
use rwinstructs::guid::{Guid};
use rshellitems::shelllist::{ShellList};
use lnkpkg::locationinfo::{LocationInfo};
use lnkpkg::datablocks::{ExtraDataBlocks};
use lnkpkg::flags::{DataFlags,FileFlags};
use lnkpkg::flags;
use lnkpkg::errors::{LnkError};
use lnkpkg::utils;
use std::io::Read;
use std::io::{Seek,SeekFrom};

#[derive(Serialize,Debug)]
// 76 bytes long,
pub struct ShellLinkHeader {
    #[serde(skip_serializing)]
    _offset: u64,
    pub header_size: u32,
    pub guid: Guid,
    pub data_flags: DataFlags,
    pub file_flags: FileFlags,
    pub created: WinTimestamp,
    pub accessed: WinTimestamp,
    pub modified: WinTimestamp,
    pub file_size: u32,
    pub icon_offset: i32,
    pub window_flag: u32,
    pub hot_key: u16,
    #[serde(skip_serializing)]
    pub unknown1: u16,
    #[serde(skip_serializing)]
    pub unknown2: u32,
    #[serde(skip_serializing)]
    pub unknown3: u32
}
impl ShellLinkHeader {
    pub fn new<Rs: Read+Seek>(mut reader: Rs) -> Result<ShellLinkHeader,LnkError> {
        let _offset = reader.seek(SeekFrom::Current(0))?;
        let header_size = reader.read_u32::<LittleEndian>()?;
        let mut guid = Guid([0; 16]);
        reader.read_exact(&mut guid.0)?;

        let data_flags = DataFlags::from_bits_truncate(
            reader.read_u32::<LittleEndian>()?
        );

        let file_flags = FileFlags::from_bits_truncate(
            reader.read_u32::<LittleEndian>()?
        );
        let created = WinTimestamp(reader.read_u64::<LittleEndian>()?);
        let accessed = WinTimestamp(reader.read_u64::<LittleEndian>()?);
        let modified = WinTimestamp(reader.read_u64::<LittleEndian>()?);
        let file_size = reader.read_u32::<LittleEndian>()?;
        let icon_offset = reader.read_i32::<LittleEndian>()?;
        let window_flag = reader.read_u32::<LittleEndian>()?;
        let hot_key = reader.read_u16::<LittleEndian>()?;
        let unknown1 = reader.read_u16::<LittleEndian>()?;
        let unknown2 = reader.read_u32::<LittleEndian>()?;
        let unknown3 = reader.read_u32::<LittleEndian>()?;

        Ok(
            ShellLinkHeader {
                _offset: _offset,
                header_size: header_size,
                guid: guid,
                data_flags: data_flags,
                file_flags: file_flags,
                created: created,
                accessed: accessed,
                modified: modified,
                file_size: file_size,
                icon_offset: icon_offset,
                window_flag: window_flag,
                hot_key: hot_key,
                unknown1: unknown1,
                unknown2: unknown2,
                unknown3: unknown3
            }
        )
    }

    pub fn get_data_flags(&self) -> DataFlags {
        self.data_flags
    }
}

#[derive(Serialize,Debug)]
pub struct TargetIdList {
    #[serde(skip_serializing)]
    _offset: u64,
    pub list_size: u16,
    pub shell_items: ShellList
}

impl TargetIdList{
    pub fn new<Rs: Read+Seek>(mut reader: Rs) -> Result<TargetIdList, LnkError> {
        let _offset = reader.seek(SeekFrom::Current(0))?;
        let list_size = reader.read_u16::<LittleEndian>()?;

        let shell_items = ShellList::new(
            &mut reader
        )?;

        Ok(
            TargetIdList {
                _offset: _offset,
                list_size: list_size,
                shell_items: shell_items
            }
        )
    }

    pub fn get_size(&self) -> u16 {
        self.list_size
    }
}

#[derive(Serialize,Debug)]
pub struct DataStrings {
    pub description: Option<String>,
    pub relative_path: Option<String>,
    pub working_directory: Option<String>,
    pub command_line_args: Option<String>,
    pub icon_location: Option<String>
}
impl DataStrings {
    pub fn new<R: Read>(mut reader: R, data_flags: DataFlags) -> Result<DataStrings,LnkError> {
        let unicode_flag = data_flags.contains(flags::IS_UNICODE);

        let mut description = None;
        if data_flags.contains(flags::HAS_NAME) {
            if unicode_flag {
                description = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                description = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut relative_path = None;
        if data_flags.contains(flags::HAS_RELATIVE_PATH) {
            if unicode_flag {
                relative_path = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                relative_path = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut working_directory = None;
        if data_flags.contains(flags::HAS_WORKING_DIR) {
            if unicode_flag {
                working_directory = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                working_directory = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut command_line_args = None;
        if data_flags.contains(flags::HAS_ARGUMENTS) {
            if unicode_flag {
                command_line_args = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                command_line_args = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        let mut icon_location = None;
        if data_flags.contains(flags::HAS_ICON_LOCATION) {
            if unicode_flag {
                icon_location = Some(utils::read_string_u16_w_size(&mut reader)?);
            } else {
                icon_location = Some(utils::read_string_u8_w_size(&mut reader)?);
            }
        }

        Ok(
            DataStrings {
                description: description,
                relative_path: relative_path,
                working_directory: working_directory,
                command_line_args: command_line_args,
                icon_location: icon_location
            }
        )
    }
}

#[derive(Serialize,Debug)]
pub struct Lnk {
    pub header: ShellLinkHeader,
    pub target_list: Option<TargetIdList>,
    pub location_info: Option<LocationInfo>,
    pub data_strings: DataStrings,
    pub extra_data: ExtraDataBlocks
}

impl Lnk {
    pub fn new<Rs: Read+Seek>(mut reader: Rs) -> Result<Lnk,LnkError> {
        let header = ShellLinkHeader::new(&mut reader)?;
        let header_flags = header.get_data_flags();

        let mut target_list = None;
        if header_flags.contains(flags::HAS_TARGET_ID_LIST) {
            target_list = Some(TargetIdList::new(&mut reader)?);
        }

        let mut location_info = None;
        if header_flags.contains(flags::HAS_LINK_INFO) {
            location_info = Some(LocationInfo::new(&mut reader)?);
        }

        let data_strings = DataStrings::new(
            &mut reader,
            header_flags
        )?;

        let extra_data = ExtraDataBlocks::new(
            &mut reader
        )?;

        Ok(
            Lnk {
                header: header,
                target_list: target_list,
                location_info: location_info,
                data_strings: data_strings,
                extra_data: extra_data
            }
        )
    }
}
<|endoftext|>


<commit_before>#![allow(missing_docs, nonstandard_style)]

use crate::io::ErrorKind;

#[cfg(any(doc, target_os = "linux"))]
pub use crate::os::linux as platform;

#[cfg(all(not(doc), target_os = "android"))]
pub use crate::os::android as platform;
#[cfg(all(not(doc), target_os = "dragonfly"))]
pub use crate::os::dragonfly as platform;
#[cfg(all(not(doc), target_os = "emscripten"))]
pub use crate::os::emscripten as platform;
#[cfg(all(not(doc), target_os = "freebsd"))]
pub use crate::os::freebsd as platform;
#[cfg(all(not(doc), target_os = "fuchsia"))]
pub use crate::os::fuchsia as platform;
#[cfg(all(not(doc), target_os = "haiku"))]
pub use crate::os::haiku as platform;
#[cfg(all(not(doc), target_os = "illumos"))]
pub use crate::os::illumos as platform;
#[cfg(all(not(doc), target_os = "ios"))]
pub use crate::os::ios as platform;
#[cfg(all(not(doc), target_os = "l4re"))]
pub use crate::os::linux as platform;
#[cfg(all(not(doc), target_os = "macos"))]
pub use crate::os::macos as platform;
#[cfg(all(not(doc), target_os = "netbsd"))]
pub use crate::os::netbsd as platform;
#[cfg(all(not(doc), target_os = "openbsd"))]
pub use crate::os::openbsd as platform;
#[cfg(all(not(doc), target_os = "redox"))]
pub use crate::os::redox as platform;
#[cfg(all(not(doc), target_os = "solaris"))]
pub use crate::os::solaris as platform;

pub use self::rand::hashmap_random_keys;
pub use libc::strlen;

#[macro_use]
pub mod weak;

pub mod alloc;
pub mod android;
pub mod args;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fd;
pub mod fs;
pub mod io;
#[cfg(target_os = "l4re")]
mod l4re;
pub mod memchr;
pub mod mutex;
#[cfg(not(target_os = "l4re"))]
pub mod net;
#[cfg(target_os = "l4re")]
pub use self::l4re::net;
pub mod os;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod rwlock;
pub mod stack_overflow;
pub mod stdio;
pub mod thread;
pub mod thread_local_dtor;
pub mod thread_local_key;
pub mod time;

pub use crate::sys_common::os_str_bytes as os_str;

#[cfg(not(test))]
pub fn init() {
    // The standard streams might be closed on application startup. To prevent
    // std::io::{stdin, stdout,stderr} objects from using other unrelated file
    // resources opened later, we reopen standards streams when they are closed.
    unsafe {
        sanitize_standard_fds();
    }

    // By default, some platforms will send a *signal* when an EPIPE error
    // would otherwise be delivered. This runtime doesn't install a SIGPIPE
    // handler, causing it to kill the program, which isn't exactly what we
    // want!
    //
    // Hence, we set SIGPIPE to ignore when the program starts up in order
    // to prevent this problem.
    unsafe {
        reset_sigpipe();
    }

    // In the case when all file descriptors are open, the poll has been
    // observed to perform better than fcntl (on GNU/Linux).
    #[cfg(not(any(
        miri,
        target_os = "emscripten",
        target_os = "fuchsia",
        // The poll on Darwin doesn't set POLLNVAL for closed fds.
        target_os = "macos",
        target_os = "ios",
        target_os = "redox",
    )))]
    unsafe fn sanitize_standard_fds() {
        use crate::sys::os::errno;
        let pfds: &mut [_] = &mut [
            libc::pollfd { fd: 0, events: 0, revents: 0 },
            libc::pollfd { fd: 1, events: 0, revents: 0 },
            libc::pollfd { fd: 2, events: 0, revents: 0 },
        ];
        while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 {
            if errno() == libc::EINTR {
                continue;
            }
            libc::abort();
        }
        for pfd in pfds {
            if pfd.revents & libc::POLLNVAL == 0 {
                continue;
            }
            if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
                // If the stream is closed but we failed to reopen it, abort the
                // process. Otherwise we wouldn't preserve the safety of
                // operations on the corresponding Rust object Stdin, Stdout, or
                // Stderr.
                libc::abort();
            }
        }
    }
    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))]
    unsafe fn sanitize_standard_fds() {
        use crate::sys::os::errno;
        for fd in 0..3 {
            if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
                if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
                    libc::abort();
                }
            }
        }
    }
    #[cfg(any(
        // The standard fds are always available in Miri.
        miri,
        target_os = "emscripten",
        target_os = "fuchsia"))]
    unsafe fn sanitize_standard_fds() {}

    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
    unsafe fn reset_sigpipe() {
        assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
    }
    #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
    unsafe fn reset_sigpipe() {}
}

#[cfg(target_os = "android")]
pub use crate::sys::android::signal;
#[cfg(not(target_os = "android"))]
pub use libc::signal;

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as libc::c_int {
        libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
        libc::ECONNRESET => ErrorKind::ConnectionReset,
        libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
        libc::EPIPE => ErrorKind::BrokenPipe,
        libc::ENOTCONN => ErrorKind::NotConnected,
        libc::ECONNABORTED => ErrorKind::ConnectionAborted,
        libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
        libc::EADDRINUSE => ErrorKind::AddrInUse,
        libc::ENOENT => ErrorKind::NotFound,
        libc::EINTR => ErrorKind::Interrupted,
        libc::EINVAL => ErrorKind::InvalidInput,
        libc::ETIMEDOUT => ErrorKind::TimedOut,
        libc::EEXIST => ErrorKind::AlreadyExists,

        // These two constants can have the same value on some systems,
        // but different values on others, so we can't use a match
        // clause
        x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => ErrorKind::WouldBlock,

        _ => ErrorKind::Other,
    }
}

#[doc(hidden)]
pub trait IsMinusOne {
    fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
    ($($t:ident)*) => ($(impl IsMinusOne for $t {
        fn is_minus_one(&self) -> bool {
            *self == -1
        }
    })*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
    if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
}

pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
where
    T: IsMinusOne,
    F: FnMut() -> T,
{
    loop {
        match cvt(f()) {
            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
            other => return other,
        }
    }
}

// On Unix-like platforms, libc::abort will unregister signal handlers
// including the SIGABRT handler, preventing the abort from being blocked, and
// fclose streams, with the side effect of flushing them so libc buffered
// output will be printed.  Additionally the shell will generally print a more
// understandable error message like "Abort trap" rather than "Illegal
// instruction" that intrinsics::abort would cause, as intrinsics::abort is
// implemented as an illegal instruction.
pub fn abort_internal() -> ! {
    unsafe { libc::abort() }
}
<commit_msg>fix building libstd for Miri on macOS<commit_after>#![allow(missing_docs, nonstandard_style)]

use crate::io::ErrorKind;

#[cfg(any(doc, target_os = "linux"))]
pub use crate::os::linux as platform;

#[cfg(all(not(doc), target_os = "android"))]
pub use crate::os::android as platform;
#[cfg(all(not(doc), target_os = "dragonfly"))]
pub use crate::os::dragonfly as platform;
#[cfg(all(not(doc), target_os = "emscripten"))]
pub use crate::os::emscripten as platform;
#[cfg(all(not(doc), target_os = "freebsd"))]
pub use crate::os::freebsd as platform;
#[cfg(all(not(doc), target_os = "fuchsia"))]
pub use crate::os::fuchsia as platform;
#[cfg(all(not(doc), target_os = "haiku"))]
pub use crate::os::haiku as platform;
#[cfg(all(not(doc), target_os = "illumos"))]
pub use crate::os::illumos as platform;
#[cfg(all(not(doc), target_os = "ios"))]
pub use crate::os::ios as platform;
#[cfg(all(not(doc), target_os = "l4re"))]
pub use crate::os::linux as platform;
#[cfg(all(not(doc), target_os = "macos"))]
pub use crate::os::macos as platform;
#[cfg(all(not(doc), target_os = "netbsd"))]
pub use crate::os::netbsd as platform;
#[cfg(all(not(doc), target_os = "openbsd"))]
pub use crate::os::openbsd as platform;
#[cfg(all(not(doc), target_os = "redox"))]
pub use crate::os::redox as platform;
#[cfg(all(not(doc), target_os = "solaris"))]
pub use crate::os::solaris as platform;

pub use self::rand::hashmap_random_keys;
pub use libc::strlen;

#[macro_use]
pub mod weak;

pub mod alloc;
pub mod android;
pub mod args;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fd;
pub mod fs;
pub mod io;
#[cfg(target_os = "l4re")]
mod l4re;
pub mod memchr;
pub mod mutex;
#[cfg(not(target_os = "l4re"))]
pub mod net;
#[cfg(target_os = "l4re")]
pub use self::l4re::net;
pub mod os;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod rwlock;
pub mod stack_overflow;
pub mod stdio;
pub mod thread;
pub mod thread_local_dtor;
pub mod thread_local_key;
pub mod time;

pub use crate::sys_common::os_str_bytes as os_str;

#[cfg(not(test))]
pub fn init() {
    // The standard streams might be closed on application startup. To prevent
    // std::io::{stdin, stdout,stderr} objects from using other unrelated file
    // resources opened later, we reopen standards streams when they are closed.
    unsafe {
        sanitize_standard_fds();
    }

    // By default, some platforms will send a *signal* when an EPIPE error
    // would otherwise be delivered. This runtime doesn't install a SIGPIPE
    // handler, causing it to kill the program, which isn't exactly what we
    // want!
    //
    // Hence, we set SIGPIPE to ignore when the program starts up in order
    // to prevent this problem.
    unsafe {
        reset_sigpipe();
    }

    cfg_if::cfg_if! {
        if #[cfg(miri)] {
            // The standard fds are always available in Miri.
            unsafe fn sanitize_standard_fds() {}
        } else if #[cfg(not(any(
            target_os = "emscripten",
            target_os = "fuchsia",
            // The poll on Darwin doesn't set POLLNVAL for closed fds.
            target_os = "macos",
            target_os = "ios",
            target_os = "redox",
        )))] {
            // In the case when all file descriptors are open, the poll has been
            // observed to perform better than fcntl (on GNU/Linux).
            unsafe fn sanitize_standard_fds() {
                use crate::sys::os::errno;
                let pfds: &mut [_] = &mut [
                    libc::pollfd { fd: 0, events: 0, revents: 0 },
                    libc::pollfd { fd: 1, events: 0, revents: 0 },
                    libc::pollfd { fd: 2, events: 0, revents: 0 },
                ];
                while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 {
                    if errno() == libc::EINTR {
                        continue;
                    }
                    libc::abort();
                }
                for pfd in pfds {
                    if pfd.revents & libc::POLLNVAL == 0 {
                        continue;
                    }
                    if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
                        // If the stream is closed but we failed to reopen it, abort the
                        // process. Otherwise we wouldn't preserve the safety of
                        // operations on the corresponding Rust object Stdin, Stdout, or
                        // Stderr.
                        libc::abort();
                    }
                }
            }
        } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
            unsafe fn sanitize_standard_fds() {
                use crate::sys::os::errno;
                for fd in 0..3 {
                    if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
                        if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
                            libc::abort();
                        }
                    }
                }
            }
        } else {
            unsafe fn sanitize_standard_fds() {}
        }
    }

    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
    unsafe fn reset_sigpipe() {
        assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
    }
    #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
    unsafe fn reset_sigpipe() {}
}

#[cfg(target_os = "android")]
pub use crate::sys::android::signal;
#[cfg(not(target_os = "android"))]
pub use libc::signal;

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as libc::c_int {
        libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
        libc::ECONNRESET => ErrorKind::ConnectionReset,
        libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
        libc::EPIPE => ErrorKind::BrokenPipe,
        libc::ENOTCONN => ErrorKind::NotConnected,
        libc::ECONNABORTED => ErrorKind::ConnectionAborted,
        libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
        libc::EADDRINUSE => ErrorKind::AddrInUse,
        libc::ENOENT => ErrorKind::NotFound,
        libc::EINTR => ErrorKind::Interrupted,
        libc::EINVAL => ErrorKind::InvalidInput,
        libc::ETIMEDOUT => ErrorKind::TimedOut,
        libc::EEXIST => ErrorKind::AlreadyExists,

        // These two constants can have the same value on some systems,
        // but different values on others, so we can't use a match
        // clause
        x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => ErrorKind::WouldBlock,

        _ => ErrorKind::Other,
    }
}

#[doc(hidden)]
pub trait IsMinusOne {
    fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
    ($($t:ident)*) => ($(impl IsMinusOne for $t {
        fn is_minus_one(&self) -> bool {
            *self == -1
        }
    })*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
    if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
}

pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
where
    T: IsMinusOne,
    F: FnMut() -> T,
{
    loop {
        match cvt(f()) {
            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
            other => return other,
        }
    }
}

// On Unix-like platforms, libc::abort will unregister signal handlers
// including the SIGABRT handler, preventing the abort from being blocked, and
// fclose streams, with the side effect of flushing them so libc buffered
// output will be printed.  Additionally the shell will generally print a more
// understandable error message like "Abort trap" rather than "Illegal
// instruction" that intrinsics::abort would cause, as intrinsics::abort is
// implemented as an illegal instruction.
pub fn abort_internal() -> ! {
    unsafe { libc::abort() }
}
<|endoftext|>


<commit_before>// Copyright 2019 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.

use std::cell::RefCell;
use std::rc::Rc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;

use base::Event;
use sync::Mutex;

use super::INTERRUPT_STATUS_CONFIG_CHANGED;
use super::INTERRUPT_STATUS_USED_RING;
use super::VIRTIO_MSI_NO_VECTOR;
use crate::irq_event::IrqLevelEvent;
use crate::pci::MsixConfig;

pub trait SignalableInterrupt {
    /// Writes to the irqfd to VMM to deliver virtual interrupt to the guest.
    fn signal(&self, vector: u16, interrupt_status_mask: u32);

    /// Notify the driver that buffers have been placed in the used queue.
    fn signal_used_queue(&self, vector: u16) {
        self.signal(vector, INTERRUPT_STATUS_USED_RING)
    }

    /// Notify the driver that the device configuration has changed.
    fn signal_config_changed(&self);

    /// Get the event to signal resampling is needed if it exists.
    fn get_resample_evt(&self) -> Option<&Event>;

    /// Reads the status and writes to the interrupt event. Doesn't read the resample event, it
    /// assumes the resample has been requested.
    fn do_interrupt_resample(&self);
}

pub struct Interrupt {
    interrupt_status: Arc<AtomicUsize>,
    interrupt_evt: IrqLevelEvent,
    msix_config: Option<Arc<Mutex<MsixConfig>>>,
    config_msix_vector: u16,
}

impl SignalableInterrupt for Interrupt {
    /// Virtqueue Interrupts From The Device
    ///
    /// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
    /// Write to the irqfd to VMM to deliver virtual interrupt to the guest
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        // Don't need to set ISR for MSI-X interrupts
        if let Some(msix_config) = &self.msix_config {
            let mut msix_config = msix_config.lock();
            if msix_config.enabled() {
                if vector != VIRTIO_MSI_NO_VECTOR {
                    msix_config.trigger(vector);
                }
                return;
            }
        }

        // Set bit in ISR and inject the interrupt if it was not already pending.
        // Don't need to inject the interrupt if the guest hasn't processed it.
        if self
            .interrupt_status
            .fetch_or(interrupt_status_mask as usize, Ordering::SeqCst)
            == 0
        {
            // Write to irqfd to inject INTx interrupt
            self.interrupt_evt.trigger().unwrap();
        }
    }

    fn signal_config_changed(&self) {
        self.signal(self.config_msix_vector, INTERRUPT_STATUS_CONFIG_CHANGED)
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        Some(self.interrupt_evt.get_resample())
    }

    fn do_interrupt_resample(&self) {
        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
            self.interrupt_evt.trigger().unwrap();
        }
    }
}

impl<I: SignalableInterrupt> SignalableInterrupt for Arc<Mutex<I>> {
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        self.lock().signal(vector, interrupt_status_mask);
    }

    fn signal_used_queue(&self, vector: u16) {
        self.lock().signal_used_queue(vector);
    }

    fn signal_config_changed(&self) {
        self.lock().signal_config_changed();
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        // Cannot get resample event from a borrowed item.
        None
    }

    fn do_interrupt_resample(&self) {}
}

impl<I: SignalableInterrupt> SignalableInterrupt for Rc<RefCell<I>> {
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        self.borrow().signal(vector, interrupt_status_mask);
    }

    fn signal_used_queue(&self, vector: u16) {
        self.borrow().signal_used_queue(vector);
    }

    fn signal_config_changed(&self) {
        self.borrow().signal_config_changed();
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        // Cannot get resample event from a borrowed item.
        None
    }

    fn do_interrupt_resample(&self) {}
}

impl Interrupt {
    pub fn new(
        interrupt_status: Arc<AtomicUsize>,
        interrupt_evt: IrqLevelEvent,
        msix_config: Option<Arc<Mutex<MsixConfig>>>,
        config_msix_vector: u16,
    ) -> Interrupt {
        Interrupt {
            interrupt_status,
            interrupt_evt,
            msix_config,
            config_msix_vector,
        }
    }

    /// Get a reference to the interrupt event.
    pub fn get_interrupt_evt(&self) -> &Event {
        self.interrupt_evt.get_trigger()
    }

    /// Handle interrupt resampling event, reading the value from the event and doing the resample.
    pub fn interrupt_resample(&self) {
        self.interrupt_evt.clear_resample();
        self.do_interrupt_resample();
    }

    /// Get a reference to the msix configuration
    pub fn get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> {
        &self.msix_config
    }
}
<commit_msg>devices: virtio: Introduce Virtio transport medium type for interrupt abstraction<commit_after>// Copyright 2019 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.

use std::cell::RefCell;
use std::rc::Rc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;

use base::Event;
use sync::Mutex;

use super::INTERRUPT_STATUS_CONFIG_CHANGED;
use super::INTERRUPT_STATUS_USED_RING;
use super::VIRTIO_MSI_NO_VECTOR;
use crate::irq_event::IrqLevelEvent;
use crate::pci::MsixConfig;

pub trait SignalableInterrupt {
    /// Writes to the irqfd to VMM to deliver virtual interrupt to the guest.
    fn signal(&self, vector: u16, interrupt_status_mask: u32);

    /// Notify the driver that buffers have been placed in the used queue.
    fn signal_used_queue(&self, vector: u16) {
        self.signal(vector, INTERRUPT_STATUS_USED_RING)
    }

    /// Notify the driver that the device configuration has changed.
    fn signal_config_changed(&self);

    /// Get the event to signal resampling is needed if it exists.
    fn get_resample_evt(&self) -> Option<&Event>;

    /// Reads the status and writes to the interrupt event. Doesn't read the resample event, it
    /// assumes the resample has been requested.
    fn do_interrupt_resample(&self);
}

struct TransportPci {
    irq_evt_lvl: IrqLevelEvent,
    msix_config: Option<Arc<Mutex<MsixConfig>>>,
    config_msix_vector: u16,
}

enum Transport {
    Pci { pci: TransportPci },
}

pub struct Interrupt {
    interrupt_status: Arc<AtomicUsize>,
    transport: Transport,
}

impl SignalableInterrupt for Interrupt {
    /// Virtqueue Interrupts From The Device
    ///
    /// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
    /// Write to the irqfd to VMM to deliver virtual interrupt to the guest
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        // Don't need to set ISR for MSI-X interrupts
        match &self.transport {
            Transport::Pci { pci } => {
                if let Some(msix_config) = &pci.msix_config {
                    let mut msix_config = msix_config.lock();
                    if msix_config.enabled() {
                        if vector != VIRTIO_MSI_NO_VECTOR {
                            msix_config.trigger(vector);
                        }
                        return;
                    }
                }
            }
        }

        // Set bit in ISR and inject the interrupt if it was not already pending.
        // Don't need to inject the interrupt if the guest hasn't processed it.
        if self
            .interrupt_status
            .fetch_or(interrupt_status_mask as usize, Ordering::SeqCst)
            == 0
        {
            // Write to irqfd to inject INTx interrupt
            match &self.transport {
                Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(),
            }
        }
    }

    fn signal_config_changed(&self) {
        let vector = match &self.transport {
            Transport::Pci { pci } => pci.config_msix_vector,
        };
        self.signal(vector, INTERRUPT_STATUS_CONFIG_CHANGED)
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        match &self.transport {
            Transport::Pci { pci } => Some(pci.irq_evt_lvl.get_resample()),
        }
    }

    fn do_interrupt_resample(&self) {
        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
            match &self.transport {
                Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(),
            }
        }
    }
}

impl<I: SignalableInterrupt> SignalableInterrupt for Arc<Mutex<I>> {
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        self.lock().signal(vector, interrupt_status_mask);
    }

    fn signal_used_queue(&self, vector: u16) {
        self.lock().signal_used_queue(vector);
    }

    fn signal_config_changed(&self) {
        self.lock().signal_config_changed();
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        // Cannot get resample event from a borrowed item.
        None
    }

    fn do_interrupt_resample(&self) {}
}

impl<I: SignalableInterrupt> SignalableInterrupt for Rc<RefCell<I>> {
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        self.borrow().signal(vector, interrupt_status_mask);
    }

    fn signal_used_queue(&self, vector: u16) {
        self.borrow().signal_used_queue(vector);
    }

    fn signal_config_changed(&self) {
        self.borrow().signal_config_changed();
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        // Cannot get resample event from a borrowed item.
        None
    }

    fn do_interrupt_resample(&self) {}
}

impl Interrupt {
    pub fn new(
        interrupt_status: Arc<AtomicUsize>,
        irq_evt_lvl: IrqLevelEvent,
        msix_config: Option<Arc<Mutex<MsixConfig>>>,
        config_msix_vector: u16,
    ) -> Interrupt {
        Interrupt {
            interrupt_status,
            transport: Transport::Pci {
                pci: TransportPci {
                    irq_evt_lvl,
                    msix_config,
                    config_msix_vector,
                },
            },
        }
    }

    /// Get a reference to the interrupt event.
    pub fn get_interrupt_evt(&self) -> &Event {
        match &self.transport {
            Transport::Pci { pci } => pci.irq_evt_lvl.get_trigger(),
        }
    }

    /// Handle interrupt resampling event, reading the value from the event and doing the resample.
    pub fn interrupt_resample(&self) {
        match &self.transport {
            Transport::Pci { pci } => {
                pci.irq_evt_lvl.clear_resample();
                self.do_interrupt_resample();
            }
        }
    }

    /// Get a reference to the msix configuration
    pub fn get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> {
        match &self.transport {
            Transport::Pci { pci } => &pci.msix_config,
        }
    }
}
<|endoftext|>


<commit_before>use connection::Connection;
use libproto::blockchain::{Block, Status};
use libproto::router::{MsgType, RoutingKey, SubModules};
use libproto::{Message, OperateType, SyncRequest, SyncResponse};
use rand::{thread_rng, Rng, ThreadRng};
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::convert::{Into, TryFrom, TryInto};
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
use std::u8;
use Source;

const SYNC_STEP: u64 = 200;
const SYNC_TIME_OUT: u64 = 60;

/// Get messages and determine if need to synchronize or broadcast the current node status
pub struct Synchronizer {
    tx_pub: mpsc::Sender<(String, Vec<u8>)>,
    con: Arc<Connection>,
    current_status: Status,
    global_status: Status,
    sync_end_height: u64, //current_status <= sync_end_status
    is_synchronizing: bool,
    latest_status_lists: BTreeMap<u64, VecDeque<u32>>,
    block_lists: BTreeMap<u64, Block>,
    rand: ThreadRng,
    remote_sync_time_out: Instant,
    /// local sync error
    local_sync_count: u8,
}

unsafe impl Sync for Synchronizer {}
unsafe impl Send for Synchronizer {}

impl Synchronizer {
    pub fn new(tx_pub: mpsc::Sender<(String, Vec<u8>)>, con: Arc<Connection>) -> Self {
        Synchronizer {
            tx_pub: tx_pub,
            con: con,
            current_status: Status::new(),
            global_status: Status::new(),
            latest_status_lists: BTreeMap::new(),
            sync_end_height: 0,
            is_synchronizing: true,
            block_lists: BTreeMap::new(),
            rand: thread_rng(),
            remote_sync_time_out: (Instant::now() - Duration::from_secs(SYNC_TIME_OUT)),
            local_sync_count: 0,
        }
    }

    /// After receiving the `Chain >> Status`, it is processed as follows:
    /// 1. The chain height suddenly becomes lower than the original,
    ///    which means that the library is deleted, that is,
    ///    the synchronization is restarted from the received height.
    /// 2. The height of the chain is greater than or equal to the original height,
    ///    less than or equal to the height that the network has synchronized
    ///        - It is equal to the original height more than 2 times, indicating that
    ///          the data in the chain or executor is lost, and the block information is
    ///          sent again from the buffer. If no buffer exists, the data is requested again from other nodes.
    ///        - When the maximum height of data has been synchronized, a synchronization
    ///          request is sent to other nodes to confirm that the current node has successfully
    ///          completed the data synchronization work.
    /// 3. The height is greater than the maximum height that has been synchronized, ie it is the latest data itself.
    pub fn update_current_status(&mut self, latest_status: Status) {
        debug!(
            "sync: update_current_status: current height = {}, \
             before height = {}, \
             sync_end_height = {}",
            latest_status.get_height(),
            self.current_status.get_height(),
            self.sync_end_height
        );
        let old_height = self.current_status.get_height();
        let new_height = latest_status.get_height();

        if new_height == old_height && self.local_sync_count < u8::MAX {
            // Chain height does not increase
            self.local_sync_count += 1;
        }

        self.latest_status_lists = self
            .latest_status_lists
            .split_off(&(latest_status.get_height() + 1));
        self.current_status = latest_status;
        self.broadcast_status();
        self.prune_block_list_cache(new_height + 1);

        if new_height < old_height {
            // Chain error, may be a problem with the database, such as the database was deleted
            self.is_synchronizing = true;
            let start_height = new_height + 1;

            if self.block_lists.contains_key(&start_height) && !self.block_lists.is_empty() {
                self.submit_blocks();
            } else {
                self.start_sync_req(start_height, 0);
            }
        } else if new_height <= self.sync_end_height {
            // In synchronization, or loss of sync data, need to resend
            debug!(
                "Syncing: update_current_status: height = {}, block_lists len = {}",
                new_height,
                self.block_lists.len()
            );

            if !self.block_lists.is_empty() && new_height < self.sync_end_height {
                if self.local_sync_count >= 3 {
                    // Chain height does not increase, must loss data, send cache to executor and chain
                    self.submit_blocks();
                    self.local_sync_count = 0;
                }
            } else {
                // If the block height is equal to the maximum height that has already been synchronized,
                // perform the synchronization operation first to see if it is the latest in the chain
                self.start_sync_req(new_height + 1, 0);
            }

            self.remote_sync_time_out = Instant::now();
            self.is_synchronizing = true;
        } else {
            // Greater than the last sync height, expressed as the latest height
            self.sync_end_height = self.current_status.get_height();
            self.is_synchronizing = false;
        }
    }

    pub fn update_global_status(&mut self, status: &Status, origin: u32) {
        debug!(
            "sync: update_global_status: current height = {}, from node = {}, height = {}",
            self.current_status.get_height(),
            origin,
            status.get_height()
        );
        let current_height = self.current_status.get_height();
        let old_global_status = self.global_status.clone();
        if self.global_status.get_height() < status.get_height() {
            self.global_status = status.clone();
        }

        if status.get_height() < current_height + 1 {
            // The current node is the latest height and does not need to be synchronized
            self.is_synchronizing = false;
        } else if status.get_height() == current_height + 1 {
            // A node on the chain blocks out, synchronizing the latest block
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            } else if self.global_status.get_height() == old_global_status.get_height() {
                if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT {
                    self.remote_sync_time_out = Instant::now();
                    self.start_sync_req(current_height + 1, status.get_height());
                    self.is_synchronizing = true;
                }
            } else {
                self.is_synchronizing = false;
            }
        } else {
            // The node is far behind the data on the chain and initiates a synchronization request
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT
                || !self.is_synchronizing
            {
                self.remote_sync_time_out = Instant::now();
                self.start_sync_req(current_height + 1, status.get_height());
            } else if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            }

            self.is_synchronizing = true;
        }
    }

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

    pub fn process_sync(&mut self, mut blocks: SyncResponse) {
        let blocks = blocks.take_blocks();
        debug!("sync: process_sync: blocks len = {}", blocks.len());

        let mut heights = vec![];
        for block in blocks.into_iter() {
            heights.push(block.get_header().get_height());
            self.block_lists
                .insert(block.get_header().get_height(), block);
        }

        debug!("sync: process_sync: heights = {:?}", heights);
        self.submit_blocks();
    }

    pub fn receive(&mut self, _from: Source, payload: (String, Vec<u8>)) {
        let (key, body) = payload;
        let mut msg = Message::try_from(&body).unwrap();
        let origin = msg.get_origin();
        match RoutingKey::from(&key) {
            routing_key!(Chain >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_current_status(status);
                };
            }
            routing_key!(Synchronizer >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_global_status(&status, origin);
                };
            }
            routing_key!(Synchronizer >> SyncResponse) => {
                if let Some(blocks) = msg.take_sync_response() {
                    self.process_sync(blocks);
                };
            }
            _ => {
                error!("receive: unexpected data key = {:?}", key);
            }
        }
    }

    // Initiate a sync request
    fn start_sync_req(&mut self, start_height: u64, end_height: u64) {
        debug!(
            "sync: start_sync_req: start_height = {}, end_height = {},current height = {}",
            start_height,
            end_height,
            self.current_status.get_height()
        );
        let mut origin = 0;
        let mut end_height = end_height;
        let mut is_send = false;

        if let Some((height, origins)) = self
            .latest_status_lists
            .iter()
            .rfind(|&(_, origins)| origins.len() > 0)
        {
            debug!(
                "sync: start_sync_req: height = {}, origins = {:?}",
                height, origins
            );
            if let Some(origins) = self.latest_status_lists.get(height) {
                if *height > self.current_status.get_height() {
                    origin = origins[self.rand.gen_range(0, origins.len())];
                    if end_height == 0 {
                        end_height = *height + 1;
                    }
                    is_send = true;
                }
            }
        }

        if is_send {
            self.sync_strategy(start_height, end_height, origin);
        }
    }

    fn sync_strategy(&self, start_height: u64, end_height: u64, origin: u32) {
        //current height = 155,start_height = 156, end height = 160, to origin = 1
        debug!(
            "sync: sync_strategy: current height = {}, \
             start_height = {}, \
             end height = {}, \
             to origin = {}",
            self.current_status.get_height(),
            start_height,
            end_height,
            origin
        );
        if start_height >= self.current_status.get_height() && start_height <= end_height {
            let mut start_height = start_height;
            let mut step_sum = SYNC_STEP;
            let mut heights = vec![];

            while start_height <= end_height {
                if step_sum == 0 {
                    step_sum = SYNC_STEP;
                    self.send_sync_req(heights, origin);
                    heights = vec![];
                }
                heights.push(start_height);

                step_sum -= 1;
                start_height += 1;
            }
            self.send_sync_req(heights, origin);
        }
    }

    fn send_sync_req(&self, heights: Vec<u64>, origin: u32) {
        if !heights.is_empty() {
            debug!(
                "sync: send_sync_req:current height = {}, \
                 heights = {:?}, \
                 origin {:?}, \
                 chain.sync: OperateType {:?}",
                self.current_status.get_height(),
                heights,
                origin,
                OperateType::Single
            );
            let mut sync_req = SyncRequest::new();
            sync_req.set_heights(heights);
            let msg = Message::init(OperateType::Single, origin, sync_req.into());
            self.con
                .broadcast(routing_key!(Synchronizer >> SyncRequest).into(), msg);
        }
    }

    fn broadcast_status(&mut self) {
        debug!(
            "sync: broadcast status {:?}, {:?} to other nodes",
            self.current_status.get_height(),
            self.current_status.get_hash()
        );
        let msg: Message = self.current_status.clone().into();
        self.con
            .broadcast(routing_key!(Synchronizer >> Status).into(), msg);
    }

    // Submit synchronization information
    fn submit_blocks(&mut self) {
        let mut height = self.current_status.get_height() + 1;
        debug!(
            "sync: submit_blocks:submit_height = {},\
             current height = {},\
             sync_end_height = {},\
             block_lists = {}",
            height,
            self.current_status.get_height(),
            self.sync_end_height,
            self.block_lists.len()
        );
        let mut blocks = vec![];
        let end_height = height + SYNC_STEP;

        loop {
            if height <= end_height {
                if let Some(block) = self.block_lists.get(&height) {
                    blocks.push(block.to_owned());
                } else {
                    break;
                }
            } else {
                break;
            }
            height += 1;
        }

        match blocks.last() {
            Some(block) => {
                if let Some(header) = block.header.as_ref() {
                    let height = header.get_height();

                    if height > self.sync_end_height {
                        self.sync_end_height = height;
                    }
                }
            }
            None => {}
        }

        if self.block_lists.contains_key(&::std::u64::MAX) {
            blocks.push(self.block_lists.remove(&::std::u64::MAX).unwrap());
        }

        self.pub_blocks(blocks);
    }

    fn pub_blocks(&mut self, blocks: Vec<Block>) {
        if !blocks.is_empty() {
            debug!(
                "sync: pub_blocks: current height = {}, \
                 sync_end_height = {}, \
                 len = {}, ",
                self.current_status.get_height(),
                self.sync_end_height,
                blocks.len()
            );
            let mut sync_res = SyncResponse::new();
            sync_res.set_blocks(blocks.into());
            let msg: Message = sync_res.into();
            self.tx_pub.send((
                routing_key!(Net >> SyncResponse).into(),
                msg.try_into().unwrap(),
            ));
        }
    }

    fn add_latest_sync_lists(&mut self, height: u64, origin: u32) {
        debug!(
            "sync: add_sync_lists: current height = {}, \
             from node = {}, \
             height = {}",
            self.current_status.get_height(),
            origin,
            height
        );
        if self
            .latest_status_lists
            .entry(height)
            .or_insert_with(VecDeque::new)
            .iter()
            .fold(HashSet::new(), |mut set, item| {
                set.insert(item);
                set
            })
            .insert(&origin)
        {
            self.latest_status_lists
                .entry(height)
                .or_insert_with(VecDeque::new)
                .push_back(origin);
        }
    }

    /// Prune block on btreemap
    fn prune_block_list_cache(&mut self, height: u64) {
        self.block_lists = self.block_lists.split_off(&height);
    }
}
<commit_msg>:bug: fix sync<commit_after>use connection::Connection;
use libproto::blockchain::{Block, Status};
use libproto::router::{MsgType, RoutingKey, SubModules};
use libproto::{Message, OperateType, SyncRequest, SyncResponse};
use rand::{thread_rng, Rng, ThreadRng};
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::convert::{Into, TryFrom, TryInto};
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
use std::u8;
use Source;

const SYNC_STEP: u64 = 200;
const SYNC_TIME_OUT: u64 = 60;

/// Get messages and determine if need to synchronize or broadcast the current node status
pub struct Synchronizer {
    tx_pub: mpsc::Sender<(String, Vec<u8>)>,
    con: Arc<Connection>,
    current_status: Status,
    global_status: Status,
    sync_end_height: u64, //current_status <= sync_end_status
    is_synchronizing: bool,
    latest_status_lists: BTreeMap<u64, VecDeque<u32>>,
    block_lists: BTreeMap<u64, Block>,
    rand: ThreadRng,
    remote_sync_time_out: Instant,
    /// local sync error
    local_sync_count: u8,
}

unsafe impl Sync for Synchronizer {}
unsafe impl Send for Synchronizer {}

impl Synchronizer {
    pub fn new(tx_pub: mpsc::Sender<(String, Vec<u8>)>, con: Arc<Connection>) -> Self {
        Synchronizer {
            tx_pub: tx_pub,
            con: con,
            current_status: Status::new(),
            global_status: Status::new(),
            latest_status_lists: BTreeMap::new(),
            sync_end_height: 0,
            is_synchronizing: true,
            block_lists: BTreeMap::new(),
            rand: thread_rng(),
            remote_sync_time_out: (Instant::now() - Duration::from_secs(SYNC_TIME_OUT)),
            local_sync_count: 0,
        }
    }

    /// After receiving the `Chain >> Status`, it is processed as follows:
    /// 1. The chain height suddenly becomes lower than the original,
    ///    which means that the library is deleted, that is,
    ///    the synchronization is restarted from the received height.
    /// 2. The height of the chain is greater than or equal to the original height,
    ///    less than or equal to the height that the network has synchronized
    ///        - It is equal to the original height more than 2 times, indicating that
    ///          the data in the chain or executor is lost, and the block information is
    ///          sent again from the buffer. If no buffer exists, the data is requested again from other nodes.
    ///        - When the maximum height of data has been synchronized, a synchronization
    ///          request is sent to other nodes to confirm that the current node has successfully
    ///          completed the data synchronization work.
    /// 3. The height is greater than the maximum height that has been synchronized, ie it is the latest data itself.
    pub fn update_current_status(&mut self, latest_status: Status) {
        debug!(
            "sync: update_current_status: current height = {}, \
             before height = {}, \
             sync_end_height = {}",
            latest_status.get_height(),
            self.current_status.get_height(),
            self.sync_end_height
        );
        let old_height = self.current_status.get_height();
        let new_height = latest_status.get_height();

        if new_height == old_height && self.local_sync_count < u8::MAX {
            // Chain height does not increase
            self.local_sync_count += 1;
        } else {
            self.local_sync_count = 0;
        }

        self.latest_status_lists = self
            .latest_status_lists
            .split_off(&(latest_status.get_height() + 1));
        self.current_status = latest_status;
        self.broadcast_status();
        self.prune_block_list_cache(new_height + 1);

        if new_height < old_height {
            // Chain error, may be a problem with the database, such as the database was deleted
            self.is_synchronizing = true;
            let start_height = new_height + 1;

            if self.block_lists.contains_key(&start_height) && !self.block_lists.is_empty() {
                self.submit_blocks();
            } else {
                self.start_sync_req(start_height, 0);
            }
        } else if new_height <= self.sync_end_height {
            // In synchronization, or loss of sync data, need to resend
            debug!(
                "Syncing: update_current_status: height = {}, block_lists len = {}",
                new_height,
                self.block_lists.len()
            );

            if !self.block_lists.is_empty() && new_height < self.sync_end_height {
                if self.local_sync_count >= 3 {
                    // Chain height does not increase, loss data or data is invalid,
                    // send cache to executor and chain, and clear cache
                    self.submit_blocks();
                    self.local_sync_count = 0;
                    self.block_lists.clear();
                }
            } else {
                // If the block height is equal to the maximum height that has already been synchronized,
                // perform the synchronization operation first to see if it is the latest in the chain
                self.start_sync_req(new_height + 1, 0);
            }

            self.remote_sync_time_out = Instant::now();
            self.is_synchronizing = true;
        } else {
            // Greater than the last sync height, expressed as the latest height
            self.sync_end_height = self.current_status.get_height();
            self.is_synchronizing = false;
        }
    }

    pub fn update_global_status(&mut self, status: &Status, origin: u32) {
        debug!(
            "sync: update_global_status: current height = {}, from node = {}, height = {}",
            self.current_status.get_height(),
            origin,
            status.get_height()
        );
        let current_height = self.current_status.get_height();
        let old_global_status = self.global_status.clone();
        if self.global_status.get_height() < status.get_height() {
            self.global_status = status.clone();
        }

        if status.get_height() < current_height + 1 {
            // The current node is the latest height and does not need to be synchronized
            self.is_synchronizing = false;
        } else if status.get_height() == current_height + 1 {
            // A node on the chain blocks out, synchronizing the latest block
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            } else if self.global_status.get_height() == old_global_status.get_height() {
                if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT {
                    self.remote_sync_time_out = Instant::now();
                    self.start_sync_req(current_height + 1, status.get_height());
                    self.is_synchronizing = true;
                }
            } else {
                self.is_synchronizing = false;
            }
        } else {
            // The node is far behind the data on the chain and initiates a synchronization request
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT
                || !self.is_synchronizing
            {
                self.remote_sync_time_out = Instant::now();
                self.start_sync_req(current_height + 1, status.get_height());
            } else if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            }

            self.is_synchronizing = true;
        }
    }

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

    pub fn process_sync(&mut self, mut blocks: SyncResponse) {
        let blocks = blocks.take_blocks();
        debug!("sync: process_sync: blocks len = {}", blocks.len());

        let mut heights = vec![];
        for block in blocks.into_iter() {
            heights.push(block.get_header().get_height());
            self.block_lists
                .insert(block.get_header().get_height(), block);
        }

        debug!("sync: process_sync: heights = {:?}", heights);
        self.submit_blocks();
    }

    pub fn receive(&mut self, _from: Source, payload: (String, Vec<u8>)) {
        let (key, body) = payload;
        let mut msg = Message::try_from(&body).unwrap();
        let origin = msg.get_origin();
        match RoutingKey::from(&key) {
            routing_key!(Chain >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_current_status(status);
                };
            }
            routing_key!(Synchronizer >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_global_status(&status, origin);
                };
            }
            routing_key!(Synchronizer >> SyncResponse) => {
                if let Some(blocks) = msg.take_sync_response() {
                    self.process_sync(blocks);
                };
            }
            _ => {
                error!("receive: unexpected data key = {:?}", key);
            }
        }
    }

    // Initiate a sync request
    fn start_sync_req(&mut self, start_height: u64, end_height: u64) {
        debug!(
            "sync: start_sync_req: start_height = {}, end_height = {},current height = {}",
            start_height,
            end_height,
            self.current_status.get_height()
        );
        let mut origin = 0;
        let mut end_height = end_height;
        let mut is_send = false;

        if let Some((height, origins)) = self
            .latest_status_lists
            .iter()
            .rfind(|&(_, origins)| origins.len() > 0)
        {
            debug!(
                "sync: start_sync_req: height = {}, origins = {:?}",
                height, origins
            );
            if let Some(origins) = self.latest_status_lists.get(height) {
                if *height > self.current_status.get_height() {
                    origin = origins[self.rand.gen_range(0, origins.len())];
                    if end_height == 0 {
                        end_height = *height + 1;
                    }
                    is_send = true;
                }
            }
        }

        if is_send {
            self.sync_strategy(start_height, end_height, origin);
        }
    }

    fn sync_strategy(&self, start_height: u64, end_height: u64, origin: u32) {
        //current height = 155,start_height = 156, end height = 160, to origin = 1
        debug!(
            "sync: sync_strategy: current height = {}, \
             start_height = {}, \
             end height = {}, \
             to origin = {}",
            self.current_status.get_height(),
            start_height,
            end_height,
            origin
        );
        if start_height >= self.current_status.get_height() && start_height <= end_height {
            let mut start_height = start_height;
            let mut step_sum = SYNC_STEP;
            let mut heights = vec![];

            while start_height <= end_height {
                if step_sum == 0 {
                    step_sum = SYNC_STEP;
                    self.send_sync_req(heights, origin);
                    heights = vec![];
                }
                heights.push(start_height);

                step_sum -= 1;
                start_height += 1;
            }
            self.send_sync_req(heights, origin);
        }
    }

    fn send_sync_req(&self, heights: Vec<u64>, origin: u32) {
        if !heights.is_empty() {
            debug!(
                "sync: send_sync_req:current height = {}, \
                 heights = {:?}, \
                 origin {:?}, \
                 chain.sync: OperateType {:?}",
                self.current_status.get_height(),
                heights,
                origin,
                OperateType::Single
            );
            let mut sync_req = SyncRequest::new();
            sync_req.set_heights(heights);
            let msg = Message::init(OperateType::Single, origin, sync_req.into());
            self.con
                .broadcast(routing_key!(Synchronizer >> SyncRequest).into(), msg);
        }
    }

    fn broadcast_status(&mut self) {
        debug!(
            "sync: broadcast status {:?}, {:?} to other nodes",
            self.current_status.get_height(),
            self.current_status.get_hash()
        );
        let msg: Message = self.current_status.clone().into();
        self.con
            .broadcast(routing_key!(Synchronizer >> Status).into(), msg);
    }

    // Submit synchronization information
    fn submit_blocks(&mut self) {
        let mut height = self.current_status.get_height() + 1;
        debug!(
            "sync: submit_blocks:submit_height = {},\
             current height = {},\
             sync_end_height = {},\
             block_lists = {}",
            height,
            self.current_status.get_height(),
            self.sync_end_height,
            self.block_lists.len()
        );
        let mut blocks = vec![];
        let end_height = height + SYNC_STEP;

        loop {
            if height <= end_height {
                if let Some(block) = self.block_lists.get(&height) {
                    blocks.push(block.to_owned());
                } else {
                    break;
                }
            } else {
                break;
            }
            height += 1;
        }

        match blocks.last() {
            Some(block) => {
                if let Some(header) = block.header.as_ref() {
                    let height = header.get_height();

                    if height > self.sync_end_height {
                        self.sync_end_height = height;
                    }
                }
            }
            None => {}
        }

        if self.block_lists.contains_key(&::std::u64::MAX) {
            blocks.push(self.block_lists.remove(&::std::u64::MAX).unwrap());
        }

        self.pub_blocks(blocks);
    }

    fn pub_blocks(&mut self, blocks: Vec<Block>) {
        if !blocks.is_empty() {
            debug!(
                "sync: pub_blocks: current height = {}, \
                 sync_end_height = {}, \
                 len = {}, ",
                self.current_status.get_height(),
                self.sync_end_height,
                blocks.len()
            );
            let mut sync_res = SyncResponse::new();
            sync_res.set_blocks(blocks.into());
            let msg: Message = sync_res.into();
            self.tx_pub.send((
                routing_key!(Net >> SyncResponse).into(),
                msg.try_into().unwrap(),
            ));
        }
    }

    fn add_latest_sync_lists(&mut self, height: u64, origin: u32) {
        debug!(
            "sync: add_sync_lists: current height = {}, \
             from node = {}, \
             height = {}",
            self.current_status.get_height(),
            origin,
            height
        );
        if self
            .latest_status_lists
            .entry(height)
            .or_insert_with(VecDeque::new)
            .iter()
            .fold(HashSet::new(), |mut set, item| {
                set.insert(item);
                set
            })
            .insert(&origin)
        {
            self.latest_status_lists
                .entry(height)
                .or_insert_with(VecDeque::new)
                .push_back(origin);
        }
    }

    /// Prune block on btreemap
    fn prune_block_list_cache(&mut self, height: u64) {
        self.block_lists = self.block_lists.split_off(&height);
    }
}
<|endoftext|>