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

//! A definition of `StorageKey` trait and implementations for common types.

use byteorder::{ByteOrder, BigEndian};
use crypto::{Hash, PublicKey, HASH_SIZE, PUBLIC_KEY_LENGTH};

/// A type that can be (de)serialized as a key in the blockchain storage.
///
/// Since keys are sorted in a serialized form, the big-endian encoding should be used
/// with unsigned integer types. Note however that the big-endian encoding
/// will **not** sort signed integer types in the natural order; a possible solution is
/// mapping the type to a corresponding unsigned one as shown in the example below.
///
/// # Examples
///
/// ```
/// # extern crate exonum;
/// # extern crate byteorder;
/// use std::mem;
/// use exonum::storage::StorageKey;
/// use byteorder::{BigEndian, ByteOrder};
///
/// struct Key {
///     a: i16,
///     b: u32,
/// }
///
/// impl StorageKey for Key {
///     fn size(&self) -> usize {
///         mem::size_of_val(&self.a) + mem::size_of_val(&self.b)
///     }
///
///     fn write(&self, buffer: &mut [u8]) {
///         // Maps `a` to the `u16` range in the natural order:
///         // -32768 -> 0, -32767 -> 1, ..., 32767 -> 65535
///         let mapped_a = self.a.wrapping_add(i16::min_value()) as u16;
///         BigEndian::write_u16(&mut buffer[0..2], mapped_a);
///         BigEndian::write_u32(&mut buffer[2..6], self.b);
///     }
///
///     fn read(buffer: &[u8]) -> Self {
///         let mapped_a = BigEndian::read_u16(&buffer[0..2]);
///         let a = mapped_a.wrapping_add(i16::min_value() as u16) as i16;
///         let b = BigEndian::read_u32(&buffer[2..6]);
///         Key { a, b }
///     }
/// }
/// # fn main() {
/// # // Check the natural ordering of keys
/// # let (mut x, mut y) = (vec![0u8; 6], vec![0u8; 6]);
/// # Key { a: -1, b: 2 }.write(&mut x);
/// # Key { a: 1, b: 513 }.write(&mut y);
/// # assert!(x < y);
/// # // Check the roundtrip
/// # let key = Key::read(&x);
/// # assert_eq!(key.a, -1);
/// # assert_eq!(key.b, 2);
/// # }
/// ```
pub trait StorageKey {
    /// Returns the size of the serialized key in bytes.
    fn size(&self) -> usize;

    /// Serializes the key into the specified buffer of bytes.
    ///
    /// The caller must guarantee that the size of the buffer is equal to the precalculated size
    /// of the serialized key.
    // TODO: should be unsafe (ECR-174)?
    fn write(&self, buffer: &mut [u8]);

    /// Deserializes the key from the specified buffer of bytes.
    // TODO: should be unsafe (ECR-174)?
    fn read(buffer: &[u8]) -> Self;
}

/// No-op implementation.
impl StorageKey for () {
    fn size(&self) -> usize {
        0
    }

    fn write(&self, _buffer: &mut [u8]) {
        // no-op
    }

    fn read(_buffer: &[u8]) -> Self {
        ()
    }
}

impl StorageKey for u8 {
    fn size(&self) -> usize {
        1
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer[0] = *self
    }

    fn read(buffer: &[u8]) -> Self {
        buffer[0]
    }
}

/// Uses big-endian encoding.
impl StorageKey for u16 {
    fn size(&self) -> usize {
        2
    }

    fn write(&self, buffer: &mut [u8]) {
        BigEndian::write_u16(buffer, *self)
    }

    fn read(buffer: &[u8]) -> Self {
        BigEndian::read_u16(buffer)
    }
}

/// Uses big-endian encoding.
impl StorageKey for u32 {
    fn size(&self) -> usize {
        4
    }

    fn write(&self, buffer: &mut [u8]) {
        BigEndian::write_u32(buffer, *self)
    }

    fn read(buffer: &[u8]) -> Self {
        BigEndian::read_u32(buffer)
    }
}

/// Uses big-endian encoding.
impl StorageKey for u64 {
    fn size(&self) -> usize {
        8
    }

    fn write(&self, buffer: &mut [u8]) {
        BigEndian::write_u64(buffer, *self)
    }

    fn read(buffer: &[u8]) -> Self {
        BigEndian::read_u64(buffer)
    }
}

/// **Not sorted in the natural order.**
impl StorageKey for i8 {
    fn size(&self) -> usize {
        1
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer[0] = *self as u8
    }

    fn read(buffer: &[u8]) -> Self {
        buffer[0] as i8
    }
}

/// Uses big-endian encoding. **Not sorted in the natural order.**
impl StorageKey for i16 {
    fn size(&self) -> usize {
        2
    }

    fn write(&self, buffer: &mut [u8]) {
        BigEndian::write_i16(buffer, *self)
    }

    fn read(buffer: &[u8]) -> Self {
        BigEndian::read_i16(buffer)
    }
}

/// Uses big-endian encoding. **Not sorted in the natural order.**
impl StorageKey for i32 {
    fn size(&self) -> usize {
        4
    }

    fn write(&self, buffer: &mut [u8]) {
        BigEndian::write_i32(buffer, *self)
    }

    fn read(buffer: &[u8]) -> Self {
        BigEndian::read_i32(buffer)
    }
}

/// Uses big-endian encoding. **Not sorted in the natural order.**
impl StorageKey for i64 {
    fn size(&self) -> usize {
        8
    }

    fn write(&self, buffer: &mut [u8]) {
        BigEndian::write_i64(buffer, *self)
    }

    fn read(buffer: &[u8]) -> Self {
        BigEndian::read_i64(buffer)
    }
}

impl StorageKey for Hash {
    fn size(&self) -> usize {
        HASH_SIZE
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self.as_ref())
    }

    fn read(buffer: &[u8]) -> Self {
        Hash::from_slice(buffer).unwrap()
    }
}

impl StorageKey for PublicKey {
    fn size(&self) -> usize {
        PUBLIC_KEY_LENGTH
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self.as_ref())
    }

    fn read(buffer: &[u8]) -> Self {
        PublicKey::from_slice(buffer).unwrap()
    }
}

impl StorageKey for Vec<u8> {
    fn size(&self) -> usize {
        self.len()
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self)
    }

    fn read(buffer: &[u8]) -> Self {
        buffer.to_vec()
    }
}

/// Uses UTF-8 string serialization.
impl StorageKey for String {
    fn size(&self) -> usize {
        self.len()
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self.as_bytes())
    }

    fn read(buffer: &[u8]) -> Self {
        unsafe { ::std::str::from_utf8_unchecked(buffer).to_string() }
    }
}
<commit_msg>Change `StorageKey` for signed ints<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A definition of `StorageKey` trait and implementations for common types.

use byteorder::{ByteOrder, BigEndian};
use crypto::{Hash, PublicKey, HASH_SIZE, PUBLIC_KEY_LENGTH};

/// A type that can be (de)serialized as a key in the blockchain storage.
///
/// Since keys are sorted in a serialized form, the big-endian encoding should be used
/// with unsigned integer types. Note however that the big-endian encoding
/// will **not** sort signed integer types in the natural order; a possible solution is
/// mapping the type to a corresponding unsigned one as shown in the example below.
///
/// # Examples
///
/// ```
/// # extern crate exonum;
/// # extern crate byteorder;
/// use std::mem;
/// use exonum::storage::StorageKey;
/// use byteorder::{BigEndian, ByteOrder};
///
/// struct Key {
///     a: i16,
///     b: u32,
/// }
///
/// impl StorageKey for Key {
///     fn size(&self) -> usize {
///         mem::size_of_val(&self.a) + mem::size_of_val(&self.b)
///     }
///
///     fn write(&self, buffer: &mut [u8]) {
///         // Maps `a` to the `u16` range in the natural order:
///         // -32768 -> 0, -32767 -> 1, ..., 32767 -> 65535
///         let mapped_a = self.a.wrapping_add(i16::min_value()) as u16;
///         BigEndian::write_u16(&mut buffer[0..2], mapped_a);
///         BigEndian::write_u32(&mut buffer[2..6], self.b);
///     }
///
///     fn read(buffer: &[u8]) -> Self {
///         let mapped_a = BigEndian::read_u16(&buffer[0..2]);
///         let a = mapped_a.wrapping_add(i16::min_value() as u16) as i16;
///         let b = BigEndian::read_u32(&buffer[2..6]);
///         Key { a, b }
///     }
/// }
/// # fn main() {
/// # // Check the natural ordering of keys
/// # let (mut x, mut y) = (vec![0u8; 6], vec![0u8; 6]);
/// # Key { a: -1, b: 2 }.write(&mut x);
/// # Key { a: 1, b: 513 }.write(&mut y);
/// # assert!(x < y);
/// # // Check the roundtrip
/// # let key = Key::read(&x);
/// # assert_eq!(key.a, -1);
/// # assert_eq!(key.b, 2);
/// # }
/// ```
pub trait StorageKey {
    /// Returns the size of the serialized key in bytes.
    fn size(&self) -> usize;

    /// Serializes the key into the specified buffer of bytes.
    ///
    /// The caller must guarantee that the size of the buffer is equal to the precalculated size
    /// of the serialized key.
    // TODO: should be unsafe (ECR-174)?
    fn write(&self, buffer: &mut [u8]);

    /// Deserializes the key from the specified buffer of bytes.
    // TODO: should be unsafe (ECR-174)?
    fn read(buffer: &[u8]) -> Self;
}

/// No-op implementation.
impl StorageKey for () {
    fn size(&self) -> usize {
        0
    }

    fn write(&self, _buffer: &mut [u8]) {
        // no-op
    }

    fn read(_buffer: &[u8]) -> Self {
        ()
    }
}

impl StorageKey for u8 {
    fn size(&self) -> usize {
        1
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer[0] = *self
    }

    fn read(buffer: &[u8]) -> Self {
        buffer[0]
    }
}

/// Uses encoding with the values mapped to `u8`
/// by adding the corresponding constant (`128`) to the value.
impl StorageKey for i8 {
    fn size(&self) -> usize {
        1
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer[0] = self.wrapping_add(i8::min_value()) as u8;
    }

    fn read(buffer: &[u8]) -> Self {
        buffer[0].wrapping_add(i8::min_value() as u8) as i8
    }
}

macro_rules! storage_key_for_ints {
    ($utype:ident, $itype:ident, $size:expr, $read_method:ident, $write_method:ident) => {
        /// Uses big-endian encoding.
        impl StorageKey for $utype {
            fn size(&self) -> usize {
                $size
            }

            fn write(&self, buffer: &mut [u8]) {
                BigEndian::$write_method(buffer, *self);
            }

            fn read(buffer: &[u8]) -> Self {
                BigEndian::$read_method(buffer)
            }
        }

        /// Uses big-endian encoding with the values mapped to the unsigned format
        /// by adding the corresponding constant to the value.
        impl StorageKey for $itype {
            fn size(&self) -> usize {
                $size
            }

            fn write(&self, buffer: &mut [u8]) {
                BigEndian::$write_method(
                    buffer,
                    self.wrapping_add($itype::min_value()) as $utype,
                );
            }

            fn read(buffer: &[u8]) -> Self {
                BigEndian::$read_method(buffer)
                    .wrapping_add($itype::min_value() as $utype) as $itype
            }
        }
    }
}

storage_key_for_ints!{u16, i16, 2, read_u16, write_u16}
storage_key_for_ints!{u32, i32, 4, read_u32, write_u32}
storage_key_for_ints!{u64, i64, 8, read_u64, write_u64}

impl StorageKey for Hash {
    fn size(&self) -> usize {
        HASH_SIZE
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self.as_ref())
    }

    fn read(buffer: &[u8]) -> Self {
        Hash::from_slice(buffer).unwrap()
    }
}

impl StorageKey for PublicKey {
    fn size(&self) -> usize {
        PUBLIC_KEY_LENGTH
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self.as_ref())
    }

    fn read(buffer: &[u8]) -> Self {
        PublicKey::from_slice(buffer).unwrap()
    }
}

impl StorageKey for Vec<u8> {
    fn size(&self) -> usize {
        self.len()
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self)
    }

    fn read(buffer: &[u8]) -> Self {
        buffer.to_vec()
    }
}

/// Uses UTF-8 string serialization.
impl StorageKey for String {
    fn size(&self) -> usize {
        self.len()
    }

    fn write(&self, buffer: &mut [u8]) {
        buffer.copy_from_slice(self.as_bytes())
    }

    fn read(buffer: &[u8]) -> Self {
        unsafe { ::std::str::from_utf8_unchecked(buffer).to_string() }
    }
}
<|endoftext|>


<commit_before>use std::ptr;

use cairo::{ImageSurface, Status};
use cairo_sys;
use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use glib;
use glib::translate::*;
use glib_sys;
use libc;

use css::{self, CssStyles, RsvgCssStyles};
use defs::{Defs, RsvgDefs};
use error::LoadingError;
use surface_utils::shared_surface::SharedImageSurface;
use util::utf8_cstr;

pub enum RsvgHandle {}

#[allow(improper_ctypes)]
extern "C" {
    fn rsvg_handle_get_defs(handle: *const RsvgHandle) -> *const RsvgDefs;

    fn rsvg_handle_resolve_uri(
        handle: *const RsvgHandle,
        uri: *const libc::c_char,
    ) -> *const libc::c_char;

    fn rsvg_handle_load_extern(
        handle: *const RsvgHandle,
        href: *const libc::c_char,
    ) -> *const RsvgHandle;

    fn rsvg_handle_get_css_styles(handle: *const RsvgHandle) -> *mut RsvgCssStyles;

    fn _rsvg_handle_acquire_data(
        handle: *mut RsvgHandle,
        href: *const libc::c_char,
        out_content_type: *mut *mut libc::c_char,
        out_len: *mut usize,
        error: *mut *mut glib_sys::GError,
    ) -> *mut u8;

    fn rsvg_handle_keep_image_data(handle: *const RsvgHandle) -> glib_sys::gboolean;

    fn rsvg_load_handle_xml_xinclude(
        handle: *mut RsvgHandle,
        href: *const libc::c_char,
    ) -> glib_sys::gboolean;
}

pub fn get_defs<'a>(handle: *const RsvgHandle) -> &'a mut Defs {
    unsafe {
        let d = rsvg_handle_get_defs(handle);
        &mut *(d as *mut Defs)
    }
}

pub fn resolve_uri(handle: *const RsvgHandle, uri: &str) -> Option<String> {
    unsafe {
        let resolved = rsvg_handle_resolve_uri(handle, uri.to_glib_none().0);
        if resolved.is_null() {
            None
        } else {
            Some(from_glib_full(resolved))
        }
    }
}

pub fn load_extern(handle: *const RsvgHandle, uri: &str) -> *const RsvgHandle {
    unsafe { rsvg_handle_load_extern(handle, uri.to_glib_none().0) }
}

pub fn get_css_styles<'a>(handle: *const RsvgHandle) -> &'a CssStyles {
    unsafe { &*(rsvg_handle_get_css_styles(handle) as *const CssStyles) }
}

pub fn get_css_styles_mut<'a>(handle: *const RsvgHandle) -> &'a mut CssStyles {
    unsafe { &mut *(rsvg_handle_get_css_styles(handle) as *mut CssStyles) }
}

pub struct BinaryData {
    pub data: Vec<u8>,
    pub content_type: Option<String>,
}

pub fn acquire_data(handle: *mut RsvgHandle, href: &str) -> Result<BinaryData, glib::Error> {
    unsafe {
        let mut content_type: *mut libc::c_char = ptr::null_mut();
        let mut len = 0;
        let mut error = ptr::null_mut();

        let buf = _rsvg_handle_acquire_data(
            handle,
            href.to_glib_none().0,
            &mut content_type as *mut *mut _,
            &mut len,
            &mut error,
        );

        if buf.is_null() {
            if error.is_null() && len == 0 {
                Ok(BinaryData {
                    data: Vec::new(),
                    content_type: None,
                })
            } else {
                Err(from_glib_full(error))
            }
        } else {
            Ok(BinaryData {
                data: FromGlibContainer::from_glib_full_num(buf as *mut u8, len),
                content_type: from_glib_full(content_type),
            })
        }
    }
}

fn keep_image_data(handle: *const RsvgHandle) -> bool {
    unsafe { from_glib(rsvg_handle_keep_image_data(handle)) }
}

pub fn image_surface_new_from_href(
    handle: *mut RsvgHandle,
    href: &str,
) -> Result<ImageSurface, LoadingError> {
    let data = acquire_data(handle, href)?;

    if data.data.len() == 0 {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = data.content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&data.data)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let surface = SharedImageSurface::from_pixbuf(&pixbuf)?.into_image_surface()?;

    if keep_image_data(handle) {
        let mime_type = data.content_type.or_else(|| {
            // Try to get the content type from the loader

            loader.get_format().and_then(|format| {
                let content_types = format.get_mime_types();

                if content_types.len() != 0 {
                    Some(content_types[0].clone())
                } else {
                    None
                }
            })
        });

        if let Some(mime_type) = mime_type {
            extern "C" {
                fn cairo_surface_set_mime_data(
                    surface: *mut cairo_sys::cairo_surface_t,
                    mime_type: *const libc::c_char,
                    data: *mut libc::c_char,
                    length: libc::c_ulong,
                    destroy: cairo_sys::cairo_destroy_func_t,
                    closure: *mut libc::c_void,
                ) -> Status;
            }

            let data_ptr = ToGlibContainerFromSlice::to_glib_full_from_slice(&data.data);

            unsafe {
                let status = cairo_surface_set_mime_data(
                    surface.to_glib_none().0,
                    mime_type.to_glib_none().0,
                    data_ptr as *mut _,
                    data.data.len() as libc::c_ulong,
                    Some(glib_sys::g_free),
                    data_ptr as *mut _,
                );

                if status != Status::Success {
                    return Err(LoadingError::Cairo(status));
                }
            }
        }
    }

    Ok(surface)
}

// FIXME: distinguish between "file not found" and "invalid XML"
pub fn load_xml_xinclude(handle: *mut RsvgHandle, href: &str) -> bool {
    unsafe { from_glib(rsvg_load_handle_xml_xinclude(handle, href.to_glib_none().0)) }
}

pub fn load_css(handle: *mut RsvgHandle, href: &str) {
    if let Ok(data) = acquire_data(handle, href) {
        let BinaryData {
            data: bytes,
            content_type,
        } = data;

        if content_type.as_ref().map(String::as_ref) != Some("text/css") {
            rsvg_log!("\"{}\" is not of type text/css; ignoring", href);
            // FIXME: report errors
            return;
        }

        if let Ok(utf8) = String::from_utf8(bytes) {
            css::parse_into_handle(handle, &utf8);
        } else {
            rsvg_log!(
                "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                href
            );
            // FIXME: report errors
            return;
        }
    } else {
        rsvg_log!("Could not load \"{}\" for CSS data", href);
        // FIXME: report errors from not being to acquire data; this should be a fatal error
    }
}

// This function just slurps CSS data from a possibly-relative href
// and parses it.  We'll move it to a better place in the end.
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_load_css(handle: *mut RsvgHandle, href: *const libc::c_char) {
    assert!(!handle.is_null());
    assert!(!href.is_null());

    let href = utf8_cstr(href);
    load_css(handle, href);
}
<commit_msg>image_surface_new_from_href(): Don't get the mime_type from the pixbuf loader<commit_after>use std::ptr;

use cairo::{ImageSurface, Status};
use cairo_sys;
use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use glib;
use glib::translate::*;
use glib_sys;
use libc;

use css::{self, CssStyles, RsvgCssStyles};
use defs::{Defs, RsvgDefs};
use error::LoadingError;
use surface_utils::shared_surface::SharedImageSurface;
use util::utf8_cstr;

pub enum RsvgHandle {}

#[allow(improper_ctypes)]
extern "C" {
    fn rsvg_handle_get_defs(handle: *const RsvgHandle) -> *const RsvgDefs;

    fn rsvg_handle_resolve_uri(
        handle: *const RsvgHandle,
        uri: *const libc::c_char,
    ) -> *const libc::c_char;

    fn rsvg_handle_load_extern(
        handle: *const RsvgHandle,
        href: *const libc::c_char,
    ) -> *const RsvgHandle;

    fn rsvg_handle_get_css_styles(handle: *const RsvgHandle) -> *mut RsvgCssStyles;

    fn _rsvg_handle_acquire_data(
        handle: *mut RsvgHandle,
        href: *const libc::c_char,
        out_content_type: *mut *mut libc::c_char,
        out_len: *mut usize,
        error: *mut *mut glib_sys::GError,
    ) -> *mut u8;

    fn rsvg_handle_keep_image_data(handle: *const RsvgHandle) -> glib_sys::gboolean;

    fn rsvg_load_handle_xml_xinclude(
        handle: *mut RsvgHandle,
        href: *const libc::c_char,
    ) -> glib_sys::gboolean;
}

pub fn get_defs<'a>(handle: *const RsvgHandle) -> &'a mut Defs {
    unsafe {
        let d = rsvg_handle_get_defs(handle);
        &mut *(d as *mut Defs)
    }
}

pub fn resolve_uri(handle: *const RsvgHandle, uri: &str) -> Option<String> {
    unsafe {
        let resolved = rsvg_handle_resolve_uri(handle, uri.to_glib_none().0);
        if resolved.is_null() {
            None
        } else {
            Some(from_glib_full(resolved))
        }
    }
}

pub fn load_extern(handle: *const RsvgHandle, uri: &str) -> *const RsvgHandle {
    unsafe { rsvg_handle_load_extern(handle, uri.to_glib_none().0) }
}

pub fn get_css_styles<'a>(handle: *const RsvgHandle) -> &'a CssStyles {
    unsafe { &*(rsvg_handle_get_css_styles(handle) as *const CssStyles) }
}

pub fn get_css_styles_mut<'a>(handle: *const RsvgHandle) -> &'a mut CssStyles {
    unsafe { &mut *(rsvg_handle_get_css_styles(handle) as *mut CssStyles) }
}

pub struct BinaryData {
    pub data: Vec<u8>,
    pub content_type: Option<String>,
}

pub fn acquire_data(handle: *mut RsvgHandle, href: &str) -> Result<BinaryData, glib::Error> {
    unsafe {
        let mut content_type: *mut libc::c_char = ptr::null_mut();
        let mut len = 0;
        let mut error = ptr::null_mut();

        let buf = _rsvg_handle_acquire_data(
            handle,
            href.to_glib_none().0,
            &mut content_type as *mut *mut _,
            &mut len,
            &mut error,
        );

        if buf.is_null() {
            if error.is_null() && len == 0 {
                Ok(BinaryData {
                    data: Vec::new(),
                    content_type: None,
                })
            } else {
                Err(from_glib_full(error))
            }
        } else {
            Ok(BinaryData {
                data: FromGlibContainer::from_glib_full_num(buf as *mut u8, len),
                content_type: from_glib_full(content_type),
            })
        }
    }
}

fn keep_image_data(handle: *const RsvgHandle) -> bool {
    unsafe { from_glib(rsvg_handle_keep_image_data(handle)) }
}

pub fn image_surface_new_from_href(
    handle: *mut RsvgHandle,
    href: &str,
) -> Result<ImageSurface, LoadingError> {
    let data = acquire_data(handle, href)?;

    if data.data.len() == 0 {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = data.content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&data.data)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let surface = SharedImageSurface::from_pixbuf(&pixbuf)?.into_image_surface()?;

    if keep_image_data(handle) {
        if let Some(mime_type) = data.content_type {
            extern "C" {
                fn cairo_surface_set_mime_data(
                    surface: *mut cairo_sys::cairo_surface_t,
                    mime_type: *const libc::c_char,
                    data: *mut libc::c_char,
                    length: libc::c_ulong,
                    destroy: cairo_sys::cairo_destroy_func_t,
                    closure: *mut libc::c_void,
                ) -> Status;
            }

            let data_ptr = ToGlibContainerFromSlice::to_glib_full_from_slice(&data.data);

            unsafe {
                let status = cairo_surface_set_mime_data(
                    surface.to_glib_none().0,
                    mime_type.to_glib_none().0,
                    data_ptr as *mut _,
                    data.data.len() as libc::c_ulong,
                    Some(glib_sys::g_free),
                    data_ptr as *mut _,
                );

                if status != Status::Success {
                    return Err(LoadingError::Cairo(status));
                }
            }
        }
    }

    Ok(surface)
}

// FIXME: distinguish between "file not found" and "invalid XML"
pub fn load_xml_xinclude(handle: *mut RsvgHandle, href: &str) -> bool {
    unsafe { from_glib(rsvg_load_handle_xml_xinclude(handle, href.to_glib_none().0)) }
}

pub fn load_css(handle: *mut RsvgHandle, href: &str) {
    if let Ok(data) = acquire_data(handle, href) {
        let BinaryData {
            data: bytes,
            content_type,
        } = data;

        if content_type.as_ref().map(String::as_ref) != Some("text/css") {
            rsvg_log!("\"{}\" is not of type text/css; ignoring", href);
            // FIXME: report errors
            return;
        }

        if let Ok(utf8) = String::from_utf8(bytes) {
            css::parse_into_handle(handle, &utf8);
        } else {
            rsvg_log!(
                "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                href
            );
            // FIXME: report errors
            return;
        }
    } else {
        rsvg_log!("Could not load \"{}\" for CSS data", href);
        // FIXME: report errors from not being to acquire data; this should be a fatal error
    }
}

// This function just slurps CSS data from a possibly-relative href
// and parses it.  We'll move it to a better place in the end.
#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_load_css(handle: *mut RsvgHandle, href: *const libc::c_char) {
    assert!(!handle.is_null());
    assert!(!href.is_null());

    let href = utf8_cstr(href);
    load_css(handle, href);
}
<|endoftext|>


<commit_before>/// Internal namespace.
pub mod internal
{

  ///
  /// Internals of a test suite.
  ///

  #[ macro_export ]
  macro_rules! impls
  {

    () => {};

    (
      $( #[ $Meta : meta ] )*
      $Vis : vis
      fn $Name : ident
      $( $Rest : tt )*
    )
    =>
    {
      $crate::impls!
      {
        @DEFINE_FN
        @META $( #[ $Meta ] )*
        @VIS{ $Vis }
        @NAME $Name
        // @INPUT ()
        // @OUTPUT
        // @BLOCK {}
        @REST
          $( #[ $Meta ] )*
          $Vis fn $Name
          $( $Rest )*
      }
    };

    // (
    //   $( #[ $Meta : meta ] )*
    //   fn $Name : ident
    //   // $( < $( $ParamName : ident : $ParamType : ty ),* > )?
    //   // $Input : tt
    //   // $( -> $Output : ty )?
    //   // $Block : block
    //   $( $Rest : tt )*
    // )
    // =>
    // {
    //   $crate::impls!
    //   {
    //     @DEFINE_FN
    //     @META $( #[ $Meta ] )*
    //     @VIS
    //     @NAME $Name
    //     @INPUT ()
    //     @OUTPUT
    //     @BLOCK {}
    //     @REST
    //       $( #[ $Meta ] )*
    //       fn $Name
    //       // $Input $( -> $Output )?
    //       // $Block
    //       $( $Rest )*
    //   }
    // };

//     (
//       $( #[ $Meta : meta ] )*
//       pub
//       fn $Name : ident
//       $Input : tt
//       $( -> $Output : ty )?
//       $Block : block
//       $( $Rest : tt )*
//     )
//     =>
//     {
//       $crate::impls!
//       {
//         @DEFINE_FN
//         @META $( #[ $Meta ] )*
//         @VIS pub
//         @NAME $Name
//         @INPUT $Input
//         @OUTPUT $( -> $Output )?
//         @BLOCK $Block
//         @REST
//           $( #[ $Meta ] )*
//           pub fn $Name $Input $( -> $Output )?
//           $Block
//           $( $Rest )*
//       }
//     };
//
//     (
//       $( #[ $Meta : meta ] )*
//       fn $Name : ident
//       $Input : tt
//       $( -> $Output : ty )?
//       $Block : block
//       $( $Rest : tt )*
//     )
//     =>
//     {
//       $crate::impls!
//       {
//         @DEFINE_FN
//         @META $( #[ $Meta ] )*
//         @VIS
//         @NAME $Name
//         @INPUT $Input
//         @OUTPUT $( -> $Output )?
//         @BLOCK $Block
//         @REST
//           $( #[ $Meta ] )*
//           fn $Name $Input $( -> $Output )?
//           $Block
//           $( $Rest )*
//       }
//     };

    (
      @DEFINE_FN
      @META $( #[ $Meta : meta ] )*
      @VIS{ $Vis : vis }
      @NAME $Name : ident
      // @INPUT $Input : tt
      // @OUTPUT $( -> $Output : ty )?
      // @BLOCK $Block : block
      @REST
        $Item : item
        $( $Rest : tt )*
    )
    =>
    {
      // #[ deny( unused_macros ) ]
      macro_rules! $Name
      {
        () =>
        {
          $Item
        };
        // ( @AS $Name : ident ) =>
        // {
        //   $( #[ $Meta ] )*
        //   fn $Name
        // };
      }

      $crate::impls!
      {
        $( $Rest )*
      }
    };

  }

  ///
  /// Index of items.
  ///

  #[ macro_export ]
  macro_rules! index
  {

    () => { };

    (
      $Name : ident ,
      $( $Rest : tt )*
    )
    =>
    {
      $Name!();
      $crate::index!( $( $Rest )* );
    };

  }

  ///
  /// Index of items.
  ///

  #[ macro_export ]
  macro_rules! ignore_macro
  {

    () => { };

    (
      $Name : ident ,
      $( $Rest : tt )*
    )
    =>
    {
      $Name!();
      stringify!( $crate::index!( $( $Rest )* ) );
    };

  }

  pub use impls;
  pub use index;
  pub use ignore_macro;
}

/// Exposed namespace of the module.
pub mod exposed
{
  // use super::internal as i;
  pub use super::prelude::*;
}

pub use exposed::*;

/// Prelude to use: `use wtools::prelude::*`.
pub mod prelude
{
  use super::internal as i;
  pub use i::impls;
  pub use i::index;
  pub use i::ignore_macro;
}
<commit_msg>extend macro impls<commit_after>/// Internal namespace.
pub mod internal
{

  ///
  /// Internals of a test suite.
  ///

  #[ macro_export ]
  macro_rules! impls
  {

    () => {};

    (
      $( #[ $Meta : meta ] )*
      $Vis : vis
      fn $Name : ident
      $( $Rest : tt )*
    )
    =>
    {
      $crate::impls!
      {
        @DEFINE_FN
        @META{ $( #[ $Meta ] )* }
        @VIS{ $Vis }
        @NAME{ $Name }
        @INPUT{ () }
        @OUTPUT{}
        @BLOCK{ {} }
        @REST
          $( #[ $Meta ] )*
          $Vis fn $Name
          $( $Rest )*
      }
    };

    (
      @DEFINE_FN
      @META{ $( #[ $Meta : meta ] )* }
      @VIS{ $Vis : vis }
      @NAME{ $Name : ident }
      @INPUT{ $Input : tt }
      @OUTPUT{ $( -> $Output : ty )? }
      @BLOCK{ $Block : block }
      @REST
        $Item : item
        $( $Rest : tt )*
    )
    =>
    {
      #[ deny( unused_macros ) ]
      macro_rules! $Name
      {
        () =>
        {
          $Item
        };
        // ( @AS $Name : ident ) =>
        // {
        //   $( #[ $Meta ] )*
        //   fn $Name
        // };
      }

      $crate::impls!
      {
        $( $Rest )*
      }
    };

  }

  ///
  /// Index of items.
  ///

  #[ macro_export ]
  macro_rules! index
  {

    () => { };

    (
      $Name : ident ,
      $( $Rest : tt )*
    )
    =>
    {
      $Name!();
      $crate::index!( $( $Rest )* );
    };

  }

  ///
  /// Index of items.
  ///

  #[ macro_export ]
  macro_rules! ignore_macro
  {

    () => { };

    (
      $Name : ident ,
      $( $Rest : tt )*
    )
    =>
    {
      $Name!();
      stringify!( $crate::index!( $( $Rest )* ) );
    };

  }

  pub use impls;
  pub use index;
  pub use ignore_macro;
}

/// Exposed namespace of the module.
pub mod exposed
{
  // use super::internal as i;
  pub use super::prelude::*;
}

pub use exposed::*;

/// Prelude to use: `use wtools::prelude::*`.
pub mod prelude
{
  use super::internal as i;
  pub use i::impls;
  pub use i::index;
  pub use i::ignore_macro;
}

//   #[ macro_export ]
//   macro_rules! impls
//   {
//
//     () => {};
//
//     (
//       $( #[ $Meta : meta ] )*
//       $Vis : vis
//       fn $Name : ident
//       $( $Rest : tt )*
//     )
//     =>
//     {
//       $crate::impls!
//       {
//         @DEFINE_FN
//         @META{ $( #[ $Meta ] )* }
//         @VIS{ $Vis }
//         @NAME{ $Name }
//         @INPUT{ () }
//         @OUTPUT{}
//         @BLOCK{ {} }
//         @REST
//           $( #[ $Meta ] )*
//           $Vis fn $Name
//           $( $Rest )*
//       }
//     };
//
//     (
//       @DEFINE_FN
//       @META{ $( #[ $Meta : meta ] )* }
//       @VIS{ $Vis : vis }
//       @NAME{ $Name : ident }
//       @INPUT{ $Input : tt }
//       @OUTPUT{ $( -> $Output : ty )? }
//       @BLOCK{ $Block : block }
//       @REST
//         $Item : item
//         $( $Rest : tt )*
//     )
//     =>
//     {
//       #[ deny( unused_macros ) ]
//       macro_rules! $Name
//       {
//         () =>
//         {
//           $Item
//         };
//         // ( @AS $Name : ident ) =>
//         // {
//         //   $( #[ $Meta ] )*
//         //   fn $Name
//         // };
//       }
//
//       $crate::impls!
//       {
//         $( $Rest )*
//       }
//     };
//
//   }

//   #[ macro_export ]
//   macro_rules! impls
//   {
//
//     () => {};
//
//     (
//       $( #[ $Meta : meta ] )*
//       $Vis : vis
//       fn $Name : ident
//       $( $Rest : tt )*
//     )
//     =>
//     {
//       $crate::impls!
//       {
//         @DEFINE_FN
//         @META $( #[ $Meta ] )*
//         @VIS{ $Vis }
//         @NAME $Name
//         // @INPUT ()
//         // @OUTPUT
//         // @BLOCK {}
//         @REST
//           $( #[ $Meta ] )*
//           $Vis fn $Name
//           $( $Rest )*
//       }
//     };
//
//     // (
//     //   $( #[ $Meta : meta ] )*
//     //   fn $Name : ident
//     //   // $( < $( $ParamName : ident : $ParamType : ty ),* > )?
//     //   // $Input : tt
//     //   // $( -> $Output : ty )?
//     //   // $Block : block
//     //   $( $Rest : tt )*
//     // )
//     // =>
//     // {
//     //   $crate::impls!
//     //   {
//     //     @DEFINE_FN
//     //     @META $( #[ $Meta ] )*
//     //     @VIS
//     //     @NAME $Name
//     //     @INPUT ()
//     //     @OUTPUT
//     //     @BLOCK {}
//     //     @REST
//     //       $( #[ $Meta ] )*
//     //       fn $Name
//     //       // $Input $( -> $Output )?
//     //       // $Block
//     //       $( $Rest )*
//     //   }
//     // };
//
// //     (
// //       $( #[ $Meta : meta ] )*
// //       pub
// //       fn $Name : ident
// //       $Input : tt
// //       $( -> $Output : ty )?
// //       $Block : block
// //       $( $Rest : tt )*
// //     )
// //     =>
// //     {
// //       $crate::impls!
// //       {
// //         @DEFINE_FN
// //         @META $( #[ $Meta ] )*
// //         @VIS pub
// //         @NAME $Name
// //         @INPUT $Input
// //         @OUTPUT $( -> $Output )?
// //         @BLOCK $Block
// //         @REST
// //           $( #[ $Meta ] )*
// //           pub fn $Name $Input $( -> $Output )?
// //           $Block
// //           $( $Rest )*
// //       }
// //     };
// //
// //     (
// //       $( #[ $Meta : meta ] )*
// //       fn $Name : ident
// //       $Input : tt
// //       $( -> $Output : ty )?
// //       $Block : block
// //       $( $Rest : tt )*
// //     )
// //     =>
// //     {
// //       $crate::impls!
// //       {
// //         @DEFINE_FN
// //         @META $( #[ $Meta ] )*
// //         @VIS
// //         @NAME $Name
// //         @INPUT $Input
// //         @OUTPUT $( -> $Output )?
// //         @BLOCK $Block
// //         @REST
// //           $( #[ $Meta ] )*
// //           fn $Name $Input $( -> $Output )?
// //           $Block
// //           $( $Rest )*
// //       }
// //     };
//
//     (
//       @DEFINE_FN
//       @META $( #[ $Meta : meta ] )*
//       @VIS{ $Vis : vis }
//       @NAME $Name : ident
//       // @INPUT $Input : tt
//       // @OUTPUT $( -> $Output : ty )?
//       // @BLOCK $Block : block
//       @REST
//         $Item : item
//         $( $Rest : tt )*
//     )
//     =>
//     {
//       // #[ deny( unused_macros ) ]
//       macro_rules! $Name
//       {
//         () =>
//         {
//           $Item
//         };
//         // ( @AS $Name : ident ) =>
//         // {
//         //   $( #[ $Meta ] )*
//         //   fn $Name
//         // };
//       }
//
//       $crate::impls!
//       {
//         $( $Rest )*
//       }
//     };
//
//   }
<|endoftext|>


<commit_before>use super::prelude::*;
use stencila_schema::Node;

/// Override of macro to implement `from_value` for all node types
macro_rules! patchable_node_variants {
    ($( $variant:path )*) => {
        impl Patchable for Node {
            patchable_variants_is_equal!($( $variant )*);
            patchable_variants_hash!($( $variant )*);
            patchable_variants_apply_add!($( $variant )*);
            patchable_variants_apply_remove!($( $variant )*);
            patchable_variants_apply_replace!($( $variant )*);
            patchable_variants_apply_move!($( $variant )*);
            patchable_variants_apply_transform!($( $variant )*);

            fn diff(&self, other: &Self, differ: &mut Differ) {
                #[allow(unreachable_patterns)]
                match (self, other) {
                    // For the atomic primitives, do replacement at this level,
                    // so that the `Replace` operation has a `value` of type
                    // `Node::Number` not a `f64` etc.
                    (Node::Boolean(..), Node::Boolean(..)) => differ.replace(other),
                    (Node::Integer(..), Node::Integer(..)) => differ.replace(other),
                    (Node::Number(..), Node::Number(..)) => differ.replace(other),
                    // For other matching pairs of other variants do diffing
                    $(
                        ($variant(me), $variant(other)) => me.diff(other, differ),
                    )*
                    // Usual fallback to replacement for unmatched variants
                    _ => differ.replace(other)
                }
            }

            fn from_value(value: &Value) -> Result<Self>
            where
                Self: Clone + Sized + 'static,
            {
                if let Some(value) = value.downcast_ref::<Self>() {
                    return Ok(value.clone());
                } else if let Some(value) = value.downcast_ref::<serde_json::Value>() {
                    if let Some(string) = value.as_str() {
                        return Ok(Node::String(string.to_string()));
                    }
                    if let Some(number) = value.as_f64() {
                        return Ok(Node::Number(number));
                    }
                    if let Some(integer) = value.as_i64() {
                        return Ok(Node::Integer(integer));
                    }
                    if let Some(boolean) = value.as_bool() {
                        return Ok(Node::Boolean(boolean));
                    }
                }
                bail!(invalid_patch_value::<Self>())
            }
        }
    };
}

patchable_node_variants!(
    Node::Array
    Node::Article
    Node::AudioObject
    Node::Boolean
    Node::Cite
    Node::CiteGroup
    Node::Claim
    Node::CodeBlock
    Node::CodeChunk
    Node::CodeExpression
    Node::CodeFragment
    Node::Datatable
    Node::DatatableColumn
    Node::Delete
    Node::Emphasis
    Node::Figure
    Node::Heading
    Node::ImageObject
    Node::Integer
    Node::Link
    Node::List
    Node::MathBlock
    Node::MathFragment
    Node::NontextualAnnotation
    Node::Note
    Node::Null
    Node::Number
    Node::Object
    Node::Paragraph
    Node::Parameter
    Node::Quote
    Node::QuoteBlock
    Node::String
    Node::Strong
    Node::Subscript
    Node::Superscript
    Node::Table
    Node::ThematicBreak
    Node::VideoObject
);
<commit_msg>fix(Node patches): Only replace atomics if not equal<commit_after>use super::prelude::*;
use stencila_schema::Node;

/// Override of macro to implement `from_value` for all node types
macro_rules! patchable_node_variants {
    ($( $variant:path )*) => {
        impl Patchable for Node {
            patchable_variants_is_equal!($( $variant )*);
            patchable_variants_hash!($( $variant )*);
            patchable_variants_apply_add!($( $variant )*);
            patchable_variants_apply_remove!($( $variant )*);
            patchable_variants_apply_replace!($( $variant )*);
            patchable_variants_apply_move!($( $variant )*);
            patchable_variants_apply_transform!($( $variant )*);

            fn diff(&self, other: &Self, differ: &mut Differ) {
                #[allow(unreachable_patterns)]
                match (self, other) {
                    // For the atomic primitives, do replacement at this level,
                    // so that the `Replace` operation has a `value` of type
                    // `Node::Number` not a `f64` etc.
                    (Node::Boolean(..), Node::Boolean(..)) |
                    (Node::Integer(..), Node::Integer(..)) |
                    (Node::Number(..), Node::Number(..)) => {
                        if !self.is_equal(other).is_ok() {
                            differ.replace(other)
                        }
                    },
                    // For other matching pairs of other variants do diffing
                    $(
                        ($variant(me), $variant(other)) => me.diff(other, differ),
                    )*
                    // Usual fallback to replacement for unmatched variants
                    _ => differ.replace(other)
                }
            }

            fn from_value(value: &Value) -> Result<Self>
            where
                Self: Clone + Sized + 'static,
            {
                if let Some(value) = value.downcast_ref::<Self>() {
                    return Ok(value.clone());
                } else if let Some(value) = value.downcast_ref::<serde_json::Value>() {
                    if let Some(string) = value.as_str() {
                        return Ok(Node::String(string.to_string()));
                    }
                    if let Some(number) = value.as_f64() {
                        return Ok(Node::Number(number));
                    }
                    if let Some(integer) = value.as_i64() {
                        return Ok(Node::Integer(integer));
                    }
                    if let Some(boolean) = value.as_bool() {
                        return Ok(Node::Boolean(boolean));
                    }
                }
                bail!(invalid_patch_value::<Self>())
            }
        }
    };
}

patchable_node_variants!(
    Node::Array
    Node::Article
    Node::AudioObject
    Node::Boolean
    Node::Cite
    Node::CiteGroup
    Node::Claim
    Node::CodeBlock
    Node::CodeChunk
    Node::CodeExpression
    Node::CodeFragment
    Node::Datatable
    Node::DatatableColumn
    Node::Delete
    Node::Emphasis
    Node::Figure
    Node::Heading
    Node::ImageObject
    Node::Integer
    Node::Link
    Node::List
    Node::MathBlock
    Node::MathFragment
    Node::NontextualAnnotation
    Node::Note
    Node::Null
    Node::Number
    Node::Object
    Node::Paragraph
    Node::Parameter
    Node::Quote
    Node::QuoteBlock
    Node::String
    Node::Strong
    Node::Subscript
    Node::Superscript
    Node::Table
    Node::ThematicBreak
    Node::VideoObject
);
<|endoftext|>


<commit_before>

#![allow(single_match, unused_assignments, unused_variables)]

fn test1() {
    let mut x = 0;
    loop { // never_loop
        x += 1;
        if x == 1 {
            return
        }
        break;
    }
}

fn test2() {
    let mut x = 0;
    loop {
        x += 1;
        if x == 1 {
            break
        }
    }
}

fn test3() {
    let mut x = 0;
    loop { // never loops
        x += 1;
        break
    }
}

fn test4() {
    let mut x = 1;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => (),
        }
    }
}

fn test5() {
    let i = 0;
	loop { // never loops
        while i == 0 { // never loops
            break
        }
        return
	}
}

fn test6() {
    let mut x = 0;
    'outer: loop {
        x += 1;
		loop { // never loops
            if x == 5 { break }
			continue 'outer
		}
		return
	}
}

fn test7() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            1 => continue,
            _ => (),
        }
        return
    }
}

fn test8() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => continue,
        }
    }
}

fn test9() {
    let x = Some(1);
    while let Some(y) = x { // never loops
        return
    }
}

fn test10() {
    for x in 0..10 { // never loops
        match x {
            1 => break,
            _ => return,
        }
    }
}

fn test11<F: FnMut() -> i32>(mut f: F) {
    loop {
        return match f() {
            1 => continue,
            _ => (),
        }
    }
}

pub fn test12(a: bool, b: bool) {
    'label: loop {
        loop {
            if a {
                continue 'label;
            }
            if b {
                break;
            }
        }
        break;
    }
}

pub fn test13() {
    let mut a = true;
    loop { // infinite loop
        while a {
            if true {
                a = false;
                continue;
            }
            return;
        }
    }
}

fn main() {
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11(|| 0);
    test12(true, false);
    test13();
}

<commit_msg>add never_loop tests<commit_after>

#![allow(single_match, unused_assignments, unused_variables)]

fn test1() {
    let mut x = 0;
    loop { // never_loop
        x += 1;
        if x == 1 {
            return
        }
        break;
    }
}

fn test2() {
    let mut x = 0;
    loop {
        x += 1;
        if x == 1 {
            break
        }
    }
}

fn test3() {
    let mut x = 0;
    loop { // never loops
        x += 1;
        break
    }
}

fn test4() {
    let mut x = 1;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => (),
        }
    }
}

fn test5() {
    let i = 0;
	loop { // never loops
        while i == 0 { // never loops
            break
        }
        return
	}
}

fn test6() {
    let mut x = 0;
    'outer: loop {
        x += 1;
		loop { // never loops
            if x == 5 { break }
			continue 'outer
		}
		return
	}
}

fn test7() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            1 => continue,
            _ => (),
        }
        return
    }
}

fn test8() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => continue,
        }
    }
}

fn test9() {
    let x = Some(1);
    while let Some(y) = x { // never loops
        return
    }
}

fn test10() {
    for x in 0..10 { // never loops
        match x {
            1 => break,
            _ => return,
        }
    }
}

fn test11<F: FnMut() -> i32>(mut f: F) {
    loop {
        return match f() {
            1 => continue,
            _ => (),
        }
    }
}

pub fn test12(a: bool, b: bool) {
    'label: loop {
        loop {
            if a {
                continue 'label;
            }
            if b {
                break;
            }
        }
        break;
    }
}

pub fn test13() {
    let mut a = true;
    loop { // infinite loop
        while a {
            if true {
                a = false;
                continue;
            }
            return;
        }
    }
}

pub fn test14() {
    let mut a = true;
    'outer: while a { // never loops
        while a {
            if a {
                a = false;
                continue
            }
        }
        break 'outer;
    }
}

fn main() {
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11(|| 0);
    test12(true, false);
    test13();
    test14();
}

<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{
    cluster::Cluster,
    experiments::{Context, Experiment, ExperimentParam},
    instance,
    instance::Instance,
    tx_emitter::EmitJobRequest,
};
use async_trait::async_trait;
use diem_logger::prelude::*;
use futures::future::try_join_all;
use std::{
    collections::HashSet,
    env, fmt,
    iter::{once, Iterator},
    time::{Duration, Instant},
};
use structopt::StructOpt;
use tokio::time;

/// Reboot `updated_instance` with newer image tag
pub async fn update_batch_instance(
    context: &mut Context<'_>,
    updated_instance: &[Instance],
    updated_lsr: &[Instance],
    updated_tag: String,
) -> anyhow::Result<()> {
    info!("Stop Existing instances.");
    let futures: Vec<_> = updated_instance.iter().map(Instance::stop).collect();
    try_join_all(futures).await?;

    if !updated_lsr.is_empty() {
        info!("Stop associated lsr instances.");
        let futures: Vec<_> = updated_lsr.iter().map(Instance::stop).collect();
        try_join_all(futures).await?;
        info!("Reinstantiate a set of new lsr.");
        let futures: Vec<_> = updated_lsr
            .iter()
            .map(|instance| {
                let mut newer_config = instance.instance_config().clone();
                newer_config.replace_tag(updated_tag.clone()).unwrap();
                context.cluster_swarm.spawn_new_instance(newer_config)
            })
            .collect();
        try_join_all(futures).await?;
        info!("Wait for the instance to sync up with peers");
        time::sleep(Duration::from_secs(20)).await;
    }

    info!("Reinstantiate a set of new nodes.");
    let futures: Vec<_> = updated_instance
        .iter()
        .map(|instance| {
            let mut newer_config = instance.instance_config().clone();
            newer_config.replace_tag(updated_tag.clone()).unwrap();
            context.cluster_swarm.spawn_new_instance(newer_config)
        })
        .collect();
    let instances = try_join_all(futures).await?;

    info!("Wait for the instances to recover.");
    let deadline = Instant::now() + Duration::from_secs(5 * 60);
    let futures: Vec<_> = instances
        .iter()
        .map(|instance| instance.wait_json_rpc(deadline))
        .collect();
    try_join_all(futures).await?;

    // Add a timeout to have wait for validators back to healthy mode.
    // TODO: Replace this with a blocking health check.
    info!("Wait for the instance to sync up with peers");
    time::sleep(Duration::from_secs(20)).await;
    Ok(())
}

pub fn get_corresponding_full_nodes<'a>(
    validators: impl Iterator<Item = &'a Instance>,
    cluster: &Cluster,
) -> Vec<Instance> {
    let validator_groups = validators
        .map(|instance| instance.validator_group())
        .collect::<Vec<_>>();
    cluster
        .fullnode_instances()
        .iter()
        .filter(|full_node| validator_groups.contains(&full_node.validator_group()))
        .cloned()
        .collect()
}

pub fn get_instance_list_str(batch: &[Instance]) -> String {
    let mut nodes_list = String::from("");
    for instance in batch.iter() {
        nodes_list.push_str(&instance.to_string());
        nodes_list.push_str(", ")
    }
    nodes_list
}

#[derive(StructOpt, Debug)]
pub struct CompatiblityTestParams {
    #[structopt(
        long,
        default_value = "15",
        help = "Number of nodes to update in the first batch"
    )]
    pub count: usize,
    #[structopt(long, help = "Image tag of newer validator software")]
    pub updated_image_tag: String,
}

pub struct CompatibilityTest {
    first_node: Instance,
    first_lsr: Vec<Instance>,
    first_full_node: Vec<Instance>,
    first_batch: Vec<Instance>,
    first_batch_lsr: Vec<Instance>,
    first_full_nodes_batch: Vec<Instance>,
    second_batch: Vec<Instance>,
    second_batch_lsr: Vec<Instance>,
    second_full_nodes_batch: Vec<Instance>,
    updated_image_tag: String,
}

impl ExperimentParam for CompatiblityTestParams {
    type E = CompatibilityTest;
    fn build(self, cluster: &Cluster) -> Self::E {
        if self.count > cluster.validator_instances().len() || self.count == 0 {
            panic!(
                "Can not reboot {} validators in cluster with {} instances",
                self.count,
                cluster.validator_instances().len()
            );
        }
        let (first_batch, second_batch) = cluster.split_n_validators_random(self.count);
        let mut first_batch = first_batch.into_validator_instances();
        let second_batch = second_batch.into_validator_instances();
        let first_node = first_batch
            .pop()
            .expect("Requires at least one validator in the first batch");
        let first_full_node = get_corresponding_full_nodes([first_node.clone()].iter(), cluster);

        let mut first_lsr = vec![];
        let mut first_batch_lsr = vec![];
        let mut second_batch_lsr = vec![];
        if !cluster.lsr_instances().is_empty() {
            first_batch_lsr = cluster.lsr_instances_for_validators(&first_batch);
            second_batch_lsr = cluster.lsr_instances_for_validators(&second_batch);
            first_lsr = cluster.lsr_instances_for_validators(&[first_node.clone()]);
        }

        let first_full_nodes_batch = get_corresponding_full_nodes(first_batch.iter(), cluster);
        let second_full_nodes_batch = get_corresponding_full_nodes(second_batch.iter(), cluster);

        Self::E {
            first_node,
            first_lsr,
            first_full_node,
            first_batch,
            first_batch_lsr,
            first_full_nodes_batch,
            second_batch,
            second_batch_lsr,
            second_full_nodes_batch,
            updated_image_tag: self.updated_image_tag,
        }
    }
}

#[async_trait]
impl Experiment for CompatibilityTest {
    fn affected_validators(&self) -> HashSet<String> {
        instance::instancelist_to_set(&self.first_batch)
            .union(&instance::instancelist_to_set(&self.second_batch))
            .cloned()
            .chain(once(self.first_node.peer_name().clone()))
            .collect()
    }

    async fn run(&mut self, context: &mut Context<'_>) -> anyhow::Result<()> {
        let job_duration = Duration::from_secs(3);
        context.report.report_text(format!(
            "Compatibility test results for {} ==> {} (PR)",
            context.current_tag, self.updated_image_tag
        ));

        // Generate some traffic
        let msg = format!(
            "1. All instances running {}, generating some traffic on network",
            context.current_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        let all_full_nodes_request = EmitJobRequest::for_instances(
            context.cluster.validator_instances().to_vec(),
            context.global_emit_job_request,
            0,
            0,
        );
        context
            .tx_emitter
            .emit_txn_for(job_duration, all_full_nodes_request)
            .await
            .map_err(|e| anyhow::format_err!("Failed to generate traffic: {}", e))?;

        let msg = format!(
            "2. First full node {} ==> {}, to validate new full node to old validator node traffic",
            context.current_tag, self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!(
            "Upgrade Full Node: {}",
            get_instance_list_str(&self.first_full_node)
        );
        update_batch_instance(
            context,
            &self.first_full_node,
            &[],
            self.updated_image_tag.clone(),
        )
        .await?;

        // Full node running at n+1, validator running n
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.first_full_node.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Storage backwards compat broken: {}", e))?;

        let msg = format!(
            "3. First Validator node {} ==> {}, to validate storage compatibility",
            context.current_tag, self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!("Upgrading validator: {}", self.first_node);
        let first_node = vec![self.first_node.clone()];
        update_batch_instance(
            context,
            &first_node,
            &self.first_lsr,
            self.updated_image_tag.clone(),
        )
        .await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.first_full_node.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Storage backwards compat broken: {}", e))?;

        let msg = format!(
            "4. First batch validators ({}) {} ==> {}, to test consensus and traffic between old full nodes and new validator node",
            self.first_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        info!(
            "Upgrading validators: {}",
            get_instance_list_str(&self.first_batch)
        );
        context.report.report_text(msg);
        update_batch_instance(
            context,
            &self.first_batch,
            &self.first_batch_lsr,
            self.updated_image_tag.clone(),
        )
        .await?;

        // Full node running at n, validator running n+1
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.first_full_nodes_batch.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Consensus backwards compat broken: {}", e))?;

        let msg = format!(
            "5. First batch full nodes ({}) {} ==> {}",
            self.first_full_nodes_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!(
            "Upgrading full nodes: {}",
            get_instance_list_str(&self.first_full_nodes_batch)
        );
        update_batch_instance(
            context,
            &self.first_full_nodes_batch,
            &[],
            self.updated_image_tag.clone(),
        )
        .await?;

        let msg = format!(
            "6. Second batch validators ({}) {} ==> {}, to upgrade rest of the validators",
            self.second_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!(
            "Upgrading validators: {}",
            get_instance_list_str(&self.second_batch)
        );
        update_batch_instance(
            context,
            &self.second_batch,
            &self.second_batch_lsr,
            self.updated_image_tag.clone(),
        )
        .await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.second_batch.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| {
                anyhow::format_err!("Failed to upgrade rest of validator images: {}", e)
            })?;

        let msg = format!(
            "7. Second batch of full nodes ({}) {} ==> {}, to finish the network upgrade",
            self.second_full_nodes_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        info!(
            "Upgrading full nodes: {}",
            get_instance_list_str(&self.second_full_nodes_batch)
        );
        context.report.report_text(msg);
        update_batch_instance(
            context,
            &self.second_full_nodes_batch,
            &[],
            self.updated_image_tag.clone(),
        )
        .await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.second_full_nodes_batch.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Failed to upgrade full node images: {}", e))?;

        Ok(())
    }

    fn deadline(&self) -> Duration {
        Duration::from_secs(16 * 60)
    }
}

impl fmt::Display for CompatibilityTest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Compatibility test, phased upgrade to {} in batches of 1, {}, {}",
            self.updated_image_tag,
            self.first_batch.len(),
            self.second_batch.len()
        )
    }
}
<commit_msg>[cluster-test] Revert 7959<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{
    cluster::Cluster,
    experiments::{Context, Experiment, ExperimentParam},
    instance,
    instance::Instance,
    tx_emitter::EmitJobRequest,
};
use async_trait::async_trait;
use diem_logger::prelude::*;
use futures::future::try_join_all;
use std::{
    collections::HashSet,
    env, fmt,
    iter::{once, Iterator},
    time::{Duration, Instant},
};
use structopt::StructOpt;
use tokio::time;

/// Reboot `updated_instance` with newer image tag
pub async fn update_batch_instance(
    context: &mut Context<'_>,
    updated_instance: &[Instance],
    updated_lsr: &[Instance],
    updated_tag: String,
) -> anyhow::Result<()> {
    info!("Stop Existing instances.");
    let futures: Vec<_> = updated_instance.iter().map(Instance::stop).collect();
    try_join_all(futures).await?;

    if !updated_lsr.is_empty() {
        info!("Stop associated lsr instances.");
        let futures: Vec<_> = updated_lsr.iter().map(Instance::stop).collect();
        try_join_all(futures).await?;
        info!("Reinstantiate a set of new lsr.");
        let futures: Vec<_> = updated_lsr
            .iter()
            .map(|instance| {
                let mut newer_config = instance.instance_config().clone();
                newer_config.replace_tag(updated_tag.clone()).unwrap();
                context.cluster_swarm.spawn_new_instance(newer_config)
            })
            .collect();
        try_join_all(futures).await?;
        info!("Wait for the instance to sync up with peers");
        time::sleep(Duration::from_secs(20)).await;
    }

    info!("Reinstantiate a set of new nodes.");
    let futures: Vec<_> = updated_instance
        .iter()
        .map(|instance| {
            let mut newer_config = instance.instance_config().clone();
            newer_config.replace_tag(updated_tag.clone()).unwrap();
            context.cluster_swarm.spawn_new_instance(newer_config)
        })
        .collect();
    let instances = try_join_all(futures).await?;

    info!("Wait for the instances to recover.");
    let deadline = Instant::now() + Duration::from_secs(5 * 60);
    let futures: Vec<_> = instances
        .iter()
        .map(|instance| instance.wait_json_rpc(deadline))
        .collect();
    try_join_all(futures).await?;

    // Add a timeout to have wait for validators back to healthy mode.
    // TODO: Replace this with a blocking health check.
    info!("Wait for the instance to sync up with peers");
    time::sleep(Duration::from_secs(20)).await;
    Ok(())
}

pub fn get_corresponding_full_nodes<'a>(
    validators: impl Iterator<Item = &'a Instance>,
    cluster: &Cluster,
) -> Vec<Instance> {
    let validator_groups = validators
        .map(|instance| instance.validator_group())
        .collect::<Vec<_>>();
    cluster
        .fullnode_instances()
        .iter()
        .filter(|full_node| validator_groups.contains(&full_node.validator_group()))
        .cloned()
        .collect()
}

pub fn get_instance_list_str(batch: &[Instance]) -> String {
    let mut nodes_list = String::from("");
    for instance in batch.iter() {
        nodes_list.push_str(&instance.to_string());
        nodes_list.push_str(", ")
    }
    nodes_list
}

#[derive(StructOpt, Debug)]
pub struct CompatiblityTestParams {
    #[structopt(
        long,
        default_value = "15",
        help = "Number of nodes to update in the first batch"
    )]
    pub count: usize,
    #[structopt(long, help = "Image tag of newer validator software")]
    pub updated_image_tag: String,
}

pub struct CompatibilityTest {
    first_node: Instance,
    first_lsr: Vec<Instance>,
    first_full_node: Vec<Instance>,
    first_batch: Vec<Instance>,
    first_batch_lsr: Vec<Instance>,
    first_full_nodes_batch: Vec<Instance>,
    second_batch: Vec<Instance>,
    second_batch_lsr: Vec<Instance>,
    second_full_nodes_batch: Vec<Instance>,
    updated_image_tag: String,
}

impl ExperimentParam for CompatiblityTestParams {
    type E = CompatibilityTest;
    fn build(self, cluster: &Cluster) -> Self::E {
        if self.count > cluster.validator_instances().len() || self.count == 0 {
            panic!(
                "Can not reboot {} validators in cluster with {} instances",
                self.count,
                cluster.validator_instances().len()
            );
        }
        let (first_batch, second_batch) = cluster.split_n_validators_random(self.count);
        let mut first_batch = first_batch.into_validator_instances();
        let second_batch = second_batch.into_validator_instances();
        let first_node = first_batch
            .pop()
            .expect("Requires at least one validator in the first batch");
        let first_full_node = get_corresponding_full_nodes([first_node.clone()].iter(), cluster);

        let mut first_lsr = vec![];
        let mut first_batch_lsr = vec![];
        let mut second_batch_lsr = vec![];
        if !cluster.lsr_instances().is_empty() {
            first_batch_lsr = cluster.lsr_instances_for_validators(&first_batch);
            second_batch_lsr = cluster.lsr_instances_for_validators(&second_batch);
            first_lsr = cluster.lsr_instances_for_validators(&[first_node.clone()]);
        }

        let first_full_nodes_batch = get_corresponding_full_nodes(first_batch.iter(), cluster);
        let second_full_nodes_batch = get_corresponding_full_nodes(second_batch.iter(), cluster);

        Self::E {
            first_node,
            first_lsr,
            first_full_node,
            first_batch,
            first_batch_lsr,
            first_full_nodes_batch,
            second_batch,
            second_batch_lsr,
            second_full_nodes_batch,
            updated_image_tag: self.updated_image_tag,
        }
    }
}

#[async_trait]
impl Experiment for CompatibilityTest {
    fn affected_validators(&self) -> HashSet<String> {
        instance::instancelist_to_set(&self.first_batch)
            .union(&instance::instancelist_to_set(&self.second_batch))
            .cloned()
            .chain(once(self.first_node.peer_name().clone()))
            .collect()
    }

    async fn run(&mut self, context: &mut Context<'_>) -> anyhow::Result<()> {
        let job_duration = Duration::from_secs(3);
        context.report.report_text(format!(
            "Compatibility test results for {} ==> {} (PR)",
            context.current_tag, self.updated_image_tag
        ));

        // Generate some traffic
        let msg = format!(
            "1. All instances running {}, generating some traffic on network",
            context.current_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        let all_full_nodes_request = EmitJobRequest::for_instances(
            context.cluster.fullnode_instances().to_vec(),
            context.global_emit_job_request,
            0,
            0,
        );
        context
            .tx_emitter
            .emit_txn_for(job_duration, all_full_nodes_request)
            .await
            .map_err(|e| anyhow::format_err!("Failed to generate traffic: {}", e))?;

        let msg = format!(
            "2. First full node {} ==> {}, to validate new full node to old validator node traffic",
            context.current_tag, self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!(
            "Upgrade Full Node: {}",
            get_instance_list_str(&self.first_full_node)
        );
        update_batch_instance(
            context,
            &self.first_full_node,
            &[],
            self.updated_image_tag.clone(),
        )
        .await?;

        // Full node running at n+1, validator running n
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.first_full_node.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Storage backwards compat broken: {}", e))?;

        let msg = format!(
            "3. First Validator node {} ==> {}, to validate storage compatibility",
            context.current_tag, self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!("Upgrading validator: {}", self.first_node);
        let first_node = vec![self.first_node.clone()];
        update_batch_instance(
            context,
            &first_node,
            &self.first_lsr,
            self.updated_image_tag.clone(),
        )
        .await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.first_full_node.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Storage backwards compat broken: {}", e))?;

        let msg = format!(
            "4. First batch validators ({}) {} ==> {}, to test consensus and traffic between old full nodes and new validator node",
            self.first_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        info!(
            "Upgrading validators: {}",
            get_instance_list_str(&self.first_batch)
        );
        context.report.report_text(msg);
        update_batch_instance(
            context,
            &self.first_batch,
            &self.first_batch_lsr,
            self.updated_image_tag.clone(),
        )
        .await?;

        // Full node running at n, validator running n+1
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.first_full_nodes_batch.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Consensus backwards compat broken: {}", e))?;

        let msg = format!(
            "5. First batch full nodes ({}) {} ==> {}",
            self.first_full_nodes_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!(
            "Upgrading full nodes: {}",
            get_instance_list_str(&self.first_full_nodes_batch)
        );
        update_batch_instance(
            context,
            &self.first_full_nodes_batch,
            &[],
            self.updated_image_tag.clone(),
        )
        .await?;

        let msg = format!(
            "6. Second batch validators ({}) {} ==> {}, to upgrade rest of the validators",
            self.second_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        info!(
            "Upgrading validators: {}",
            get_instance_list_str(&self.second_batch)
        );
        update_batch_instance(
            context,
            &self.second_batch,
            &self.second_batch_lsr,
            self.updated_image_tag.clone(),
        )
        .await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.second_batch.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| {
                anyhow::format_err!("Failed to upgrade rest of validator images: {}", e)
            })?;

        let msg = format!(
            "7. Second batch of full nodes ({}) {} ==> {}, to finish the network upgrade",
            self.second_full_nodes_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        info!(
            "Upgrading full nodes: {}",
            get_instance_list_str(&self.second_full_nodes_batch)
        );
        context.report.report_text(msg);
        update_batch_instance(
            context,
            &self.second_full_nodes_batch,
            &[],
            self.updated_image_tag.clone(),
        )
        .await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(
                    self.second_full_nodes_batch.clone(),
                    context.global_emit_job_request,
                    0,
                    0,
                ),
            )
            .await
            .map_err(|e| anyhow::format_err!("Failed to upgrade full node images: {}", e))?;

        Ok(())
    }

    fn deadline(&self) -> Duration {
        Duration::from_secs(16 * 60)
    }
}

impl fmt::Display for CompatibilityTest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Compatibility test, phased upgrade to {} in batches of 1, {}, {}",
            self.updated_image_tag,
            self.first_batch.len(),
            self.second_batch.len()
        )
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use rustc::hir::{self, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use std::boxed;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use syntax::ast;
use weedle;

declare_lint!(
    WEBIDL_INHERIT_CORRECT,
    Deny,
    "Warn and report usage of incorrect webidl inheritance"
);

pub(crate) struct WebIdlPass {
    symbols: crate::Symbols,
}

#[derive(Clone, Debug)]
pub struct ParentMismatchError {
    name: String,
    rust_parent: String,
    webidl_parent: String,
}

impl fmt::Display for ParentMismatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "webidl-rust inheritance mismatch, rust: {:?}, rust parent: {:?}, webidl parent: {:?}",
            self.name, self.rust_parent, self.webidl_parent
        )
    }
}

impl Error for ParentMismatchError {
    fn description(&self) -> &str {
        "ParentMismatchError"
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl WebIdlPass {
    pub fn new(symbols: crate::Symbols) -> WebIdlPass {
        WebIdlPass { symbols }
    }
}

fn get_ty_name(ty: &str) -> &str {
    if let Some(i) = ty.rfind(':') {
        &ty[i + 1..]
    } else {
        ty
    }
}

fn get_webidl_path(struct_name: &str) -> io::Result<path::PathBuf> {
    let mut dir = env::current_dir()?;
    dir.push("components/script/dom/webidls/");
    dir.push(format!("{}.webidl", struct_name));

    Ok(dir)
}

fn is_webidl_ty(symbols: &crate::Symbols, cx: &LateContext, ty: &ty::TyS) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::Adt(did, _substs) => {
                if cx.tcx.has_attr(did.did, symbols.webidl) {
                    ret = true;
                }
                false
            },
            ty::Ref(..) => false,    // don't recurse down &ptrs
            ty::RawPtr(..) => false, // don't recurse down *ptrs
            ty::FnDef(..) | ty::FnPtr(_) => false,
            _ => true,
        }
    });
    ret
}

fn check_inherits(code: &str, name: &str, parent_name: &str) -> Result<(), Box<Error>> {
    let idl = weedle::parse(code).expect("Invalid webidl provided");
    let mut inherits = "";

    for def in idl {
        if let weedle::Definition::Interface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        } else if let weedle::Definition::CallbackInterface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        }
    }

    if inherits == parent_name {
        return Ok(());
    }

    // If there is no parent, first field must be of type Reflector.
    if inherits == "" && parent_name == "Reflector" {
        return Ok(());
    }

    if inherits == "" &&
        name == "PaintRenderingContext2D" &&
        parent_name == "CanvasRenderingContext2D"
    {
        // PaintRenderingContext2D embeds a CanvasRenderingContext2D
        // instead of a Reflector as an optimization,
        // but this is fine since CanvasRenderingContext2D
        // also has a reflector
        return Ok(());
    }

    Err(boxed::Box::from(ParentMismatchError {
        name: name.to_string(),
        rust_parent: parent_name.to_string(),
        webidl_parent: inherits.to_string(),
    }))
}

fn check_webidl(name: &str, parent_name: &Option<String>) -> Result<(), Box<Error>> {
    let path = get_webidl_path(&name)?;
    if let Some(parent) = parent_name {
        let code = fs::read_to_string(path)?;
        return check_inherits(&code, name, &parent);
    }

    Ok(())
}

impl LintPass for WebIdlPass {
    fn name(&self) -> &'static str {
        "ServoWebIDLPass"
    }

    fn get_lints(&self) -> LintArray {
        lint_array!(WEBIDL_INHERIT_CORRECT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WebIdlPass {
    fn check_struct_def(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        def: &'tcx hir::VariantData,
        n: ast::Name,
        _gen: &'tcx hir::Generics,
        id: HirId,
    ) {
        let def_id = cx.tcx.hir().local_def_id_from_hir_id(id);
        if !is_webidl_ty(&self.symbols, cx, cx.tcx.type_of(def_id)) {
            return;
        }

        let item = match cx.tcx.hir().get_by_hir_id(id) {
            hir::Node::Item(item) => item,
            _ => cx
                .tcx
                .hir()
                .expect_item_by_hir_id(cx.tcx.hir().get_parent_item(id)),
        };

        let parent_name = def.fields().iter().next().map(|field| {
            let def_id = cx.tcx.hir().local_def_id_from_hir_id(field.hir_id);
            let ty = cx.tcx.type_of(def_id).to_string();
            get_ty_name(&ty).to_string()
        });

        let struct_name = n.to_string();
        match check_webidl(&struct_name, &parent_name) {
            Ok(()) => {},
            Err(e) => {
                let description = format!("{}", e);
                cx.span_lint(WEBIDL_INHERIT_CORRECT, item.ident.span, &description)
            },
        };
    }
}
<commit_msg>Made webidl search path relative to manifest.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use rustc::hir::{self, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use std::boxed;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use syntax::ast;
use weedle;

declare_lint!(
    WEBIDL_INHERIT_CORRECT,
    Deny,
    "Warn and report usage of incorrect webidl inheritance"
);

pub(crate) struct WebIdlPass {
    symbols: crate::Symbols,
}

#[derive(Clone, Debug)]
pub struct ParentMismatchError {
    name: String,
    rust_parent: String,
    webidl_parent: String,
}

impl fmt::Display for ParentMismatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "webidl-rust inheritance mismatch, rust: {:?}, rust parent: {:?}, webidl parent: {:?}",
            self.name, self.rust_parent, self.webidl_parent
        )
    }
}

impl Error for ParentMismatchError {
    fn description(&self) -> &str {
        "ParentMismatchError"
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl WebIdlPass {
    pub fn new(symbols: crate::Symbols) -> WebIdlPass {
        WebIdlPass { symbols }
    }
}

fn get_ty_name(ty: &str) -> &str {
    if let Some(i) = ty.rfind(':') {
        &ty[i + 1..]
    } else {
        ty
    }
}

fn get_manifest_dir() -> io::Result<path::PathBuf> {
    match env::var("CARGO_MANIFEST_DIR") {
        Ok(var) => {
            let mut dir = path::PathBuf::new();
            dir.push(var);
            Ok(dir)
        },
        Err(env::VarError::NotPresent) => {
            Err(io::Error::new(io::ErrorKind::NotFound, "CARGO_MANIFEST_DIR environment variable was not found"))
        },
        Err(env::VarError::NotUnicode(_)) => {
            Err(io::Error::new(io::ErrorKind::InvalidData, "CARGO_MANIFEST_DIR environment variable's contents are non valid UTF-8"))
        },
    }
}

fn get_webidl_path(struct_name: &str) -> io::Result<path::PathBuf> {
    let mut dir = get_manifest_dir()?;
    dir.push("dom/webidls/");
    dir.push(format!("{}.webidl", struct_name));

    Ok(dir)
}

fn is_webidl_ty(symbols: &crate::Symbols, cx: &LateContext, ty: &ty::TyS) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::Adt(did, _substs) => {
                if cx.tcx.has_attr(did.did, symbols.webidl) {
                    ret = true;
                }
                false
            },
            ty::Ref(..) => false,    // don't recurse down &ptrs
            ty::RawPtr(..) => false, // don't recurse down *ptrs
            ty::FnDef(..) | ty::FnPtr(_) => false,
            _ => true,
        }
    });
    ret
}

fn check_inherits(code: &str, name: &str, parent_name: &str) -> Result<(), Box<Error>> {
    let idl = weedle::parse(code).expect("Invalid webidl provided");
    let mut inherits = "";

    for def in idl {
        if let weedle::Definition::Interface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        } else if let weedle::Definition::CallbackInterface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        }
    }

    if inherits == parent_name {
        return Ok(());
    }

    // If there is no parent, first field must be of type Reflector.
    if inherits == "" && parent_name == "Reflector" {
        return Ok(());
    }

    if inherits == "" &&
        name == "PaintRenderingContext2D" &&
        parent_name == "CanvasRenderingContext2D"
    {
        // PaintRenderingContext2D embeds a CanvasRenderingContext2D
        // instead of a Reflector as an optimization,
        // but this is fine since CanvasRenderingContext2D
        // also has a reflector
        return Ok(());
    }

    Err(boxed::Box::from(ParentMismatchError {
        name: name.to_string(),
        rust_parent: parent_name.to_string(),
        webidl_parent: inherits.to_string(),
    }))
}

fn check_webidl(name: &str, parent_name: &Option<String>) -> Result<(), Box<Error>> {
    let path = get_webidl_path(&name)?;
    if let Some(parent) = parent_name {
        let code = fs::read_to_string(path)?;
        return check_inherits(&code, name, &parent);
    }

    Ok(())
}

impl LintPass for WebIdlPass {
    fn name(&self) -> &'static str {
        "ServoWebIDLPass"
    }

    fn get_lints(&self) -> LintArray {
        lint_array!(WEBIDL_INHERIT_CORRECT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WebIdlPass {
    fn check_struct_def(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        def: &'tcx hir::VariantData,
        n: ast::Name,
        _gen: &'tcx hir::Generics,
        id: HirId,
    ) {
        let def_id = cx.tcx.hir().local_def_id_from_hir_id(id);
        if !is_webidl_ty(&self.symbols, cx, cx.tcx.type_of(def_id)) {
            return;
        }

        let item = match cx.tcx.hir().get_by_hir_id(id) {
            hir::Node::Item(item) => item,
            _ => cx
                .tcx
                .hir()
                .expect_item_by_hir_id(cx.tcx.hir().get_parent_item(id)),
        };

        let parent_name = def.fields().iter().next().map(|field| {
            let def_id = cx.tcx.hir().local_def_id_from_hir_id(field.hir_id);
            let ty = cx.tcx.type_of(def_id).to_string();
            get_ty_name(&ty).to_string()
        });

        let struct_name = n.to_string();
        match check_webidl(&struct_name, &parent_name) {
            Ok(()) => {},
            Err(e) => {
                let description = format!("{}", e);
                cx.span_lint(WEBIDL_INHERIT_CORRECT, item.ident.span, &description)
            },
        };
    }
}
<|endoftext|>