<commit_before>use super::buf::AsyncBuf;
use super::DEFAULT_BUFSIZE;

use std::io;
use std::io::Read;

use futures::{Async, Poll};
use tokio_core::io::Io;

pub struct AsyncBufReader<R> {
    inner: R,
    buf: AsyncBuf
}

impl <R> AsyncBufReader<R> {
    pub fn new(inner: R) -> AsyncBufReader<R> {
        AsyncBufReader::with_capacity(DEFAULT_BUFSIZE, inner)
    }

    pub fn with_capacity(capacity: usize, inner: R) -> AsyncBufReader<R> {
        AsyncBufReader {
            inner: inner,
            buf: AsyncBuf::with_capacity(capacity)
        }
    }
}

impl <R: Read> AsyncBufReader<R> {
    fn fill_buf_no_eof(&mut self) -> io::Result<()> {
        let rsize = try!(self.inner.read(self.buf.get_mut()));
        if rsize == 0 {
            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "Unexpected EOF"));
        }
        self.buf.fill(rsize);
        Ok(())
    }

    pub fn nb_read_exact(&mut self, n: usize) -> Poll<&[u8], io::Error> {
        if self.buf.data_size() >= n {
            Ok(Async::Ready(self.buf.consume_and_get(n)))
        } else {
            self.buf.reserve(n);
            if let Err(e) = self.fill_buf_no_eof() {
                if let io::ErrorKind::WouldBlock = e.kind() {
                    return Ok(Async::NotReady);
                } else {
                    return Err(e)
                }
            }

            if self.buf.data_size() < n {
                Ok(Async::NotReady)
            } else {
                Ok(Async::Ready(self.buf.consume_and_get(n)))
            }
        }
    }
}

pub trait AsyncPollRead {
    fn async_poll_read(&mut self) -> Async<()>;
}

impl <R: Io> AsyncPollRead for AsyncBufReader<R> {
    fn async_poll_read(&mut self) -> Async<()> {
        self.inner.poll_read()
    }
}

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

    use std::{cmp, io};
    use std::io::{Cursor, Read};
    use futures::Async;

    pub struct MockAsyncReader {
        buf: Vec<u8>,
        pos: usize,
    }

    impl Read for MockAsyncReader {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            let datasize = cmp::min(buf.len(), self.buf.len() - self.pos);
            if datasize == 0 {
                return Err(io::Error::new(io::ErrorKind::WouldBlock, "data not ready"));
            }
            let new_pos = self.pos + datasize;
            buf[.. datasize].copy_from_slice(&self.buf[self.pos .. new_pos]);
            self.pos = new_pos;
            Ok(datasize)
        }
    }

    fn mock_async_reader(data: &[u8]) -> MockAsyncReader {
        MockAsyncReader {
            buf: data.to_vec(),
            pos: 0
        }
    }

    #[test]
    fn read_exact() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(16, reader);

        assert_eq!(Async::Ready(b"Hello".as_ref()), bufreader.nb_read_exact(5).expect("error!"));
    }

    #[test]
    fn read_exact_larger_than_buffer() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hello".as_ref()), bufreader.nb_read_exact(5).expect("error!"));
    }

    #[test]
    fn read_exact_with_small_buffer() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hell".as_ref()), bufreader.nb_read_exact(4).expect("error!"));
        assert_eq!(Async::Ready(b"o".as_ref()), bufreader.nb_read_exact(1).expect("error!"));
        assert_eq!(Async::Ready(b", ".as_ref()), bufreader.nb_read_exact(2).expect("error!"));
        assert_eq!(Async::Ready(b"world!".as_ref()), bufreader.nb_read_exact(6).expect("error!"));
    }

    #[test]
    fn read_exact_unexpected_eof() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hell".as_ref()), bufreader.nb_read_exact(4).expect("error!"));
        assert_eq!(Async::Ready(b"o".as_ref()), bufreader.nb_read_exact(1).expect("error!"));
        assert_eq!(Async::Ready(b", ".as_ref()), bufreader.nb_read_exact(2).expect("error!"));
        // The first call returns `NotReady`, then the second call returns `UnexpectedEof`
        assert_eq!(Async::NotReady, bufreader.nb_read_exact(7).expect("error!"));
        match bufreader.nb_read_exact(7) {
            Ok(x) => panic!("Expected EOF, but got {:?}", x),
            Err(e) => assert_eq!(io::ErrorKind::UnexpectedEof, e.kind())
        }
    }

    #[test]
    fn read_exact_async() {
        let reader = mock_async_reader(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hell".as_ref()), bufreader.nb_read_exact(4).expect("error!"));
        assert_eq!(Async::Ready(b"o".as_ref()), bufreader.nb_read_exact(1).expect("error!"));
        assert_eq!(Async::Ready(b", ".as_ref()), bufreader.nb_read_exact(2).expect("error!"));

        // The call returns `NotReady` indefinitely if the data is too short
        assert_eq!(Async::NotReady, bufreader.nb_read_exact(7).expect("error!"));
        assert_eq!(Async::NotReady, bufreader.nb_read_exact(7).expect("error!"));

        assert_eq!(Async::Ready(b"world!".as_ref()), bufreader.nb_read_exact(6).expect("error!"));
    }
}
<commit_msg>AsyncBufReader::nb_read_until<commit_after>use super::buf::AsyncBuf;
use super::DEFAULT_BUFSIZE;

use std::io;
use std::io::Read;

use futures::{Async, Poll};
use tokio_core::io::Io;

pub struct AsyncBufReader<R> {
    inner: R,
    buf: AsyncBuf
}

impl <R> AsyncBufReader<R> {
    pub fn new(inner: R) -> AsyncBufReader<R> {
        AsyncBufReader::with_capacity(DEFAULT_BUFSIZE, inner)
    }

    pub fn with_capacity(capacity: usize, inner: R) -> AsyncBufReader<R> {
        AsyncBufReader {
            inner: inner,
            buf: AsyncBuf::with_capacity(capacity)
        }
    }
}

impl <R: Read> AsyncBufReader<R> {
    fn fill_buf_no_eof(&mut self) -> io::Result<()> {
        let rsize = try!(self.inner.read(self.buf.get_mut()));
        if rsize == 0 {
            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "Unexpected EOF"));
        }
        self.buf.fill(rsize);
        Ok(())
    }

    pub fn nb_read_exact(&mut self, n: usize) -> Poll<&[u8], io::Error> {
        if self.buf.data_size() >= n {
            Ok(Async::Ready(self.buf.consume_and_get(n)))
        } else {
            self.buf.reserve(n);
            if let Err(e) = self.fill_buf_no_eof() {
                if let io::ErrorKind::WouldBlock = e.kind() {
                    return Ok(Async::NotReady);
                } else {
                    return Err(e)
                }
            }

            if self.buf.data_size() < n {
                Ok(Async::NotReady)
            } else {
                Ok(Async::Ready(self.buf.consume_and_get(n)))
            }
        }
    }

    pub fn nb_read_until(&mut self, byte: u8, limit: usize) -> Poll<&[u8], io::Error> {
        if let Some(idx) = self.buf.get_ref().iter().position(|&c| c == byte) {
            return Ok(Async::Ready(self.buf.consume_and_get(idx)));
        } else {
            self.buf.reserve(limit);
            if let Err(e) = self.fill_buf_no_eof() {
                if let io::ErrorKind::WouldBlock = e.kind() {
                    return Ok(Async::NotReady);
                } else {
                    return Err(e)
                }
            }

            if let Some(idx) = self.buf.get_ref().iter().position(|&c| c == byte) {
                Ok(Async::Ready(self.buf.consume_and_get(idx)))
            } else if self.buf.data_size() < limit {
                Ok(Async::NotReady)
            } else {
                Err(io::Error::new(io::ErrorKind::InvalidData, "delimiter not found"))
            }
        }
    }
}

pub trait AsyncPollRead {
    fn async_poll_read(&mut self) -> Async<()>;
}

impl <R: Io> AsyncPollRead for AsyncBufReader<R> {
    fn async_poll_read(&mut self) -> Async<()> {
        self.inner.poll_read()
    }
}

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

    use std::{cmp, io};
    use std::io::{Cursor, Read};
    use futures::Async;

    pub struct MockAsyncReader {
        buf: Vec<u8>,
        pos: usize,
    }

    impl Read for MockAsyncReader {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            let datasize = cmp::min(buf.len(), self.buf.len() - self.pos);
            if datasize == 0 {
                return Err(io::Error::new(io::ErrorKind::WouldBlock, "data not ready"));
            }
            let new_pos = self.pos + datasize;
            buf[.. datasize].copy_from_slice(&self.buf[self.pos .. new_pos]);
            self.pos = new_pos;
            Ok(datasize)
        }
    }

    fn mock_async_reader(data: &[u8]) -> MockAsyncReader {
        MockAsyncReader {
            buf: data.to_vec(),
            pos: 0
        }
    }

    #[test]
    fn read_exact() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(16, reader);

        assert_eq!(Async::Ready(b"Hello".as_ref()), bufreader.nb_read_exact(5).expect("error!"));
    }

    #[test]
    fn read_exact_larger_than_buffer() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hello".as_ref()), bufreader.nb_read_exact(5).expect("error!"));
    }

    #[test]
    fn read_exact_with_small_buffer() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hell".as_ref()), bufreader.nb_read_exact(4).expect("error!"));
        assert_eq!(Async::Ready(b"o".as_ref()), bufreader.nb_read_exact(1).expect("error!"));
        assert_eq!(Async::Ready(b", ".as_ref()), bufreader.nb_read_exact(2).expect("error!"));
        assert_eq!(Async::Ready(b"world!".as_ref()), bufreader.nb_read_exact(6).expect("error!"));
    }

    #[test]
    fn read_exact_unexpected_eof() {
        let reader = Cursor::new(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hell".as_ref()), bufreader.nb_read_exact(4).expect("error!"));
        assert_eq!(Async::Ready(b"o".as_ref()), bufreader.nb_read_exact(1).expect("error!"));
        assert_eq!(Async::Ready(b", ".as_ref()), bufreader.nb_read_exact(2).expect("error!"));
        // The first call returns `NotReady`, then the second call returns `UnexpectedEof`
        assert_eq!(Async::NotReady, bufreader.nb_read_exact(7).expect("error!"));
        match bufreader.nb_read_exact(7) {
            Ok(x) => panic!("Expected EOF, but got {:?}", x),
            Err(e) => assert_eq!(io::ErrorKind::UnexpectedEof, e.kind())
        }
    }

    #[test]
    fn read_exact_async() {
        let reader = mock_async_reader(b"Hello, world!");
        let mut bufreader = AsyncBufReader::with_capacity(4, reader);

        assert_eq!(Async::Ready(b"Hell".as_ref()), bufreader.nb_read_exact(4).expect("error!"));
        assert_eq!(Async::Ready(b"o".as_ref()), bufreader.nb_read_exact(1).expect("error!"));
        assert_eq!(Async::Ready(b", ".as_ref()), bufreader.nb_read_exact(2).expect("error!"));

        // The call returns `NotReady` indefinitely if the data is too short
        assert_eq!(Async::NotReady, bufreader.nb_read_exact(7).expect("error!"));
        assert_eq!(Async::NotReady, bufreader.nb_read_exact(7).expect("error!"));

        assert_eq!(Async::Ready(b"world!".as_ref()), bufreader.nb_read_exact(6).expect("error!"));
    }
}
<|endoftext|>


<commit_before>use cairo;
use gdk::{self, WindowExt};
use gtk::{self, BoxExt, ContainerExt, DrawingArea, ScrolledWindowExt, StateFlags, WidgetExt};
use std::cell::RefCell;

use std::rc::Rc;

use color::Color;
use utils::RotateVec;

const LEFT_WIDTH: f64 = 31.;

pub struct Graph {
    colors: Vec<Color>,
    pub data: Vec<RotateVec<f64>>,
    vertical_layout: gtk::Box,
    scroll_layout: gtk::ScrolledWindow,
    horizontal_layout: gtk::Box,
    pub area: DrawingArea,
    max: Option<RefCell<f64>>,
    keep_max: bool,
    display_labels: RefCell<bool>,
    initial_diff: Option<i32>,
    label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    labels_layout_width: i32,
    /// `minimum` is used only if `max` is set: it'll be the minimum that the `max` value will
    /// be able to go down.
    minimum: Option<f64>,
    // In %, from 0 to whatever
    overhead: Option<f64>,
}

impl Graph {
    /// If `max` is `None`, the graph will expect values between 0 and 1.
    ///
    /// If `keep_max` is set to `true`, then this value will never go down, meaning that graphs
    /// won't rescale down. It is not taken into account if `max` is `None`.
    pub fn new(max: Option<f64>, keep_max: bool) -> Graph {
        let g = Graph {
            colors: vec![],
            data: vec![],
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0),
            scroll_layout: gtk::ScrolledWindow::new(
                None::<&gtk::Adjustment>,
                None::<&gtk::Adjustment>,
            ),
            horizontal_layout: gtk::Box::new(gtk::Orientation::Horizontal, 0),
            area: DrawingArea::new(),
            max: if let Some(max) = max {
                Some(RefCell::new(max))
            } else {
                None
            },
            keep_max,
            display_labels: RefCell::new(true),
            initial_diff: None,
            label_callbacks: None,
            labels_layout_width: 80,
            minimum: None,
            overhead: None,
        };
        g.scroll_layout.set_min_content_width(g.labels_layout_width);
        g.scroll_layout.add(&g.vertical_layout);
        g.horizontal_layout.pack_start(&g.area, true, true, 0);
        g.horizontal_layout
            .pack_start(&g.scroll_layout, false, true, 10);
        g.horizontal_layout.set_margin_start(5);
        g
    }

    pub fn set_minimum(&mut self, minimum: Option<f64>) {
        self.minimum = minimum;
    }

    pub fn set_overhead(&mut self, overhead: Option<f64>) {
        if let Some(o) = overhead {
            assert!(o >= 0.);
        }
        self.overhead = overhead;
    }

    /// Changes the size of the layout containing labels (the one on the right).
    pub fn set_labels_width(&mut self, labels_layout_width: u32) {
        self.scroll_layout
            .set_min_content_width(labels_layout_width as i32);
        self.labels_layout_width = labels_layout_width as i32;
    }

    pub fn set_label_callbacks(
        &mut self,
        label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    ) {
        self.label_callbacks = label_callbacks;
    }

    pub fn set_display_labels(&self, display_labels: bool) {
        *self.display_labels.borrow_mut() = display_labels;
        if display_labels {
            self.scroll_layout.show_all();
        } else {
            self.scroll_layout.hide();
        }
        self.invalidate();
    }

    pub fn hide(&self) {
        self.horizontal_layout.hide();
    }

    pub fn show_all(&self) {
        self.horizontal_layout.show_all();
        if !*self.display_labels.borrow() {
            self.scroll_layout.hide();
        }
    }

    pub fn attach_to(&self, to: &gtk::Box) {
        to.add(&self.horizontal_layout);
    }

    pub fn push(&mut self, d: RotateVec<f64>, s: &str, override_color: Option<usize>) {
        let c = if let Some(over) = override_color {
            Color::generate(over)
        } else {
            Color::generate(self.data.len() + 11)
        };
        let l = gtk::Label::new(Some(s));
        l.override_color(
            StateFlags::from_bits(0).expect("from_bits failed"),
            Some(&c.to_gdk()),
        );
        self.vertical_layout.add(&l);
        self.colors.push(c);
        self.data.push(d);
    }

    fn draw_labels(&self, c: &cairo::Context, max: f64, height: f64) {
        if let Some(ref call) = self.label_callbacks {
            let entries = call(max);
            let font_size = 8.;

            c.set_source_rgb(0., 0., 0.);
            c.set_font_size(font_size);

            c.move_to(LEFT_WIDTH - 4. - entries[0].len() as f64 * 4., font_size);
            c.show_text(entries[0].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[1].len() as f64 * 4., height / 2.);
            c.show_text(entries[1].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[2].len() as f64 * 4., height - 2.);
            c.show_text(entries[2].as_str());

            c.move_to(
                font_size - 1.,
                height / 2. + 4. * (entries[3].len() >> 1) as f64,
            );
            c.rotate(-::std::f64::consts::FRAC_PI_2);
            c.show_text(entries[3].as_str());
        }
    }

    pub fn draw(&self, c: &cairo::Context, width: f64, height: f64) {
        let x_start = if self.label_callbacks.is_some() {
            LEFT_WIDTH
        } else {
            1.0
        };

        c.set_source_rgb(0.95, 0.95, 0.95);
        c.rectangle(x_start, 1.0, width - 1.0, height - 2.0);
        c.fill();
        c.set_source_rgb(0.0, 0.0, 0.0);
        c.set_line_width(1.0);
        c.move_to(x_start, 0.0);
        c.line_to(x_start, height);
        c.move_to(width, 0.0);
        c.line_to(width, height);
        c.move_to(x_start, 0.0);
        c.line_to(width, 0.0);
        c.move_to(x_start, height);
        c.line_to(width, height);

        // For now it's always 60 seconds.
        let time = 60.;
        let x_step = (width - 2.0 - x_start) * 5.0 / (time as f64);
        let mut current = width * (x_step / 5.0) - 1.0;
        if x_step < 0.1 {
            c.stroke();
            return;
        }

        while current > x_start {
            c.move_to(current, 0.0);
            c.line_to(current, height);
            current -= x_step;
        }
        let step = height / 10.0;
        current = step - 1.0;
        while current < height - 1. {
            c.move_to(x_start, current);
            c.line_to(width - 1.0, current);
            current += step;
        }
        c.stroke();

        if let Some(ref self_max) = self.max {
            let mut max = if self.keep_max {
                *self_max.borrow()
            } else {
                1.
            };
            let len = self.data[0].len() - 1;
            for x in 0..len {
                for entry in &self.data {
                    if entry[x] > max {
                        max = entry[x];
                    }
                }
            }
            if let Some(min) = self.minimum {
                if min > max {
                    max = min;
                }
            } else if let Some(over) = self.overhead {
                max = max + max * over / 100.;
            }
            if !self.data.is_empty() && !self.data[0].is_empty() {
                let len = self.data[0].len() - 1;
                let step = (width - 2.0 - x_start) / len as f64;
                current = x_start + 1.0;
                let mut index = len;
                while current > x_start && index > 0 {
                    for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                        c.set_source_rgb(color.r, color.g, color.b);
                        c.move_to(
                            current + step,
                            height - entry[index - 1] / max * (height - 1.0),
                        );
                        c.line_to(current, height - entry[index] / max * (height - 1.0));
                        c.stroke();
                    }
                    current += step;
                    index -= 1;
                }
            }
            if max > *self_max.borrow() || !self.keep_max {
                *self_max.borrow_mut() = max;
            }
            self.draw_labels(c, max, height);
        } else if !self.data.is_empty() && !self.data[0].is_empty() {
            let len = self.data[0].len() - 1;
            let step = (width - 2.0 - x_start) / (len as f64);
            current = x_start + 1.0;
            let mut index = len;
            while current > x_start && index > 0 {
                for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                    c.set_source_rgb(color.r, color.g, color.b);
                    c.move_to(current + step, height - entry[index - 1] * (height - 1.0));
                    c.line_to(current, height - entry[index] * (height - 1.0));
                    c.stroke();
                }
                current += step;
                index -= 1;
            }
            // To be called in last to avoid having to restore state (rotation).
            self.draw_labels(c, 100., height);
        }
    }

    pub fn invalidate(&self) {
        if let Some(t_win) = self.area.get_window() {
            let (x, y) = self
                .area
                .translate_coordinates(&self.area, 0, 0)
                .expect("translate_coordinates failed");
            let rect = gdk::Rectangle {
                x,
                y,
                width: self.area.get_allocated_width(),
                height: self.area.get_allocated_height(),
            };
            t_win.invalidate_rect(Some(&rect), true);
        }
    }

    pub fn send_size_request(&self, width: Option<i32>) {
        let mut width = match width {
            Some(w) => w,
            None => {
                if let Some(parent) = self.area.get_parent() {
                    parent.get_allocation().width
                        - parent.get_margin_start()
                        - parent.get_margin_end()
                } else {
                    eprintln!(
                        "<Graph::send_size_request> A parent is required if no width is \
                               provided..."
                    );
                    return;
                }
            }
        };
        // This condition is to avoid having a graph with a bigger width than the window.
        if let Some(top) = self.area.get_toplevel() {
            let max_width = top.get_allocation().width;
            if width > max_width {
                width = max_width;
            }
        }
        self.area.set_size_request(
            if *self.display_labels.borrow() {
                width
                    - if width >= self.labels_layout_width {
                        self.labels_layout_width
                    } else {
                        width
                    }
            } else {
                width
            },
            200,
        );
    }
}

pub trait Connecter {
    fn connect_to_window_events(&self);
}

impl Connecter for Rc<RefCell<Graph>> {
    fn connect_to_window_events(&self) {
        let s = self.clone();
        if let Some(parent) = self.borrow().horizontal_layout.get_toplevel() {
            // TODO: ugly way to resize drawing area, I should find a better way
            parent.connect_configure_event(move |w, _| {
                let need_diff = s.borrow().initial_diff.is_none();
                if need_diff {
                    let mut s = s.borrow_mut();
                    let parent_width = if let Some(p) = s.area.get_parent() {
                        p.get_allocation().width
                    } else {
                        0
                    };
                    s.initial_diff = Some(w.get_allocation().width - parent_width);
                }
                s.borrow().send_size_request(None);
                false
            });
        } else {
            eprintln!("This method needs to be called *after* it has been put inside a window");
        }
    }
}
<commit_msg>Improve line drawing in graphs<commit_after>use cairo;
use gdk::{self, WindowExt};
use gtk::{self, BoxExt, ContainerExt, DrawingArea, ScrolledWindowExt, StateFlags, WidgetExt};
use std::cell::RefCell;

use std::rc::Rc;

use color::Color;
use utils::RotateVec;

const LEFT_WIDTH: f64 = 31.;

pub struct Graph {
    colors: Vec<Color>,
    pub data: Vec<RotateVec<f64>>,
    vertical_layout: gtk::Box,
    scroll_layout: gtk::ScrolledWindow,
    horizontal_layout: gtk::Box,
    pub area: DrawingArea,
    max: Option<RefCell<f64>>,
    keep_max: bool,
    display_labels: RefCell<bool>,
    initial_diff: Option<i32>,
    label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    labels_layout_width: i32,
    /// `minimum` is used only if `max` is set: it'll be the minimum that the `max` value will
    /// be able to go down.
    minimum: Option<f64>,
    // In %, from 0 to whatever
    overhead: Option<f64>,
}

impl Graph {
    /// If `max` is `None`, the graph will expect values between 0 and 1.
    ///
    /// If `keep_max` is set to `true`, then this value will never go down, meaning that graphs
    /// won't rescale down. It is not taken into account if `max` is `None`.
    pub fn new(max: Option<f64>, keep_max: bool) -> Graph {
        let g = Graph {
            colors: vec![],
            data: vec![],
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0),
            scroll_layout: gtk::ScrolledWindow::new(
                None::<&gtk::Adjustment>,
                None::<&gtk::Adjustment>,
            ),
            horizontal_layout: gtk::Box::new(gtk::Orientation::Horizontal, 0),
            area: DrawingArea::new(),
            max: if let Some(max) = max {
                Some(RefCell::new(max))
            } else {
                None
            },
            keep_max,
            display_labels: RefCell::new(true),
            initial_diff: None,
            label_callbacks: None,
            labels_layout_width: 80,
            minimum: None,
            overhead: None,
        };
        g.scroll_layout.set_min_content_width(g.labels_layout_width);
        g.scroll_layout.add(&g.vertical_layout);
        g.horizontal_layout.pack_start(&g.area, true, true, 0);
        g.horizontal_layout
            .pack_start(&g.scroll_layout, false, true, 10);
        g.horizontal_layout.set_margin_start(5);
        g
    }

    pub fn set_minimum(&mut self, minimum: Option<f64>) {
        self.minimum = minimum;
    }

    pub fn set_overhead(&mut self, overhead: Option<f64>) {
        if let Some(o) = overhead {
            assert!(o >= 0.);
        }
        self.overhead = overhead;
    }

    /// Changes the size of the layout containing labels (the one on the right).
    pub fn set_labels_width(&mut self, labels_layout_width: u32) {
        self.scroll_layout
            .set_min_content_width(labels_layout_width as i32);
        self.labels_layout_width = labels_layout_width as i32;
    }

    pub fn set_label_callbacks(
        &mut self,
        label_callbacks: Option<Box<dyn Fn(f64) -> [String; 4]>>,
    ) {
        self.label_callbacks = label_callbacks;
    }

    pub fn set_display_labels(&self, display_labels: bool) {
        *self.display_labels.borrow_mut() = display_labels;
        if display_labels {
            self.scroll_layout.show_all();
        } else {
            self.scroll_layout.hide();
        }
        self.invalidate();
    }

    pub fn hide(&self) {
        self.horizontal_layout.hide();
    }

    pub fn show_all(&self) {
        self.horizontal_layout.show_all();
        if !*self.display_labels.borrow() {
            self.scroll_layout.hide();
        }
    }

    pub fn attach_to(&self, to: &gtk::Box) {
        to.add(&self.horizontal_layout);
    }

    pub fn push(&mut self, d: RotateVec<f64>, s: &str, override_color: Option<usize>) {
        let c = if let Some(over) = override_color {
            Color::generate(over)
        } else {
            Color::generate(self.data.len() + 11)
        };
        let l = gtk::Label::new(Some(s));
        l.override_color(
            StateFlags::from_bits(0).expect("from_bits failed"),
            Some(&c.to_gdk()),
        );
        self.vertical_layout.add(&l);
        self.colors.push(c);
        self.data.push(d);
    }

    fn draw_labels(&self, c: &cairo::Context, max: f64, height: f64) {
        if let Some(ref call) = self.label_callbacks {
            let entries = call(max);
            let font_size = 8.;

            c.set_source_rgb(0., 0., 0.);
            c.set_font_size(font_size);

            c.move_to(LEFT_WIDTH - 4. - entries[0].len() as f64 * 4., font_size);
            c.show_text(entries[0].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[1].len() as f64 * 4., height / 2.);
            c.show_text(entries[1].as_str());

            c.move_to(LEFT_WIDTH - 4. - entries[2].len() as f64 * 4., height - 2.);
            c.show_text(entries[2].as_str());

            c.move_to(
                font_size - 1.,
                height / 2. + 4. * (entries[3].len() >> 1) as f64,
            );
            c.rotate(-::std::f64::consts::FRAC_PI_2);
            c.show_text(entries[3].as_str());
        }
    }

    pub fn draw(&self, c: &cairo::Context, width: f64, height: f64) {
        let x_start = if self.label_callbacks.is_some() {
            LEFT_WIDTH
        } else {
            1.0
        };

        // to limit line "fuzziness"
        #[inline]
        fn rounder(x: f64) -> f64 {
            let fract = x.fract();
            if fract < 0.5 {
                x.trunc()
            } else {
                x.trunc() + 1.
            }
        }

        c.set_source_rgb(0., 0., 0.);
        c.rectangle(x_start, 0., width, height);
        c.fill();
        c.set_source_rgb(0.5, 0.5, 0.5);
        c.set_line_width(0.5);

        // For now it's always 60 seconds.
        let time = 60.;
        let x_step = (width - 2.0 - x_start) * 5.0 / (time as f64);
        let mut current = width * (x_step / 5.0) - 1.0;
        if x_step < 0.1 {
            c.stroke();
            return;
        }

        while current > x_start {
            c.move_to(rounder(current), 0.0);
            c.line_to(rounder(current), height);
            current -= x_step;
        }
        let step = height / 10.0;
        current = step - 1.0;
        while current < height - 1. {
            c.move_to(x_start, rounder(current));
            c.line_to(width, rounder(current));
            current += step;
        }
        c.stroke();

        c.set_line_width(1.);

        if let Some(ref self_max) = self.max {
            let mut max = if self.keep_max {
                *self_max.borrow()
            } else {
                1.
            };
            let len = self.data[0].len() - 1;
            for x in 0..len {
                for entry in &self.data {
                    if entry[x] > max {
                        max = entry[x];
                    }
                }
            }
            if let Some(min) = self.minimum {
                if min > max {
                    max = min;
                }
            } else if let Some(over) = self.overhead {
                max = max + max * over / 100.;
            }
            if !self.data.is_empty() && !self.data[0].is_empty() {
                let len = self.data[0].len() - 1;
                let step = (width - 2.0 - x_start) / len as f64;
                current = x_start + 1.0;
                let mut index = len;
                while current > x_start && index > 0 {
                    for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                        c.set_source_rgb(color.r, color.g, color.b);
                        c.move_to(
                            current + step,
                            height - entry[index - 1] / max * (height - 1.0),
                        );
                        c.line_to(current, height - entry[index] / max * (height - 1.0));
                        c.stroke();
                    }
                    current += step;
                    index -= 1;
                }
            }
            if max > *self_max.borrow() || !self.keep_max {
                *self_max.borrow_mut() = max;
            }
            self.draw_labels(c, max, height);
        } else if !self.data.is_empty() && !self.data[0].is_empty() {
            let len = self.data[0].len() - 1;
            let step = (width - 2.0 - x_start) / (len as f64);
            current = x_start + 1.0;
            let mut index = len;
            while current > x_start && index > 0 {
                for (entry, color) in self.data.iter().zip(self.colors.iter()) {
                    c.set_source_rgb(color.r, color.g, color.b);
                    c.move_to(current + step, height - entry[index - 1] * (height - 1.0));
                    c.line_to(current, height - entry[index] * (height - 1.0));
                    c.stroke();
                }
                current += step;
                index -= 1;
            }
            // To be called in last to avoid having to restore state (rotation).
            self.draw_labels(c, 100., height);
        }
    }

    pub fn invalidate(&self) {
        if let Some(t_win) = self.area.get_window() {
            let (x, y) = self
                .area
                .translate_coordinates(&self.area, 0, 0)
                .expect("translate_coordinates failed");
            let rect = gdk::Rectangle {
                x,
                y,
                width: self.area.get_allocated_width(),
                height: self.area.get_allocated_height(),
            };
            t_win.invalidate_rect(Some(&rect), true);
        }
    }

    pub fn send_size_request(&self, width: Option<i32>) {
        let mut width = match width {
            Some(w) => w,
            None => {
                if let Some(parent) = self.area.get_parent() {
                    parent.get_allocation().width
                        - parent.get_margin_start()
                        - parent.get_margin_end()
                } else {
                    eprintln!(
                        "<Graph::send_size_request> A parent is required if no width is \
                               provided..."
                    );
                    return;
                }
            }
        };
        // This condition is to avoid having a graph with a bigger width than the window.
        if let Some(top) = self.area.get_toplevel() {
            let max_width = top.get_allocation().width;
            if width > max_width {
                width = max_width;
            }
        }
        self.area.set_size_request(
            if *self.display_labels.borrow() {
                width
                    - if width >= self.labels_layout_width {
                        self.labels_layout_width
                    } else {
                        width
                    }
            } else {
                width
            },
            200,
        );
    }
}

pub trait Connecter {
    fn connect_to_window_events(&self);
}

impl Connecter for Rc<RefCell<Graph>> {
    fn connect_to_window_events(&self) {
        let s = self.clone();
        if let Some(parent) = self.borrow().horizontal_layout.get_toplevel() {
            // TODO: ugly way to resize drawing area, I should find a better way
            parent.connect_configure_event(move |w, _| {
                let need_diff = s.borrow().initial_diff.is_none();
                if need_diff {
                    let mut s = s.borrow_mut();
                    let parent_width = if let Some(p) = s.area.get_parent() {
                        p.get_allocation().width
                    } else {
                        0
                    };
                    s.initial_diff = Some(w.get_allocation().width - parent_width);
                }
                s.borrow().send_size_request(None);
                false
            });
        } else {
            eprintln!("This method needs to be called *after* it has been put inside a window");
        }
    }
}
<|endoftext|>


<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2014, 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use ffi;
use gstr;
use object::ObjectType;
use util::is_true;

use std::fmt;

pub mod raw {
    pub type GType = ::ffi::GType;
}

#[derive(Copy, Eq, PartialEq)]
pub struct GType(raw::GType);

impl GType {

    #[inline]
    pub unsafe fn new(type_id: raw::GType) -> GType {
        GType(type_id)
    }

    #[inline]
    pub fn to_raw(&self) -> raw::GType {
        let GType(type_id) = *self;
        type_id
    }
}

impl fmt::Show for GType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = unsafe { ffi::g_type_name(self.to_raw()) };
        match unsafe { gstr::parse_as_utf8(&name) } {
            Ok(s) => write!(f, "{}", s),
            Err(..) => Err(fmt::Error)
        }
    }
}

pub fn check_instance_is_a<T>(inst: &T, type_id: GType) -> bool
    where T: ObjectType
{
    let raw_inst = inst as *const T as *const ffi::GTypeInstance;
    let raw_type = type_id.to_raw();
    is_true(unsafe { ffi::g_type_check_instance_is_a(raw_inst, raw_type) })
}
<commit_msg>gtype: Added fundamental type constants<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2014, 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use ffi;
use gstr;
use object::ObjectType;
use util::is_true;

use std::fmt;

pub mod raw {
    pub type GType = ::ffi::GType;
}

#[derive(Copy, Eq, PartialEq)]
pub struct GType(raw::GType);

macro_rules! g_type_fundamental {
    ($num:expr) => (GType($num << 2));
}

pub const INVALID   : GType = g_type_fundamental!(0);
pub const NONE      : GType = g_type_fundamental!(1);
pub const INTERFACE : GType = g_type_fundamental!(2);
pub const CHAR      : GType = g_type_fundamental!(3);
pub const UCHAR     : GType = g_type_fundamental!(4);
pub const BOOLEAN   : GType = g_type_fundamental!(5);
pub const INT       : GType = g_type_fundamental!(6);
pub const UINT      : GType = g_type_fundamental!(7);
pub const LONG      : GType = g_type_fundamental!(8);
pub const ULONG     : GType = g_type_fundamental!(9);
pub const INT64     : GType = g_type_fundamental!(10);
pub const UINT64    : GType = g_type_fundamental!(11);
pub const ENUM      : GType = g_type_fundamental!(12);
pub const FLAGS     : GType = g_type_fundamental!(13);
pub const FLOAT     : GType = g_type_fundamental!(14);
pub const DOUBLE    : GType = g_type_fundamental!(15);
pub const STRING    : GType = g_type_fundamental!(16);
pub const POINTER   : GType = g_type_fundamental!(17);
pub const BOXED     : GType = g_type_fundamental!(18);
pub const PARAM     : GType = g_type_fundamental!(19);
pub const OBJECT    : GType = g_type_fundamental!(20);
pub const VARIANT   : GType = g_type_fundamental!(21);

impl GType {

    #[inline]
    pub unsafe fn new(type_id: raw::GType) -> GType {
        GType(type_id)
    }

    #[inline]
    pub fn to_raw(&self) -> raw::GType {
        let GType(type_id) = *self;
        type_id
    }
}

impl fmt::Show for GType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = unsafe { ffi::g_type_name(self.to_raw()) };
        match unsafe { gstr::parse_as_utf8(&name) } {
            Ok(s) => write!(f, "{}", s),
            Err(..) => Err(fmt::Error)
        }
    }
}

pub fn check_instance_is_a<T>(inst: &T, type_id: GType) -> bool
    where T: ObjectType
{
    let raw_inst = inst as *const T as *const ffi::GTypeInstance;
    let raw_type = type_id.to_raw();
    is_true(unsafe { ffi::g_type_check_instance_is_a(raw_inst, raw_type) })
}
<|endoftext|>


<commit_before>// Copyright © 2018 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Quake PAK archive manipulation.

use std::collections::hash_map::Iter;
use std::collections::HashMap;
use std::fs;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

use byteorder::{LittleEndian, ReadBytesExt};
use failure::Error;

const PAK_MAGIC: [u8; 4] = [b'P', b'A', b'C', b'K'];
const PAK_ENTRY_SIZE: usize = 64;

pub struct Pak(HashMap<String, Box<[u8]>>);

impl Pak {
    pub fn new<P>(path: P) -> Result<Pak, Error> where P: AsRef<Path> {
        debug!("Opening {}", path.as_ref().to_str().unwrap());

        let mut infile = try!(fs::File::open(path));

        let mut magic = [0u8; 4];
        try!(infile.read(&mut magic));

        ensure!(magic == PAK_MAGIC, "Invalid magic number");

        // Locate the file table

        let wad_offset = match try!(infile.read_i32::<LittleEndian>()) {
            o if o <= 0 => bail!("Negative file table offset"),
            o => o as u32,
        };

        let wad_size = match try!(infile.read_i32::<LittleEndian>()) {
            s if s <= 0 => bail!("Negative file table size"),
            s => s as u32,
        };

        let mut map = HashMap::new();

        for i in 0..(wad_size as usize / PAK_ENTRY_SIZE) {
            let entry_offset = wad_offset as u64 + (i * PAK_ENTRY_SIZE) as u64;
            infile.seek(SeekFrom::Start(entry_offset))?;

            let mut path_bytes = [0u8; 56];
            infile.read(&mut path_bytes)?;

            let file_offset = match infile.read_i32::<LittleEndian>()? {
                o if o <= 0 => bail!("Negative file offset"),
                o => o as u32,
            };

            let file_size = match infile.read_i32::<LittleEndian>()? {
                s if s <= 0 => bail!("Negative file size"),
                s => s as u32,
            };

            let last = {
                let mut _last: usize = 0;
                while path_bytes[_last] != 0 {
                    _last += 1;
                }
                _last
            };

            let path = String::from_utf8(path_bytes[0..last].to_vec())?;

            infile.seek(SeekFrom::Start(file_offset as u64))?;

            let mut data: Vec<u8> = Vec::with_capacity(file_size as usize);
            (&mut infile).take(file_size as u64).read_to_end(&mut data)?;

            map.insert(path, data.into_boxed_slice());
        }

        Ok(Pak(map))
    }

    /// Opens a file in the file tree for reading.
    ///
    /// # Examples
    /// ```no_run
    /// # extern crate richter;
    /// use richter::common::pak::Pak;
    ///
    /// # fn main() {
    /// let mut pak = Pak::new();
    /// pak.add("pak0.pak").unwrap();
    /// let progs_dat = pak.open("progs.dat").unwrap();
    /// # }
    /// ```
    pub fn open<S>(&self, path: S) -> Result<&[u8], Error>
    where
        S: AsRef<str>,
    {
        match self.0.get(path.as_ref()) {
            Some(data) => Ok(&data),
            None => bail!("No \"{}\" in pakfile", path.as_ref()),
        }
    }

    pub fn iter<'a>(&self) -> Iter<String, impl AsRef<[u8]>> {
        self.0.iter()
    }
}
<commit_msg>Update pak doctest<commit_after>// Copyright © 2018 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Quake PAK archive manipulation.

use std::collections::hash_map::Iter;
use std::collections::HashMap;
use std::fs;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

use byteorder::{LittleEndian, ReadBytesExt};
use failure::Error;

const PAK_MAGIC: [u8; 4] = [b'P', b'A', b'C', b'K'];
const PAK_ENTRY_SIZE: usize = 64;

pub struct Pak(HashMap<String, Box<[u8]>>);

impl Pak {
    pub fn new<P>(path: P) -> Result<Pak, Error>
    where
        P: AsRef<Path>,
    {
        debug!("Opening {}", path.as_ref().to_str().unwrap());

        let mut infile = try!(fs::File::open(path));

        let mut magic = [0u8; 4];
        try!(infile.read(&mut magic));

        ensure!(magic == PAK_MAGIC, "Invalid magic number");

        // Locate the file table

        let wad_offset = match try!(infile.read_i32::<LittleEndian>()) {
            o if o <= 0 => bail!("Negative file table offset"),
            o => o as u32,
        };

        let wad_size = match try!(infile.read_i32::<LittleEndian>()) {
            s if s <= 0 => bail!("Negative file table size"),
            s => s as u32,
        };

        let mut map = HashMap::new();

        for i in 0..(wad_size as usize / PAK_ENTRY_SIZE) {
            let entry_offset = wad_offset as u64 + (i * PAK_ENTRY_SIZE) as u64;
            infile.seek(SeekFrom::Start(entry_offset))?;

            let mut path_bytes = [0u8; 56];
            infile.read(&mut path_bytes)?;

            let file_offset = match infile.read_i32::<LittleEndian>()? {
                o if o <= 0 => bail!("Negative file offset"),
                o => o as u32,
            };

            let file_size = match infile.read_i32::<LittleEndian>()? {
                s if s <= 0 => bail!("Negative file size"),
                s => s as u32,
            };

            let last = {
                let mut _last: usize = 0;
                while path_bytes[_last] != 0 {
                    _last += 1;
                }
                _last
            };

            let path = String::from_utf8(path_bytes[0..last].to_vec())?;

            infile.seek(SeekFrom::Start(file_offset as u64))?;

            let mut data: Vec<u8> = Vec::with_capacity(file_size as usize);
            (&mut infile).take(file_size as u64).read_to_end(&mut data)?;

            map.insert(path, data.into_boxed_slice());
        }

        Ok(Pak(map))
    }

    /// Opens a file in the file tree for reading.
    ///
    /// # Examples
    /// ```no_run
    /// # extern crate richter;
    /// use richter::common::pak::Pak;
    ///
    /// # fn main() {
    /// let mut pak = Pak::new("pak0.pak").unwrap();
    /// let progs_dat = pak.open("progs.dat").unwrap();
    /// # }
    /// ```
    pub fn open<S>(&self, path: S) -> Result<&[u8], Error>
    where
        S: AsRef<str>,
    {
        match self.0.get(path.as_ref()) {
            Some(data) => Ok(&data),
            None => bail!("No \"{}\" in pakfile", path.as_ref()),
        }
    }

    pub fn iter<'a>(&self) -> Iter<String, impl AsRef<[u8]>> {
        self.0.iter()
    }
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 Tyler Neely

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
extern crate libc;
use self::libc::{c_char, c_int, c_void, size_t};
use std::ffi::CString;
use std::mem;
use std::ptr;
use std::slice;

use rocksdb_options::{RocksDBOptions};
use rocksdb::RocksDB;

pub struct ComparatorCallback {
    pub name: CString,
    pub f: fn (&[u8], &[u8]) -> i32,
}

pub extern "C" fn destructor_callback(raw_cb: *mut c_void) {
    // turn this back into a local variable so rust will reclaim it
    let _: Box<ComparatorCallback> = unsafe {mem::transmute(raw_cb)};
}

pub extern "C" fn name_callback(raw_cb: *mut c_void) -> *const c_char {
    unsafe {
        let cb: &mut ComparatorCallback =
            &mut *(raw_cb as *mut ComparatorCallback);
        let ptr = cb.name.as_ptr();
        ptr as *const c_char
    }
}

pub extern "C" fn compare_callback(
    raw_cb: *mut c_void,
    a_raw: *const c_char, a_len: size_t,
    b_raw: *const c_char, b_len: size_t) -> c_int {
    unsafe {
        let cb: &mut ComparatorCallback =
            &mut *(raw_cb as *mut ComparatorCallback);
        let a: &[u8] = slice::from_raw_parts(a_raw as *const u8, a_len as usize);
        let b: &[u8] = slice::from_raw_parts(b_raw as *const u8, b_len as usize);
        (cb.f)(a, b)
    }
}

fn test_reverse_compare(a: &[u8], b: &[u8]) -> c_int {
    if a < b {
        1
    } else if a > b {
        -1
    } else {
        0
    }
}

#[allow(dead_code)]
#[test]
fn compare_works() {
    let path = "_rust_rocksdb_comparetest";
    let opts = RocksDBOptions::new();
    opts.create_if_missing(true);
    opts.add_comparator("test comparator", test_reverse_compare);
    let db = RocksDB::open(opts, path).unwrap();
    // TODO add interesting test
    db.close();
    assert!(RocksDB::destroy(opts, path).is_ok());
}
<commit_msg>Cleanup imports.<commit_after>/*
   Copyright 2014 Tyler Neely

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
extern crate libc;
use self::libc::{c_char, c_int, c_void, size_t};
use std::ffi::CString;
use std::mem;
use std::ptr;
use std::slice;

use rocksdb_options::RocksDBOptions;
use rocksdb::RocksDB;

pub struct ComparatorCallback {
    pub name: CString,
    pub f: fn (&[u8], &[u8]) -> i32,
}

pub extern "C" fn destructor_callback(raw_cb: *mut c_void) {
    // turn this back into a local variable so rust will reclaim it
    let _: Box<ComparatorCallback> = unsafe {mem::transmute(raw_cb)};
}

pub extern "C" fn name_callback(raw_cb: *mut c_void) -> *const c_char {
    unsafe {
        let cb: &mut ComparatorCallback =
            &mut *(raw_cb as *mut ComparatorCallback);
        let ptr = cb.name.as_ptr();
        ptr as *const c_char
    }
}

pub extern "C" fn compare_callback(
    raw_cb: *mut c_void,
    a_raw: *const c_char, a_len: size_t,
    b_raw: *const c_char, b_len: size_t) -> c_int {
    unsafe {
        let cb: &mut ComparatorCallback =
            &mut *(raw_cb as *mut ComparatorCallback);
        let a: &[u8] = slice::from_raw_parts(a_raw as *const u8, a_len as usize);
        let b: &[u8] = slice::from_raw_parts(b_raw as *const u8, b_len as usize);
        (cb.f)(a, b)
    }
}

fn test_reverse_compare(a: &[u8], b: &[u8]) -> c_int {
    if a < b {
        1
    } else if a > b {
        -1
    } else {
        0
    }
}

#[allow(dead_code)]
#[test]
fn compare_works() {
    let path = "_rust_rocksdb_comparetest";
    let opts = RocksDBOptions::new();
    opts.create_if_missing(true);
    opts.add_comparator("test comparator", test_reverse_compare);
    let db = RocksDB::open(opts, path).unwrap();
    // TODO add interesting test
    db.close();
    assert!(RocksDB::destroy(opts, path).is_ok());
}
<|endoftext|>


<commit_before>/* diosix hypervisor manifest file-system management
 *
 * (c) Chris Williams, 2020-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::physmem;
use super::error::Cause;
use super::capsule;
use super::hardware;
use super::loader;
use super::virtmem::Mapping;
use super::vcore::Priority;
use dmfs::{ManifestImageIter, ManifestObject, ManifestObjectType, ManifestObjectData};
use alloc::string::String;
use alloc::vec::Vec;

/* bring in the built-in dmfs image */
use core::slice;
use core::intrinsics::transmute;
extern "C"
{
    static _binary_dmfs_img_start: u8;
    static _binary_dmfs_img_size: u8;
}

/* convert the included dmfs image into a byte slice */
macro_rules! get_dmfs_image
{
    () =>
    {
        unsafe
        {
            slice::from_raw_parts
            (
                transmute(&_binary_dmfs_img_start),
                transmute(&_binary_dmfs_img_size)
            )
        }
    }
}

/* return a list of a DMFS image's asset names and descriptions
   <= array of (names, descriptions) of image's assets */
pub fn list_assets() ->  Result<Vec<(String, String)>, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    let mut list: Vec<(String, String)> = Vec::new();
    for asset in manifest
    {
        list.push((asset.get_name(), asset.get_description()));
    }

    Ok(list)
}

/* look up an asset from the given DMFS image by its name */
pub fn get_named_asset(name: &str) -> Result<ManifestObject, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        /* sadly no simple strcmp() in rust? */
        if asset.get_name().as_str().starts_with(name) == true && asset.get_name().len() == name.len()
        {
            return Ok(asset);
        }
    }

    Err(Cause::ManifestNoSuchAsset)
}

/* parse the hypervisor's bundled manifest, creating services and capsules as required,
   and output any included boot banner messages, during system start up */
pub fn unpack_at_boot() -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        match asset.get_type()
        {
            /* only unpack and process boot messages and system services at startup */
            ManifestObjectType::BootMsg => load_asset(asset)?,
            ManifestObjectType::SystemService => load_asset(asset)?,
            ManifestObjectType::GuestOS => load_asset(asset)?,
            _ => ()
        }
    }

    Ok(())
}

/* process the given asset, such as printing it to the debug output stream if it's a boot message
   or parsing it and running it if it's an executable, from the given DMFS image
   => asset = manifest asset to parse and process into memory
*/
pub fn load_asset(asset: ManifestObject) -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let properties = asset.get_properties();
    let content = match asset.get_contents()
    {
        ManifestObjectData::Bytes(b) => b.as_slice(),
        ManifestObjectData::Region(r) => &image[r.start..r.end]
    };

    match asset.get_type()
    {
        /* print the included boot message */
        ManifestObjectType::BootMsg =>
        {
            hvdebugraw!("\n{}\n\n", String::from_utf8_lossy(content));
            debughousekeeper!(); /* ensure the message is seen */
        },

        /* create and run a system service */
        ManifestObjectType::SystemService => match create_capsule_from_exec(content, Some(properties))
        {
            Ok(cid) => hvdebug!("Created system service {} ({}) {} bytes (capsule {})",
                        asset.get_name(), asset.get_description(), asset.get_contents_size(), cid),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        /* create an included guest OS (which does not have any special permissions) */
        ManifestObjectType::GuestOS => match create_capsule_from_exec(content, None)
        {
            Ok(cid) => hvdebug!("Created guest OS {} ({}) {} bytes (capsule {})",
                        asset.get_name(), asset.get_description(), asset.get_contents_size(), cid),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        t => hvdebug!("Found manifest object type {:?}", t)
    }

    Ok(())
}

/* create a capsule from an executable in a DMFS image
   => binary = slice containing the executable to parse and load
      properties = permissions and other properties to grant the capsule, or None
   <= Ok with capusle ID, or an error code
*/
fn create_capsule_from_exec(binary: &[u8], properties: Option<Vec<String>>) -> Result<capsule::CapsuleID, Cause>
{
    /* assign one virtual CPU core to the capsule */
    let cpus = 1;

    /* create capsule with the given properties */
    let capid = capsule::create(properties, cpus)?;

    /* reserve 128MB of physical RAM for the capsule */
    let size = 128 * 1024 * 1024;
    let ram = physmem::alloc_region(size)?;

    /* create device tree blob for the virtual hardware available to the guest
    capsule and copy into the end of the region's physical RAM.
    a zero-length DTB indicates something went wrong */
    let guest_dtb = hardware::clone_dtb_for_capsule(cpus, 0, ram.base(), ram.size())?;
    if guest_dtb.len() == 0
    {
        return Err(Cause::BootDeviceTreeBad);
    }
    let guest_dtb_base = ram.fill_end(guest_dtb)?;

    /* map that physical RAM into the capsule */
    let mut mapping = Mapping::new();
    mapping.set_physical(ram);
    mapping.identity_mapping()?;
    capsule::map_memory(capid, mapping)?;

    /* parse + copy the capsule's binary into its physical RAM */
    let entry = loader::load(ram, binary)?;

    /* create virtual CPU cores for the capsule as required */
    for vcoreid in 0..cpus
    {
        capsule::add_vcore(capid, vcoreid, entry, guest_dtb_base, Priority::High)?;
    }

    Ok(capid)
}<commit_msg>Up base RAM for capsules to 256MB<commit_after>/* diosix hypervisor manifest file-system management
 *
 * (c) Chris Williams, 2020-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::physmem;
use super::error::Cause;
use super::capsule;
use super::hardware;
use super::loader;
use super::virtmem::Mapping;
use super::vcore::Priority;
use dmfs::{ManifestImageIter, ManifestObject, ManifestObjectType, ManifestObjectData};
use alloc::string::String;
use alloc::vec::Vec;

/* bring in the built-in dmfs image */
use core::slice;
use core::intrinsics::transmute;
extern "C"
{
    static _binary_dmfs_img_start: u8;
    static _binary_dmfs_img_size: u8;
}

/* convert the included dmfs image into a byte slice */
macro_rules! get_dmfs_image
{
    () =>
    {
        unsafe
        {
            slice::from_raw_parts
            (
                transmute(&_binary_dmfs_img_start),
                transmute(&_binary_dmfs_img_size)
            )
        }
    }
}

/* return a list of a DMFS image's asset names and descriptions
   <= array of (names, descriptions) of image's assets */
pub fn list_assets() ->  Result<Vec<(String, String)>, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    let mut list: Vec<(String, String)> = Vec::new();
    for asset in manifest
    {
        list.push((asset.get_name(), asset.get_description()));
    }

    Ok(list)
}

/* look up an asset from the given DMFS image by its name */
pub fn get_named_asset(name: &str) -> Result<ManifestObject, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        /* sadly no simple strcmp() in rust? */
        if asset.get_name().as_str().starts_with(name) == true && asset.get_name().len() == name.len()
        {
            return Ok(asset);
        }
    }

    Err(Cause::ManifestNoSuchAsset)
}

/* parse the hypervisor's bundled manifest, creating services and capsules as required,
   and output any included boot banner messages, during system start up */
pub fn unpack_at_boot() -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        match asset.get_type()
        {
            /* only unpack and process boot messages and system services at startup */
            ManifestObjectType::BootMsg => load_asset(asset)?,
            ManifestObjectType::SystemService => load_asset(asset)?,
            ManifestObjectType::GuestOS => load_asset(asset)?,
            _ => ()
        }
    }

    Ok(())
}

/* process the given asset, such as printing it to the debug output stream if it's a boot message
   or parsing it and running it if it's an executable, from the given DMFS image
   => asset = manifest asset to parse and process into memory
*/
pub fn load_asset(asset: ManifestObject) -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let properties = asset.get_properties();
    let content = match asset.get_contents()
    {
        ManifestObjectData::Bytes(b) => b.as_slice(),
        ManifestObjectData::Region(r) => &image[r.start..r.end]
    };

    match asset.get_type()
    {
        /* print the included boot message */
        ManifestObjectType::BootMsg =>
        {
            hvdebugraw!("\n{}\n\n", String::from_utf8_lossy(content));
            debughousekeeper!(); /* ensure the message is seen */
        },

        /* create and run a system service */
        ManifestObjectType::SystemService => match create_capsule_from_exec(content, Some(properties))
        {
            Ok(cid) => hvdebug!("Created system service {} ({}) {} bytes (capsule {})",
                        asset.get_name(), asset.get_description(), asset.get_contents_size(), cid),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        /* create an included guest OS (which does not have any special permissions) */
        ManifestObjectType::GuestOS => match create_capsule_from_exec(content, None)
        {
            Ok(cid) => hvdebug!("Created guest OS {} ({}) {} bytes (capsule {})",
                        asset.get_name(), asset.get_description(), asset.get_contents_size(), cid),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        t => hvdebug!("Found manifest object type {:?}", t)
    }

    Ok(())
}

/* create a capsule from an executable in a DMFS image
   => binary = slice containing the executable to parse and load
      properties = permissions and other properties to grant the capsule, or None
   <= Ok with capusle ID, or an error code
*/
fn create_capsule_from_exec(binary: &[u8], properties: Option<Vec<String>>) -> Result<capsule::CapsuleID, Cause>
{
    /* assign one virtual CPU core to the capsule */
    let cpus = 1;

    /* create capsule with the given properties */
    let capid = capsule::create(properties, cpus)?;

    /* reserve 256MB of physical RAM for the capsule */
    let size = 256 * 1024 * 1024;
    let ram = physmem::alloc_region(size)?;

    /* create device tree blob for the virtual hardware available to the guest
    capsule and copy into the end of the region's physical RAM.
    a zero-length DTB indicates something went wrong */
    let guest_dtb = hardware::clone_dtb_for_capsule(cpus, 0, ram.base(), ram.size())?;
    if guest_dtb.len() == 0
    {
        return Err(Cause::BootDeviceTreeBad);
    }
    let guest_dtb_base = ram.fill_end(guest_dtb)?;

    /* map that physical RAM into the capsule */
    let mut mapping = Mapping::new();
    mapping.set_physical(ram);
    mapping.identity_mapping()?;
    capsule::map_memory(capid, mapping)?;

    /* parse + copy the capsule's binary into its physical RAM */
    let entry = loader::load(ram, binary)?;

    /* create virtual CPU cores for the capsule as required */
    for vcoreid in 0..cpus
    {
        capsule::add_vcore(capid, vcoreid, entry, guest_dtb_base, Priority::High)?;
    }

    Ok(capid)
}<|endoftext|>