<commit_before>use std::fmt;
use std::marker::PhantomData;
use gfx::{self, Resources, ProgramError};
use gfx::traits::{IntoCanvas, Factory, FactoryExt, Stream};
use gfx::shade::TextureParam;
use gfx::device::tex::TextureError;
use gfx::extra::canvas::Canvas;
use gfx::batch::OwnedBatch;
use gfx::batch::Error as BatchError;
use gfx_device_gl as dgl;
use gfx_window_glutin as gfxw;
use glutin::{CreationError, WindowBuilder};
use glutin::Event::{Closed, KeyboardInput};
use glutin::ElementState::Pressed;
use glutin::VirtualKeyCode as Key;
use gfx_text;
use ::ivf;
use ::vpx;

#[derive(Debug)]
pub enum Error {
    GlutinCreationError(CreationError),
    GfxProgramError(ProgramError),
    GfxTextureError(TextureError),
    GfxBatchError(BatchError),
    TextError(gfx_text::Error),
}

impl From<CreationError> for Error {
    fn from(e: CreationError) -> Error { Error::GlutinCreationError(e) }
}

impl From<ProgramError> for Error {
    fn from(e: ProgramError) -> Error { Error::GfxProgramError(e) }
}

impl From<TextureError> for Error {
    fn from(e: TextureError) -> Error { Error::GfxTextureError(e) }
}

impl From<BatchError> for Error {
    fn from(e: BatchError) -> Error { Error::GfxBatchError(e) }
}

impl From<gfx_text::Error> for Error {
    fn from(e: gfx_text::Error) -> Error { Error::TextError(e) }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let descr = match *self {
            Error::GlutinCreationError(ref err) => format!("{}", err),
            Error::GfxProgramError(ref err) => format!("{:?}", err),
            Error::GfxTextureError(ref err) => format!("{:?}", err),
            Error::GfxBatchError(ref err) => format!("{:?}", err),
            Error::TextError(ref err) => format!("{:?}", err),
        };
        f.write_str(&descr)
    }
}

gfx_vertex!( Vertex {
    a_Pos@ pos: [f32; 2],
    a_TexCoord@ tex: [f32; 2],
});

gfx_parameters!( ShaderParams/ParamsLink {
    t_Color@ color: TextureParam<R>,
});

static VERTEX_SRC: &'static [u8] = b"
    #version 120

    attribute vec4 a_Pos;
    attribute vec2 a_TexCoord;
    varying vec2 v_TexCoord;

    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = a_Pos;
    }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 120

    varying vec2 v_TexCoord;
    uniform sampler2D t_Color;

    void main() {
        gl_FragColor = texture2D(t_Color, v_TexCoord);
    }
";

const VERTEX_DATA: &'static [Vertex] = &[
    // 1
    // |\
    // | \
    // 2--3
    Vertex {pos: [-1.0,  1.0], tex: [0.0, 0.0]},
    Vertex {pos: [-1.0, -1.0], tex: [0.0, 1.0]},
    Vertex {pos: [ 1.0, -1.0], tex: [1.0, 1.0]},
    // 1--3
    //  \ |
    //   \|
    //    2
    Vertex {pos: [-1.0,  1.0], tex: [0.0, 0.0]},
    Vertex {pos: [ 1.0, -1.0], tex: [1.0, 1.0]},
    Vertex {pos: [ 1.0,  1.0], tex: [1.0, 0.0]},
];
const BACKGROUND: gfx::ClearData = gfx::ClearData {
    color: [0.0, 0.0, 0.0, 1.0],
    depth: 1.0,
    stencil: 0,
};
const TEXT_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const TEXT_HEIGHT: i32 = 16;

type CanvasT = Canvas<gfxw::Output<dgl::Resources>, dgl::Device, dgl::Factory>;
type BatchT = OwnedBatch<ShaderParams<dgl::Resources>>;
type TextRendererT = gfx_text::Renderer<dgl::Resources>;

pub struct Gui {
    reader: ivf::Reader,
    decoder: vpx::Decoder,
    viewport_width: u16,
    viewport_height: u16,
    canvas: CanvasT,
    batch: BatchT,
    text: TextRendererT,
}

pub fn init(reader: ivf::Reader, decoder: vpx::Decoder) -> Result<Gui, Error> {
    let viewport_width = reader.get_width();
    let viewport_height = reader.get_height();
    let mut canvas = {
        // TODO(Kagami): Fullscreen.
        let window = try!(WindowBuilder::new()
            .with_dimensions(viewport_width as u32, viewport_height as u32)
            // Use simple initial title to allow to match the window in tiling
            // window managers.
            .with_title(format!("vpxview"))
            .build());
        gfxw::init(window).into_canvas()
    };
    let batch = {
        let mesh = canvas.factory.create_mesh(VERTEX_DATA);
        let program = try!(canvas.factory.link_program(VERTEX_SRC, FRAGMENT_SRC));
        let texture = try!(canvas.factory.create_texture_rgba8(
            reader.get_width(),
            reader.get_height()));
        let param = ShaderParams {color: (texture, None), _r: PhantomData};
        try!(OwnedBatch::new(mesh, program, param))
    };
    let text = try!(gfx_text::new(&mut canvas.factory).build());
    Ok(Gui {
        reader: reader,
        decoder: decoder,
        viewport_width: viewport_width,
        viewport_height: viewport_height,
        canvas: canvas,
        batch: batch,
        text: text,
    })
}

impl Gui {
    pub fn run(&mut self) {
        self.next_video_frame();
        loop {
            // Skip all pending events except the first because in some cases frame
            // decoding may take too long so interface will be brozen because of
            // big events queue.
            let maybe_event = {
                let mut iter = self.canvas.output.window.poll_events();
                let ev = iter.next();
                iter.count();  // Consume entire iterator
                ev
            };
            match maybe_event {
                Some(Closed) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Escape))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Q))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Left))) => {
                    // TODO(Kagami).
                },
                Some(KeyboardInput(Pressed, _, Some(Key::Right))) => {
                    self.next_video_frame();
                },
                _ => {},
            }
            self.canvas.clear(BACKGROUND);
            let draw_result = self.canvas.draw(&self.batch);
            try_print!(draw_result, "Error occured while drawing the frame: {:?}");
            self.render_hud();
            self.canvas.present();
        }
    }

    /// Read next IVF frame, decode VPx frame if possible and update the
    /// texture.
    fn next_video_frame(&mut self) {
        let maybe_frame = self.reader.next();
        self.update_title();
        let ivf_frame = maybe_print!(maybe_frame, "End of file");
        match self.decoder.decode_many(&ivf_frame) {
            Ok(mut iter) => {
                let image = maybe_print!(iter.next(), "No VPx frames in this IVF frame");
                // TODO(Kagami): IVF frame may consist of several VPx frames, we
                // correctly display only 1 IVF <-> 1 VPx case as for now.
                let remaining = iter.count();
                if remaining != 0 {
                    printerr!("Skipping {} other VPx frames", remaining);
                }
                // TODO(Kagami): Dimensions of decoded VPx image can vary from
                // frame to frame, we can adjust texture size accordingly.
                assert_eq!(image.get_display_width(), self.viewport_width);
                assert_eq!(image.get_display_height(), self.viewport_height);
                let texture = &self.batch.param.color.0;
                let update_result = self.canvas.factory.update_texture_raw(
                    texture,
                    &texture.get_info().to_image_info(),
                    &image.get_rgba8(),
                    None);
                try_print!(update_result, "Error occured while updating texture: {:?}");
            },
            Err(err) => {
                printerr!("Cannot decode IVF frame: {}", err);
            },
        };
    }

    fn get_frame_count(&self) -> String {
        self.reader.get_frame_count().map_or_else(|| "?".to_string(), |n| n.to_string())
    }

    fn update_title(&self) {
        let title = format!("vpxview - {} - {}/{}",
                            self.reader.get_filename(),
                            self.reader.get_frame_pos(),
                            self.get_frame_count());
        self.canvas.output.window.set_title(&title);
    }

    /// Draw given lines sequentially from top to bottom.
    fn draw_lines(&mut self, start_pos: [i32; 2], lines: &[String]) {
        let (x, mut y) = (start_pos[0], start_pos[1]);
        for line in lines {
            self.text.draw(line, [x, y], TEXT_COLOR);
            y += TEXT_HEIGHT;
        }
    }

    /// Render some VPx frame details on canvas.
    fn render_hud(&mut self) {
        let lines = [
            format!("Filename: {}", self.reader.get_filename()),
            format!("Frame: {}/{}", self.reader.get_frame_pos(), self.get_frame_count()),
        ];
        self.draw_lines([10, 10], &lines);
        let draw_result = self.text.draw_end(&mut self.canvas);
        try_print!(draw_result, "Error occured why drawing the text: {:?}");
    }
}
<commit_msg>Use version 150 core for better compatibility<commit_after>use std::fmt;
use std::marker::PhantomData;
use gfx::{self, Resources, ProgramError};
use gfx::traits::{IntoCanvas, Factory, FactoryExt, Stream};
use gfx::shade::TextureParam;
use gfx::device::tex::TextureError;
use gfx::extra::canvas::Canvas;
use gfx::batch::OwnedBatch;
use gfx::batch::Error as BatchError;
use gfx_device_gl as dgl;
use gfx_window_glutin as gfxw;
use glutin::{CreationError, WindowBuilder, GL_CORE};
use glutin::Event::{Closed, KeyboardInput};
use glutin::ElementState::Pressed;
use glutin::VirtualKeyCode as Key;
use gfx_text;
use ::ivf;
use ::vpx;

#[derive(Debug)]
pub enum Error {
    GlutinCreationError(CreationError),
    GfxProgramError(ProgramError),
    GfxTextureError(TextureError),
    GfxBatchError(BatchError),
    TextError(gfx_text::Error),
}

impl From<CreationError> for Error {
    fn from(e: CreationError) -> Error { Error::GlutinCreationError(e) }
}

impl From<ProgramError> for Error {
    fn from(e: ProgramError) -> Error { Error::GfxProgramError(e) }
}

impl From<TextureError> for Error {
    fn from(e: TextureError) -> Error { Error::GfxTextureError(e) }
}

impl From<BatchError> for Error {
    fn from(e: BatchError) -> Error { Error::GfxBatchError(e) }
}

impl From<gfx_text::Error> for Error {
    fn from(e: gfx_text::Error) -> Error { Error::TextError(e) }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let descr = match *self {
            Error::GlutinCreationError(ref err) => format!("{}", err),
            Error::GfxProgramError(ref err) => format!("{:?}", err),
            Error::GfxTextureError(ref err) => format!("{:?}", err),
            Error::GfxBatchError(ref err) => format!("{:?}", err),
            Error::TextError(ref err) => format!("{:?}", err),
        };
        f.write_str(&descr)
    }
}

gfx_vertex!( Vertex {
    a_Pos@ pos: [f32; 2],
    a_TexCoord@ tex: [f32; 2],
});

gfx_parameters!( ShaderParams/ParamsLink {
    t_Color@ color: TextureParam<R>,
});

static VERTEX_SRC: &'static [u8] = b"
    #version 150 core

    in vec4 a_Pos;
    in vec2 a_TexCoord;
    out vec2 v_TexCoord;

    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = a_Pos;
    }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core

    in vec2 v_TexCoord;
    out vec4 o_Color;
    uniform sampler2D t_Color;

    void main() {
        o_Color = texture(t_Color, v_TexCoord);
    }
";

const VERTEX_DATA: &'static [Vertex] = &[
    // 1
    // |\
    // | \
    // 2--3
    Vertex {pos: [-1.0,  1.0], tex: [0.0, 0.0]},
    Vertex {pos: [-1.0, -1.0], tex: [0.0, 1.0]},
    Vertex {pos: [ 1.0, -1.0], tex: [1.0, 1.0]},
    // 1--3
    //  \ |
    //   \|
    //    2
    Vertex {pos: [-1.0,  1.0], tex: [0.0, 0.0]},
    Vertex {pos: [ 1.0, -1.0], tex: [1.0, 1.0]},
    Vertex {pos: [ 1.0,  1.0], tex: [1.0, 0.0]},
];
const BACKGROUND: gfx::ClearData = gfx::ClearData {
    color: [0.0, 0.0, 0.0, 1.0],
    depth: 1.0,
    stencil: 0,
};
const TEXT_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const TEXT_HEIGHT: i32 = 16;

type CanvasT = Canvas<gfxw::Output<dgl::Resources>, dgl::Device, dgl::Factory>;
type BatchT = OwnedBatch<ShaderParams<dgl::Resources>>;
type TextRendererT = gfx_text::Renderer<dgl::Resources>;

pub struct Gui {
    reader: ivf::Reader,
    decoder: vpx::Decoder,
    viewport_width: u16,
    viewport_height: u16,
    canvas: CanvasT,
    batch: BatchT,
    text: TextRendererT,
}

pub fn init(reader: ivf::Reader, decoder: vpx::Decoder) -> Result<Gui, Error> {
    let viewport_width = reader.get_width();
    let viewport_height = reader.get_height();
    let mut canvas = {
        // TODO(Kagami): Fullscreen.
        let window = try!(WindowBuilder::new()
            .with_dimensions(viewport_width as u32, viewport_height as u32)
            // Use simple initial title to allow to match the window in tiling
            // window managers.
            .with_title(format!("vpxview"))
            // To use core shader profile.
            .with_gl(GL_CORE)
            .build());
        gfxw::init(window).into_canvas()
    };
    let batch = {
        let mesh = canvas.factory.create_mesh(VERTEX_DATA);
        let program = try!(canvas.factory.link_program(VERTEX_SRC, FRAGMENT_SRC));
        let texture = try!(canvas.factory.create_texture_rgba8(
            reader.get_width(),
            reader.get_height()));
        let param = ShaderParams {color: (texture, None), _r: PhantomData};
        try!(OwnedBatch::new(mesh, program, param))
    };
    let text = try!(gfx_text::new(&mut canvas.factory).build());
    Ok(Gui {
        reader: reader,
        decoder: decoder,
        viewport_width: viewport_width,
        viewport_height: viewport_height,
        canvas: canvas,
        batch: batch,
        text: text,
    })
}

impl Gui {
    pub fn run(&mut self) {
        self.next_video_frame();
        loop {
            // Skip all pending events except the first because in some cases frame
            // decoding may take too long so interface will be brozen because of
            // big events queue.
            let maybe_event = {
                let mut iter = self.canvas.output.window.poll_events();
                let ev = iter.next();
                iter.count();  // Consume entire iterator
                ev
            };
            match maybe_event {
                Some(Closed) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Escape))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Q))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Left))) => {
                    // TODO(Kagami).
                },
                Some(KeyboardInput(Pressed, _, Some(Key::Right))) => {
                    self.next_video_frame();
                },
                _ => {},
            }
            self.canvas.clear(BACKGROUND);
            let draw_result = self.canvas.draw(&self.batch);
            try_print!(draw_result, "Error occured while drawing the frame: {:?}");
            self.render_hud();
            self.canvas.present();
        }
    }

    /// Read next IVF frame, decode VPx frame if possible and update the
    /// texture.
    fn next_video_frame(&mut self) {
        let maybe_frame = self.reader.next();
        self.update_title();
        let ivf_frame = maybe_print!(maybe_frame, "End of file");
        match self.decoder.decode_many(&ivf_frame) {
            Ok(mut iter) => {
                let image = maybe_print!(iter.next(), "No VPx frames in this IVF frame");
                // TODO(Kagami): IVF frame may consist of several VPx frames, we
                // correctly display only 1 IVF <-> 1 VPx case as for now.
                let remaining = iter.count();
                if remaining != 0 {
                    printerr!("Skipping {} other VPx frames", remaining);
                }
                // TODO(Kagami): Dimensions of decoded VPx image can vary from
                // frame to frame, we can adjust texture size accordingly.
                assert_eq!(image.get_display_width(), self.viewport_width);
                assert_eq!(image.get_display_height(), self.viewport_height);
                let texture = &self.batch.param.color.0;
                let update_result = self.canvas.factory.update_texture_raw(
                    texture,
                    &texture.get_info().to_image_info(),
                    &image.get_rgba8(),
                    None);
                try_print!(update_result, "Error occured while updating texture: {:?}");
            },
            Err(err) => {
                printerr!("Cannot decode IVF frame: {}", err);
            },
        };
    }

    fn get_frame_count(&self) -> String {
        self.reader.get_frame_count().map_or_else(|| "?".to_string(), |n| n.to_string())
    }

    fn update_title(&self) {
        let title = format!("vpxview - {} - {}/{}",
                            self.reader.get_filename(),
                            self.reader.get_frame_pos(),
                            self.get_frame_count());
        self.canvas.output.window.set_title(&title);
    }

    /// Draw given lines sequentially from top to bottom.
    fn draw_lines(&mut self, start_pos: [i32; 2], lines: &[String]) {
        let (x, mut y) = (start_pos[0], start_pos[1]);
        for line in lines {
            self.text.draw(line, [x, y], TEXT_COLOR);
            y += TEXT_HEIGHT;
        }
    }

    /// Render some VPx frame details on canvas.
    fn render_hud(&mut self) {
        let lines = [
            format!("Filename: {}", self.reader.get_filename()),
            format!("Frame: {}/{}", self.reader.get_frame_pos(), self.get_frame_count()),
        ];
        self.draw_lines([10, 10], &lines);
        let draw_result = self.text.draw_end(&mut self.canvas);
        try_print!(draw_result, "Error occured why drawing the text: {:?}");
    }
}
<|endoftext|>


<commit_before>//! This crate provides native rust implementations of
//! image encoders and decoders and basic image manipulation
//! functions.
//!
//! Additional documentation can currently also be found in the
//! [README.md file which is most easily viewed on github](https://github.com/image-rs/image/blob/master/README.md).
//!
//! [Jump forward to crate content](#reexports)
//!
//! # Overview
//!
//! There are two core problems for which this library provides solutions: a unified interface for image
//! encodings and simple generic buffers for their content. It's possible to use either feature
//! without the other. The focus is on a small and stable set of common operations that can be
//! supplemented by other specialized crates. The library also prefers safe solutions with few
//! dependencies.
//!
//! | Format | Decoding | Encoding |
//! | ------ | -------- | -------- |
//! | PNG    | All supported color types | Same as decoding |
//! | JPEG   | Baseline and progressive | Baseline JPEG |
//! | GIF    | Yes | Yes |
//! | BMP    | Yes | RGB(8), RGBA(8), Gray(8), GrayA(8) |
//! | ICO    | Yes | Yes |
//! | TIFF   | Baseline(no fax support) + LZW + PackBits | RGB(8), RGBA(8), Gray(8) |
//! | WebP   | Lossy(Luma channel only) | No |
//! | PNM    | PBM, PGM, PPM, standard PAM | Yes |
//! | DDS    | DXT1, DXT3, DXT5 | No |
//! | TGA    | Yes | RGB(8), RGBA(8), BGR(8), BGRA(8), Gray(8), GrayA(8) |
//! | farbfeld | Yes | Yes |
//!
//! ## Using images decoders
//!
//! There exists a huge variety of image formats that are concerned with efficiently encoding image
//! pixel data and auxiliary meta data for many different purposes. The `image` library provides
//! decoders for many common formats, depending on the active features. The best way to use them
//! depends on your use case.
//!
//! * [`open`] is a very simple way to load images from the file system, automatically deducing the
//!   format but offering little customization.
//! * [`load_from_memory`], [`load_from_memory_with_format`] present a similar interface for images
//!   whose encoded data is already present in memory.
//! * [`io::Reader`] is a builder providing a superset of the functions. It offers both
//!   customization and auto-deduction but is slightly more involved. The main benefit is that the
//!   interface is easier to evolve.
//! * [`ImageDecoder`] is a trait for querying meta data and reading image pixels into a generic
//!   byte buffer. It also contains a `Read` adaptor for stream reading the pixels.
//! * [`DynamicImage::from_decoder`] can be used for creating a buffer from a single specific or
//!   any custom decoder implementing the [`ImageDecoder`] trait.
//!
//! [`open`]: fn.open.html
//! [`load_from_memory`]: fn.load_from_memory.html
//! [`load_from_memory_with_format`]: fn.load_from_memory_with_format.html
//! [`io::Reader`]: io/struct.Reader.html
//! [`DynamicImage::from_decoder`]: enum.DynamicImage.html#method.from_decoder
//! [`ImageDecoder`]: trait.ImageDecoder.html
//!
//! ## Using image encoders
//!
//! Encoding pixel data is supported for the majority of formats but not quite as broadly.
//!
//! * [`DynamicImage::save`] is the converse of `open` and stores a `DynamicImage`.
//! * [`DynamicImage::write_to`] can be used to encode an image into any writer, for example into a
//!   vector of bytes in memory.
//! * [`save_buffer`], [`save_buffer_with_format`] provide a low-level interface for saving an image
//!   in the file system, where the library initializes the chosen encoder.
//! * [`ImageEncoder`] is a trait for encoding a byte buffer of image data and the inverse of the
//!   `ImageDecoder` interface.
//!
//! [`save_buffer`]: #fn.save_buffer.html
//! [`save_buffer_with_format`]: #fn.save_buffer_with_format.html
//! [`DynamicImage::save`]: enum.DynamicImage.html#method.save
//! [`DynamicImage::write_to`]: enum.DynamicImage.html#method.write_to
//! [`ImageEncoder`]: trait.ImageEncoder.html
//!
//! ## Image buffers
//!
//! The library adds containers for channel data which together form some representation of a 2D
//! matrix of pixels. These are all statically typed to avoid misinterpretation of byte data (and
//! since Rust has no standard safe encapsulation for reinterpreting byte slices as another type).
//! The main traits [`GenericImageView`] and [`GenericImage`] model a view on a 2D-matrix of
//! addressable pixels and a buffer of independently accessible pixels respectively.
//!
//! The two main types for owning pixel data are [`ImageBuffer`] and [`DynamicImage`]. Note that
//! the latter is an enum over well-supported pixel types that also offers conversion
//! functionality.
//!
//! Additionally, the [`flat`] module contains items for interoperability with generic channel
//! matrices and foreign interface. While still strictly typed, these dynamically validate length and
//! other layout assumptions required to provide the trait interface. While they are quite generic, you
//! should be prepared for a bit of boilerplate when using these types.
//!
//! [`GenericImageView`]: trait.GenericImageView.html
//! [`GenericImage`]: trait.GenericImage.html
//! [`ImageBuffer`]: struct.ImageBuffer.html
//! [`DynamicImage`]: enum.DynamicImage.html
//! [`flat`]: flat/index.html
//!
//! ## A note on format specific features
//!
//! One of the main goals of `image` is stability, in runtime but also for programmers. This
//! ensures that performance as well as safety fixes reach a majority of its user base with little
//! effort. Re-exporting all details of its dependencies would run counter to this goal as it
//! linked _all_ major version bumps between them and `image`. As such, we are wary of exposing too
//! many details, or configuration options, that are not shared between different image formats.
//!
//! Nevertheless, the advantage of precise control is hard to ignore. We will thus consider
//! _wrappers_, not direct re-exports, in either of the following cases:
//!
//! 1. A standard specifies that configuration _x_ is required for decoders/encoders and there
//!    exists an essentially canonical way to control it.
//! 2. At least two different implementations agree on some (sub-)set of features in practice.
//! 3. A technical argument including measurements of the performance, space benefits, or otherwise
//!    objectively quantified benefits can be made, and the added interface is unlikely to require
//!    breaking changes.
//!
//! Features that fulfill two or more criteria are preferred.
//!
//! Re-exports of dependencies that reach version `1` will be discussed when it happens.

#![warn(missing_docs)]
#![warn(unused_qualifications)]
#![deny(unreachable_pub)]
#![deny(deprecated)]
#![deny(missing_copy_implementations)]
#![cfg_attr(all(test, feature = "benchmarks"), feature(test))]
// it's a bit of a pain otherwise
#![allow(clippy::many_single_char_names)]

#[cfg(all(test, feature = "benchmarks"))]
extern crate test;

#[cfg(test)]
#[macro_use]
extern crate quickcheck;

use std::io::Write;

pub use crate::color::{ColorType, ExtendedColorType};

pub use crate::color::{Luma, LumaA, Rgb, Rgba, Bgr, Bgra};

pub use crate::error::{ImageError, ImageResult};

pub use crate::image::{AnimationDecoder,
                GenericImage,
                GenericImageView,
                ImageDecoder,
                ImageDecoderExt,
                ImageEncoder,
                ImageFormat,
                ImageOutputFormat,
                Progress,
                // Iterators
                Pixels,
                SubImage};

pub use crate::buffer_::{
                 GrayAlphaImage,
                 GrayImage,
                 // Image types
                 ImageBuffer,
                 RgbImage,
                 RgbaImage};

pub use crate::flat::FlatSamples;

// Traits
pub use crate::traits::{EncodableLayout, Primitive, Pixel};

// Opening and loading images
pub use crate::io::free_functions::{guess_format, load};
pub use crate::dynimage::{load_from_memory, load_from_memory_with_format, open,
                   save_buffer, save_buffer_with_format, image_dimensions};

pub use crate::dynimage::DynamicImage;

pub use crate::animation::{Delay, Frame, Frames};

// More detailed error type
pub mod error;

/// Iterators and other auxiliary structure for the `ImageBuffer` type.
pub mod buffer {
    // Only those not exported at the top-level
    pub use crate::buffer_::{
        ConvertBuffer,
        EnumeratePixels,
        EnumeratePixelsMut,
        EnumerateRows,
        EnumerateRowsMut,
        Pixels,
        PixelsMut,
        Rows,
        RowsMut,
    };
}

// Math utils
pub mod math;

// Image processing functions
pub mod imageops;

// Io bindings
pub mod io;

// Buffer representations for ffi.
pub mod flat;

/// Encoding and decoding for various image file formats.
pub mod codecs {
    #[cfg(feature = "avif")]
    pub mod avif;
    #[cfg(feature = "bmp")]
    pub mod bmp;
    #[cfg(feature = "dds")]
    pub mod dds;
    #[cfg(feature = "dxt")]
    pub mod dxt;
    #[cfg(feature = "farbfeld")]
    pub mod farbfeld;
    #[cfg(feature = "gif")]
    pub mod gif;
    #[cfg(feature = "hdr")]
    pub mod hdr;
    #[cfg(feature = "ico")]
    pub mod ico;
    #[cfg(feature = "jpeg")]
    pub mod jpeg;
    #[cfg(feature = "png")]
    pub mod png;
    #[cfg(feature = "pnm")]
    pub mod pnm;
    #[cfg(feature = "tga")]
    pub mod tga;
    #[cfg(feature = "tiff")]
    pub mod tiff;
    #[cfg(feature = "webp")]
    pub mod webp;
}

#[cfg(feature = "avif")]
#[deprecated = "Use codecs::avif instead"]
pub mod avif {
    //! Encoding of AVIF images.
    pub use crate::codecs::avif::AvifEncoder;
}
#[cfg(feature = "bmp")]
#[deprecated = "Use codecs::bmp instead"]
pub mod bmp {
    //! Decoding and Encoding of BMP Images
    #[allow(deprecated)]
    pub use crate::codecs::bmp::{BMPEncoder, BmpDecoder, BmpEncoder};
}
#[cfg(feature = "dds")]
#[deprecated = "Use codecs::dds instead"]
pub mod dds {
    //! Decoding of DDS images
    pub use crate::codecs::dds::DdsDecoder;
}
#[cfg(feature = "dxt")]
#[deprecated = "Use codecs:: instead"]
pub mod dxt {
    //! Decoding of DXT (S3TC) compression
    #[allow(deprecated)]
    pub use crate::codecs::dxt::{
        DXTEncoder, DXTReader, DXTVariant, DxtDecoder, DxtEncoder, DxtReader, DxtVariant,
    };
}
#[cfg(feature = "farbfeld")]
#[deprecated = "Use codecs::farbfeld instead"]
pub mod farbfeld {
    //! Decoding of farbfeld images
    pub use crate::codecs::farbfeld::{FarbfeldDecoder, FarbfeldEncoder, FarbfeldReader};
}
#[cfg(feature = "gif")]
#[deprecated = "Use codecs::gif instead"]
pub mod gif {
    //! Decoding of GIF Images
    #[allow(deprecated)]
    pub use crate::codecs::gif::{Encoder, GifDecoder, GifEncoder, GifReader};
}
#[cfg(feature = "hdr")]
#[deprecated = "Use codecs::hdr instead"]
pub mod hdr {
    //! Decoding of Radiance HDR Images
    #[allow(deprecated)]
    pub use crate::codecs::hdr::{
        read_raw_file, rgbe8, to_rgbe8, HDRAdapter, HDREncoder, HDRImageDecoderIterator,
        HDRMetadata, HdrAdapter, HdrDecoder, HdrEncoder, HdrImageDecoderIterator, HdrMetadata,
        HdrReader, RGBE8Pixel, Rgbe8Pixel, SIGNATURE,
    };
}
#[cfg(feature = "ico")]
#[deprecated = "Use codecs::ico instead"]
pub mod ico {
    //! Decoding and Encoding of ICO files
    #[allow(deprecated)]
    pub use crate::codecs::ico::{ICOEncoder, IcoDecoder, IcoEncoder};
}
#[cfg(feature = "jpeg")]
#[deprecated = "Use codecs::jpeg instead"]
pub mod jpeg {
    //! Decoding and Encoding of JPEG Images
    #[allow(deprecated)]
    pub use crate::codecs::jpeg::{
        JPEGEncoder, JpegDecoder, JpegEncoder, PixelDensity, PixelDensityUnit,
    };
}
#[cfg(feature = "png")]
#[deprecated = "Use codecs::png instead"]
pub mod png {
    //! Decoding and Encoding of PNG Images
    #[allow(deprecated)]
    pub use crate::codecs::png::{
        ApngDecoder, PNGEncoder, PNGReader, PngDecoder, PngEncoder, PngReader,
    };
}
#[cfg(feature = "pnm")]
#[deprecated = "Use codecs::pnm instead"]
pub mod pnm {
    //! Decoding and Encoding of netpbm image formats (pbm, pgm, ppm and pam)
    #[allow(deprecated)]
    pub use crate::codecs::pnm::{
        ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PNMEncoder, PNMHeader,
        PNMSubtype, PixmapHeader, PnmDecoder, PnmEncoder, PnmHeader, PnmSubtype, SampleEncoding,
    };
}
#[cfg(feature = "tga")]
#[deprecated = "Use codecs::tga instead"]
pub mod tga {
    //! Decoding and Encoding of TGA Images
    #[allow(deprecated)]
    pub use crate::codecs::tga::{TgaDecoder, TgaEncoder};
}
#[cfg(feature = "tiff")]
#[deprecated = "Use codecs::tiff instead"]
pub mod tiff {
    //! Decoding and Encoding of TIFF Images
    #[allow(deprecated)]
    pub use crate::codecs::tiff::{TiffDecoder, TiffEncoder, TiffReader};
}
#[cfg(feature = "webp")]
#[deprecated = "Use codecs::webp instead"]
pub mod webp {
    //! Decoding of WebP Images
    #[allow(deprecated)]
    pub use crate::codecs::webp::{vp8, WebPDecoder};
}

mod animation;
#[path = "buffer.rs"]
mod buffer_;
mod color;
mod dynimage;
mod image;
mod traits;
mod utils;

// Can't use the macro-call itself within the `doc` attribute. So force it to eval it as part of
// the macro invocation.
//
// The inspiration for the macro and implementation is from
// <https://github.com/GuillaumeGomez/doc-comment>
//
// MIT License
//
// Copyright (c) 2018 Guillaume Gomez
macro_rules! insert_as_doc {
    { $content:expr } => {
        #[doc = $content] extern { }
    }
}

// Provides the README.md as doc, to ensure the example works!
insert_as_doc!(include_str!("../README.md"));

// Copies data from `src` to `dst`
//
// Panics if the length of `dst` is less than the length of `src`.
#[inline]
fn copy_memory(src: &[u8], mut dst: &mut [u8]) {
    let len_src = src.len();
    assert!(dst.len() >= len_src);
    dst.write_all(src).unwrap();
}
<commit_msg>Streamline docs.rs documentation<commit_after>//! # Overview
//!
//! This crate provides native rust implementations of image encoding and decoding as well as some
//! basic image manipulation functions. Additional documentation can currently also be found in the
//! [README.md file which is most easily viewed on
//! github](https://github.com/image-rs/image/blob/master/README.md).
//!
//! There are two core problems for which this library provides solutions: a unified interface for image
//! encodings and simple generic buffers for their content. It's possible to use either feature
//! without the other. The focus is on a small and stable set of common operations that can be
//! supplemented by other specialized crates. The library also prefers safe solutions with few
//! dependencies.
//!
//! # High level API
//!
//! Load images using [`io::Reader`]:
//!
//! ```rust,no_run
//! # use std::io::Cursor;
//! use image::io::Reader as ImageReader;
//! # fn main() -> Result<(), image::ImageError> {
//! # let bytes = vec![0u8];
//!
//! let img = ImageReader::open("myimage.png")?.decode()?;
//! let img2 = ImageReader::new(Cursor::new(bytes)).decode()?;
//! # Ok(())
//! # }
//! ```
//!
//! And save them using [`save`] or [`write_to`] methods:
//!
//! ```rust,no_run
//! # use std::io::Write;
//! # use image::ImageOutputFormat;
//! # use image::DynamicImage;
//! # #[cfg(feature = "png")]
//! # fn main() -> Result<(), image::ImageError> {
//! # let img: DynamicImage = unimplemented!();
//! # let img2: DynamicImage = unimplemented!();
//! img.save("empty.jpg")?;
//!
//! let mut bytes: Vec<u8> = Vec::new();
//! img2.write_to(&mut bytes, image::ImageOutputFormat::Png)?;
//! # Ok(())
//! # }
//! ```
//!
//! With default features, the crate includes support for [many common image formats](codecs/index.html#supported-formats).
//!
//! [`save`]: enum.DynamicImage.html#method.save
//! [`write_to`]: enum.DynamicImage.html#method.write_to
//! [`io::Reader`]: io/struct.Reader.html
//!
//! # Image buffers
//!
//! The two main types for storing images:
//! * [`ImageBuffer`] which holds statically typed image contents.
//! * [`DynamicImage`] which is an enum over the supported ImageBuffer formats
//!     and supports conversions between them.
//!
//! As well as a few more specialized options:
//! * [`GenericImage`] trait for a mutable image buffer.
//! * [`GenericImageView`] trait for read only references to a GenericImage.
//! * [`flat`] module containing types for interoperability with generic channel
//!     matrices and foreign interfaces.
//!
//! [`GenericImageView`]: trait.GenericImageView.html
//! [`GenericImage`]: trait.GenericImage.html
//! [`ImageBuffer`]: struct.ImageBuffer.html
//! [`DynamicImage`]: enum.DynamicImage.html
//! [`flat`]: flat/index.html
//!
//! # Low level encoding/decoding API
//!
//! The [`ImageDecoder`] and [`ImageDecoderExt`] traits are implemented for many image file
//! formats. They decode image data by directly on raw byte slices. Given an ImageDecoder, you can
//! produce a DynamicImage via [`DynamicImage::from_decoder`].
//!
//! [`ImageEncoder`] provides the analogous functionality for encoding image data.
//!
//! [`DynamicImage::from_decoder`]: enum.DynamicImage.html#method.from_decoder
//! [`ImageDecoderExt`]: trait.ImageDecoderExt.html
//! [`ImageDecoder`]: trait.ImageDecoder.html
//! [`ImageEncoder`]: trait.ImageEncoder.html
#![warn(missing_docs)]
#![warn(unused_qualifications)]
#![deny(unreachable_pub)]
#![deny(deprecated)]
#![deny(missing_copy_implementations)]
#![cfg_attr(all(test, feature = "benchmarks"), feature(test))]
// it's a bit of a pain otherwise
#![allow(clippy::many_single_char_names)]

#[cfg(all(test, feature = "benchmarks"))]
extern crate test;

#[cfg(test)]
#[macro_use]
extern crate quickcheck;

use std::io::Write;

pub use crate::color::{ColorType, ExtendedColorType};

pub use crate::color::{Luma, LumaA, Rgb, Rgba, Bgr, Bgra};

pub use crate::error::{ImageError, ImageResult};

pub use crate::image::{AnimationDecoder,
                GenericImage,
                GenericImageView,
                ImageDecoder,
                ImageDecoderExt,
                ImageEncoder,
                ImageFormat,
                ImageOutputFormat,
                Progress,
                // Iterators
                Pixels,
                SubImage};

pub use crate::buffer_::{
                 GrayAlphaImage,
                 GrayImage,
                 // Image types
                 ImageBuffer,
                 RgbImage,
                 RgbaImage};

pub use crate::flat::FlatSamples;

// Traits
pub use crate::traits::{EncodableLayout, Primitive, Pixel};

// Opening and loading images
pub use crate::io::free_functions::{guess_format, load};
pub use crate::dynimage::{load_from_memory, load_from_memory_with_format, open,
                   save_buffer, save_buffer_with_format, image_dimensions};

pub use crate::dynimage::DynamicImage;

pub use crate::animation::{Delay, Frame, Frames};

// More detailed error type
pub mod error;

/// Iterators and other auxiliary structure for the `ImageBuffer` type.
pub mod buffer {
    // Only those not exported at the top-level
    pub use crate::buffer_::{
        ConvertBuffer,
        EnumeratePixels,
        EnumeratePixelsMut,
        EnumerateRows,
        EnumerateRowsMut,
        Pixels,
        PixelsMut,
        Rows,
        RowsMut,
    };
}

// Math utils
pub mod math;

// Image processing functions
pub mod imageops;

// Io bindings
pub mod io;

// Buffer representations for ffi.
pub mod flat;

/// Encoding and decoding for various image file formats.
///
/// # Supported formats
///
/// | Format | Decoding | Encoding |
/// | ------ | -------- | -------- |
/// | PNG    | All supported color types | Same as decoding |
/// | JPEG   | Baseline and progressive | Baseline JPEG |
/// | GIF    | Yes | Yes |
/// | BMP    | Yes | RGB8, RGBA8, Gray8, GrayA8 |
/// | ICO    | Yes | Yes |
/// | TIFF   | Baseline(no fax support) + LZW + PackBits | RGB8, RGBA8, Gray8 |
/// | WebP   | Lossy(Luma channel only) | No |
/// | PNM    | PBM, PGM, PPM, standard PAM | Yes |
/// | DDS    | DXT1, DXT3, DXT5 | No |
/// | TGA    | Yes | RGB8, RGBA8, BGR8, BGRA8, Gray8, GrayA8 |
/// | farbfeld | Yes | Yes |
///
/// ## A note on format specific features
///
/// One of the main goals of `image` is stability, in runtime but also for programmers. This
/// ensures that performance as well as safety fixes reach a majority of its user base with little
/// effort. Re-exporting all details of its dependencies would run counter to this goal as it
/// linked _all_ major version bumps between them and `image`. As such, we are wary of exposing too
/// many details, or configuration options, that are not shared between different image formats.
///
/// Nevertheless, the advantage of precise control is hard to ignore. We will thus consider
/// _wrappers_, not direct re-exports, in either of the following cases:
///
/// 1. A standard specifies that configuration _x_ is required for decoders/encoders and there
///    exists an essentially canonical way to control it.
/// 2. At least two different implementations agree on some (sub-)set of features in practice.
/// 3. A technical argument including measurements of the performance, space benefits, or otherwise
///    objectively quantified benefits can be made, and the added interface is unlikely to require
///    breaking changes.
///
/// Features that fulfill two or more criteria are preferred.
///
/// Re-exports of dependencies that reach version `1` will be discussed when it happens.
pub mod codecs {
    #[cfg(feature = "avif")]
    pub mod avif;
    #[cfg(feature = "bmp")]
    pub mod bmp;
    #[cfg(feature = "dds")]
    pub mod dds;
    #[cfg(feature = "dxt")]
    pub mod dxt;
    #[cfg(feature = "farbfeld")]
    pub mod farbfeld;
    #[cfg(feature = "gif")]
    pub mod gif;
    #[cfg(feature = "hdr")]
    pub mod hdr;
    #[cfg(feature = "ico")]
    pub mod ico;
    #[cfg(feature = "jpeg")]
    pub mod jpeg;
    #[cfg(feature = "png")]
    pub mod png;
    #[cfg(feature = "pnm")]
    pub mod pnm;
    #[cfg(feature = "tga")]
    pub mod tga;
    #[cfg(feature = "tiff")]
    pub mod tiff;
    #[cfg(feature = "webp")]
    pub mod webp;
}

#[cfg(feature = "avif")]
#[deprecated = "Use codecs::avif instead"]
pub mod avif {
    //! Encoding of AVIF images.
    pub use crate::codecs::avif::AvifEncoder;
}
#[cfg(feature = "bmp")]
#[deprecated = "Use codecs::bmp instead"]
pub mod bmp {
    //! Decoding and Encoding of BMP Images
    #[allow(deprecated)]
    pub use crate::codecs::bmp::{BMPEncoder, BmpDecoder, BmpEncoder};
}
#[cfg(feature = "dds")]
#[deprecated = "Use codecs::dds instead"]
pub mod dds {
    //! Decoding of DDS images
    pub use crate::codecs::dds::DdsDecoder;
}
#[cfg(feature = "dxt")]
#[deprecated = "Use codecs:: instead"]
pub mod dxt {
    //! Decoding of DXT (S3TC) compression
    #[allow(deprecated)]
    pub use crate::codecs::dxt::{
        DXTEncoder, DXTReader, DXTVariant, DxtDecoder, DxtEncoder, DxtReader, DxtVariant,
    };
}
#[cfg(feature = "farbfeld")]
#[deprecated = "Use codecs::farbfeld instead"]
pub mod farbfeld {
    //! Decoding of farbfeld images
    pub use crate::codecs::farbfeld::{FarbfeldDecoder, FarbfeldEncoder, FarbfeldReader};
}
#[cfg(feature = "gif")]
#[deprecated = "Use codecs::gif instead"]
pub mod gif {
    //! Decoding of GIF Images
    #[allow(deprecated)]
    pub use crate::codecs::gif::{Encoder, GifDecoder, GifEncoder, GifReader};
}
#[cfg(feature = "hdr")]
#[deprecated = "Use codecs::hdr instead"]
pub mod hdr {
    //! Decoding of Radiance HDR Images
    #[allow(deprecated)]
    pub use crate::codecs::hdr::{
        read_raw_file, rgbe8, to_rgbe8, HDRAdapter, HDREncoder, HDRImageDecoderIterator,
        HDRMetadata, HdrAdapter, HdrDecoder, HdrEncoder, HdrImageDecoderIterator, HdrMetadata,
        HdrReader, RGBE8Pixel, Rgbe8Pixel, SIGNATURE,
    };
}
#[cfg(feature = "ico")]
#[deprecated = "Use codecs::ico instead"]
pub mod ico {
    //! Decoding and Encoding of ICO files
    #[allow(deprecated)]
    pub use crate::codecs::ico::{ICOEncoder, IcoDecoder, IcoEncoder};
}
#[cfg(feature = "jpeg")]
#[deprecated = "Use codecs::jpeg instead"]
pub mod jpeg {
    //! Decoding and Encoding of JPEG Images
    #[allow(deprecated)]
    pub use crate::codecs::jpeg::{
        JPEGEncoder, JpegDecoder, JpegEncoder, PixelDensity, PixelDensityUnit,
    };
}
#[cfg(feature = "png")]
#[deprecated = "Use codecs::png instead"]
pub mod png {
    //! Decoding and Encoding of PNG Images
    #[allow(deprecated)]
    pub use crate::codecs::png::{
        ApngDecoder, PNGEncoder, PNGReader, PngDecoder, PngEncoder, PngReader,
    };
}
#[cfg(feature = "pnm")]
#[deprecated = "Use codecs::pnm instead"]
pub mod pnm {
    //! Decoding and Encoding of netpbm image formats (pbm, pgm, ppm and pam)
    #[allow(deprecated)]
    pub use crate::codecs::pnm::{
        ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PNMEncoder, PNMHeader,
        PNMSubtype, PixmapHeader, PnmDecoder, PnmEncoder, PnmHeader, PnmSubtype, SampleEncoding,
    };
}
#[cfg(feature = "tga")]
#[deprecated = "Use codecs::tga instead"]
pub mod tga {
    //! Decoding and Encoding of TGA Images
    #[allow(deprecated)]
    pub use crate::codecs::tga::{TgaDecoder, TgaEncoder};
}
#[cfg(feature = "tiff")]
#[deprecated = "Use codecs::tiff instead"]
pub mod tiff {
    //! Decoding and Encoding of TIFF Images
    #[allow(deprecated)]
    pub use crate::codecs::tiff::{TiffDecoder, TiffEncoder, TiffReader};
}
#[cfg(feature = "webp")]
#[deprecated = "Use codecs::webp instead"]
pub mod webp {
    //! Decoding of WebP Images
    #[allow(deprecated)]
    pub use crate::codecs::webp::{vp8, WebPDecoder};
}

mod animation;
#[path = "buffer.rs"]
mod buffer_;
mod color;
mod dynimage;
mod image;
mod traits;
mod utils;

// Can't use the macro-call itself within the `doc` attribute. So force it to eval it as part of
// the macro invocation.
//
// The inspiration for the macro and implementation is from
// <https://github.com/GuillaumeGomez/doc-comment>
//
// MIT License
//
// Copyright (c) 2018 Guillaume Gomez
macro_rules! insert_as_doc {
    { $content:expr } => {
        #[doc = $content] extern { }
    }
}

// Provides the README.md as doc, to ensure the example works!
insert_as_doc!(include_str!("../README.md"));

// Copies data from `src` to `dst`
//
// Panics if the length of `dst` is less than the length of `src`.
#[inline]
fn copy_memory(src: &[u8], mut dst: &mut [u8]) {
    let len_src = src.len();
    assert!(dst.len() >= len_src);
    dst.write_all(src).unwrap();
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate url;
extern crate serde;
extern crate serde_json;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate error_chain;

mod error;

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

use hyper::status::StatusCode;

#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(deny_unknown_fields)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct JournalEntry {
    #[serde(rename = "__CURSOR")]
    pub cursor: String,
    #[serde(rename = "__REALTIME_TIMESTAMP")]
    pub realtime_timestamp: String,
    #[serde(rename = "__MONOTONIC_TIMESTAMP")]
    pub monotonic_timestamp: String,
    #[serde(rename = "_BOOT_ID")]
    pub boot_id: String,
    #[serde(rename = "_MACHINE_ID")]
    pub machine_id: String,
    #[serde(rename = "_HOSTNAME")]
    pub hostname: String,
    pub priority: Option<String>,
    #[serde(rename = "_UID")]
    pub uid: Option<String>,
    #[serde(rename = "_GID")]
    pub gid: Option<String>,
    #[serde(rename = "_SYSTEMD_SLICE")]
    pub systemd_slice: Option<String>,
    #[serde(rename = "_CAP_EFFECTIVE")]
    pub cap_effective: Option<String>,
    #[serde(rename = "_TRANSPORT")]
    pub transport: String,
    #[serde(rename = "_COMM")]
    pub command: Option<String>,
    #[serde(rename = "_EXE")]
    pub executable: Option<String>,
    #[serde(rename = "_SYSTEMD_CGROUP")]
    pub systemd_cgroup: Option<String>,
    #[serde(rename = "_SYSTEMD_UNIT")]
    pub systemd_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_INVOCATION_ID")]
    pub systemd_invocation_id: Option<String>,
    #[serde(rename = "_SYSTEMD_OWNER_UID")]
    pub systemd_owner_uid: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_SLICE")]
    pub systemd_user_slice: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_UNIT")]
    pub systemd_user_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_SESSION")]
    pub systemd_session: Option<String>,
    pub syslog_facility: Option<String>,
    pub syslog_identifier: Option<String>,
    #[serde(rename = "_CMDLINE")]
    pub command_line: Option<String>,
    #[serde(rename = "_AUDIT_LOGINUID")]
    pub audit_login_uid: Option<String>,
    pub message: String,
    pub code_file: Option<String>,
    pub code_line: Option<String>,
    pub code_function: Option<String>,
    pub unit: Option<String>,
    pub user_unit: Option<String>,
    pub userspace_usec: Option<String>,
    pub kernel_usec: Option<String>,
    pub message_id: Option<String>,
    pub result: Option<String>,
    pub user_id: Option<String>,
    pub seat_id: Option<String>,
    pub leader: Option<String>,
    pub journal_name: Option<String>,
    pub journal_path: Option<String>,
    pub current_use: Option<String>,
    pub current_use_pretty: Option<String>,
    pub max_use: Option<String>,
    pub max_use_pretty: Option<String>,
    pub disk_keep_free: Option<String>,
    pub disk_keep_free_pretty: Option<String>,
    pub disk_available: Option<String>,
    pub disk_available_pretty: Option<String>,
    pub available: Option<String>,
    pub available_pretty: Option<String>,
    pub limit: Option<String>,
    pub limit_pretty: Option<String>,
    pub session_id: Option<String>,
    pub target: Option<String>,
    pub syslog_pid: Option<String>,
    #[serde(rename = "_PID")]
    pub pid: Option<String>,
    #[serde(rename = "_AUDIT_SESSION")]
    pub audit_session: Option<String>,
    #[serde(rename = "_AUDIT_TYPE")]
    pub audit_type: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_NAME")]
    pub audit_field_name: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_APPARMOR")]
    pub audit_field_apparmor: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_OPERATION")]
    pub audit_field_operation: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_PROFILE")]
    pub audit_field_profile: Option<String>,
    #[serde(rename = "_AUDIT_ID")]
    pub audit_id: Option<String>,
    #[serde(rename = "_KERNEL_SUBSYSTEM")]
    pub kernel_subsystem: Option<String>,
    #[serde(rename = "_KERNEL_DEVICE")]
    pub kernel_device: Option<String>,
    #[serde(rename = "_UDEV_SYSNAME")]
    pub udev_sysname: Option<String>,
    #[serde(rename = "_UDEV_DEVNODE")]
    pub udev_devnode: Option<String>,
    #[serde(rename = "_UDEV_DEVLINK")]
    pub udev_devlink: Option<String>,
    #[serde(rename = "_SOURCE_REALTIME_TIMESTAMP")]
    pub source_realtime_timestamp: Option<String>,
    #[serde(rename = "_SOURCE_MONOTONIC_TIMESTAMP")]
    pub source_monotonic_timestamp: Option<String>,
}

pub struct JournalGateway {
    baseurl: url::Url,
    client: hyper::Client,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PaginationParams {
    pub cursor: Option<String>,
    pub skip: Option<i32>,
    pub length: Option<u32>,
}

impl fmt::Display for PaginationParams {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut str_data = String::new();
        if let Some(ref cursor_str) = self.cursor {
            str_data = str_data + cursor_str;
        }
        if let Some(ref skip_int) = self.skip {
            str_data = format!("{}:{}", str_data, skip_int);
        }
        if let Some(ref length_int) = self.length {
            str_data = format!("{}:{}", str_data, length_int);
        }
        write!(f, "{}", str_data)
    }
}

impl JournalGateway {
    pub fn new(baseurl_str: &str) -> error::Result<JournalGateway> {
        let baseurl = try!(url::Url::parse(baseurl_str));
        Ok(JournalGateway {
               baseurl: baseurl.to_owned(),
               client: hyper::Client::new(),
           })
    }

    pub fn get_all_entries(&self) -> error::Result<Vec<JournalEntry>> {
        self.get_entries(None, None)
    }

    pub fn get_first_entry(&self,
                           filters: Option<&Vec<(String, String)>>)
                           -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: None,
            length: Some(1),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.is_empty() {
            true => Err(error::ErrorKind::NoEntries.into()),
            false => Ok(list[0].clone()),
        }
    }

    pub fn get_last_entry(&self,
                          filters: Option<&Vec<(String, String)>>)
                          -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: Some(-1),
            length: Some(2),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.len() {
            0 => Err(error::ErrorKind::NoEntries.into()),
            1 => Ok(list[0].clone()),
            2 => Ok(list[1].clone()),
            len => panic!("Requested 2 elements, but got more: {}", len),
        }
    }

    pub fn get_entries(&self,
                       filters: Option<&Vec<(String, String)>>,
                       pagination: Option<PaginationParams>)
                       -> error::Result<Vec<JournalEntry>> {

        let mut url = try!(self.baseurl.join("entries"));
        if let Some(filters_unwrapped) = filters {
            if !filters_unwrapped.is_empty() {
                url.query_pairs_mut().extend_pairs(filters_unwrapped);
            }
        }

        let mut request = self.client
            .request(hyper::method::Method::Get, url.as_str())
            .header(hyper::header::Accept::json());

        if let Some(pagi) = pagination {
            request =
                request.header(hyper::header::Range::Unregistered("entries".to_string(),
                                                                  pagi.to_string()));
        }

        let mut response = try!(request.send());
        let mut body = String::new();
        try!(response.read_to_string(&mut body));

        if response.status != StatusCode::Ok {
            return Err(error::ErrorKind::RequestError(response.status, body).into());
        }

        let mut res: Vec<JournalEntry> = vec![];
        for line in body.split("\n") {
            if line.len() > 0 {
                let entry = try!(serde_json::from_str(&line));
                res.push(entry);
            }
        }

        Ok(res)
    }
}

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

    #[test]
    fn get_entries() {

        // TODO make this into a self contained test

        let journal_gw = JournalGateway::new("http://192.168.33.19:19531")
            .expect("JournalGateway initialization failed");
        //let res = journal_gw.get_all_entries();

        let filter = vec![("SYSLOG_IDENTIFIER".to_string(), "wash-manager".to_string())];

        let res: Vec<JournalEntry> = journal_gw
            .get_entries(Some(&filter), None)
            .expect("should  have some entries");

        println!("Received {} entries", res.len());
        for entry in res {
            println!("{}: {}",
                     entry.syslog_identifier.unwrap_or("N/A".to_string()),
                     entry.message);
        }
        println!("First entry: {:?}\n",
                 journal_gw.get_first_entry(Some(&filter)));
        println!("Last entry: {:?}\n",
                 journal_gw.get_last_entry(Some(&filter)));
    }

    // TODO create another test with a filter, e.g.
    // let res = journal_gw.get_entries(Some(vec![("_COMM".to_string(), "cat".to_string())]));
}
<commit_msg>make message optional, because sometimes it's empty<commit_after>extern crate hyper;
extern crate url;
extern crate serde;
extern crate serde_json;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate error_chain;

mod error;

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

use hyper::status::StatusCode;

#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(deny_unknown_fields)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct JournalEntry {
    #[serde(rename = "__CURSOR")]
    pub cursor: String,
    #[serde(rename = "__REALTIME_TIMESTAMP")]
    pub realtime_timestamp: String,
    #[serde(rename = "__MONOTONIC_TIMESTAMP")]
    pub monotonic_timestamp: String,
    #[serde(rename = "_BOOT_ID")]
    pub boot_id: String,
    #[serde(rename = "_MACHINE_ID")]
    pub machine_id: String,
    #[serde(rename = "_HOSTNAME")]
    pub hostname: String,
    pub priority: Option<String>,
    #[serde(rename = "_UID")]
    pub uid: Option<String>,
    #[serde(rename = "_GID")]
    pub gid: Option<String>,
    #[serde(rename = "_SYSTEMD_SLICE")]
    pub systemd_slice: Option<String>,
    #[serde(rename = "_CAP_EFFECTIVE")]
    pub cap_effective: Option<String>,
    #[serde(rename = "_TRANSPORT")]
    pub transport: String,
    #[serde(rename = "_COMM")]
    pub command: Option<String>,
    #[serde(rename = "_EXE")]
    pub executable: Option<String>,
    #[serde(rename = "_SYSTEMD_CGROUP")]
    pub systemd_cgroup: Option<String>,
    #[serde(rename = "_SYSTEMD_UNIT")]
    pub systemd_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_INVOCATION_ID")]
    pub systemd_invocation_id: Option<String>,
    #[serde(rename = "_SYSTEMD_OWNER_UID")]
    pub systemd_owner_uid: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_SLICE")]
    pub systemd_user_slice: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_UNIT")]
    pub systemd_user_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_SESSION")]
    pub systemd_session: Option<String>,
    pub syslog_facility: Option<String>,
    pub syslog_identifier: Option<String>,
    #[serde(rename = "_CMDLINE")]
    pub command_line: Option<String>,
    #[serde(rename = "_AUDIT_LOGINUID")]
    pub audit_login_uid: Option<String>,
    pub message: Option<String>,
    pub code_file: Option<String>,
    pub code_line: Option<String>,
    pub code_function: Option<String>,
    pub unit: Option<String>,
    pub user_unit: Option<String>,
    pub userspace_usec: Option<String>,
    pub kernel_usec: Option<String>,
    pub message_id: Option<String>,
    pub result: Option<String>,
    pub user_id: Option<String>,
    pub seat_id: Option<String>,
    pub leader: Option<String>,
    pub journal_name: Option<String>,
    pub journal_path: Option<String>,
    pub current_use: Option<String>,
    pub current_use_pretty: Option<String>,
    pub max_use: Option<String>,
    pub max_use_pretty: Option<String>,
    pub disk_keep_free: Option<String>,
    pub disk_keep_free_pretty: Option<String>,
    pub disk_available: Option<String>,
    pub disk_available_pretty: Option<String>,
    pub available: Option<String>,
    pub available_pretty: Option<String>,
    pub limit: Option<String>,
    pub limit_pretty: Option<String>,
    pub session_id: Option<String>,
    pub target: Option<String>,
    pub syslog_pid: Option<String>,
    #[serde(rename = "_PID")]
    pub pid: Option<String>,
    #[serde(rename = "_AUDIT_SESSION")]
    pub audit_session: Option<String>,
    #[serde(rename = "_AUDIT_TYPE")]
    pub audit_type: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_NAME")]
    pub audit_field_name: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_APPARMOR")]
    pub audit_field_apparmor: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_OPERATION")]
    pub audit_field_operation: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_PROFILE")]
    pub audit_field_profile: Option<String>,
    #[serde(rename = "_AUDIT_ID")]
    pub audit_id: Option<String>,
    #[serde(rename = "_KERNEL_SUBSYSTEM")]
    pub kernel_subsystem: Option<String>,
    #[serde(rename = "_KERNEL_DEVICE")]
    pub kernel_device: Option<String>,
    #[serde(rename = "_UDEV_SYSNAME")]
    pub udev_sysname: Option<String>,
    #[serde(rename = "_UDEV_DEVNODE")]
    pub udev_devnode: Option<String>,
    #[serde(rename = "_UDEV_DEVLINK")]
    pub udev_devlink: Option<String>,
    #[serde(rename = "_SOURCE_REALTIME_TIMESTAMP")]
    pub source_realtime_timestamp: Option<String>,
    #[serde(rename = "_SOURCE_MONOTONIC_TIMESTAMP")]
    pub source_monotonic_timestamp: Option<String>,
}

pub struct JournalGateway {
    baseurl: url::Url,
    client: hyper::Client,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PaginationParams {
    pub cursor: Option<String>,
    pub skip: Option<i32>,
    pub length: Option<u32>,
}

impl fmt::Display for PaginationParams {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut str_data = String::new();
        if let Some(ref cursor_str) = self.cursor {
            str_data = str_data + cursor_str;
        }
        if let Some(ref skip_int) = self.skip {
            str_data = format!("{}:{}", str_data, skip_int);
        }
        if let Some(ref length_int) = self.length {
            str_data = format!("{}:{}", str_data, length_int);
        }
        write!(f, "{}", str_data)
    }
}

impl JournalGateway {
    pub fn new(baseurl_str: &str) -> error::Result<JournalGateway> {
        let baseurl = try!(url::Url::parse(baseurl_str));
        Ok(JournalGateway {
               baseurl: baseurl.to_owned(),
               client: hyper::Client::new(),
           })
    }

    pub fn get_all_entries(&self) -> error::Result<Vec<JournalEntry>> {
        self.get_entries(None, None)
    }

    pub fn get_first_entry(&self,
                           filters: Option<&Vec<(String, String)>>)
                           -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: None,
            length: Some(1),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.is_empty() {
            true => Err(error::ErrorKind::NoEntries.into()),
            false => Ok(list[0].clone()),
        }
    }

    pub fn get_last_entry(&self,
                          filters: Option<&Vec<(String, String)>>)
                          -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: Some(-1),
            length: Some(2),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.len() {
            0 => Err(error::ErrorKind::NoEntries.into()),
            1 => Ok(list[0].clone()),
            2 => Ok(list[1].clone()),
            len => panic!("Requested 2 elements, but got more: {}", len),
        }
    }

    pub fn get_entries(&self,
                       filters: Option<&Vec<(String, String)>>,
                       pagination: Option<PaginationParams>)
                       -> error::Result<Vec<JournalEntry>> {

        let mut url = try!(self.baseurl.join("entries"));
        if let Some(filters_unwrapped) = filters {
            if !filters_unwrapped.is_empty() {
                url.query_pairs_mut().extend_pairs(filters_unwrapped);
            }
        }

        let mut request = self.client
            .request(hyper::method::Method::Get, url.as_str())
            .header(hyper::header::Accept::json());

        if let Some(pagi) = pagination {
            request =
                request.header(hyper::header::Range::Unregistered("entries".to_string(),
                                                                  pagi.to_string()));
        }

        let mut response = try!(request.send());
        let mut body = String::new();
        try!(response.read_to_string(&mut body));

        if response.status != StatusCode::Ok {
            return Err(error::ErrorKind::RequestError(response.status, body).into());
        }

        let mut res: Vec<JournalEntry> = vec![];
        for line in body.split("\n") {
            if line.len() > 0 {
                let entry = try!(serde_json::from_str(&line));
                res.push(entry);
            }
        }

        Ok(res)
    }
}

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

    #[test]
    fn get_entries() {

        // TODO make this into a self contained test

        let journal_gw = JournalGateway::new("http://192.168.33.19:19531")
            .expect("JournalGateway initialization failed");
        //let res = journal_gw.get_all_entries();

        let filter = vec![("SYSLOG_IDENTIFIER".to_string(), "wash-manager".to_string())];

        let res: Vec<JournalEntry> = journal_gw
            .get_entries(Some(&filter), None)
            .expect("should  have some entries");

        println!("Received {} entries", res.len());
        for entry in res {
            println!("{}: {}",
                     entry.syslog_identifier.unwrap_or("N/A".to_string()),
                     entry.message);
        }
        println!("First entry: {:?}\n",
                 journal_gw.get_first_entry(Some(&filter)));
        println!("Last entry: {:?}\n",
                 journal_gw.get_last_entry(Some(&filter)));
    }

    // TODO create another test with a filter, e.g.
    // let res = journal_gw.get_entries(Some(vec![("_COMM".to_string(), "cat".to_string())]));
}
<|endoftext|>


<commit_before>//! # Leaky Bucket Rate-Limiting (as a meter) in Rust
//! This crate implements
//! the
//! [generic cell rate algorithm](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm) (GCRA)
//! for rate-limiting and scheduling in Rust.
//!
//! ## Interface
//!
//! This crate implements two "serious" rate-limiting/traffic-shaping
//! algorithms:
//! [GCRA](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm)
//! and a [Leaky
//! Bucket](https://en.wikipedia.org/wiki/Leaky_bucket#As_a_meter). An
//! "unserious" implementation is provided also, the
//! [`Allower`](example_algorithms/struct.Allower.html), which returns
//! "Yes" to all rate-limiting queries.
//!
//! The Generic Cell Rate Algorithm can be used by creating a builder
//! from the [`GCRA`](algorithms/gcra/struct.GCRA.html) struct:
//!
//! ``` rust
//! use std::time::Duration;
//! use ratelimit_meter::{Decider, GCRA, Decision};
//!
//! let mut lim = GCRA::for_capacity(50).unwrap() // Allow 50 units of work
//!     .per(Duration::from_secs(1)) // We calculate per-second (this is the default).
//!     .cell_weight(1).unwrap() // Each cell is one unit of work "heavy".
//!     .build(); // Construct a non-threadsafe GCRA decider.
//! assert_eq!(Decision::Yes, lim.check().unwrap());
//! ```
//!
//! The rate-limiter interface is intentionally geared towards only
//! providing callers with the information they need to make decisions
//! about what to do with each cell. Whenever possible, additional
//! information about why a cell should be denied - the `GCRA`
//! implementation will return a `time::Instant` alongside the decision to
//! allow callers to e.g. provide better error messages to users.
//!
//! Due to this, the `ratelimit_meter` crate does not provide any facility
//! to wait until a cell would be allowed - if you require this, you
//! should use the `Instant` returned with negative decisions and wait
//! in your own, e.g. event loop.
//!
//! ## Rate-limiting Algorithms
//!
//! ### Design and implementation of GCRA
//!
//! The GCRA limits the rate of cells by determining when the "next"
//! cell is expected to arrive; any cells that arrive before that time
//! are classified as non-conforming; the methods for checking cells
//! also return an expected arrival time for these cells, so that
//! callers can choose to wait (adding jitter), or reject the cell.
//!
//! Since using the GCRA results in a much smoother usage pattern, it
//! appears to be very useful for "outgoing" traffic behaviors,
//! e.g. throttling API call rates, or emails sent to a person in a
//! period of time.
//!
//! Unlike token or leaky bucket algorithms, the GCRA assumes that all
//! units of work are of the same "weight", and so allows some
//! optimizations which result in much more concise and fast code (it
//! does not even use multiplication or division in the "hot" path).
//!
//! See [the documentation of the GCRA type](algorithms/gcra/struct.GCRA.html) for
//! more details on its implementation and on trade-offs that apply to
//! it.
//!
//! ### Design and implementation of the leaky bucket
//!
//! In contrast to the GCRA, the leaky bucket algorithm does not place
//! any constraints on the next cell's arrival time: Whenever there is
//! capacity left in the bucket, it can be used. This means that the
//! distribution of "yes" decisions from heavy usage on the leaky
//! bucket rate-limiter will be clustered together. On average, the
//! cell rates of both the GCRA and the leaky bucket will be the same,
//! but in terms of observable behavior, the leaky bucket will appear
//! to allow requests at a more predictable rate.
//!
//! This kind of behavior is usually what people of online APIs expect
//! these days, which makes the leaky bucket a very popular technique
//! for rate-limiting on these kinds of services.
//!
//! The leaky bucket algorithm implemented in this crate is fairly
//! standard: It only updates the bucket fill gauge when a cell is
//! checked, and supports checking "batches" of cells in a single call
//! with no problems.
//!
//! ## Thread-safe operation
//!
//! None of the stateful implementations in this crate can be used
//! across threads by default. However, there is a wrapper struct
//! [`Threadsafe`](algorithms/struct.Threadsafe.html), that wraps each
//! implementation's hot path in an atomically reference-counted
//! mutex. It still manages to be pretty fast (see the benchmarks
//! above), but the lock comes with an overhead even in
//! single-threaded operation.
//!
//! Example:
//!
//! ```
//! use std::time::Duration;
//! use ratelimit_meter::{Decider, GCRA, Decision};
//!
//! let mut lim = GCRA::for_capacity(50).unwrap() // Allow 50 units of work
//!     .per(Duration::from_secs(1)) // We calculate per-second (this is the default).
//!     .cell_weight(1).unwrap() // Each cell is one unit of work "heavy".
//!     .build_sync(); // Construct a threadsafe GCRA decider.
//! assert_eq!(Decision::Yes, lim.check().unwrap());
//! ```

pub mod example_algorithms;
pub mod errors;
pub mod algorithms;
mod implementation;

extern crate crossbeam;
#[macro_use]
extern crate error_chain;

use std::time::Instant;

pub use errors::*;
pub use self::algorithms::*;
use implementation::*;

#[derive(PartialEq, Debug)]
/// A decision on a single cell from the metered rate-limiter.
pub enum Decision<T> {
    /// The cell is conforming, allow it through.
    Yes,

    /// The cell is non-conforming. A rate-limiting algorithm
    /// implementation may return additional information for the
    /// caller, e.g. a time when the cell was expected to arrive.
    No(T),
}

impl<T> Decision<T> {
    /// Check if a decision on a cell indicates the cell is compliant
    /// or not. Returns `true` iff the cell was compliant, i.e. the
    /// decision was `Decision::Yes`.
    ///
    /// Note: This method is mostly useful in tests.
    pub fn is_compliant(&self) -> bool {
        match *self {
            Decision::Yes => true,
            Decision::No(_) => false,
        }
    }
}

/// A prerequisite for implementing any Decider trait. It provides the
/// associated type for [`Decision`](enum.Decision.html)'s additional
/// information for negative decisions.
pub trait TypedDecider {
    /// The type for additional information on negative decisions.
    type T;
}

/// The main decision trait. It allows checking a single cell against
/// the rate-limiter, either at the current time instant, or at a
/// given instant in time, both destructively.
pub trait Decider: DeciderImpl {
    /// Tests if a single cell can be accommodated at
    /// `Instant::now()`. See [`check_at`](#method.check_at).
    fn check(&mut self) -> Result<Decision<Self::T>> {
        self.test_and_update(Instant::now())
    }

    /// Tests is a single cell can be accommodated at the given time
    /// stamp.
    fn check_at(&mut self, at: Instant) -> Result<Decision<Self::T>> {
        self.test_and_update(at)
    }
}

pub trait MultiDecider: MultiDeciderImpl {
    /// Tests if `n` cells can be accommodated at the given time
    /// stamp. An error [`ErrorKind::InsufficientCapacity`](errors/enum.ErrorKind.html) is
    /// returned if `n` exceeds the bucket capacity.
    fn check_n_at(&mut self, n: u32, at: Instant) -> Result<Decision<Self::T>> {
        self.test_n_and_update(n, at)
    }

    /// Tests if `n` cells can be accommodated at the current time
    /// (`Instant::now()`). An error
    /// [`ErrorKind::InsufficientCapacity`](errors/enum.ErrorKind.html)
    /// is returned if `n` exceeds the bucket capacity.
    fn check_n(&mut self, n: u32) -> Result<Decision<Self::T>> {
        self.test_n_and_update(n, Instant::now())
    }
}
<commit_msg>Update the example to not use Threadsafe<commit_after>//! # Leaky Bucket Rate-Limiting (as a meter) in Rust
//! This crate implements
//! the
//! [generic cell rate algorithm](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm) (GCRA)
//! for rate-limiting and scheduling in Rust.
//!
//! ## Interface
//!
//! This crate implements two "serious" rate-limiting/traffic-shaping
//! algorithms:
//! [GCRA](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm)
//! and a [Leaky
//! Bucket](https://en.wikipedia.org/wiki/Leaky_bucket#As_a_meter). An
//! "unserious" implementation is provided also, the
//! [`Allower`](example_algorithms/struct.Allower.html), which returns
//! "Yes" to all rate-limiting queries.
//!
//! The Generic Cell Rate Algorithm can be used by creating a builder
//! from the [`GCRA`](algorithms/gcra/struct.GCRA.html) struct:
//!
//! ``` rust
//! use std::time::Duration;
//! use ratelimit_meter::{Decider, GCRA, Decision};
//!
//! let mut lim = GCRA::for_capacity(50).unwrap() // Allow 50 units of work
//!     .per(Duration::from_secs(1)) // We calculate per-second (this is the default).
//!     .cell_weight(1).unwrap() // Each cell is one unit of work "heavy".
//!     .build(); // Construct a GCRA decider.
//! assert_eq!(Decision::Yes, lim.check().unwrap());
//! ```
//!
//! The rate-limiter interface is intentionally geared towards only
//! providing callers with the information they need to make decisions
//! about what to do with each cell. Whenever possible, additional
//! information about why a cell should be denied - the `GCRA`
//! implementation will return a `time::Instant` alongside the decision to
//! allow callers to e.g. provide better error messages to users.
//!
//! Due to this, the `ratelimit_meter` crate does not provide any facility
//! to wait until a cell would be allowed - if you require this, you
//! should use the `Instant` returned with negative decisions and wait
//! in your own, e.g. event loop.
//!
//! ## Rate-limiting Algorithms
//!
//! ### Design and implementation of GCRA
//!
//! The GCRA limits the rate of cells by determining when the "next"
//! cell is expected to arrive; any cells that arrive before that time
//! are classified as non-conforming; the methods for checking cells
//! also return an expected arrival time for these cells, so that
//! callers can choose to wait (adding jitter), or reject the cell.
//!
//! Since using the GCRA results in a much smoother usage pattern, it
//! appears to be very useful for "outgoing" traffic behaviors,
//! e.g. throttling API call rates, or emails sent to a person in a
//! period of time.
//!
//! Unlike token or leaky bucket algorithms, the GCRA assumes that all
//! units of work are of the same "weight", and so allows some
//! optimizations which result in much more concise and fast code (it
//! does not even use multiplication or division in the "hot" path).
//!
//! See [the documentation of the GCRA type](algorithms/gcra/struct.GCRA.html) for
//! more details on its implementation and on trade-offs that apply to
//! it.
//!
//! ### Design and implementation of the leaky bucket
//!
//! In contrast to the GCRA, the leaky bucket algorithm does not place
//! any constraints on the next cell's arrival time: Whenever there is
//! capacity left in the bucket, it can be used. This means that the
//! distribution of "yes" decisions from heavy usage on the leaky
//! bucket rate-limiter will be clustered together. On average, the
//! cell rates of both the GCRA and the leaky bucket will be the same,
//! but in terms of observable behavior, the leaky bucket will appear
//! to allow requests at a more predictable rate.
//!
//! This kind of behavior is usually what people of online APIs expect
//! these days, which makes the leaky bucket a very popular technique
//! for rate-limiting on these kinds of services.
//!
//! The leaky bucket algorithm implemented in this crate is fairly
//! standard: It only updates the bucket fill gauge when a cell is
//! checked, and supports checking "batches" of cells in a single call
//! with no problems.
//!
//! ## Thread-safe operation
//!
//! The implementations in this crate use compare-and-set to keep
//! state, and are safe to share across threads..
//!
//! Example:
//!
//! ```
//! use std::thread;
//! use std::time::Duration;
//! use ratelimit_meter::{Decider, GCRA, Decision};
//!
//! let mut lim = GCRA::for_capacity(50).unwrap() // Allow 50 units of work
//!     .per(Duration::from_secs(1)) // We calculate per-second (this is the default).
//!     .cell_weight(1).unwrap() // Each cell is one unit of work "heavy".
//!     .build(); // Construct a GCRA decider.
//! let mut thread_lim = lim.clone();
//! thread::spawn(move || { assert_eq!(Decision::Yes, thread_lim.check().unwrap()); });
//! assert_eq!(Decision::Yes, lim.check().unwrap());
//! ```

pub mod example_algorithms;
pub mod errors;
pub mod algorithms;
mod implementation;

extern crate crossbeam;
#[macro_use]
extern crate error_chain;

use std::time::Instant;

pub use errors::*;
pub use self::algorithms::*;
use implementation::*;

#[derive(PartialEq, Debug)]
/// A decision on a single cell from the metered rate-limiter.
pub enum Decision<T> {
    /// The cell is conforming, allow it through.
    Yes,

    /// The cell is non-conforming. A rate-limiting algorithm
    /// implementation may return additional information for the
    /// caller, e.g. a time when the cell was expected to arrive.
    No(T),
}

impl<T> Decision<T> {
    /// Check if a decision on a cell indicates the cell is compliant
    /// or not. Returns `true` iff the cell was compliant, i.e. the
    /// decision was `Decision::Yes`.
    ///
    /// Note: This method is mostly useful in tests.
    pub fn is_compliant(&self) -> bool {
        match *self {
            Decision::Yes => true,
            Decision::No(_) => false,
        }
    }
}

/// A prerequisite for implementing any Decider trait. It provides the
/// associated type for [`Decision`](enum.Decision.html)'s additional
/// information for negative decisions.
pub trait TypedDecider {
    /// The type for additional information on negative decisions.
    type T;
}

/// The main decision trait. It allows checking a single cell against
/// the rate-limiter, either at the current time instant, or at a
/// given instant in time, both destructively.
pub trait Decider: DeciderImpl {
    /// Tests if a single cell can be accommodated at
    /// `Instant::now()`. See [`check_at`](#method.check_at).
    fn check(&mut self) -> Result<Decision<Self::T>> {
        self.test_and_update(Instant::now())
    }

    /// Tests is a single cell can be accommodated at the given time
    /// stamp.
    fn check_at(&mut self, at: Instant) -> Result<Decision<Self::T>> {
        self.test_and_update(at)
    }
}

pub trait MultiDecider: MultiDeciderImpl {
    /// Tests if `n` cells can be accommodated at the given time
    /// stamp. An error [`ErrorKind::InsufficientCapacity`](errors/enum.ErrorKind.html) is
    /// returned if `n` exceeds the bucket capacity.
    fn check_n_at(&mut self, n: u32, at: Instant) -> Result<Decision<Self::T>> {
        self.test_n_and_update(n, at)
    }

    /// Tests if `n` cells can be accommodated at the current time
    /// (`Instant::now()`). An error
    /// [`ErrorKind::InsufficientCapacity`](errors/enum.ErrorKind.html)
    /// is returned if `n` exceeds the bucket capacity.
    fn check_n(&mut self, n: u32) -> Result<Decision<Self::T>> {
        self.test_n_and_update(n, Instant::now())
    }
}
<|endoftext|>


<commit_before>#![deny(
    missing_docs,
    missing_debug_implementations, missing_copy_implementations,
    trivial_casts, trivial_numeric_casts,
    unsafe_code, unstable_features,
    unused_import_braces, unused_qualifications
)]

//! *merkle* implements a Merkle Tree in Rust.

extern crate ring;

#[cfg(feature = "serialization-protobuf")]
extern crate protobuf;

mod merkletree;
pub use merkletree::MerkleTree;

mod proof;
pub use proof::Proof;

mod hashutils;

mod tree;
pub use tree::{ LeavesIterator, LeavesIntoIterator };

#[cfg(feature = "serialization-protobuf")]
mod proto;

#[cfg(test)]
mod tests;

<commit_msg>Fix nightly build. (#23)<commit_after>#![deny(
    missing_docs, unused_qualifications,
    missing_debug_implementations, missing_copy_implementations,
    trivial_casts, trivial_numeric_casts,
    unsafe_code, unstable_features,
    unused_import_braces
)]

//! *merkle* implements a Merkle Tree in Rust.

extern crate ring;

#[cfg(feature = "serialization-protobuf")]
extern crate protobuf;

mod merkletree;
pub use merkletree::MerkleTree;

mod proof;
pub use proof::Proof;

mod hashutils;

mod tree;
pub use tree::{ LeavesIterator, LeavesIntoIterator };

#[cfg(feature = "serialization-protobuf")]
#[allow(unused_qualifications)]
mod proto;

#[cfg(test)]
mod tests;

<|endoftext|>


<commit_before>use std::collections::{HashMap, VecDeque};
use std::iter::Iterator;
use std::u64;
use std::usize;

pub type VertexId = usize;

/// Represent a Graph structure
#[derive(Debug)]
pub struct Graph<V: Copy, E: Copy> {
    pub vertexes: HashMap<VertexId, Vertex<V>>,
    pub edges: HashMap<(VertexId, VertexId), E>,
    adjacency_matrix: Vec<Vec<bool>>,
}

#[derive(Debug)]
pub struct Vertex<V: Copy> {
    pub value: V,
    pub neighbors: Vec<VertexId>,
}

pub struct BfsIterator<'a, V: 'a + Copy, E: 'a + Copy, F> {
    queue: VecDeque<VertexId>,
    graph: &'a Graph<V, E>,
    distances: Vec<u64>,
    parents: Vec<VertexId>,
    predicate: F
}

impl<'a, V: Copy, E: Copy, F> BfsIterator<'a, V, E, F> {
    fn new(graph: &'a Graph<V, E>, source: VertexId, predicate: F) -> BfsIterator<'a, V, E, F> {
        let mut queue = VecDeque::new();
        queue.push_back(source);
        let mut distances = vec![u64::MAX; graph.n_vertexes()];
        let parents = vec![usize::MAX; graph.n_vertexes()];
        distances[source] = 0;
        BfsIterator {
            graph: graph,
            queue: queue,
            distances: distances,
            parents: parents,
            predicate: predicate
        }
    }
}

/// Iterator for a breadth first search over a graph
/// Returns in order a tuple of (vertex, distance, parent)
impl<'a, V: Copy, E: Copy, F> Iterator for BfsIterator<'a, V, E, F> where F: Fn(V, E, V) -> bool {
    type Item = (VertexId, u64, VertexId);
    fn next(&mut self) -> Option<(VertexId, u64, VertexId)> {
        let g = self.graph;
        match self.queue.pop_front() {
            Some(vertex) => {
                for v in self.graph.adjacent_vertexes(&vertex) {
                    let predicate = &self.predicate;
                    let pred = predicate(g.vertexes[&vertex].value, g.edges[&(vertex, *v)], g.vertexes[v].value);
                    if self.distances[*v] == u64::MAX && pred {
                        self.distances[*v] = self.distances[vertex] + 1;
                        self.parents[*v] = vertex;
                        self.queue.push_back(*v);
                    }
                }
                Some((vertex, self.distances[vertex], self.parents[vertex]))
            }
            _ => None
        }
    }
}

impl<'a, V: Copy, E: Copy> Graph<V, E> {
    pub fn new(vertex_list: &Vec<(VertexId, V)>, edge_list: &Vec<(VertexId, VertexId, E)>) -> Graph<V, E> {
        let mut vertexes: HashMap<VertexId, Vertex<V>> = HashMap::new();
        for v in vertex_list {
            vertexes.insert(v.0,
                            Vertex {
                                value: v.1,
                                neighbors: Vec::new(),
                            });
        }
        let n_vertexes: usize = vertexes.len();

        let mut adjacency_matrix: Vec<Vec<bool>> = vec![vec![false; n_vertexes]; n_vertexes];
        let mut edges: HashMap<(VertexId, VertexId), E> = HashMap::new();
        for edge in edge_list {
            adjacency_matrix[edge.0][edge.1] = true;
            vertexes.get_mut(&edge.0).unwrap().neighbors.push(edge.1);
            edges.insert((edge.0, edge.1), edge.2);
        }

        Graph {
            vertexes: vertexes,
            edges: edges,
            adjacency_matrix: adjacency_matrix,
        }
    }
    pub fn size(&self) -> (usize, usize) {
        (self.vertexes.len(), self.edges.len())
    }
    pub fn n_vertexes(&self) -> usize {
        self.vertexes.len()
    }
    pub fn n_edges(&self) -> usize {
        self.edges.len()
    }
    pub fn adjacent_vertexes(&self, vertex: &VertexId) -> &Vec<VertexId> {
        &self.vertexes[vertex].neighbors
    }
    pub fn bfs_iter(&self, source: VertexId) -> BfsIterator<V, E, fn(V, E, V) -> bool> {
        BfsIterator::new(self, source, bfs_true_predicate)
    }
    pub fn bfs_iter_predicate<F>(&'a self, source: VertexId, predicate: F) -> BfsIterator<V, E, F>
    where F: Fn(V, E, V) -> bool {
        BfsIterator::new(self, source, predicate)
    }
    pub fn augmenting_path(&self, source: VertexId) -> Option<Vec<VertexId>> {
        None
    }
}

fn bfs_true_predicate<'a, V: Copy, E: Copy>(_: V, _: E, _: V) -> bool {
    true
}

#[cfg(test)]
mod tests {
    use Graph;
    use VertexId;
    use std::collections::HashSet;
    use std::usize;

    #[test]
    fn test_new_graph() {
        let vertex_list = vec![(0, 0), (1, 0), (2, 0), (3, 0), (4, 0)];
        let edge_list = vec![(0, 1, 5), (0, 2, 2), (2, 3, 3), (4, 3, 1)];
        let g = Graph::new(&vertex_list, &edge_list);
        assert_eq!(g.size(), (5, 4));
        for k in 0..5 {
            assert!(g.vertexes.contains_key(&k));
        }
        assert_eq!(g.edges[&(0, 1)], 5);
        assert_eq!(g.edges[&(0, 2)], 2);
        assert_eq!(g.edges[&(2, 3)], 3);
        assert_eq!(g.edges[&(4, 3)], 1);
    }

    #[test]
    fn test_bfs() {
        let vertex_list = vec![(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)];
        let edge_list = vec![(0, 1, 1), (1, 2, 1), (0, 3, 1), (3, 4, 1), (4, 1, 0), (4, 5, 1), (5, 2, 1)];
        let g = Graph::new(&vertex_list, &edge_list);
        let result: Vec<(VertexId, u64, VertexId)> = g.bfs_iter(0).collect();
        let mut result_set = HashSet::new();
        result_set.extend(result);
        let mut expect = HashSet::new();
        expect.insert((0, 0, usize::MAX));
        expect.insert((1, 1, 0));
        expect.insert((2, 2, 1));
        expect.insert((3, 1, 0));
        expect.insert((4, 2, 3));
        expect.insert((5, 3, 4));
        assert_eq!(result_set, expect);
    }
}
<commit_msg>Change traits<commit_after>use std::collections::{HashMap, VecDeque};
use std::iter::Iterator;
use std::u64;
use std::usize;

pub type VertexId = usize;

pub trait Property {}
impl<T> Property for T where T: Copy + PartialOrd + PartialEq {}

/// Represent a Graph structure
#[derive(Debug)]
pub struct Graph<V: Property, E: Property> {
    pub vertexes: HashMap<VertexId, Vertex<V>>,
    pub edges: HashMap<(VertexId, VertexId), E>,
    adjacency_matrix: Vec<Vec<bool>>,
}

#[derive(Debug)]
pub struct Vertex<V: Property> {
    pub value: V,
    pub neighbors: Vec<VertexId>,
}

pub struct BfsIterator<'a, V: 'a + Property, E: 'a + Property, F> {
    queue: VecDeque<VertexId>,
    graph: &'a Graph<V, E>,
    distances: Vec<u64>,
    parents: Vec<VertexId>,
    predicate: F
}

impl<'a, V: Property, E: Property, F> BfsIterator<'a, V, E, F> {
    fn new(graph: &'a Graph<V, E>, source: VertexId, predicate: F) -> BfsIterator<'a, V, E, F> {
        let mut queue = VecDeque::new();
        queue.push_back(source);
        let mut distances = vec![u64::MAX; graph.n_vertexes()];
        let parents = vec![usize::MAX; graph.n_vertexes()];
        distances[source] = 0;
        BfsIterator {
            graph: graph,
            queue: queue,
            distances: distances,
            parents: parents,
            predicate: predicate
        }
    }
}

/// Iterator for a breadth first search over a graph
/// Returns in order a tuple of (vertex, distance, parent)
impl<'a, V: Property, E: Property, F> Iterator for BfsIterator<'a, V, E, F>
    where F: Fn(V, E, V) -> bool {
    type Item = (VertexId, u64, VertexId);
    fn next(&mut self) -> Option<(VertexId, u64, VertexId)> {
        let g = self.graph;
        match self.queue.pop_front() {
            Some(vertex) => {
                for v in self.graph.adjacent_vertexes(&vertex) {
                    let predicate = &self.predicate;
                    let pred = predicate(g.vertexes[&vertex].value, g.edges[&(vertex, *v)], g.vertexes[v].value);
                    if self.distances[*v] == u64::MAX && pred {
                        self.distances[*v] = self.distances[vertex] + 1;
                        self.parents[*v] = vertex;
                        self.queue.push_back(*v);
                    }
                }
                Some((vertex, self.distances[vertex], self.parents[vertex]))
            }
            _ => None
        }
    }
}

impl<'a, V: Property, E: Property> Graph<V, E> {
    pub fn new(vertex_list: &Vec<(VertexId, V)>, edge_list: &Vec<(VertexId, VertexId, E)>) -> Graph<V, E> {
        let mut vertexes: HashMap<VertexId, Vertex<V>> = HashMap::new();
        for v in vertex_list {
            vertexes.insert(v.0,
                            Vertex {
                                value: v.1,
                                neighbors: Vec::new(),
                            });
        }
        let n_vertexes: usize = vertexes.len();

        let mut adjacency_matrix: Vec<Vec<bool>> = vec![vec![false; n_vertexes]; n_vertexes];
        let mut edges: HashMap<(VertexId, VertexId), E> = HashMap::new();
        for edge in edge_list {
            adjacency_matrix[edge.0][edge.1] = true;
            vertexes.get_mut(&edge.0).unwrap().neighbors.push(edge.1);
            edges.insert((edge.0, edge.1), edge.2);
        }

        Graph {
            vertexes: vertexes,
            edges: edges,
            adjacency_matrix: adjacency_matrix,
        }
    }
    pub fn size(&self) -> (usize, usize) {
        (self.vertexes.len(), self.edges.len())
    }
    pub fn n_vertexes(&self) -> usize {
        self.vertexes.len()
    }
    pub fn n_edges(&self) -> usize {
        self.edges.len()
    }
    pub fn adjacent_vertexes(&self, vertex: &VertexId) -> &Vec<VertexId> {
        &self.vertexes[vertex].neighbors
    }
    pub fn bfs_iter(&self, source: VertexId) -> BfsIterator<V, E, fn(V, E, V) -> bool> {
        BfsIterator::new(self, source, bfs_true_predicate)
    }
    pub fn bfs_iter_predicate<F>(&'a self, source: VertexId, predicate: F) -> BfsIterator<V, E, F>
    where F: Fn(V, E, V) -> bool {
        BfsIterator::new(self, source, predicate)
    }
}

pub trait FlowGraph<V> {}

impl<'a, V: Property> FlowGraph<V> for Graph<V, i64> {}

fn bfs_true_predicate<'a, V: Property, E: Property>(_: V, _: E, _: V) -> bool {
    true
}

fn flow_predicate<'a, V: Property>(_: V, edge: i64, _: V) {
    edge > 0;
}

#[cfg(test)]
mod tests {
    use Graph;
    use VertexId;
    use std::collections::HashSet;
    use std::usize;

    #[test]
    fn test_new_graph() {
        let vertex_list = vec![(0, 0), (1, 0), (2, 0), (3, 0), (4, 0)];
        let edge_list = vec![(0, 1, 5), (0, 2, 2), (2, 3, 3), (4, 3, 1)];
        let g = Graph::new(&vertex_list, &edge_list);
        assert_eq!(g.size(), (5, 4));
        for k in 0..5 {
            assert!(g.vertexes.contains_key(&k));
        }
        assert_eq!(g.edges[&(0, 1)], 5);
        assert_eq!(g.edges[&(0, 2)], 2);
        assert_eq!(g.edges[&(2, 3)], 3);
        assert_eq!(g.edges[&(4, 3)], 1);
    }

    #[test]
    fn test_bfs() {
        let vertex_list = vec![(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)];
        let edge_list = vec![(0, 1, 1), (1, 2, 1), (0, 3, 1), (3, 4, 1), (4, 1, 0), (4, 5, 1), (5, 2, 1)];
        let g = Graph::new(&vertex_list, &edge_list);
        let result: Vec<(VertexId, u64, VertexId)> = g.bfs_iter(0).collect();
        let mut result_set = HashSet::new();
        result_set.extend(result);
        let mut expect = HashSet::new();
        expect.insert((0, 0, usize::MAX));
        expect.insert((1, 1, 0));
        expect.insert((2, 2, 1));
        expect.insert((3, 1, 0));
        expect.insert((4, 2, 3));
        expect.insert((5, 3, 4));
        assert_eq!(result_set, expect);
    }
}
<|endoftext|>


<commit_before>//! Z80 emulation library
extern crate libc;
mod z80e_core;
use libc::{ c_void, c_int };

/// An interface for a 16-bit addressed container of bytes.
pub trait Z80Memory {
    fn read_byte(&mut self, address: u16) -> u8;
    fn write_byte(&mut self, address: u16, value: u8);
}

impl Z80Memory for Vec<u8> {
    fn read_byte(&mut self, address: u16) -> u8 {
        self[address as usize]
    }
    fn write_byte(&mut self, address: u16, value: u8) {
        self[address as usize] = value;
    }
}

extern fn read_z80_memory<T: Z80Memory>(memory: *mut c_void, address: u16) -> u8 {
    let memory: &mut T = unsafe { &mut *(memory as *mut T) };
    memory.read_byte(address)
}

extern fn write_z80_memory<T: Z80Memory>(memory: *mut c_void, address: u16, value: u8) {
    let memory: &mut T = unsafe { &mut *(memory as *mut T) };
    memory.write_byte(address, value)
}

/// An interface for implementing one Z80 I/O port.
pub trait Z80IODevice {
    fn read_in(&mut self) -> u8;
    fn write_out(&mut self, value: u8);
}

extern fn read_z80_device<T: Z80IODevice>(device: *mut c_void) -> u8 {
    let device: &mut T = unsafe { &mut *(device as *mut T) };
    device.read_in()
}

extern fn write_z80_device<T: Z80IODevice>(device: *mut c_void, value: u8) {
    let device: &mut T = unsafe { &mut *(device as *mut T) };
    device.write_out(value)
}

/// Z80 CPU implementation.
pub struct Z80 {
    core: *mut z80e_core::z80e_cpu,
}

impl Z80 {
    /// Allocate a new Z80 core and install its memory.
    pub fn new<T: Z80Memory>(memory: &mut T) -> Self {
        unsafe {
            let cpu = Z80 {
                core: z80e_core::cpu_init(),
            };
            (*cpu.core).memory = (memory as *mut _) as *mut c_void;
            (*cpu.core).read_byte = read_z80_memory::<T>;
            (*cpu.core).write_byte = write_z80_memory::<T>;
            cpu
        }
    }
    /// Install a device on a Z80 I/O port.
    pub fn install_device<T: Z80IODevice>(&mut self, port: u8, device: &mut T) {
        let port: usize = port as usize;
        unsafe {
            (*self.core).devices[port].device = (device as *mut _) as *mut c_void;
            (*self.core).devices[port].read_in = read_z80_device::<T>;
            (*self.core).devices[port].write_out = write_z80_device::<T>;
        }
    }
    pub fn execute(&mut self, cycles: u32) -> u32 {
        unsafe {
            z80e_core::cpu_execute(self.core, cycles as c_int) as u32
        }
    }
}

#[test]
fn it_works() {
}
<commit_msg>impl Z80Memory for [u8; 0x1000]<commit_after>//! Z80 emulation library
extern crate libc;
mod z80e_core;
use libc::{ c_void, c_int };

/// An interface for a 16-bit addressed container of bytes.
pub trait Z80Memory {
    fn read_byte(&mut self, address: u16) -> u8;
    fn write_byte(&mut self, address: u16, value: u8);
}

impl Z80Memory for Vec<u8> {
    fn read_byte(&mut self, address: u16) -> u8 {
        self[address as usize]
    }
    fn write_byte(&mut self, address: u16, value: u8) {
        self[address as usize] = value;
    }
}

impl Z80Memory for [u8; 0x10000] {
    fn read_byte(&mut self, address: u16) -> u8 {
        self[address as usize]
    }
    fn write_byte(&mut self, address: u16, value: u8) {
        self[address as usize] = value;
    }
}

extern fn read_z80_memory<T: Z80Memory>(memory: *mut c_void, address: u16) -> u8 {
    let memory: &mut T = unsafe { &mut *(memory as *mut T) };
    memory.read_byte(address)
}

extern fn write_z80_memory<T: Z80Memory>(memory: *mut c_void, address: u16, value: u8) {
    let memory: &mut T = unsafe { &mut *(memory as *mut T) };
    memory.write_byte(address, value)
}

/// An interface for implementing one Z80 I/O port.
pub trait Z80IODevice {
    fn read_in(&mut self) -> u8;
    fn write_out(&mut self, value: u8);
}

extern fn read_z80_device<T: Z80IODevice>(device: *mut c_void) -> u8 {
    let device: &mut T = unsafe { &mut *(device as *mut T) };
    device.read_in()
}

extern fn write_z80_device<T: Z80IODevice>(device: *mut c_void, value: u8) {
    let device: &mut T = unsafe { &mut *(device as *mut T) };
    device.write_out(value)
}

/// Z80 CPU implementation.
pub struct Z80 {
    core: *mut z80e_core::z80e_cpu,
}

impl Z80 {
    /// Allocate a new Z80 core and install its memory.
    pub fn new<T: Z80Memory>(memory: &mut T) -> Self {
        unsafe {
            let cpu = Z80 {
                core: z80e_core::cpu_init(),
            };
            (*cpu.core).memory = (memory as *mut _) as *mut c_void;
            (*cpu.core).read_byte = read_z80_memory::<T>;
            (*cpu.core).write_byte = write_z80_memory::<T>;
            cpu
        }
    }
    /// Install a device on a Z80 I/O port.
    pub fn install_device<T: Z80IODevice>(&mut self, port: u8, device: &mut T) {
        let port: usize = port as usize;
        unsafe {
            (*self.core).devices[port].device = (device as *mut _) as *mut c_void;
            (*self.core).devices[port].read_in = read_z80_device::<T>;
            (*self.core).devices[port].write_out = write_z80_device::<T>;
        }
    }
    pub fn execute(&mut self, cycles: u32) -> u32 {
        unsafe {
            z80e_core::cpu_execute(self.core, cycles as c_int) as u32
        }
    }
}

#[test]
fn it_works() {
}
<|endoftext|>