<commit_before>use std;
use std::io::{Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::sys::signal;
use nix::sys::termios;

use char_iter;
use consts::{self, KeyPress};
use ::Result;
use ::error;

pub type Mode = termios::Termios;
const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns() -> usize {
    let (cols, _) = get_win_size();
    cols
}

/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows() -> usize {
    let (_, rows) = get_win_size();
    rows
}

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = (*iter).eq_ignore_ascii_case(&term)
            }
            unsupported
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

/// Enable RAW mode for the terminal.
pub fn enable_raw_mode() -> Result<Mode> {
    use nix::errno::Errno::ENOTTY;
    use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                            /* OPOST, */ VMIN, VTIME};
    if !is_a_tty(STDIN_FILENO) {
        try!(Err(nix::Error::from_errno(ENOTTY)));
    }
    let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
    let mut raw = original_mode;
    // disable BREAK interrupt, CR to NL conversion on input,
    // input parity check, strip high bit (bit 8), output flow control
    raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    // we don't want raw output, it turns newlines into straight linefeeds
    // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
    raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
    // disable echoing, canonical mode, extended input processing and signals
    raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 1; // One character-at-a-time input
    raw.c_cc[VTIME] = 0; // with blocking read
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
    Ok(original_mode)
}

/// Disable RAW mode for the terminal.
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &original_mode));
    Ok(())
}

fn clear_screen(w: &mut Write) -> Result<()> {
    try!(w.write_all(b"\x1b[H\x1b[2J"));
    try!(w.flush());
    Ok(())
}

/// Console input reader
pub struct RawReader<R> {
    chars: char_iter::Chars<R>,
}

impl<R: Read> RawReader<R> {
    pub fn new(stdin: R) -> Result<RawReader<R>> {
        Ok(RawReader { chars: char_iter::chars(stdin) })
    }

    // As there is no read timeout to properly handle single ESC key,
    // we make possible to deactivate escape sequence processing.
    pub fn next_key(&mut self, esc_seq: bool) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if esc_seq && key == KeyPress::Esc {
            // escape sequence
            key = try!(self.escape_sequence());
        }
        Ok(key)
    }

    pub fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlag::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: signal::SigNum) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone, Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl PosixTerminal {
    pub fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    pub fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    pub fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Get the number of columns in the current terminal.
    pub fn get_columns(&self) -> usize {
        get_columns()
    }

    /// Get the number of rows in the current terminal.
    pub fn get_rows(&self) -> usize {
        get_rows()
    }

    /// Create a RAW reader
    pub fn create_reader(&self) -> Result<RawReader<std::io::Stdin>> {
        RawReader::new(std::io::stdin())
    }

    /// Check if a SIGWINCH signal has been received
    pub fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    pub fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        clear_screen(w)
    }
}
<commit_msg>fix is_unsupported_term<commit_after>use std;
use std::io::{Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::sys::signal;
use nix::sys::termios;

use char_iter;
use consts::{self, KeyPress};
use ::Result;
use ::error;

pub type Mode = termios::Termios;
const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns() -> usize {
    let (cols, _) = get_win_size();
    cols
}

/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows() -> usize {
    let (_, rows) = get_win_size();
    rows
}

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            for iter in &UNSUPPORTED_TERM {
                if (*iter).eq_ignore_ascii_case(&term) {
                    return true
                }
            }
            false
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

/// Enable RAW mode for the terminal.
pub fn enable_raw_mode() -> Result<Mode> {
    use nix::errno::Errno::ENOTTY;
    use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                            /* OPOST, */ VMIN, VTIME};
    if !is_a_tty(STDIN_FILENO) {
        try!(Err(nix::Error::from_errno(ENOTTY)));
    }
    let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
    let mut raw = original_mode;
    // disable BREAK interrupt, CR to NL conversion on input,
    // input parity check, strip high bit (bit 8), output flow control
    raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    // we don't want raw output, it turns newlines into straight linefeeds
    // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
    raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
    // disable echoing, canonical mode, extended input processing and signals
    raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 1; // One character-at-a-time input
    raw.c_cc[VTIME] = 0; // with blocking read
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
    Ok(original_mode)
}

/// Disable RAW mode for the terminal.
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &original_mode));
    Ok(())
}

fn clear_screen(w: &mut Write) -> Result<()> {
    try!(w.write_all(b"\x1b[H\x1b[2J"));
    try!(w.flush());
    Ok(())
}

/// Console input reader
pub struct RawReader<R> {
    chars: char_iter::Chars<R>,
}

impl<R: Read> RawReader<R> {
    pub fn new(stdin: R) -> Result<RawReader<R>> {
        Ok(RawReader { chars: char_iter::chars(stdin) })
    }

    // As there is no read timeout to properly handle single ESC key,
    // we make possible to deactivate escape sequence processing.
    pub fn next_key(&mut self, esc_seq: bool) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if esc_seq && key == KeyPress::Esc {
            // escape sequence
            key = try!(self.escape_sequence());
        }
        Ok(key)
    }

    pub fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlag::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: signal::SigNum) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone, Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl PosixTerminal {
    pub fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    pub fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    pub fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Get the number of columns in the current terminal.
    pub fn get_columns(&self) -> usize {
        get_columns()
    }

    /// Get the number of rows in the current terminal.
    pub fn get_rows(&self) -> usize {
        get_rows()
    }

    /// Create a RAW reader
    pub fn create_reader(&self) -> Result<RawReader<std::io::Stdin>> {
        RawReader::new(std::io::stdin())
    }

    /// Check if a SIGWINCH signal has been received
    pub fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    pub fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        clear_screen(w)
    }
}

#[cfg(all(unix,test))]
mod test {
    #[test]
    fn test_unsupported_term() {
        ::std::env::set_var("TERM", "xterm");
        assert_eq!(false, super::is_unsupported_term());

        ::std::env::set_var("TERM", "dumb");
        assert_eq!(true, super::is_unsupported_term());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <https://opensource.org/licenses/MIT>

use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::slice;

use enums::{Format, SurfaceType};
use error::Error;
use ffi;
#[cfg(feature = "use_glib")]
use glib::translate::*;

use std::fmt;
use surface::Surface;
use utils::status_to_result;
use BorrowError;

declare_surface!(ImageSurface, SurfaceType::Image);

impl ImageSurface {
    pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Error> {
        unsafe {
            Self::from_raw_full(ffi::cairo_image_surface_create(
                format.into(),
                width,
                height,
            ))
        }
    }

    /**
     * Creates an image surface for the provided pixel data.
     * The pointer `data` is the beginning of the underlying slice,
     * and at least `width * stride` succeeding bytes should be allocated.
     * Additionally, `data` must live longer than any reference to the returned surface.
     */
    pub unsafe fn create_for_data_unsafe(
        data: *mut u8,
        format: Format,
        width: i32,
        height: i32,
        stride: i32,
    ) -> Result<ImageSurface, Error> {
        let data = std::slice::from_raw_parts_mut(data, (stride * height) as usize);
        Self::create_for_data(data, format, width, height, stride)
    }

    pub fn create_for_data<D: AsMut<[u8]> + 'static>(
        data: D,
        format: Format,
        width: i32,
        height: i32,
        stride: i32,
    ) -> Result<ImageSurface, Error> {
        let mut data: Box<dyn AsMut<[u8]>> = Box::new(data);

        let (ptr, len) = {
            let data: &mut [u8] = (*data).as_mut();

            (data.as_mut_ptr(), data.len())
        };

        assert!(len >= (height * stride) as usize);
        let result = unsafe {
            ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
                ptr,
                format.into(),
                width,
                height,
                stride,
            ))
        };
        if let Ok(surface) = &result {
            static IMAGE_SURFACE_DATA: crate::UserDataKey<Box<dyn AsMut<[u8]>>> =
                crate::UserDataKey::new();
            surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data))
        }
        result
    }

    pub fn get_data(&mut self) -> Result<ImageSurfaceData, BorrowError> {
        unsafe {
            if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
                return Err(BorrowError::NonExclusive);
            }

            self.flush();
            let status = ffi::cairo_surface_status(self.to_raw_none());
            if let Some(err) = status_to_result(status).err() {
                return Err(BorrowError::from(err));
            }
            if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(self)
            {
                return Err(BorrowError::from(Error::SurfaceFinished));
            }
            Ok(ImageSurfaceData::new(self))
        }
    }

    pub fn with_data<F: FnOnce(&[u8])>(&self, f: F) -> Result<(), BorrowError> {
        self.flush();
        unsafe {
            let status = ffi::cairo_surface_status(self.to_raw_none());
            if let Some(err) = status_to_result(status).err() {
                return Err(BorrowError::from(err));
            }
            let ptr = ffi::cairo_image_surface_get_data(self.to_raw_none());
            if ptr.is_null() || is_finished(self) {
                return Err(BorrowError::from(Error::SurfaceFinished));
            }
            let len = self.get_height() as usize * self.get_stride() as usize;
            f(slice::from_raw_parts(ptr, len));
        }
        Ok(())
    }

    pub fn get_format(&self) -> Format {
        unsafe { Format::from(ffi::cairo_image_surface_get_format(self.to_raw_none())) }
    }

    pub fn get_height(&self) -> i32 {
        unsafe { ffi::cairo_image_surface_get_height(self.to_raw_none()) }
    }

    pub fn get_stride(&self) -> i32 {
        unsafe { ffi::cairo_image_surface_get_stride(self.to_raw_none()) }
    }

    pub fn get_width(&self) -> i32 {
        unsafe { ffi::cairo_image_surface_get_width(self.to_raw_none()) }
    }
}

#[derive(Debug)]
pub struct ImageSurfaceData<'a> {
    surface: &'a mut ImageSurface,
    slice: &'a mut [u8],
    dirty: bool,
}

impl<'a> ImageSurfaceData<'a> {
    fn new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a> {
        unsafe {
            let ptr = ffi::cairo_image_surface_get_data(surface.to_raw_none());
            debug_assert!(!ptr.is_null());
            let len = (surface.get_stride() as usize) * (surface.get_height() as usize);
            ImageSurfaceData {
                surface,
                slice: slice::from_raw_parts_mut(ptr, len),
                dirty: false,
            }
        }
    }
}

impl<'a> Drop for ImageSurfaceData<'a> {
    fn drop(&mut self) {
        if self.dirty {
            self.surface.mark_dirty()
        }
    }
}

impl<'a> Deref for ImageSurfaceData<'a> {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        self.slice
    }
}

impl<'a> DerefMut for ImageSurfaceData<'a> {
    fn deref_mut(&mut self) -> &mut [u8] {
        self.dirty = true;
        self.slice
    }
}

impl<'a> fmt::Display for ImageSurfaceData<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ImageSurfaceData")
    }
}

// Workaround for cairo not having a direct way to check if the surface is finished.
// See: https://gitlab.freedesktop.org/cairo/cairo/-/issues/406
fn is_finished(surface: &ImageSurface) -> bool {
    use super::Context;
    let ctxt = Context::new(surface);
    ctxt.status().is_err()
}

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

    #[test]
    fn create_with_invalid_size_yields_error() {
        let result = ImageSurface::create(Format::ARgb32, 50000, 50000);
        assert!(result.is_err());
    }

    #[test]
    fn create_for_data_with_invalid_stride_yields_error() {
        let result = ImageSurface::create_for_data(vec![0u8; 10], Format::ARgb32, 1, 2, 5); // unaligned stride
        assert!(result.is_err());
    }

    #[test]
    fn create_with_valid_size() {
        let result = ImageSurface::create(Format::ARgb32, 10, 10);
        assert!(result.is_ok());

        let result = ImageSurface::create_for_data(vec![0u8; 40 * 10], Format::ARgb32, 10, 10, 40);
        assert!(result.is_ok());
    }

    #[test]
    fn no_crash_after_finish() {
        let mut surf = ImageSurface::create(Format::ARgb32, 1024, 1024).unwrap();

        surf.finish();

        assert!(surf.get_data().is_err());
    }
}
<commit_msg>Update document & simplify code<commit_after>// Copyright 2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <https://opensource.org/licenses/MIT>

use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::slice;

use enums::{Format, SurfaceType};
use error::Error;
use ffi;
#[cfg(feature = "use_glib")]
use glib::translate::*;

use std::fmt;
use surface::Surface;
use utils::status_to_result;
use BorrowError;

declare_surface!(ImageSurface, SurfaceType::Image);

impl ImageSurface {
    pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Error> {
        unsafe {
            Self::from_raw_full(ffi::cairo_image_surface_create(
                format.into(),
                width,
                height,
            ))
        }
    }

    /// Creates an image surface for the provided pixel data.
    /// - The pointer `data` is the beginning of the underlying slice,
    ///   and at least `width * stride` succeeding bytes should be allocated.
    /// - `data` must live longer than any reference to the returned surface.
    /// - You have to free `data` by yourself.
    pub unsafe fn create_for_data_unsafe(
        data: *mut u8,
        format: Format,
        width: i32,
        height: i32,
        stride: i32,
    ) -> Result<ImageSurface, Error> {
        ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
            data,
            format.into(),
            width,
            height,
            stride,
        ))
    }

    pub fn create_for_data<D: AsMut<[u8]> + 'static>(
        data: D,
        format: Format,
        width: i32,
        height: i32,
        stride: i32,
    ) -> Result<ImageSurface, Error> {
        let mut data: Box<dyn AsMut<[u8]>> = Box::new(data);

        let (ptr, len) = {
            let data: &mut [u8] = (*data).as_mut();

            (data.as_mut_ptr(), data.len())
        };

        assert!(len >= (height * stride) as usize);
        let result = unsafe {
            ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
                ptr,
                format.into(),
                width,
                height,
                stride,
            ))
        };
        if let Ok(surface) = &result {
            static IMAGE_SURFACE_DATA: crate::UserDataKey<Box<dyn AsMut<[u8]>>> =
                crate::UserDataKey::new();
            surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data))
        }
        result
    }

    pub fn get_data(&mut self) -> Result<ImageSurfaceData, BorrowError> {
        unsafe {
            if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
                return Err(BorrowError::NonExclusive);
            }

            self.flush();
            let status = ffi::cairo_surface_status(self.to_raw_none());
            if let Some(err) = status_to_result(status).err() {
                return Err(BorrowError::from(err));
            }
            if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(self)
            {
                return Err(BorrowError::from(Error::SurfaceFinished));
            }
            Ok(ImageSurfaceData::new(self))
        }
    }

    pub fn with_data<F: FnOnce(&[u8])>(&self, f: F) -> Result<(), BorrowError> {
        self.flush();
        unsafe {
            let status = ffi::cairo_surface_status(self.to_raw_none());
            if let Some(err) = status_to_result(status).err() {
                return Err(BorrowError::from(err));
            }
            let ptr = ffi::cairo_image_surface_get_data(self.to_raw_none());
            if ptr.is_null() || is_finished(self) {
                return Err(BorrowError::from(Error::SurfaceFinished));
            }
            let len = self.get_height() as usize * self.get_stride() as usize;
            f(slice::from_raw_parts(ptr, len));
        }
        Ok(())
    }

    pub fn get_format(&self) -> Format {
        unsafe { Format::from(ffi::cairo_image_surface_get_format(self.to_raw_none())) }
    }

    pub fn get_height(&self) -> i32 {
        unsafe { ffi::cairo_image_surface_get_height(self.to_raw_none()) }
    }

    pub fn get_stride(&self) -> i32 {
        unsafe { ffi::cairo_image_surface_get_stride(self.to_raw_none()) }
    }

    pub fn get_width(&self) -> i32 {
        unsafe { ffi::cairo_image_surface_get_width(self.to_raw_none()) }
    }
}

#[derive(Debug)]
pub struct ImageSurfaceData<'a> {
    surface: &'a mut ImageSurface,
    slice: &'a mut [u8],
    dirty: bool,
}

impl<'a> ImageSurfaceData<'a> {
    fn new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a> {
        unsafe {
            let ptr = ffi::cairo_image_surface_get_data(surface.to_raw_none());
            debug_assert!(!ptr.is_null());
            let len = (surface.get_stride() as usize) * (surface.get_height() as usize);
            ImageSurfaceData {
                surface,
                slice: slice::from_raw_parts_mut(ptr, len),
                dirty: false,
            }
        }
    }
}

impl<'a> Drop for ImageSurfaceData<'a> {
    fn drop(&mut self) {
        if self.dirty {
            self.surface.mark_dirty()
        }
    }
}

impl<'a> Deref for ImageSurfaceData<'a> {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        self.slice
    }
}

impl<'a> DerefMut for ImageSurfaceData<'a> {
    fn deref_mut(&mut self) -> &mut [u8] {
        self.dirty = true;
        self.slice
    }
}

impl<'a> fmt::Display for ImageSurfaceData<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ImageSurfaceData")
    }
}

// Workaround for cairo not having a direct way to check if the surface is finished.
// See: https://gitlab.freedesktop.org/cairo/cairo/-/issues/406
fn is_finished(surface: &ImageSurface) -> bool {
    use super::Context;
    let ctxt = Context::new(surface);
    ctxt.status().is_err()
}

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

    #[test]
    fn create_with_invalid_size_yields_error() {
        let result = ImageSurface::create(Format::ARgb32, 50000, 50000);
        assert!(result.is_err());
    }

    #[test]
    fn create_for_data_with_invalid_stride_yields_error() {
        let result = ImageSurface::create_for_data(vec![0u8; 10], Format::ARgb32, 1, 2, 5); // unaligned stride
        assert!(result.is_err());
    }

    #[test]
    fn create_with_valid_size() {
        let result = ImageSurface::create(Format::ARgb32, 10, 10);
        assert!(result.is_ok());

        let result = ImageSurface::create_for_data(vec![0u8; 40 * 10], Format::ARgb32, 10, 10, 40);
        assert!(result.is_ok());
    }

    #[test]
    fn no_crash_after_finish() {
        let mut surf = ImageSurface::create(Format::ARgb32, 1024, 1024).unwrap();

        surf.finish();

        assert!(surf.get_data().is_err());
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate url;

use hyper::{HttpError, HttpResult, Url};
use hyper::client::Request;
use hyper::header::ContentLength;
use url::form_urlencoded;

fn get_content(url: &str) -> HttpResult<String> {
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let fresh_request = try!(Request::get(url));
    let streaming_request = try!(fresh_request.start());
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

    type Query<'a> = Vec<(&'a str, &'a str)>;

    fn post_query(url: &str, query: Query) -> HttpResult<String> {
        let url = match Url::parse(url) {
            Ok(url) => url,
            Err(_) => return Err(HttpError::HttpUriError),
        };
        let body = form_urlencoded::serialize(query.into_iter());
        let mut fresh_request = try!(Request::post(url));
        fresh_request.headers_mut().set(ContentLength(body.len()));
        let mut streaming_request = try!(fresh_request.start());
        try!(streaming_request.write_str(body[]));
        let mut response = try!(streaming_request.send());
        Ok(try!(response.read_to_string()))
    }

fn main() {
    println!("24 days of Rust - hyper (day 5)");
    println!("{}", get_content("http://httpbin.org/status/200"));
    let query = vec![("key", "value"), ("foo", "bar")];
    println!("{}", post_query("http://httpbin.org/post", query));
}
<commit_msg>Fixed indentation.<commit_after>extern crate hyper;
extern crate url;

use hyper::{HttpError, HttpResult, Url};
use hyper::client::Request;
use hyper::header::ContentLength;
use url::form_urlencoded;

fn get_content(url: &str) -> HttpResult<String> {
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let fresh_request = try!(Request::get(url));
    let streaming_request = try!(fresh_request.start());
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

type Query<'a> = Vec<(&'a str, &'a str)>;

fn post_query(url: &str, query: Query) -> HttpResult<String> {
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let body = form_urlencoded::serialize(query.into_iter());
    let mut fresh_request = try!(Request::post(url));
    fresh_request.headers_mut().set(ContentLength(body.len()));
    let mut streaming_request = try!(fresh_request.start());
    try!(streaming_request.write_str(body[]));
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

fn main() {
    println!("24 days of Rust - hyper (day 5)");
    println!("{}", get_content("http://httpbin.org/status/200"));
    let query = vec![("key", "value"), ("foo", "bar")];
    println!("{}", post_query("http://httpbin.org/post", query));
}
<|endoftext|>


<commit_before>use super::btree::{BPlusTree, RTCursor as BPlusTreeCursor};
use super::sstable::*;
use super::*;
use client::AsyncClient;
use itertools::Itertools;
use parking_lot::RwLock;
use ram::segs::MAX_SEGMENT_SIZE;
use std::collections::BTreeSet;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::{mem, ptr};

const LEVEL_M_MAX_ELEMENTS_COUNT: usize = LEVEL_M * LEVEL_M * LEVEL_M;
const LEVEL_ELEMENTS_MULTIPLIER: usize = 10;
const LEVEL_DIFF_MULTIPLIER: usize = 10;
const LEVEL_M: usize = super::btree::NUM_KEYS;
const LEVEL_1: usize = LEVEL_M * LEVEL_DIFF_MULTIPLIER;
const LEVEL_2: usize = LEVEL_1 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_3: usize = LEVEL_2 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_4: usize = LEVEL_3 * LEVEL_DIFF_MULTIPLIER;

type LevelTrees = Vec<Box<SSLevelTree>>;

macro_rules! with_levels {
    ($($sym:ident, $level_size:ident;)*) => {
        $(
            type $sym = [EntryKey; $level_size];
            impl_sspage_slice!($sym, EntryKey, $level_size);
        )*

        fn init_lsm_level_trees(neb_client: &Arc<AsyncClient>) -> (LevelTrees, Vec<usize>, Vec<usize>) {
            let mut trees = LevelTrees::new();
            let mut max_elements_list = Vec::new();
            let mut max_elements = LEVEL_M_MAX_ELEMENTS_COUNT;
            let mut page_sizes = Vec::new();
            $(
                max_elements *= LEVEL_ELEMENTS_MULTIPLIER;
                trees.push(box LevelTree::<$sym>::new(neb_client));
                max_elements_list.push(max_elements);
                page_sizes.push($level_size);
                const_assert!($level_size * KEY_SIZE <= MAX_SEGMENT_SIZE);
            )*
            return (trees, max_elements_list, page_sizes);
        }
    };
}

with_levels!{
    L1, LEVEL_1;
    L2, LEVEL_2;
    L3, LEVEL_3;
    L4, LEVEL_4;
}

pub struct LSMTree {
    level_m: BPlusTree,
    trees: LevelTrees,
    // use Vec here for convenience
    max_sizes: Vec<usize>,
    page_sizes: Vec<usize>,
}

unsafe impl Send for LSMTree {}
unsafe impl Sync for LSMTree {}

impl LSMTree {
    pub fn new(neb_client: &Arc<AsyncClient>) -> Arc<Self> {
        let (trees, max_sizes, page_sizes) = init_lsm_level_trees(neb_client);
        let lsm_tree = Arc::new(LSMTree {
            level_m: BPlusTree::new(neb_client),
            trees,
            max_sizes,
            page_sizes,
        });
        let tree_clone = lsm_tree.clone();
        thread::spawn(move || {
            tree_clone.sentinel();
        });
        lsm_tree
    }

    pub fn insert(&self, mut key: EntryKey, id: &Id) -> Result<(), ()> {
        key_with_id(&mut key, id);
        self.level_m.insert(&key).map_err(|e| ())
    }

    pub fn remove(&self, mut key: EntryKey, id: &Id) -> Result<bool, ()> {
        key_with_id(&mut key, id);
        let m_deleted = self.level_m.remove(&key).map_err(|e| ())?;
        let levels_deleted = self
            .trees
            .iter()
            .map(|tree| tree.mark_deleted(&key))
            .collect_vec() // collect here to prevent short circuit
            .into_iter()
            .any(|d| d);
        Ok(m_deleted || levels_deleted)
    }

    pub fn seek(&self, mut key: EntryKey, ordering: Ordering) -> LSMTreeCursor {
        match ordering {
            Ordering::Forward => key_with_id(&mut key, &Id::unit_id()),
            Ordering::Backward => key_with_id(&mut key, &Id::new(::std::u64::MAX, ::std::u64::MAX)),
        };
        let mut cursors = vec![self.level_m.seek(&key, ordering).unwrap()];
        for tree in &self.trees {
            cursors.push(tree.seek(&key, ordering));
        }
        return LSMTreeCursor::new(
            cursors,
            self.trees.iter().map(|t| t.tombstones()).collect_vec(),
        );
    }

    fn sentinel(&self) {
        self.check_and_merge(
            LEVEL_M_MAX_ELEMENTS_COUNT,
            self.page_sizes[0],
            &mut BTreeSet::new(),
            &self.level_m,
            &*self.trees[0],
        );
        for i in 0..self.trees.len() - 1 {
            let upper = &*self.trees[i];
            let lower = &*self.trees[i + 1];
            let upper_tombstons_ref = upper.tombstones();
            let mut upper_tombstones = upper_tombstons_ref.write();
            self.check_and_merge(
                self.max_sizes[i],
                self.page_sizes[i + 1],
                &mut *upper_tombstones,
                upper,
                lower,
            );
        }
        thread::sleep(Duration::from_millis(100));
    }

    fn check_and_merge<U, L>(
        &self,
        max_elements: usize,
        upper_page_size: usize,
        upper_tombstones: &mut TombstonesInner,
        upper_level: &U,
        lower_level: &L,
    ) where
        U: MergeableTree + ?Sized,
        L: SSLevelTree + ?Sized,
    {
        let elements = upper_level.elements();
        if elements > max_elements {
            debug!(
                "upper level have elements {} exceeds {}, start level merging",
                elements, max_elements
            );
            let entries_to_merge = upper_page_size;
            let guard = upper_level.prepare_level_merge();
            let mut pages = vec![guard.last_page()];
            let mut collected_entries = pages.last().unwrap().keys().len();
            while collected_entries < entries_to_merge {
                let next_page = pages.last().unwrap().next();
                collected_entries += next_page.keys().len();
                pages.push(next_page);
            }
            let mut entries = vec![];
            for page in &pages {
                entries.append(&mut Vec::from(page.keys()));
            }
            debug!("going to merge {} entries", entries.len());
            lower_level.merge(entries.as_mut_slice(), upper_tombstones);
            guard.remove_pages(pages.iter().map(|p| p.keys()).collect_vec().as_slice())
        }
    }

    pub fn len(&self) -> usize {
        let mem_len = self.level_m.len();
        let levels_len_sum = self.trees.iter().map(|tree| tree.len()).sum::<usize>();
        return mem_len + levels_len_sum;
    }
}

pub struct LSMTreeCursor {
    level_cursors: Vec<Box<Cursor>>,
    tombstones: Vec<Arc<Tombstones>>,
}

impl LSMTreeCursor {
    fn new(cursors: Vec<Box<Cursor>>, tombstones: Vec<Arc<Tombstones>>) -> Self {
        LSMTreeCursor {
            tombstones,
            level_cursors: cursors,
        }
    }

    fn next_candidate(&mut self) -> (bool, usize) {
        let min_tree = self
            .level_cursors
            .iter()
            .enumerate()
            .map(|(i, cursor)| (i, cursor.current()))
            .filter_map(|(i, current)| current.map(|current_val| (i, current_val)))
            .min_by_key(|(i, val)| *val)
            .map(|(id, _)| id);
        if let Some(id) = min_tree {
            let min_has_next = self.level_cursors[id].next();
            if !min_has_next {
                let next = self
                    .level_cursors
                    .iter()
                    .enumerate()
                    .filter_map(|(level, cursor)| cursor.current().map(|key| (id, key)))
                    .min_by_key(|(level, key)| *key);
                if let Some((id, _)) = next {
                    return (true, id);
                }
            } else {
                return (true, id);
            }
        }
        return (false, 0);
    }
}

impl Cursor for LSMTreeCursor {
    fn next(&mut self) -> bool {
        loop {
            let (has_next, candidate_level) = self.next_candidate();
            if !has_next {
                break;
            }
            if candidate_level == 0 {
                return has_next;
            }
            let candidate_key = self.level_cursors[candidate_level].current().unwrap();
            if self.tombstones[candidate_level + 1]
                .read()
                .contains(candidate_key)
            {
                // marked in tombstone, skip
                continue;
            }
            return true;
        }
        return false;
    }

    fn current(&self) -> Option<&EntryKey> {
        self.level_cursors.iter().filter_map(|c| c.current()).min()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use byteorder::BigEndian;
    use byteorder::WriteBytesExt;
    use client;
    use futures::prelude::*;
    use rand::distributions::Uniform;
    use rand::thread_rng;
    use rand::Rng;
    use rayon::prelude::*;
    use server::NebServer;
    use server::ServerOptions;
    use std::env;
    use std::io::Cursor as StdCursor;

    fn u64_to_slice(n: u64) -> [u8; 8] {
        let mut key_slice = [0u8; 8];
        {
            let mut cursor = StdCursor::new(&mut key_slice[..]);
            cursor.write_u64::<BigEndian>(n);
        };
        key_slice
    }

    #[test]
    pub fn insertions() {
        env_logger::init();
        let server_group = "sstable_index_init";
        let server_addr = String::from("127.0.0.1:5300");
        let server = NebServer::new_from_opts(
            &ServerOptions {
                chunk_count: 1,
                memory_size: 4 * 1024 * 1024 * 1024,
                backup_storage: None,
                wal_storage: None,
            },
            &server_addr,
            &server_group,
        );
        let client = Arc::new(
            client::AsyncClient::new(&server.rpc, &vec![server_addr], server_group).unwrap(),
        );
        client.new_schema_with_id(super::get_schema()).wait();
        let tree = LSMTree::new(&client);
        let num = env::var("LSM_TREE_TEST_ITEMS")
            // this value cannot do anything useful to the test
            // must arrange a long-term test to cover every levels
            .unwrap_or("1000".to_string())
            .parse::<u64>()
            .unwrap();

        (0..num).collect::<Vec<_>>().par_iter().for_each(|i| {
            let i = *i;
            let id = Id::new(0, i);
            let key_slice = u64_to_slice(i);
            let key = SmallVec::from_slice(&key_slice);
            tree.insert(key, &id).unwrap();
        });

        if tree.len() > LEVEL_M_MAX_ELEMENTS_COUNT {
            debug!("Sleep 5 minute for level merge");
            thread::sleep(Duration::from_secs(5 * 60));
        }

        debug!("Start validations");
        (0..num).collect::<Vec<_>>().par_iter().for_each(|i| {
            let mut rng = rand::rngs::OsRng::new().unwrap();
            let die_range = Uniform::new_inclusive(1, 6);
            let mut roll_die = rng.sample_iter(&die_range);
            let i = *i;
            let id = Id::new(0, i);
            let key_slice = u64_to_slice(i);
            let mut key = SmallVec::from_slice(&key_slice);
            debug!("checking: {}", i);
            let mut cursor = tree.seek(key.clone(), Ordering::Forward);
            key_with_id(&mut key, &id);
            assert_eq!(cursor.current(), Some(&key), "{}", i);
            if roll_die.next().unwrap() == 6 {
                for j in i..num {
                    let id = Id::new(0, j);
                    let key_slice = u64_to_slice(j);
                    let mut key = SmallVec::from_slice(&key_slice);
                    key_with_id(&mut key, &id);
                    assert_eq!(cursor.current(), Some(&key), "{}/{}", i, j);
                    assert_eq!(cursor.next(), j != num - 1, "{}/{}", i, j);
                }
            }
        });
    }
}
<commit_msg>tree elements counter thread<commit_after>use super::btree::{BPlusTree, RTCursor as BPlusTreeCursor};
use super::sstable::*;
use super::*;
use client::AsyncClient;
use itertools::Itertools;
use parking_lot::RwLock;
use ram::segs::MAX_SEGMENT_SIZE;
use std::collections::BTreeSet;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::{mem, ptr};

const LEVEL_M_MAX_ELEMENTS_COUNT: usize = LEVEL_M * LEVEL_M * LEVEL_M;
const LEVEL_ELEMENTS_MULTIPLIER: usize = 10;
const LEVEL_DIFF_MULTIPLIER: usize = 10;
const LEVEL_M: usize = super::btree::NUM_KEYS;
const LEVEL_1: usize = LEVEL_M * LEVEL_DIFF_MULTIPLIER;
const LEVEL_2: usize = LEVEL_1 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_3: usize = LEVEL_2 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_4: usize = LEVEL_3 * LEVEL_DIFF_MULTIPLIER;

type LevelTrees = Vec<Box<SSLevelTree>>;

macro_rules! with_levels {
    ($($sym:ident, $level_size:ident;)*) => {
        $(
            type $sym = [EntryKey; $level_size];
            impl_sspage_slice!($sym, EntryKey, $level_size);
        )*

        fn init_lsm_level_trees(neb_client: &Arc<AsyncClient>) -> (LevelTrees, Vec<usize>, Vec<usize>) {
            let mut trees = LevelTrees::new();
            let mut max_elements_list = Vec::new();
            let mut max_elements = LEVEL_M_MAX_ELEMENTS_COUNT;
            let mut page_sizes = Vec::new();
            $(
                max_elements *= LEVEL_ELEMENTS_MULTIPLIER;
                trees.push(box LevelTree::<$sym>::new(neb_client));
                max_elements_list.push(max_elements);
                page_sizes.push($level_size);
                const_assert!($level_size * KEY_SIZE <= MAX_SEGMENT_SIZE);
            )*
            return (trees, max_elements_list, page_sizes);
        }
    };
}

with_levels! {
    L1, LEVEL_1;
    L2, LEVEL_2;
    L3, LEVEL_3;
    L4, LEVEL_4;
}

pub struct LSMTree {
    level_m: BPlusTree,
    trees: LevelTrees,
    // use Vec here for convenience
    max_sizes: Vec<usize>,
    page_sizes: Vec<usize>,
}

unsafe impl Send for LSMTree {}
unsafe impl Sync for LSMTree {}

impl LSMTree {
    pub fn new(neb_client: &Arc<AsyncClient>) -> Arc<Self> {
        let (trees, max_sizes, page_sizes) = init_lsm_level_trees(neb_client);
        let lsm_tree = Arc::new(LSMTree {
            level_m: BPlusTree::new(neb_client),
            trees,
            max_sizes,
            page_sizes,
        });
        let tree_clone = lsm_tree.clone();
        thread::spawn(move || {
            tree_clone.sentinel();
        });
        lsm_tree
    }

    pub fn insert(&self, mut key: EntryKey, id: &Id) -> Result<(), ()> {
        key_with_id(&mut key, id);
        self.level_m.insert(&key).map_err(|e| ())
    }

    pub fn remove(&self, mut key: EntryKey, id: &Id) -> Result<bool, ()> {
        key_with_id(&mut key, id);
        let m_deleted = self.level_m.remove(&key).map_err(|e| ())?;
        let levels_deleted = self
            .trees
            .iter()
            .map(|tree| tree.mark_deleted(&key))
            .collect_vec() // collect here to prevent short circuit
            .into_iter()
            .any(|d| d);
        Ok(m_deleted || levels_deleted)
    }

    pub fn seek(&self, mut key: EntryKey, ordering: Ordering) -> LSMTreeCursor {
        match ordering {
            Ordering::Forward => key_with_id(&mut key, &Id::unit_id()),
            Ordering::Backward => key_with_id(&mut key, &Id::new(::std::u64::MAX, ::std::u64::MAX)),
        };
        let mut cursors = vec![self.level_m.seek(&key, ordering).unwrap()];
        for tree in &self.trees {
            cursors.push(tree.seek(&key, ordering));
        }
        return LSMTreeCursor::new(
            cursors,
            self.trees.iter().map(|t| t.tombstones()).collect_vec(),
        );
    }

    fn sentinel(&self) {
        self.check_and_merge(
            LEVEL_M_MAX_ELEMENTS_COUNT,
            self.page_sizes[0],
            &mut BTreeSet::new(),
            &self.level_m,
            &*self.trees[0],
        );
        for i in 0..self.trees.len() - 1 {
            let upper = &*self.trees[i];
            let lower = &*self.trees[i + 1];
            let upper_tombstons_ref = upper.tombstones();
            let mut upper_tombstones = upper_tombstons_ref.write();
            self.check_and_merge(
                self.max_sizes[i],
                self.page_sizes[i + 1],
                &mut *upper_tombstones,
                upper,
                lower,
            );
        }
        thread::sleep(Duration::from_millis(100));
    }

    fn check_and_merge<U, L>(
        &self,
        max_elements: usize,
        upper_page_size: usize,
        upper_tombstones: &mut TombstonesInner,
        upper_level: &U,
        lower_level: &L,
    ) where
        U: MergeableTree + ?Sized,
        L: SSLevelTree + ?Sized,
    {
        let elements = upper_level.elements();
        if elements > max_elements {
            debug!(
                "upper level have elements {} exceeds {}, start level merging",
                elements, max_elements
            );
            let entries_to_merge = upper_page_size;
            let guard = upper_level.prepare_level_merge();
            let mut pages = vec![guard.last_page()];
            let mut collected_entries = pages.last().unwrap().keys().len();
            while collected_entries < entries_to_merge {
                let next_page = pages.last().unwrap().next();
                collected_entries += next_page.keys().len();
                pages.push(next_page);
            }
            let mut entries = vec![];
            for page in &pages {
                entries.append(&mut Vec::from(page.keys()));
            }
            debug!("going to merge {} entries", entries.len());
            lower_level.merge(entries.as_mut_slice(), upper_tombstones);
            guard.remove_pages(pages.iter().map(|p| p.keys()).collect_vec().as_slice())
        }
    }

    pub fn len(&self) -> usize {
        let mem_len = self.level_m.len();
        let levels_len_sum = self.trees.iter().map(|tree| tree.len()).sum::<usize>();
        return mem_len + levels_len_sum;
    }
}

pub struct LSMTreeCursor {
    level_cursors: Vec<Box<Cursor>>,
    tombstones: Vec<Arc<Tombstones>>,
}

impl LSMTreeCursor {
    fn new(cursors: Vec<Box<Cursor>>, tombstones: Vec<Arc<Tombstones>>) -> Self {
        LSMTreeCursor {
            tombstones,
            level_cursors: cursors,
        }
    }

    fn next_candidate(&mut self) -> (bool, usize) {
        let min_tree = self
            .level_cursors
            .iter()
            .enumerate()
            .map(|(i, cursor)| (i, cursor.current()))
            .filter_map(|(i, current)| current.map(|current_val| (i, current_val)))
            .min_by_key(|(i, val)| *val)
            .map(|(id, _)| id);
        if let Some(id) = min_tree {
            let min_has_next = self.level_cursors[id].next();
            if !min_has_next {
                let next = self
                    .level_cursors
                    .iter()
                    .enumerate()
                    .filter_map(|(level, cursor)| cursor.current().map(|key| (id, key)))
                    .min_by_key(|(level, key)| *key);
                if let Some((id, _)) = next {
                    return (true, id);
                }
            } else {
                return (true, id);
            }
        }
        return (false, 0);
    }
}

impl Cursor for LSMTreeCursor {
    fn next(&mut self) -> bool {
        loop {
            let (has_next, candidate_level) = self.next_candidate();
            if !has_next {
                break;
            }
            if candidate_level == 0 {
                return has_next;
            }
            let candidate_key = self.level_cursors[candidate_level].current().unwrap();
            if self.tombstones[candidate_level + 1]
                .read()
                .contains(candidate_key)
            {
                // marked in tombstone, skip
                continue;
            }
            return true;
        }
        return false;
    }

    fn current(&self) -> Option<&EntryKey> {
        self.level_cursors.iter().filter_map(|c| c.current()).min()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use byteorder::BigEndian;
    use byteorder::WriteBytesExt;
    use client;
    use futures::prelude::*;
    use rand::distributions::Uniform;
    use rand::thread_rng;
    use rand::Rng;
    use rayon::prelude::*;
    use server::NebServer;
    use server::ServerOptions;
    use std::env;
    use std::io::Cursor as StdCursor;

    fn u64_to_slice(n: u64) -> [u8; 8] {
        let mut key_slice = [0u8; 8];
        {
            let mut cursor = StdCursor::new(&mut key_slice[..]);
            cursor.write_u64::<BigEndian>(n);
        };
        key_slice
    }

    #[test]
    pub fn insertions() {
        env_logger::init();
        let server_group = "sstable_index_init";
        let server_addr = String::from("127.0.0.1:5300");
        let server = NebServer::new_from_opts(
            &ServerOptions {
                chunk_count: 1,
                memory_size: 4 * 1024 * 1024 * 1024,
                backup_storage: None,
                wal_storage: None,
            },
            &server_addr,
            &server_group,
        );
        let client = Arc::new(
            client::AsyncClient::new(&server.rpc, &vec![server_addr], server_group).unwrap(),
        );
        client.new_schema_with_id(super::get_schema()).wait();
        let tree = LSMTree::new(&client);
        let num = env::var("LSM_TREE_TEST_ITEMS")
            // this value cannot do anything useful to the test
            // must arrange a long-term test to cover every levels
            .unwrap_or("1000".to_string())
            .parse::<u64>()
            .unwrap();

        let tree_clone = tree.clone();
        thread::spawn(move || {
            thread::sleep(Duration::from_secs(10));
            let tree_len = tree_clone.len();
            debug!(
                "LSM-Tree now have {}/{} elements, total {:.2}%",
                tree_len,
                num,
                tree_len as f32 / num as f32 * 100.0
            );
        });

        (0..num).collect::<Vec<_>>().par_iter().for_each(|i| {
            let i = *i;
            let id = Id::new(0, i);
            let key_slice = u64_to_slice(i);
            let key = SmallVec::from_slice(&key_slice);
            tree.insert(key, &id).unwrap();
        });

        if tree.len() > LEVEL_M_MAX_ELEMENTS_COUNT {
            debug!("Sleep 5 minute for level merge");
            thread::sleep(Duration::from_secs(5 * 60));
        }

        debug!("Start validations");
        (0..num).collect::<Vec<_>>().par_iter().for_each(|i| {
            let mut rng = rand::rngs::OsRng::new().unwrap();
            let die_range = Uniform::new_inclusive(1, 6);
            let mut roll_die = rng.sample_iter(&die_range);
            let i = *i;
            let id = Id::new(0, i);
            let key_slice = u64_to_slice(i);
            let mut key = SmallVec::from_slice(&key_slice);
            debug!("checking: {}", i);
            let mut cursor = tree.seek(key.clone(), Ordering::Forward);
            key_with_id(&mut key, &id);
            assert_eq!(cursor.current(), Some(&key), "{}", i);
            if roll_die.next().unwrap() == 6 {
                for j in i..num {
                    let id = Id::new(0, j);
                    let key_slice = u64_to_slice(j);
                    let mut key = SmallVec::from_slice(&key_slice);
                    key_with_id(&mut key, &id);
                    assert_eq!(cursor.current(), Some(&key), "{}/{}", i, j);
                    assert_eq!(cursor.next(), j != num - 1, "{}/{}", i, j);
                }
            }
        });
    }
}
<|endoftext|>


<commit_before>
#[cfg(test)]
mod test {
    #[test]
    fn validate_http_version() {
        unimplemented!()
    }
}
<commit_msg>Add unit tests for checking HTTP version<commit_after>extern crate hyper;
extern crate snatch;

#[cfg(test)]
mod test_http_versions {
    use hyper::version::HttpVersion;
    use snatch::http_version::is_valid_http_version;

    #[test]
    fn test_version_09() {
        assert!(!is_valid_http_version(HttpVersion::Http09))
    }

    #[test]
    fn test_version_10() {
        assert!(!is_valid_http_version(HttpVersion::Http10))
    }

    #[test]
    fn test_version_11() {
        assert!(is_valid_http_version(HttpVersion::Http11))
    }

    #[test]
    fn test_version_20() {
        assert!(is_valid_http_version(HttpVersion::Http20))
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use node;

pub type Index = usize;

pub struct Edge {
    pub identifier: String,
    pub target: node::Index,
    pub attributes: Option<HashMap<String, String>>,
    pub next_outgoing_edge: Option<Index>,
}
<commit_msg>Add basic total equality matcher for Edge<commit_after>use std::collections::HashMap;
use node;

pub type Index = usize;

pub struct Edge {
    pub identifier: String,
    pub target: node::Index,
    pub attributes: Option<HashMap<String, String>>,
    pub next_outgoing_edge: Option<Index>,
}

impl Edge {
    pub fn equal(&self, edge: Edge) -> bool {
        self.identifier == edge.identifier &&
            self.attributes == edge.attributes
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use super::*;
    #[test]
    fn edge_equality() {
        let mut attributes = HashMap::new();
        attributes.insert("key".to_string(), "value".to_string());
        let mut attributes2 = HashMap::new();
        attributes.insert("core".to_string(), "worth".to_string());

        let edge0 = Edge {
            identifier: "edgeid".to_string(),
            attributes: Some(attributes.clone()),
            next_outgoing_edge: None,
            target: 0,
        };
        let edge1 = Edge {
            identifier: "edgeid".to_string(),
            attributes: Some(attributes.clone()),
            next_outgoing_edge: None,
            target: 0,
        };
        let edge2 = Edge {
            identifier: "edgeid".to_string(),
            attributes: Some(attributes2.clone()),
            next_outgoing_edge: None,
            target: 0,
        };
        assert!(edge0.equal(edge1));
        assert_eq!(edge0.equal(edge2), false);
    }
}
<|endoftext|>


<commit_before>//! Uniform interface for shaders.

use glium::uniforms::UniformValue;

pub trait IntoUniformValue {
    fn into_uniform_value<'a>(&'a self) -> UniformValue<'a>;
}

impl IntoUniformValue for [f32; 3] {
    fn into_uniform_value<'a>(&'a self) -> UniformValue<'a> { UniformValue::Vec3(*self) }
}

#[cfg(test)]
mod test {
    use super::*;
    use glium::uniforms::Uniforms;
    use uniform_derive::UniformSet;

    #[derive(UniformSet)]
    pub struct MyUniforms {
        camera: [f32; 3],
    }

    #[test]
    fn test() {
        let mut visited = std::collections::VecDeque::new();
        let uniforms = MyUniforms {
            camera: [0., 0., 0.],
        };

        Uniforms::visit_values(&uniforms, |name, value| {
            visited.push_back((name.to_string(), value))
        });

        let (camera, camera_value) = visited.pop_front().expect("A visited value");
        assert_eq!(camera, String::from("camera"));
    }
}
<commit_msg>Uniform interface for primitive types<commit_after>//! Uniform interface for shaders.

use glium::uniforms::UniformValue;

pub trait IntoUniformValue {
    fn into_uniform_value<'a>(&'a self) -> UniformValue<'a>;
}

macro_rules! primitive_uniform_value {
    ($primitive:ty, $wrapper:expr) => {
        impl IntoUniformValue for $primitive {
            fn into_uniform_value<'a>(&'a self) -> UniformValue<'a> { $wrapper(*self) }
        }
    };
}

primitive_uniform_value!(f32, |v| UniformValue::Float(v));
primitive_uniform_value!([f32; 2], |v| UniformValue::Vec2(v));
primitive_uniform_value!([f32; 3], |v| UniformValue::Vec3(v));
primitive_uniform_value!([f32; 4], |v| UniformValue::Vec4(v));
primitive_uniform_value!([[f32; 2]; 2], |v| UniformValue::Mat2(v));
primitive_uniform_value!([[f32; 3]; 3], |v| UniformValue::Mat3(v));
primitive_uniform_value!([[f32; 4]; 4], |v| UniformValue::Mat4(v));

primitive_uniform_value!(f64, |v| UniformValue::Double(v));
primitive_uniform_value!([f64; 2], |v| UniformValue::DoubleVec2(v));
primitive_uniform_value!([f64; 3], |v| UniformValue::DoubleVec3(v));
primitive_uniform_value!([f64; 4], |v| UniformValue::DoubleVec4(v));
primitive_uniform_value!([[f64; 2]; 2], |v| UniformValue::DoubleMat2(v));
primitive_uniform_value!([[f64; 3]; 3], |v| UniformValue::DoubleMat3(v));
primitive_uniform_value!([[f64; 4]; 4], |v| UniformValue::DoubleMat4(v));

primitive_uniform_value!(i32, |v| UniformValue::SignedInt(v));
primitive_uniform_value!([i32; 2], |v| UniformValue::IntVec2(v));
primitive_uniform_value!([i32; 3], |v| UniformValue::IntVec3(v));
primitive_uniform_value!([i32; 4], |v| UniformValue::IntVec4(v));

primitive_uniform_value!(i64, |v| UniformValue::Int64(v));
primitive_uniform_value!([i64; 2], |v| UniformValue::Int64Vec2(v));
primitive_uniform_value!([i64; 3], |v| UniformValue::Int64Vec3(v));
primitive_uniform_value!([i64; 4], |v| UniformValue::Int64Vec4(v));

primitive_uniform_value!(u64, |v| UniformValue::UnsignedInt64(v));
primitive_uniform_value!([u64; 2], |v| UniformValue::UnsignedInt64Vec2(v));
primitive_uniform_value!([u64; 3], |v| UniformValue::UnsignedInt64Vec3(v));
primitive_uniform_value!([u64; 4], |v| UniformValue::UnsignedInt64Vec4(v));

primitive_uniform_value!(u32, |v| UniformValue::UnsignedInt(v));
primitive_uniform_value!([u32; 2], |v| UniformValue::UnsignedIntVec2(v));
primitive_uniform_value!([u32; 3], |v| UniformValue::UnsignedIntVec3(v));
primitive_uniform_value!([u32; 4], |v| UniformValue::UnsignedIntVec4(v));

primitive_uniform_value!(bool, |v| UniformValue::Bool(v));
primitive_uniform_value!([bool; 2], |v| UniformValue::BoolVec2(v));
primitive_uniform_value!([bool; 3], |v| UniformValue::BoolVec3(v));
primitive_uniform_value!([bool; 4], |v| UniformValue::BoolVec4(v));

#[cfg(test)]
mod test {
    use super::*;
    use glium::uniforms::Uniforms;
    use uniform_derive::UniformSet;

    #[derive(UniformSet)]
    pub struct MyUniforms {
        camera: [f32; 3],
    }

    #[test]
    fn test() {
        let mut visited = std::collections::VecDeque::new();
        let uniforms = MyUniforms {
            camera: [0., 0., 0.],
        };

        Uniforms::visit_values(&uniforms, |name, value| {
            visited.push_back((name.to_string(), value))
        });

        let (camera, camera_value) = visited.pop_front().expect("A visited value");
        assert_eq!(camera, String::from("camera"));
    }
}
<|endoftext|>


<commit_before>use std::libc;
use std::str::raw;
use std::os::getenv;
use std::path::Path;
use std::ptr;

struct Dataset {
    c_dataset: *int,
}

#[link(name = "gdal")]
extern {
    fn GDALVersionInfo(key: *libc::c_char) -> *libc::c_char;
    fn GDALOpen(pszFilename: *libc::c_char, eAccess: int) -> *int;
    fn GDALAllRegister();
}
static GA_ReadOnly: int = 0;
static GA_Update: int = 1;

pub fn version_info(key: &str) -> ~str {
    let info = key.with_c_str(|c_key| {
        return unsafe { raw::from_c_str(GDALVersionInfo(c_key)) };
    });
    return info;
}

pub fn open(path: &Path) -> Option<Dataset> {
    unsafe { GDALAllRegister(); }  // TODO call once
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { GDALOpen(c_filename, GA_ReadOnly) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(Dataset{c_dataset: c_dataset}),
    };
}

#[test]
fn test_version_info() {
    let release_date = version_info("RELEASE_DATE");
    let release_name = version_info("RELEASE_NAME");
    let version_text = version_info("--version");

    let expected_text: ~str = "GDAL " + release_name + ", " +
        "released " + release_date.slice(0, 4) + "/" +
        release_date.slice(4, 6) + "/" + release_date.slice(6, 8);

    assert_eq!(version_text.into_owned(), expected_text);
}

fn fixture_path(name: &str) -> Path {
    let envvar = "RUSTILES_TEST_FIXTURES";
    let fixtures = match getenv(envvar) {
        Some(p) => Path::new(p),
        None => fail!("Environment variable {} not set", envvar)
    };
    let rv = fixtures.join(name);
    return rv;
}

#[test]
fn test_open() {
    let dataset = open(&fixture_path("tinymarble.jpeg"));
    assert!(dataset.is_some());

    let missing_dataset = open(&fixture_path("no_such_file.jpeg"));
    assert!(missing_dataset.is_none());
}
<commit_msg>use c types in external interface<commit_after>use std::libc;
use std::str::raw;
use std::os::getenv;
use std::path::Path;
use std::libc::c_int;

struct Dataset {
    c_dataset: *(),
}

#[link(name = "gdal")]
extern {
    fn GDALVersionInfo(key: *libc::c_char) -> *libc::c_char;
    fn GDALOpen(pszFilename: *libc::c_char, eAccess: c_int) -> *();
    fn GDALAllRegister();
}
static GA_ReadOnly: c_int = 0;
static GA_Update: c_int = 1;

pub fn version_info(key: &str) -> ~str {
    let info = key.with_c_str(|c_key| {
        return unsafe { raw::from_c_str(GDALVersionInfo(c_key)) };
    });
    return info;
}

pub fn open(path: &Path) -> Option<Dataset> {
    unsafe { GDALAllRegister(); }  // TODO call once
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { GDALOpen(c_filename, GA_ReadOnly) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(Dataset{c_dataset: c_dataset}),
    };
}

#[test]
fn test_version_info() {
    let release_date = version_info("RELEASE_DATE");
    let release_name = version_info("RELEASE_NAME");
    let version_text = version_info("--version");

    let expected_text: ~str = "GDAL " + release_name + ", " +
        "released " + release_date.slice(0, 4) + "/" +
        release_date.slice(4, 6) + "/" + release_date.slice(6, 8);

    assert_eq!(version_text.into_owned(), expected_text);
}

fn fixture_path(name: &str) -> Path {
    let envvar = "RUSTILES_TEST_FIXTURES";
    let fixtures = match getenv(envvar) {
        Some(p) => Path::new(p),
        None => fail!("Environment variable {} not set", envvar)
    };
    let rv = fixtures.join(name);
    return rv;
}

#[test]
fn test_open() {
    let dataset = open(&fixture_path("tinymarble.jpeg"));
    assert!(dataset.is_some());

    let missing_dataset = open(&fixture_path("no_such_file.jpeg"));
    assert!(missing_dataset.is_none());
}
<|endoftext|>


<commit_before>extern crate libloading as libl;

use std::collections::HashMap;
use std::ffi::CString;
use std::os::raw::{c_char, c_int, c_uint, c_void};

fn to_c_str<T: Into<Vec<u8>>>(t: T) -> *const c_char {
    CString::new(t).unwrap().as_ptr() as *const c_char
}

pub struct CoreClr {
    host_handle: *const c_void,
    domain_id: c_uint
}

pub type CoreClrInitializeFn = unsafe extern fn(
    *const c_char,
    *const c_char,
    c_int,
    *const *const c_char,
    *const *const c_char,
    *const *const c_void,
    *const c_uint) -> c_int;

pub type CoreClrShutdownFn = unsafe extern fn(*const c_void, c_uint);

impl CoreClr {
    pub fn init(
        exe_path: &str,
        app_domain_friendly_name: &str,
        properties_option: Option<HashMap<&str, &str>>) -> libl::Result<CoreClr>
    {
        let host_handle = 0 as *const c_void;
        let host_handle_ref = &host_handle as *const *const c_void;

        let domain_id = 0 as c_uint;
        let domain_id_ref = &domain_id as *const c_uint;

        let exe_path_raw = to_c_str(exe_path);
        let app_domain_friendly_name_raw = to_c_str(app_domain_friendly_name);

        let properties = properties_option.unwrap_or_else(HashMap::new);
        let properties_count = properties.len() as c_int;

        let properties_keys: Vec<*const c_char> = properties.keys()
                .map(|&k| to_c_str(k))
                .collect();

        let properties_values: Vec<*const c_char> = properties.values()
                .map(|&v| to_c_str(v))
                .collect();

        let properties_keys_ref = &properties_keys[0] as *const *const c_char;
        let properties_values_ref = &properties_values[0] as *const *const c_char;

        let coreclr_library = libl::Library::new("/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.0.0")?;

        unsafe {
            let coreclr_initialize_fn: libl::Symbol<CoreClrInitializeFn> = coreclr_library.get(b"coreclr_initialize")?;

            assert_eq!(
                coreclr_initialize_fn(
                    exe_path_raw,
                    app_domain_friendly_name_raw,
                    properties_count,
                    properties_keys_ref,
                    properties_values_ref,
                    host_handle_ref,
                    domain_id_ref), 0);
        }

        Ok(CoreClr {
            host_handle: host_handle,
            domain_id: domain_id
        })
    }

    pub fn shutdown(self: Self) -> libl::Result<()> {
        let coreclr_library = libl::Library::new("/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.0.0")?;

        unsafe {
            let coreclr_shutdown_fn: libl::Symbol<CoreClrShutdownFn> = coreclr_library.get(b"coreclr_shutdown")?;
            assert_eq!(coreclr_shutdown_fn(self.host_handle, self.domain_id), 0)
        }

        Ok(())
    }
}<commit_msg>Added second shutdown function and fixed formatting<commit_after>extern crate libloading as libl;

use std::collections::HashMap;
use std::ffi::CString;
use std::io;
use std::os::raw::{c_char, c_int, c_uint, c_void};

fn to_c_str<T: Into<Vec<u8>>>(t: T) -> *const c_char {
    CString::new(t).unwrap().as_ptr() as *const c_char
}

pub struct CoreClr {
    host_handle: *const c_void,
    domain_id: c_uint
}

pub type CoreClrInitializeFn = unsafe extern fn(
    *const c_char,
    *const c_char,
    c_int,
    *const *const c_char,
    *const *const c_char,
    *const *const c_void,
    *const c_uint) -> c_int;

pub type CoreClrShutdownFn = unsafe extern fn(*const c_void, c_uint) -> c_int;
pub type CoreClrShutdown2Fn = unsafe extern fn(*const c_void, c_uint, *const c_int) -> c_int;

impl CoreClr {
    fn library() -> libl::Result<libl::Library> {
        libl::Library::new("/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.0.0")
    }

    pub fn init(
        exe_path: &str,
        app_domain_friendly_name: &str,
        properties_option: Option<HashMap<&str, &str>>) -> libl::Result<CoreClr>
    {
        // Create the host handle and its ref
        let host_handle = 0 as *const c_void;
        let host_handle_ref = &host_handle as *const *const c_void;

        // Create the domain id and its ref
        let domain_id = 0 as c_uint;
        let domain_id_ref = &domain_id as *const c_uint;

        // Raw C string refs from exe_path and app_domain_friendly_name slices
        let exe_path_raw = to_c_str(exe_path);
        let app_domain_friendly_name_raw = to_c_str(app_domain_friendly_name);

        // Either use the provided option or a blank hashmap
        let properties = properties_option.unwrap_or_else(HashMap::new);

        // Count for upcoming vecs
        let properties_count = properties.len() as c_int;

        // Collect property keys into a vec
        let properties_keys: Vec<*const c_char> = properties.keys()
                .map(|&k| to_c_str(k))
                .collect();

        // Collect equivalent property values into another vec
        let properties_values: Vec<*const c_char> = properties.values()
                .map(|&v| to_c_str(v))
                .collect();

        // Grab refs of the two to pass into the actual function call
        let properties_keys_ref = &properties_keys[0] as *const *const c_char;
        let properties_values_ref = &properties_values[0] as *const *const c_char;

        unsafe {
            let coreclr_library = CoreClr::library()?;
            let coreclr_initialize_fn: libl::Symbol<CoreClrInitializeFn> = coreclr_library.get(b"coreclr_initialize")?;

            assert_eq!(
                coreclr_initialize_fn(
                    exe_path_raw,
                    app_domain_friendly_name_raw,
                    properties_count,
                    properties_keys_ref,
                    properties_values_ref,
                    host_handle_ref,
                    domain_id_ref), 0);
        }

        Ok(CoreClr {
            host_handle: host_handle,
            domain_id: domain_id
        })
    }

    pub fn shutdown(self: Self) -> io::Result<()> {
        unsafe {
            let coreclr_library = CoreClr::library()?;
            let coreclr_shutdown_fn: libl::Symbol<CoreClrShutdownFn> = coreclr_library.get(b"coreclr_shutdown")?;

            match coreclr_shutdown_fn(self.host_handle, self.domain_id) {
                0 => Ok(()),
                _ => panic!("Failed to shutdown")
            }
        }
    }

    pub fn shutdown_2(self: Self) -> io::Result<c_int> {
        let latched_exit_code = -1 as c_int;
        let latched_exit_code_ref = &latched_exit_code as *const c_int;

        unsafe {
            let coreclr_library = CoreClr::library()?;
            let coreclr_shutdown_2_fn: libl::Symbol<CoreClrShutdown2Fn> = coreclr_library.get(b"coreclr_shutdown_2")?;

            match coreclr_shutdown_2_fn(self.host_handle, self.domain_id, latched_exit_code_ref) {
                0 => Ok(latched_exit_code),
                _ => panic!("Failed to shutdown")
            }
        }
    }
}<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::ptr;

use gdk_pixbuf::{Colorspace, Pixbuf};
use gio::prelude::*;
use glib::translate::*;
use rgb::{FromSlice, RGBA8};
use url::Url;

use crate::c_api::checked_i32;

use rsvg_internals::{
    Dpi, Handle, LoadOptions, LoadingError, Pixel, RenderingError, SharedImageSurface, SurfaceType,
};

use crate::c_api::set_gerror;

fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> {
    assert!(width > 0 && height > 0);

    Pixbuf::new(Colorspace::Rgb, true, 8, width, height).ok_or(RenderingError::OutOfMemory)
}

pub fn empty_pixbuf() -> Result<Pixbuf, RenderingError> {
    // GdkPixbuf does not allow zero-sized pixbufs, but Cairo allows zero-sized
    // surfaces.  In this case, return a 1-pixel transparent pixbuf.

    let pixbuf = pixbuf_new(1, 1)?;
    pixbuf.put_pixel(0, 0, 0, 0, 0, 0);

    Ok(pixbuf)
}

pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, RenderingError> {
    let width = surface.width();
    let height = surface.height();

    let pixbuf = pixbuf_new(width, height)?;
    assert!(pixbuf.get_colorspace() == Colorspace::Rgb);
    assert!(pixbuf.get_bits_per_sample() == 8);
    assert!(pixbuf.get_n_channels() == 4);

    let pixels = unsafe { pixbuf.get_pixels() };
    let width = width as usize;
    let height = height as usize;
    let stride = pixbuf.get_rowstride() as usize;
    let width_in_bytes = width * 4;
    assert!(width_in_bytes <= stride);

    // We use chunks_mut(), not chunks_exact_mut(), because gdk-pixbuf tends
    // to make the last row *not* have the full stride (i.e. it is
    // only as wide as the pixels in that row).
    let pixbuf_rows = pixels.chunks_mut(stride).take(height);

    for (src_row, dest_row) in surface.rows().zip(pixbuf_rows) {
        let row: &mut [RGBA8] = dest_row[..width_in_bytes].as_rgba_mut();

        for (src, dest) in src_row.iter().zip(row.iter_mut()) {
            let (r, g, b, a) = if src.a == 0 {
                (0, 0, 0, 0)
            } else {
                let pixel = Pixel {
                    r: src.r,
                    g: src.g,
                    b: src.b,
                    a: src.a,
                }
                .unpremultiply();

                (pixel.r, pixel.g, pixel.b, pixel.a)
            };

            dest.r = r;
            dest.g = g;
            dest.b = b;
            dest.a = a;
        }
    }

    Ok(pixbuf)
}

enum SizeKind {
    Zoom,
    WidthHeight,
    WidthHeightMax,
    ZoomMax,
}

struct SizeMode {
    kind: SizeKind,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
}

fn get_final_size(in_width: f64, in_height: f64, size_mode: &SizeMode) -> (f64, f64) {
    if in_width == 0.0 || in_height == 0.0 {
        return (0.0, 0.0);
    }

    let mut out_width;
    let mut out_height;

    match size_mode.kind {
        SizeKind::Zoom => {
            out_width = size_mode.x_zoom * in_width;
            out_height = size_mode.y_zoom * in_height;
        }

        SizeKind::ZoomMax => {
            out_width = size_mode.x_zoom * in_width;
            out_height = size_mode.y_zoom * in_height;

            if out_width > f64::from(size_mode.width) || out_height > f64::from(size_mode.height) {
                let zoom_x = f64::from(size_mode.width) / out_width;
                let zoom_y = f64::from(size_mode.height) / out_height;
                let zoom = zoom_x.min(zoom_y);

                out_width = zoom * out_width;
                out_height = zoom * out_height;
            }
        }

        SizeKind::WidthHeightMax => {
            let zoom_x = f64::from(size_mode.width) / in_width;
            let zoom_y = f64::from(size_mode.height) / in_height;

            let zoom = zoom_x.min(zoom_y);

            out_width = zoom * in_width;
            out_height = zoom * in_height;
        }

        SizeKind::WidthHeight => {
            if size_mode.width != -1 {
                out_width = f64::from(size_mode.width);
            } else {
                out_width = in_width;
            }

            if size_mode.height != -1 {
                out_height = f64::from(size_mode.height);
            } else {
                out_height = in_height;
            }
        }
    }

    (out_width, out_height)
}

fn render_to_pixbuf_at_size(
    handle: &Handle,
    document_width: f64,
    document_height: f64,
    desired_width: f64,
    desired_height: f64,
    dpi: Dpi,
) -> Result<Pixbuf, RenderingError> {
    if desired_width == 0.0
        || desired_height == 0.0
        || document_width == 0.0
        || document_height == 0.0
    {
        return empty_pixbuf();
    }

    let surface = cairo::ImageSurface::create(
        cairo::Format::ARgb32,
        checked_i32(desired_width.round())?,
        checked_i32(desired_height.round())?,
    )?;

    {
        let cr = cairo::Context::new(&surface);
        cr.scale(
            desired_width / document_width,
            desired_height / document_height,
        );

        let viewport = cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: document_width,
            height: document_height,
        };

        // We do it with a cr transform so we can scale non-proportionally.
        handle.render_document(&cr, &viewport, dpi, false)?;
    }

    let shared_surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;

    pixbuf_from_surface(&shared_surface)
}

fn get_default_dpi() -> Dpi {
    // This is ugly, but it preserves the C API semantics of
    //
    //   rsvg_set_default_dpi(...);
    //   pixbuf = rsvg_pixbuf_from_file(...);
    //
    // Passing negative numbers here means that the global default DPI will be used.
    Dpi::new(-1.0, -1.0)
}

fn url_from_file(file: &gio::File) -> Result<Url, LoadingError> {
    Ok(Url::parse(&file.get_uri()).map_err(|_| LoadingError::BadUrl)?)
}

fn pixbuf_from_file_with_size_mode(
    filename: *const libc::c_char,
    size_mode: &SizeMode,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    let dpi = get_default_dpi();

    unsafe {
        let path = PathBuf::from_glib_none(filename);
        let file = gio::File::new_for_path(path);

        let base_url = match url_from_file(&file) {
            Ok(url) => url,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        let load_options = LoadOptions::new(Some(base_url));

        let cancellable: Option<&gio::Cancellable> = None;
        let handle = match file
            .read(cancellable)
            .map_err(LoadingError::from)
            .and_then(|stream| Handle::from_stream(&load_options, stream.as_ref(), None))
        {
            Ok(handle) => handle,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        handle
            .get_geometry_sub(None, dpi, false)
            .and_then(|(ink_r, _)| {
                let (document_width, document_height) = (ink_r.width(), ink_r.height());
                let (desired_width, desired_height) =
                    get_final_size(document_width, document_height, size_mode);

                render_to_pixbuf_at_size(
                    &handle,
                    document_width,
                    document_height,
                    desired_width,
                    desired_height,
                    dpi,
                )
            })
            .map(|pixbuf| pixbuf.to_glib_full())
            .unwrap_or_else(|e| {
                set_gerror(error, 0, &format!("{}", e));
                ptr::null_mut()
            })
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_size => ptr::null_mut();

        !filename.is_null(),
        (width >= 1 && height >= 1) || (width == -1 && height == -1),
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeight,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::Zoom,
            x_zoom,
            y_zoom,
            width: 0,
            height: 0,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom_with_max(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom_with_max => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::ZoomMax,
            x_zoom,
            y_zoom,
            width: max_width,
            height: max_height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_max_size(
    filename: *const libc::c_char,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_max_size => ptr::null_mut();

        !filename.is_null(),
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeightMax,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width: max_width,
            height: max_height,
        },
        error,
    )
}
<commit_msg>Do not manually implement an assign operation<commit_after>use std::path::PathBuf;
use std::ptr;

use gdk_pixbuf::{Colorspace, Pixbuf};
use gio::prelude::*;
use glib::translate::*;
use rgb::{FromSlice, RGBA8};
use url::Url;

use crate::c_api::checked_i32;

use rsvg_internals::{
    Dpi, Handle, LoadOptions, LoadingError, Pixel, RenderingError, SharedImageSurface, SurfaceType,
};

use crate::c_api::set_gerror;

fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> {
    assert!(width > 0 && height > 0);

    Pixbuf::new(Colorspace::Rgb, true, 8, width, height).ok_or(RenderingError::OutOfMemory)
}

pub fn empty_pixbuf() -> Result<Pixbuf, RenderingError> {
    // GdkPixbuf does not allow zero-sized pixbufs, but Cairo allows zero-sized
    // surfaces.  In this case, return a 1-pixel transparent pixbuf.

    let pixbuf = pixbuf_new(1, 1)?;
    pixbuf.put_pixel(0, 0, 0, 0, 0, 0);

    Ok(pixbuf)
}

pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, RenderingError> {
    let width = surface.width();
    let height = surface.height();

    let pixbuf = pixbuf_new(width, height)?;
    assert!(pixbuf.get_colorspace() == Colorspace::Rgb);
    assert!(pixbuf.get_bits_per_sample() == 8);
    assert!(pixbuf.get_n_channels() == 4);

    let pixels = unsafe { pixbuf.get_pixels() };
    let width = width as usize;
    let height = height as usize;
    let stride = pixbuf.get_rowstride() as usize;
    let width_in_bytes = width * 4;
    assert!(width_in_bytes <= stride);

    // We use chunks_mut(), not chunks_exact_mut(), because gdk-pixbuf tends
    // to make the last row *not* have the full stride (i.e. it is
    // only as wide as the pixels in that row).
    let pixbuf_rows = pixels.chunks_mut(stride).take(height);

    for (src_row, dest_row) in surface.rows().zip(pixbuf_rows) {
        let row: &mut [RGBA8] = dest_row[..width_in_bytes].as_rgba_mut();

        for (src, dest) in src_row.iter().zip(row.iter_mut()) {
            let (r, g, b, a) = if src.a == 0 {
                (0, 0, 0, 0)
            } else {
                let pixel = Pixel {
                    r: src.r,
                    g: src.g,
                    b: src.b,
                    a: src.a,
                }
                .unpremultiply();

                (pixel.r, pixel.g, pixel.b, pixel.a)
            };

            dest.r = r;
            dest.g = g;
            dest.b = b;
            dest.a = a;
        }
    }

    Ok(pixbuf)
}

enum SizeKind {
    Zoom,
    WidthHeight,
    WidthHeightMax,
    ZoomMax,
}

struct SizeMode {
    kind: SizeKind,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
}

fn get_final_size(in_width: f64, in_height: f64, size_mode: &SizeMode) -> (f64, f64) {
    if in_width == 0.0 || in_height == 0.0 {
        return (0.0, 0.0);
    }

    let mut out_width;
    let mut out_height;

    match size_mode.kind {
        SizeKind::Zoom => {
            out_width = size_mode.x_zoom * in_width;
            out_height = size_mode.y_zoom * in_height;
        }

        SizeKind::ZoomMax => {
            out_width = size_mode.x_zoom * in_width;
            out_height = size_mode.y_zoom * in_height;

            if out_width > f64::from(size_mode.width) || out_height > f64::from(size_mode.height) {
                let zoom_x = f64::from(size_mode.width) / out_width;
                let zoom_y = f64::from(size_mode.height) / out_height;
                let zoom = zoom_x.min(zoom_y);

                out_width *= zoom;
                out_height *= zoom;
            }
        }

        SizeKind::WidthHeightMax => {
            let zoom_x = f64::from(size_mode.width) / in_width;
            let zoom_y = f64::from(size_mode.height) / in_height;

            let zoom = zoom_x.min(zoom_y);

            out_width = zoom * in_width;
            out_height = zoom * in_height;
        }

        SizeKind::WidthHeight => {
            if size_mode.width != -1 {
                out_width = f64::from(size_mode.width);
            } else {
                out_width = in_width;
            }

            if size_mode.height != -1 {
                out_height = f64::from(size_mode.height);
            } else {
                out_height = in_height;
            }
        }
    }

    (out_width, out_height)
}

fn render_to_pixbuf_at_size(
    handle: &Handle,
    document_width: f64,
    document_height: f64,
    desired_width: f64,
    desired_height: f64,
    dpi: Dpi,
) -> Result<Pixbuf, RenderingError> {
    if desired_width == 0.0
        || desired_height == 0.0
        || document_width == 0.0
        || document_height == 0.0
    {
        return empty_pixbuf();
    }

    let surface = cairo::ImageSurface::create(
        cairo::Format::ARgb32,
        checked_i32(desired_width.round())?,
        checked_i32(desired_height.round())?,
    )?;

    {
        let cr = cairo::Context::new(&surface);
        cr.scale(
            desired_width / document_width,
            desired_height / document_height,
        );

        let viewport = cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: document_width,
            height: document_height,
        };

        // We do it with a cr transform so we can scale non-proportionally.
        handle.render_document(&cr, &viewport, dpi, false)?;
    }

    let shared_surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;

    pixbuf_from_surface(&shared_surface)
}

fn get_default_dpi() -> Dpi {
    // This is ugly, but it preserves the C API semantics of
    //
    //   rsvg_set_default_dpi(...);
    //   pixbuf = rsvg_pixbuf_from_file(...);
    //
    // Passing negative numbers here means that the global default DPI will be used.
    Dpi::new(-1.0, -1.0)
}

fn url_from_file(file: &gio::File) -> Result<Url, LoadingError> {
    Ok(Url::parse(&file.get_uri()).map_err(|_| LoadingError::BadUrl)?)
}

fn pixbuf_from_file_with_size_mode(
    filename: *const libc::c_char,
    size_mode: &SizeMode,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    let dpi = get_default_dpi();

    unsafe {
        let path = PathBuf::from_glib_none(filename);
        let file = gio::File::new_for_path(path);

        let base_url = match url_from_file(&file) {
            Ok(url) => url,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        let load_options = LoadOptions::new(Some(base_url));

        let cancellable: Option<&gio::Cancellable> = None;
        let handle = match file
            .read(cancellable)
            .map_err(LoadingError::from)
            .and_then(|stream| Handle::from_stream(&load_options, stream.as_ref(), None))
        {
            Ok(handle) => handle,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        handle
            .get_geometry_sub(None, dpi, false)
            .and_then(|(ink_r, _)| {
                let (document_width, document_height) = (ink_r.width(), ink_r.height());
                let (desired_width, desired_height) =
                    get_final_size(document_width, document_height, size_mode);

                render_to_pixbuf_at_size(
                    &handle,
                    document_width,
                    document_height,
                    desired_width,
                    desired_height,
                    dpi,
                )
            })
            .map(|pixbuf| pixbuf.to_glib_full())
            .unwrap_or_else(|e| {
                set_gerror(error, 0, &format!("{}", e));
                ptr::null_mut()
            })
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_size => ptr::null_mut();

        !filename.is_null(),
        (width >= 1 && height >= 1) || (width == -1 && height == -1),
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeight,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::Zoom,
            x_zoom,
            y_zoom,
            width: 0,
            height: 0,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom_with_max(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom_with_max => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::ZoomMax,
            x_zoom,
            y_zoom,
            width: max_width,
            height: max_height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_max_size(
    filename: *const libc::c_char,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_max_size => ptr::null_mut();

        !filename.is_null(),
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeightMax,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width: max_width,
            height: max_height,
        },
        error,
    )
}
<|endoftext|>