<commit_before>//! # Vertex sources definition
//!
//! When you create a graphics pipeline object, you need to pass an object which indicates the
//! layout of the vertex buffer(s) that will serve as input for the vertex shader. This is done
//! by passing an implementation of the `Definition` trait.
//!
//! The most common situation is a single vertex buffer and no instancing, in which case you can
//! pass a `SingleBufferDefinition` when you create the pipeline.
//!
//! # Implementing `Vertex`
//!
//! The implementations of the `Definition` trait that are provided by vulkano (like
//! `SingleBufferDefinition`) require you to use a buffer whose content is `[V]` where `V`
//! implements the `Vertex` trait.
//!
//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!`
//! macro.
//!
//! # Example
//!
//! ```no_run
//! # #[macro_use] extern crate vulkano
//! # fn main() {
//! # use std::sync::Arc;
//! # use vulkano::device::Device;
//! # use vulkano::device::Queue;
//! use vulkano::buffer::Buffer;
//! use vulkano::buffer::Usage as BufferUsage;
//! use vulkano::memory::HostVisible;
//! use vulkano::pipeline::vertex::;
//! # let device: Arc<Device> = unsafe { std::mem::uninitialized() };
//! # let queue: Arc<Queue> = unsafe { std::mem::uninitialized() };
//!
//! struct Vertex {
//!     position: [f32; 2]
//! }
//!
//! impl_vertex!(Vertex, position);
//!
//! let usage = BufferUsage {
//!     vertex_buffer: true,
//!     .. BufferUsage::none()
//! };
//!
//! let vertex_buffer = Buffer::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue)
//!                                                     .expect("failed to create buffer");
//!
//! // TODO: finish example
//! # }
//! ```
//!
use std::marker::PhantomData;
use std::mem;
use std::option::IntoIter as OptionIntoIter;
use std::sync::Arc;
use std::vec::IntoIter as VecIntoIter;

use buffer::Buffer;
use buffer::AbstractBuffer;
use format::Format;
use memory::MemorySourceChunk;
use vk;

#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum InputRate {
    Vertex = vk::VERTEX_INPUT_RATE_VERTEX,
    Instance = vk::VERTEX_INPUT_RATE_INSTANCE,
}

/// Describes an individual `Vertex`. In other words a collection of attributes that can be read
/// from a vertex shader.
pub unsafe trait Vertex {
    /// Returns the characteristics of a vertex attribute.
    fn attrib(name: &str) -> Option<AttributeInfo>;
}

/// Information about a single attribute within a vertex.
pub struct AttributeInfo {
    /// Number of bytes between the start of a vertex and the location of attribute.
    pub offset: usize,
    /// Data type of the attribute.
    pub format: Format,
}

/// Trait for types that contain a definition of the vertex input used by a graphics pipeline.
pub unsafe trait Definition {
    /// Iterator used to enumerate the list of buffers.
    type InfoIter: ExactSizeIterator<Item = (usize, InputRate)>;

    /// Returns information about an attribute, and the index of the buffer (within the iterator
    /// returned by `buffers`) in which the attribute is found.
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)>;

    /// Produces an iterator that returns the stride (in bytes) and input rate of each buffer.
    fn buffers(&self) -> Self::InfoIter;
}

/// Extension for `Definition` trait. The `L` parameter is an acceptable vertex source for this
/// vertex definition.
pub unsafe trait Source<L>: Definition {
    /// Iterator used by `decode`.
    type Iter: ExactSizeIterator<Item = Arc<AbstractBuffer>>;

    /// Checks and returns the list of buffers, number of vertices and number of instances.
    fn decode(&self, L) -> (Self::Iter, usize, usize);
}

/// Implementation of `Definition` for a single vertex buffer.
pub struct SingleBufferDefinition<T>(pub PhantomData<T>);

impl<T> SingleBufferDefinition<T> {
    #[inline]
    pub fn new() -> SingleBufferDefinition<T> { SingleBufferDefinition(PhantomData) }
}

unsafe impl<T> Definition for SingleBufferDefinition<T> where T: Vertex {
    type InfoIter = OptionIntoIter<(usize, InputRate)>;

    #[inline]
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)> {
        <T as Vertex>::attrib(name).map(|info| (0, info))
    }

    #[inline]
    fn buffers(&self) -> Self::InfoIter {
        Some((mem::size_of::<T>(), InputRate::Vertex)).into_iter()
    }
}

unsafe impl<'a, V, M> Source<&'a Arc<Buffer<[V], M>>> for SingleBufferDefinition<V>
    where V: Vertex + 'static, M: MemorySourceChunk + 'static
{
    type Iter = OptionIntoIter<Arc<AbstractBuffer>>;

    #[inline]
    fn decode(&self, source: &'a Arc<Buffer<[V], M>>)
              -> (OptionIntoIter<Arc<AbstractBuffer>>, usize, usize)
    {
        let iter = Some(source.clone() as Arc<_>).into_iter();
        (iter, source.len(), 1)
    }
}

/// Unstable.
// TODO: shouldn't be just `Two` but `Multi`
pub struct TwoBuffersDefinition<T, U>(pub PhantomData<(T, U)>);

impl<T, U> TwoBuffersDefinition<T, U> {
    #[inline]
    pub fn new() -> TwoBuffersDefinition<T, U> { TwoBuffersDefinition(PhantomData) }
}

unsafe impl<T, U> Definition for TwoBuffersDefinition<T, U> where T: Vertex, U: Vertex {
    type InfoIter = VecIntoIter<(usize, InputRate)>;

    #[inline]
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)> {
        if let Some(a) = <T as Vertex>::attrib(name) {
            Some((0, a))
        } else if let Some(a) = <U as Vertex>::attrib(name) {
            Some((1, a))
        } else {
            None
        }
    }

    #[inline]
    fn buffers(&self) -> Self::InfoIter {
        vec![
            (mem::size_of::<T>(), InputRate::Vertex),
            (mem::size_of::<U>(), InputRate::Vertex)
        ].into_iter()
    }
}

unsafe impl<'a, T, U, Mt, Mu> Source<(&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>)> for TwoBuffersDefinition<T, U>
    where T: Vertex + 'static, Mt: MemorySourceChunk + 'static,
          U: Vertex + 'static, Mu: MemorySourceChunk + 'static
{
    type Iter = VecIntoIter<Arc<AbstractBuffer>>;

    #[inline]
    fn decode(&self, source: (&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>))
              -> (VecIntoIter<Arc<AbstractBuffer>>, usize, usize)
    {
        let iter = vec![source.0.clone() as Arc<_>, source.1.clone() as Arc<_>].into_iter();
        (iter, [source.0.len(), source.1.len()].iter().cloned().min().unwrap(), 1)
    }
}

/// Unstable.
// TODO: bad way to do things
pub struct OneVertexOneInstanceDefinition<T, U>(pub PhantomData<(T, U)>);

impl<T, U> OneVertexOneInstanceDefinition<T, U> {
    #[inline]
    pub fn new() -> OneVertexOneInstanceDefinition<T, U> { OneVertexOneInstanceDefinition(PhantomData) }
}

unsafe impl<T, U> Definition for OneVertexOneInstanceDefinition<T, U> where T: Vertex, U: Vertex {
    type InfoIter = VecIntoIter<(usize, InputRate)>;

    #[inline]
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)> {
        if let Some(a) = <T as Vertex>::attrib(name) {
            Some((0, a))
        } else if let Some(a) = <U as Vertex>::attrib(name) {
            Some((1, a))
        } else {
            None
        }
    }

    #[inline]
    fn buffers(&self) -> Self::InfoIter {
        vec![
            (mem::size_of::<T>(), InputRate::Vertex),
            (mem::size_of::<U>(), InputRate::Instance)
        ].into_iter()
    }
}

unsafe impl<'a, T, U, Mt, Mu> Source<(&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>)> for OneVertexOneInstanceDefinition<T, U>
    where T: Vertex + 'static, Mt: MemorySourceChunk + 'static,
          U: Vertex + 'static, Mu: MemorySourceChunk + 'static
{
    type Iter = VecIntoIter<Arc<AbstractBuffer>>;

    #[inline]
    fn decode(&self, source: (&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>))
              -> (VecIntoIter<Arc<AbstractBuffer>>, usize, usize)
    {
        let iter = vec![source.0.clone() as Arc<_>, source.1.clone() as Arc<_>].into_iter();
        (iter, [source.0.len(), source.1.len()].iter().cloned().min().unwrap(), 1)
    }
}

/// Implements the `Vertex` trait on a struct.
// TODO: add example
#[macro_export]
macro_rules! impl_vertex {
    ($out:ident $(, $member:ident)*) => (
        unsafe impl $crate::pipeline::vertex::Vertex for $out {
            #[inline(always)]
            fn attrib(name: &str) -> Option<$crate::pipeline::vertex::AttributeInfo> {
                $(
                    if name == stringify!($member) {
                        return Some($crate::pipeline::vertex::AttributeInfo {
                            offset: unsafe {
                                let dummy = 0usize as *const $out;
                                let member = (&(&*dummy).$member) as *const _;
                                member as usize
                            },

                            format: unsafe {
                                #[inline] fn f<T: $crate::pipeline::vertex::Data>(_: &T) -> $crate::format::Format { T::format() }
                                let dummy = 0usize as *const $out;
                                f(&(&*dummy).$member)
                            },
                        });
                    }
                )*

                None
            }
        }
    )
}

/// Trait for data types that can be used as vertex attributes. Used by the `impl_vertex!` macro.
pub unsafe trait Data {
    /// Returns the format of the attribute.
    fn format() -> Format;
}

unsafe impl Data for f32 {
    #[inline]
    fn format() -> Format {
        Format::R32Sfloat
    }
}

unsafe impl Data for [f32; 1] {
    #[inline]
    fn format() -> Format {
        Format::R32Sfloat
    }
}

unsafe impl Data for [f32; 2] {
    #[inline]
    fn format() -> Format {
        Format::R32G32Sfloat
    }
}

unsafe impl Data for [f32; 3] {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32Sfloat
    }
}

unsafe impl Data for [f32; 4] {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32A32Sfloat
    }
}

unsafe impl Data for (f32,) {
    #[inline]
    fn format() -> Format {
        Format::R32Sfloat
    }
}

unsafe impl Data for (f32, f32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32Sfloat
    }
}

unsafe impl Data for (f32, f32, f32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32Sfloat
    }
}

unsafe impl Data for (f32, f32, f32, f32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32A32Sfloat
    }
}
<commit_msg>Implement Data for u32 types<commit_after>//! # Vertex sources definition
//!
//! When you create a graphics pipeline object, you need to pass an object which indicates the
//! layout of the vertex buffer(s) that will serve as input for the vertex shader. This is done
//! by passing an implementation of the `Definition` trait.
//!
//! The most common situation is a single vertex buffer and no instancing, in which case you can
//! pass a `SingleBufferDefinition` when you create the pipeline.
//!
//! # Implementing `Vertex`
//!
//! The implementations of the `Definition` trait that are provided by vulkano (like
//! `SingleBufferDefinition`) require you to use a buffer whose content is `[V]` where `V`
//! implements the `Vertex` trait.
//!
//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!`
//! macro.
//!
//! # Example
//!
//! ```no_run
//! # #[macro_use] extern crate vulkano
//! # fn main() {
//! # use std::sync::Arc;
//! # use vulkano::device::Device;
//! # use vulkano::device::Queue;
//! use vulkano::buffer::Buffer;
//! use vulkano::buffer::Usage as BufferUsage;
//! use vulkano::memory::HostVisible;
//! use vulkano::pipeline::vertex::;
//! # let device: Arc<Device> = unsafe { std::mem::uninitialized() };
//! # let queue: Arc<Queue> = unsafe { std::mem::uninitialized() };
//!
//! struct Vertex {
//!     position: [f32; 2]
//! }
//!
//! impl_vertex!(Vertex, position);
//!
//! let usage = BufferUsage {
//!     vertex_buffer: true,
//!     .. BufferUsage::none()
//! };
//!
//! let vertex_buffer = Buffer::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue)
//!                                                     .expect("failed to create buffer");
//!
//! // TODO: finish example
//! # }
//! ```
//!
use std::marker::PhantomData;
use std::mem;
use std::option::IntoIter as OptionIntoIter;
use std::sync::Arc;
use std::vec::IntoIter as VecIntoIter;

use buffer::Buffer;
use buffer::AbstractBuffer;
use format::Format;
use memory::MemorySourceChunk;
use vk;

#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum InputRate {
    Vertex = vk::VERTEX_INPUT_RATE_VERTEX,
    Instance = vk::VERTEX_INPUT_RATE_INSTANCE,
}

/// Describes an individual `Vertex`. In other words a collection of attributes that can be read
/// from a vertex shader.
pub unsafe trait Vertex {
    /// Returns the characteristics of a vertex attribute.
    fn attrib(name: &str) -> Option<AttributeInfo>;
}

/// Information about a single attribute within a vertex.
pub struct AttributeInfo {
    /// Number of bytes between the start of a vertex and the location of attribute.
    pub offset: usize,
    /// Data type of the attribute.
    pub format: Format,
}

/// Trait for types that contain a definition of the vertex input used by a graphics pipeline.
pub unsafe trait Definition {
    /// Iterator used to enumerate the list of buffers.
    type InfoIter: ExactSizeIterator<Item = (usize, InputRate)>;

    /// Returns information about an attribute, and the index of the buffer (within the iterator
    /// returned by `buffers`) in which the attribute is found.
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)>;

    /// Produces an iterator that returns the stride (in bytes) and input rate of each buffer.
    fn buffers(&self) -> Self::InfoIter;
}

/// Extension for `Definition` trait. The `L` parameter is an acceptable vertex source for this
/// vertex definition.
pub unsafe trait Source<L>: Definition {
    /// Iterator used by `decode`.
    type Iter: ExactSizeIterator<Item = Arc<AbstractBuffer>>;

    /// Checks and returns the list of buffers, number of vertices and number of instances.
    fn decode(&self, L) -> (Self::Iter, usize, usize);
}

/// Implementation of `Definition` for a single vertex buffer.
pub struct SingleBufferDefinition<T>(pub PhantomData<T>);

impl<T> SingleBufferDefinition<T> {
    #[inline]
    pub fn new() -> SingleBufferDefinition<T> { SingleBufferDefinition(PhantomData) }
}

unsafe impl<T> Definition for SingleBufferDefinition<T> where T: Vertex {
    type InfoIter = OptionIntoIter<(usize, InputRate)>;

    #[inline]
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)> {
        <T as Vertex>::attrib(name).map(|info| (0, info))
    }

    #[inline]
    fn buffers(&self) -> Self::InfoIter {
        Some((mem::size_of::<T>(), InputRate::Vertex)).into_iter()
    }
}

unsafe impl<'a, V, M> Source<&'a Arc<Buffer<[V], M>>> for SingleBufferDefinition<V>
    where V: Vertex + 'static, M: MemorySourceChunk + 'static
{
    type Iter = OptionIntoIter<Arc<AbstractBuffer>>;

    #[inline]
    fn decode(&self, source: &'a Arc<Buffer<[V], M>>)
              -> (OptionIntoIter<Arc<AbstractBuffer>>, usize, usize)
    {
        let iter = Some(source.clone() as Arc<_>).into_iter();
        (iter, source.len(), 1)
    }
}

/// Unstable.
// TODO: shouldn't be just `Two` but `Multi`
pub struct TwoBuffersDefinition<T, U>(pub PhantomData<(T, U)>);

impl<T, U> TwoBuffersDefinition<T, U> {
    #[inline]
    pub fn new() -> TwoBuffersDefinition<T, U> { TwoBuffersDefinition(PhantomData) }
}

unsafe impl<T, U> Definition for TwoBuffersDefinition<T, U> where T: Vertex, U: Vertex {
    type InfoIter = VecIntoIter<(usize, InputRate)>;

    #[inline]
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)> {
        if let Some(a) = <T as Vertex>::attrib(name) {
            Some((0, a))
        } else if let Some(a) = <U as Vertex>::attrib(name) {
            Some((1, a))
        } else {
            None
        }
    }

    #[inline]
    fn buffers(&self) -> Self::InfoIter {
        vec![
            (mem::size_of::<T>(), InputRate::Vertex),
            (mem::size_of::<U>(), InputRate::Vertex)
        ].into_iter()
    }
}

unsafe impl<'a, T, U, Mt, Mu> Source<(&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>)> for TwoBuffersDefinition<T, U>
    where T: Vertex + 'static, Mt: MemorySourceChunk + 'static,
          U: Vertex + 'static, Mu: MemorySourceChunk + 'static
{
    type Iter = VecIntoIter<Arc<AbstractBuffer>>;

    #[inline]
    fn decode(&self, source: (&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>))
              -> (VecIntoIter<Arc<AbstractBuffer>>, usize, usize)
    {
        let iter = vec![source.0.clone() as Arc<_>, source.1.clone() as Arc<_>].into_iter();
        (iter, [source.0.len(), source.1.len()].iter().cloned().min().unwrap(), 1)
    }
}

/// Unstable.
// TODO: bad way to do things
pub struct OneVertexOneInstanceDefinition<T, U>(pub PhantomData<(T, U)>);

impl<T, U> OneVertexOneInstanceDefinition<T, U> {
    #[inline]
    pub fn new() -> OneVertexOneInstanceDefinition<T, U> { OneVertexOneInstanceDefinition(PhantomData) }
}

unsafe impl<T, U> Definition for OneVertexOneInstanceDefinition<T, U> where T: Vertex, U: Vertex {
    type InfoIter = VecIntoIter<(usize, InputRate)>;

    #[inline]
    fn attrib(&self, name: &str) -> Option<(usize, AttributeInfo)> {
        if let Some(a) = <T as Vertex>::attrib(name) {
            Some((0, a))
        } else if let Some(a) = <U as Vertex>::attrib(name) {
            Some((1, a))
        } else {
            None
        }
    }

    #[inline]
    fn buffers(&self) -> Self::InfoIter {
        vec![
            (mem::size_of::<T>(), InputRate::Vertex),
            (mem::size_of::<U>(), InputRate::Instance)
        ].into_iter()
    }
}

unsafe impl<'a, T, U, Mt, Mu> Source<(&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>)> for OneVertexOneInstanceDefinition<T, U>
    where T: Vertex + 'static, Mt: MemorySourceChunk + 'static,
          U: Vertex + 'static, Mu: MemorySourceChunk + 'static
{
    type Iter = VecIntoIter<Arc<AbstractBuffer>>;

    #[inline]
    fn decode(&self, source: (&'a Arc<Buffer<[T], Mt>>, &'a Arc<Buffer<[U], Mu>>))
              -> (VecIntoIter<Arc<AbstractBuffer>>, usize, usize)
    {
        let iter = vec![source.0.clone() as Arc<_>, source.1.clone() as Arc<_>].into_iter();
        (iter, [source.0.len(), source.1.len()].iter().cloned().min().unwrap(), 1)
    }
}

/// Implements the `Vertex` trait on a struct.
// TODO: add example
#[macro_export]
macro_rules! impl_vertex {
    ($out:ident $(, $member:ident)*) => (
        unsafe impl $crate::pipeline::vertex::Vertex for $out {
            #[inline(always)]
            fn attrib(name: &str) -> Option<$crate::pipeline::vertex::AttributeInfo> {
                $(
                    if name == stringify!($member) {
                        return Some($crate::pipeline::vertex::AttributeInfo {
                            offset: unsafe {
                                let dummy = 0usize as *const $out;
                                let member = (&(&*dummy).$member) as *const _;
                                member as usize
                            },

                            format: unsafe {
                                #[inline] fn f<T: $crate::pipeline::vertex::Data>(_: &T) -> $crate::format::Format { T::format() }
                                let dummy = 0usize as *const $out;
                                f(&(&*dummy).$member)
                            },
                        });
                    }
                )*

                None
            }
        }
    )
}

/// Trait for data types that can be used as vertex attributes. Used by the `impl_vertex!` macro.
pub unsafe trait Data {
    /// Returns the format of the attribute.
    fn format() -> Format;
}

unsafe impl Data for f32 {
    #[inline]
    fn format() -> Format {
        Format::R32Sfloat
    }
}

unsafe impl Data for [f32; 1] {
    #[inline]
    fn format() -> Format {
        Format::R32Sfloat
    }
}

unsafe impl Data for [f32; 2] {
    #[inline]
    fn format() -> Format {
        Format::R32G32Sfloat
    }
}

unsafe impl Data for [f32; 3] {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32Sfloat
    }
}

unsafe impl Data for [f32; 4] {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32A32Sfloat
    }
}

unsafe impl Data for (f32,) {
    #[inline]
    fn format() -> Format {
        Format::R32Sfloat
    }
}

unsafe impl Data for (f32, f32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32Sfloat
    }
}

unsafe impl Data for (f32, f32, f32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32Sfloat
    }
}

unsafe impl Data for (f32, f32, f32, f32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32A32Sfloat
    }
}

unsafe impl Data for u32 {
    #[inline]
    fn format() -> Format {
        Format::R32Uint
    }
}

unsafe impl Data for [u32; 1] {
    #[inline]
    fn format() -> Format {
        Format::R32Uint
    }
}

unsafe impl Data for [u32; 2] {
    #[inline]
    fn format() -> Format {
        Format::R32G32Uint
    }
}

unsafe impl Data for [u32; 3] {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32Uint
    }
}

unsafe impl Data for [u32; 4] {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32A32Uint
    }
}

unsafe impl Data for (u32,) {
    #[inline]
    fn format() -> Format {
        Format::R32Uint
    }
}

unsafe impl Data for (u32, u32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32Uint
    }
}

unsafe impl Data for (u32, u32, u32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32Uint
    }
}

unsafe impl Data for (u32, u32, u32, u32) {
    #[inline]
    fn format() -> Format {
        Format::R32G32B32A32Uint
    }
}
<|endoftext|>


<commit_before>//! Simple script to verify the coding style of this library
//!
//! ## How to run
//!
//! The first argument to this script is the directory to run on, so running
//! this script should be as simple as:
//!
//! ```notrust
//! rustc ci/style.rs
//! ./style src
//! ```
//!
//! ## Guidelines
//!
//! The current style is:
//!
//! * Specific module layout:
//!     1. use directives
//!     2. typedefs
//!     3. structs
//!     4. constants
//!     5. f! { ... } functions
//!     6. extern functions
//!     7. modules + pub use
//!
//! Things not verified:
//!
//! * alignment
//! * leading colons on paths

use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => panic!("{} failed with {}", stringify!($e), e),
        }
    };
}

fn main() {
    let arg = env::args().skip(1).next().unwrap_or(".".to_string());

    let mut errors = Errors { errs: false };
    walk(Path::new(&arg), &mut errors);

    if errors.errs {
        panic!("found some lint errors");
    } else {
        println!("good style!");
    }
}

fn walk(path: &Path, err: &mut Errors) {
    for entry in t!(path.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        if t!(entry.file_type()).is_dir() {
            walk(&path, err);
            continue;
        }

        let name = entry.file_name().into_string().unwrap();
        match &name[..] {
            n if !n.ends_with(".rs") => continue,

            "lib.rs" | "macros.rs" => continue,

            _ => {}
        }

        let mut contents = String::new();
        t!(t!(fs::File::open(&path)).read_to_string(&mut contents));

        check_style(&contents, &path, err);
    }
}

struct Errors {
    errs: bool,
}

#[derive(Clone, Copy, PartialEq)]
enum State {
    Start,
    Imports,
    Typedefs,
    Structs,
    Constants,
    FunctionDefinitions,
    Functions,
    Modules,
}

fn check_style(file: &str, path: &Path, err: &mut Errors) {
    let mut state = State::Start;
    let mut s_macros = 0;
    let mut f_macros = 0;
    let mut in_impl = false;

    for (i, line) in file.lines().enumerate() {
        if line.contains("#[cfg(")
            && line.contains(']')
            && !line.contains(" if ")
            && !(line.contains("target_endian") || line.contains("target_arch"))
        {
            if state != State::Structs {
                err.error(path, i, "use cfg_if! and submodules instead of #[cfg]");
            }
        }
        if line.contains("#[derive(") && (line.contains("Copy") || line.contains("Clone")) {
            err.error(path, i, "impl ::Copy and ::Clone manually");
        }
        if line.contains("impl") {
            in_impl = true;
        }
        if in_impl && line.starts_with('}') {
            in_impl = false;
        }

        let orig_line = line;
        let line = line.trim_start();
        let is_pub = line.starts_with("pub ");
        let line = if is_pub { &line[4..] } else { line };

        let line_state = if line.starts_with("use ") {
            if line.contains("c_void") {
                continue;
            }
            if is_pub {
                State::Modules
            } else {
                State::Imports
            }
        } else if line.starts_with("const ") {
            State::Constants
        } else if line.starts_with("type ") && !in_impl {
            State::Typedefs
        } else if line.starts_with("s! {") {
            s_macros += 1;
            State::Structs
        } else if line.starts_with("f! {") {
            f_macros += 1;
            State::FunctionDefinitions
        } else if line.starts_with("extern ") && !orig_line.starts_with(" ") {
            State::Functions
        } else if line.starts_with("mod ") {
            State::Modules
        } else {
            continue;
        };

        if state as usize > line_state as usize {
            err.error(
                path,
                i,
                &format!(
                    "{} found after {} when it belongs before",
                    line_state.desc(),
                    state.desc()
                ),
            );
        }

        if f_macros == 2 {
            f_macros += 1;
            err.error(path, i, "multiple f! macros in one module");
        }
        if s_macros == 2 {
            s_macros += 1;
            err.error(path, i, "multiple s! macros in one module");
        }

        state = line_state;
    }
}

impl State {
    fn desc(&self) -> &str {
        match *self {
            State::Start => "start",
            State::Imports => "import",
            State::Typedefs => "typedef",
            State::Structs => "struct",
            State::Constants => "constant",
            State::FunctionDefinitions => "function definition",
            State::Functions => "extern function",
            State::Modules => "module",
        }
    }
}

impl Errors {
    fn error(&mut self, path: &Path, line: usize, msg: &str) {
        self.errs = true;
        println!("{}:{} - {}", path.display(), line + 1, msg);
    }
}
<commit_msg>style: Format errors so that editors can jump to file/line<commit_after>//! Simple script to verify the coding style of this library
//!
//! ## How to run
//!
//! The first argument to this script is the directory to run on, so running
//! this script should be as simple as:
//!
//! ```notrust
//! rustc ci/style.rs
//! ./style src
//! ```
//!
//! ## Guidelines
//!
//! The current style is:
//!
//! * Specific module layout:
//!     1. use directives
//!     2. typedefs
//!     3. structs
//!     4. constants
//!     5. f! { ... } functions
//!     6. extern functions
//!     7. modules + pub use
//!
//! Things not verified:
//!
//! * alignment
//! * leading colons on paths

use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => panic!("{} failed with {}", stringify!($e), e),
        }
    };
}

fn main() {
    let arg = env::args().skip(1).next().unwrap_or(".".to_string());

    let mut errors = Errors { errs: false };
    walk(Path::new(&arg), &mut errors);

    if errors.errs {
        panic!("found some lint errors");
    } else {
        println!("good style!");
    }
}

fn walk(path: &Path, err: &mut Errors) {
    for entry in t!(path.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        if t!(entry.file_type()).is_dir() {
            walk(&path, err);
            continue;
        }

        let name = entry.file_name().into_string().unwrap();
        match &name[..] {
            n if !n.ends_with(".rs") => continue,

            "lib.rs" | "macros.rs" => continue,

            _ => {}
        }

        let mut contents = String::new();
        t!(t!(fs::File::open(&path)).read_to_string(&mut contents));

        check_style(&contents, &path, err);
    }
}

struct Errors {
    errs: bool,
}

#[derive(Clone, Copy, PartialEq)]
enum State {
    Start,
    Imports,
    Typedefs,
    Structs,
    Constants,
    FunctionDefinitions,
    Functions,
    Modules,
}

fn check_style(file: &str, path: &Path, err: &mut Errors) {
    let mut state = State::Start;
    let mut s_macros = 0;
    let mut f_macros = 0;
    let mut in_impl = false;

    for (i, line) in file.lines().enumerate() {
        if line.contains("#[cfg(")
            && line.contains(']')
            && !line.contains(" if ")
            && !(line.contains("target_endian") || line.contains("target_arch"))
        {
            if state != State::Structs {
                err.error(path, i, "use cfg_if! and submodules instead of #[cfg]");
            }
        }
        if line.contains("#[derive(") && (line.contains("Copy") || line.contains("Clone")) {
            err.error(path, i, "impl ::Copy and ::Clone manually");
        }
        if line.contains("impl") {
            in_impl = true;
        }
        if in_impl && line.starts_with('}') {
            in_impl = false;
        }

        let orig_line = line;
        let line = line.trim_start();
        let is_pub = line.starts_with("pub ");
        let line = if is_pub { &line[4..] } else { line };

        let line_state = if line.starts_with("use ") {
            if line.contains("c_void") {
                continue;
            }
            if is_pub {
                State::Modules
            } else {
                State::Imports
            }
        } else if line.starts_with("const ") {
            State::Constants
        } else if line.starts_with("type ") && !in_impl {
            State::Typedefs
        } else if line.starts_with("s! {") {
            s_macros += 1;
            State::Structs
        } else if line.starts_with("f! {") {
            f_macros += 1;
            State::FunctionDefinitions
        } else if line.starts_with("extern ") && !orig_line.starts_with(" ") {
            State::Functions
        } else if line.starts_with("mod ") {
            State::Modules
        } else {
            continue;
        };

        if state as usize > line_state as usize {
            err.error(
                path,
                i,
                &format!(
                    "{} found after {} when it belongs before",
                    line_state.desc(),
                    state.desc()
                ),
            );
        }

        if f_macros == 2 {
            f_macros += 1;
            err.error(path, i, "multiple f! macros in one module");
        }
        if s_macros == 2 {
            s_macros += 1;
            err.error(path, i, "multiple s! macros in one module");
        }

        state = line_state;
    }
}

impl State {
    fn desc(&self) -> &str {
        match *self {
            State::Start => "start",
            State::Imports => "import",
            State::Typedefs => "typedef",
            State::Structs => "struct",
            State::Constants => "constant",
            State::FunctionDefinitions => "function definition",
            State::Functions => "extern function",
            State::Modules => "module",
        }
    }
}

impl Errors {
    fn error(&mut self, path: &Path, line: usize, msg: &str) {
        self.errs = true;
        println!("{}:{}: {}", path.display(), line + 1, msg);
    }
}
<|endoftext|>


<commit_before>//! Kafka consumers.

use std::ptr;
use std::sync::Arc;
use std::time::Duration;

use rdkafka_sys as rdsys;
use rdkafka_sys::types::*;

use crate::client::{Client, ClientContext, NativeClient};
use crate::error::KafkaResult;
use crate::groups::GroupList;
use crate::log::{error, trace};
use crate::message::BorrowedMessage;
use crate::metadata::Metadata;
use crate::topic_partition_list::{Offset, TopicPartitionList};
use crate::util::{cstr_to_owned, KafkaDrop, NativePtr, Timeout};

pub mod base_consumer;
pub mod stream_consumer;

// Re-exports.
#[doc(inline)]
pub use self::base_consumer::BaseConsumer;
#[doc(inline)]
pub use self::stream_consumer::{MessageStream, StreamConsumer};

/// Rebalance information.
#[derive(Clone, Debug)]
pub enum Rebalance<'a> {
    /// A new partition assignment is received.
    Assign(&'a TopicPartitionList),
    /// A new partition revocation is received.
    Revoke(&'a TopicPartitionList),
    /// Unexpected error from Kafka.
    Error(String),
}

/// Consumer-specific context.
///
/// This user-defined object can be used to provide custom callbacks for
/// consumer events. Refer to the list of methods to check which callbacks can
/// be specified.
///
/// See also the [`ClientContext`] trait.
pub trait ConsumerContext: ClientContext {
    /// Implements the default rebalancing strategy and calls the
    /// [`pre_rebalance`](ConsumerContext::pre_rebalance) and
    /// [`post_rebalance`](ConsumerContext::post_rebalance) methods. If this
    /// method is overridden, it will be responsibility of the user to call them
    /// if needed.
    fn rebalance(
        &self,
        native_client: &NativeClient,
        err: RDKafkaRespErr,
        tpl: &mut TopicPartitionList,
    ) {
        let rebalance = match err {
            RDKafkaRespErr::RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS => Rebalance::Assign(tpl),
            RDKafkaRespErr::RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS => Rebalance::Revoke(tpl),
            _ => {
                let error = unsafe { cstr_to_owned(rdsys::rd_kafka_err2str(err)) };
                error!("Error rebalancing: {}", error);
                Rebalance::Error(error)
            }
        };

        trace!("Running pre-rebalance with {:?}", rebalance);
        self.pre_rebalance(&rebalance);

        trace!("Running rebalance with {:?}", rebalance);
        // Execute rebalance
        unsafe {
            match err {
                RDKafkaRespErr::RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS => {
                    match native_client.rebalance_protocol() {
                        RebalanceProtocol::Cooperative => {
                            rdsys::rd_kafka_incremental_assign(native_client.ptr(), tpl.ptr());
                        }
                        _ => {
                            rdsys::rd_kafka_assign(native_client.ptr(), tpl.ptr());
                        }
                    }
                }
                _ => match native_client.rebalance_protocol() {
                    RebalanceProtocol::Cooperative => {
                        rdsys::rd_kafka_incremental_unassign(native_client.ptr(), tpl.ptr());
                    }
                    _ => {
                        rdsys::rd_kafka_assign(native_client.ptr(), ptr::null());
                    }
                },
            }
        }
        trace!("Running post-rebalance with {:?}", rebalance);
        self.post_rebalance(&rebalance);
    }

    /// Pre-rebalance callback. This method will run before the rebalance and
    /// should terminate its execution quickly.
    #[allow(unused_variables)]
    fn pre_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {}

    /// Post-rebalance callback. This method will run after the rebalance and
    /// should terminate its execution quickly.
    #[allow(unused_variables)]
    fn post_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {}

    // TODO: convert pointer to structure
    /// Post commit callback. This method will run after a group of offsets was
    /// committed to the offset store.
    #[allow(unused_variables)]
    fn commit_callback(&self, result: KafkaResult<()>, offsets: &TopicPartitionList) {}

    /// Returns the minimum interval at which to poll the main queue, which
    /// services the logging, stats, and error callbacks.
    ///
    /// The main queue is polled once whenever [`BaseConsumer::poll`] is called.
    /// If `poll` is called with a timeout that is larger than this interval,
    /// then the main queue will be polled at that interval while the consumer
    /// queue is blocked.
    ///
    /// For example, if the main queue's minimum poll interval is 200ms and
    /// `poll` is called with a timeout of 1s, then `poll` may block for up to
    /// 1s waiting for a message, but it will poll the main queue every 200ms
    /// while it is waiting.
    ///
    /// By default, the minimum poll interval for the main queue is 1s.
    fn main_queue_min_poll_interval(&self) -> Timeout {
        Timeout::After(Duration::from_secs(1))
    }
}

/// An inert [`ConsumerContext`] that can be used when no customizations are
/// needed.
#[derive(Clone, Debug, Default)]
pub struct DefaultConsumerContext;

impl ClientContext for DefaultConsumerContext {}
impl ConsumerContext for DefaultConsumerContext {}

/// Specifies whether a commit should be performed synchronously or
/// asynchronously.
#[derive(Clone, Copy, Debug)]
pub enum CommitMode {
    /// Synchronous commit.
    Sync = 0,
    /// Asynchronous commit.
    Async = 1,
}

/// Consumer group metadata.
///
/// For use with [`Producer::send_offsets_to_transaction`].
///
/// [`Producer::send_offsets_to_transaction`]: crate::producer::Producer::send_offsets_to_transaction
pub struct ConsumerGroupMetadata(NativePtr<RDKafkaConsumerGroupMetadata>);

impl ConsumerGroupMetadata {
    pub(crate) fn ptr(&self) -> *const RDKafkaConsumerGroupMetadata {
        self.0.ptr()
    }
}

unsafe impl KafkaDrop for RDKafkaConsumerGroupMetadata {
    const TYPE: &'static str = "consumer_group_metadata";
    const DROP: unsafe extern "C" fn(*mut Self) = rdsys::rd_kafka_consumer_group_metadata_destroy;
}

unsafe impl Send for ConsumerGroupMetadata {}
unsafe impl Sync for ConsumerGroupMetadata {}

/// The rebalance protocol for a consumer.
pub enum RebalanceProtocol {
    /// The consumer has not (yet) joined a group.
    None,
    /// Eager rebalance protocol.
    Eager,
    /// Cooperative rebalance protocol.
    Cooperative,
}

/// Common trait for all consumers.
///
/// # Note about object safety
///
/// Doing type erasure on consumers is expected to be rare (eg. `Box<dyn
/// Consumer>`). Therefore, the API is optimised for the case where a concrete
/// type is available. As a result, some methods are not available on trait
/// objects, since they are generic.
pub trait Consumer<C = DefaultConsumerContext>
where
    C: ConsumerContext,
{
    /// Returns the [`Client`] underlying this consumer.
    fn client(&self) -> &Client<C>;

    /// Returns a reference to the [`ConsumerContext`] used to create this
    /// consumer.
    fn context(&self) -> &Arc<C> {
        self.client().context()
    }

    /// Returns the current consumer group metadata associated with the
    /// consumer.
    ///
    /// If the consumer was not configured with a `group.id`, returns `None`.
    /// For use with [`Producer::send_offsets_to_transaction`].
    ///
    /// [`Producer::send_offsets_to_transaction`]: crate::producer::Producer::send_offsets_to_transaction
    fn group_metadata(&self) -> Option<ConsumerGroupMetadata>;

    /// Subscribes the consumer to a list of topics.
    fn subscribe(&self, topics: &[&str]) -> KafkaResult<()>;

    /// Unsubscribes the current subscription list.
    fn unsubscribe(&self);

    /// Manually assigns topics and partitions to the consumer. If used,
    /// automatic consumer rebalance won't be activated.
    fn assign(&self, assignment: &TopicPartitionList) -> KafkaResult<()>;

    /// Seeks to `offset` for the specified `topic` and `partition`. After a
    /// successful call to `seek`, the next poll of the consumer will return the
    /// message with `offset`.
    fn seek<T: Into<Timeout>>(
        &self,
        topic: &str,
        partition: i32,
        offset: Offset,
        timeout: T,
    ) -> KafkaResult<()>;

    /// Commits the offset of the specified message. The commit can be sync
    /// (blocking), or async. Notice that when a specific offset is committed,
    /// all the previous offsets are considered committed as well. Use this
    /// method only if you are processing messages in order.
    fn commit(
        &self,
        topic_partition_list: &TopicPartitionList,
        mode: CommitMode,
    ) -> KafkaResult<()>;

    /// Commits the current consumer state. Notice that if the consumer fails
    /// after a message has been received, but before the message has been
    /// processed by the user code, this might lead to data loss. Check the
    /// "at-least-once delivery" section in the readme for more information.
    fn commit_consumer_state(&self, mode: CommitMode) -> KafkaResult<()>;

    /// Commit the provided message. Note that this will also automatically
    /// commit every message with lower offset within the same partition.
    fn commit_message(&self, message: &BorrowedMessage<'_>, mode: CommitMode) -> KafkaResult<()>;

    /// Stores offset to be used on the next (auto)commit. When
    /// using this `enable.auto.offset.store` should be set to `false` in the
    /// config.
    fn store_offset(&self, topic: &str, partition: i32, offset: i64) -> KafkaResult<()>;

    /// Like [`Consumer::store_offset`], but the offset to store is derived from
    /// the provided message.
    fn store_offset_from_message(&self, message: &BorrowedMessage<'_>) -> KafkaResult<()>;

    /// Store offsets to be used on the next (auto)commit. When using this
    /// `enable.auto.offset.store` should be set to `false` in the config.
    fn store_offsets(&self, tpl: &TopicPartitionList) -> KafkaResult<()>;

    /// Returns the current topic subscription.
    fn subscription(&self) -> KafkaResult<TopicPartitionList>;

    /// Returns the current partition assignment.
    fn assignment(&self) -> KafkaResult<TopicPartitionList>;

    /// Retrieves the committed offsets for topics and partitions.
    fn committed<T>(&self, timeout: T) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Retrieves the committed offsets for specified topics and partitions.
    fn committed_offsets<T>(
        &self,
        tpl: TopicPartitionList,
        timeout: T,
    ) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>;

    /// Looks up the offsets for this consumer's partitions by timestamp.
    fn offsets_for_timestamp<T>(
        &self,
        timestamp: i64,
        timeout: T,
    ) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Looks up the offsets for the specified partitions by timestamp.
    fn offsets_for_times<T>(
        &self,
        timestamps: TopicPartitionList,
        timeout: T,
    ) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Retrieve current positions (offsets) for topics and partitions.
    fn position(&self) -> KafkaResult<TopicPartitionList>;

    /// Returns the metadata information for the specified topic, or for all
    /// topics in the cluster if no topic is specified.
    fn fetch_metadata<T>(&self, topic: Option<&str>, timeout: T) -> KafkaResult<Metadata>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Returns the low and high watermarks for a specific topic and partition.
    fn fetch_watermarks<T>(
        &self,
        topic: &str,
        partition: i32,
        timeout: T,
    ) -> KafkaResult<(i64, i64)>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Returns the group membership information for the given group. If no group is
    /// specified, all groups will be returned.
    fn fetch_group_list<T>(&self, group: Option<&str>, timeout: T) -> KafkaResult<GroupList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Pauses consumption for the provided list of partitions.
    fn pause(&self, partitions: &TopicPartitionList) -> KafkaResult<()>;

    /// Resumes consumption for the provided list of partitions.
    fn resume(&self, partitions: &TopicPartitionList) -> KafkaResult<()>;

    /// Reports the rebalance protocol in use.
    fn rebalance_protocol(&self) -> RebalanceProtocol;
}
<commit_msg>Improve documentation of CommitMode enum (#449)<commit_after>//! Kafka consumers.

use std::ptr;
use std::sync::Arc;
use std::time::Duration;

use rdkafka_sys as rdsys;
use rdkafka_sys::types::*;

use crate::client::{Client, ClientContext, NativeClient};
use crate::error::KafkaResult;
use crate::groups::GroupList;
use crate::log::{error, trace};
use crate::message::BorrowedMessage;
use crate::metadata::Metadata;
use crate::topic_partition_list::{Offset, TopicPartitionList};
use crate::util::{cstr_to_owned, KafkaDrop, NativePtr, Timeout};

pub mod base_consumer;
pub mod stream_consumer;

// Re-exports.
#[doc(inline)]
pub use self::base_consumer::BaseConsumer;
#[doc(inline)]
pub use self::stream_consumer::{MessageStream, StreamConsumer};

/// Rebalance information.
#[derive(Clone, Debug)]
pub enum Rebalance<'a> {
    /// A new partition assignment is received.
    Assign(&'a TopicPartitionList),
    /// A new partition revocation is received.
    Revoke(&'a TopicPartitionList),
    /// Unexpected error from Kafka.
    Error(String),
}

/// Consumer-specific context.
///
/// This user-defined object can be used to provide custom callbacks for
/// consumer events. Refer to the list of methods to check which callbacks can
/// be specified.
///
/// See also the [`ClientContext`] trait.
pub trait ConsumerContext: ClientContext {
    /// Implements the default rebalancing strategy and calls the
    /// [`pre_rebalance`](ConsumerContext::pre_rebalance) and
    /// [`post_rebalance`](ConsumerContext::post_rebalance) methods. If this
    /// method is overridden, it will be responsibility of the user to call them
    /// if needed.
    fn rebalance(
        &self,
        native_client: &NativeClient,
        err: RDKafkaRespErr,
        tpl: &mut TopicPartitionList,
    ) {
        let rebalance = match err {
            RDKafkaRespErr::RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS => Rebalance::Assign(tpl),
            RDKafkaRespErr::RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS => Rebalance::Revoke(tpl),
            _ => {
                let error = unsafe { cstr_to_owned(rdsys::rd_kafka_err2str(err)) };
                error!("Error rebalancing: {}", error);
                Rebalance::Error(error)
            }
        };

        trace!("Running pre-rebalance with {:?}", rebalance);
        self.pre_rebalance(&rebalance);

        trace!("Running rebalance with {:?}", rebalance);
        // Execute rebalance
        unsafe {
            match err {
                RDKafkaRespErr::RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS => {
                    match native_client.rebalance_protocol() {
                        RebalanceProtocol::Cooperative => {
                            rdsys::rd_kafka_incremental_assign(native_client.ptr(), tpl.ptr());
                        }
                        _ => {
                            rdsys::rd_kafka_assign(native_client.ptr(), tpl.ptr());
                        }
                    }
                }
                _ => match native_client.rebalance_protocol() {
                    RebalanceProtocol::Cooperative => {
                        rdsys::rd_kafka_incremental_unassign(native_client.ptr(), tpl.ptr());
                    }
                    _ => {
                        rdsys::rd_kafka_assign(native_client.ptr(), ptr::null());
                    }
                },
            }
        }
        trace!("Running post-rebalance with {:?}", rebalance);
        self.post_rebalance(&rebalance);
    }

    /// Pre-rebalance callback. This method will run before the rebalance and
    /// should terminate its execution quickly.
    #[allow(unused_variables)]
    fn pre_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {}

    /// Post-rebalance callback. This method will run after the rebalance and
    /// should terminate its execution quickly.
    #[allow(unused_variables)]
    fn post_rebalance<'a>(&self, rebalance: &Rebalance<'a>) {}

    // TODO: convert pointer to structure
    /// Post commit callback. This method will run after a group of offsets was
    /// committed to the offset store.
    #[allow(unused_variables)]
    fn commit_callback(&self, result: KafkaResult<()>, offsets: &TopicPartitionList) {}

    /// Returns the minimum interval at which to poll the main queue, which
    /// services the logging, stats, and error callbacks.
    ///
    /// The main queue is polled once whenever [`BaseConsumer::poll`] is called.
    /// If `poll` is called with a timeout that is larger than this interval,
    /// then the main queue will be polled at that interval while the consumer
    /// queue is blocked.
    ///
    /// For example, if the main queue's minimum poll interval is 200ms and
    /// `poll` is called with a timeout of 1s, then `poll` may block for up to
    /// 1s waiting for a message, but it will poll the main queue every 200ms
    /// while it is waiting.
    ///
    /// By default, the minimum poll interval for the main queue is 1s.
    fn main_queue_min_poll_interval(&self) -> Timeout {
        Timeout::After(Duration::from_secs(1))
    }
}

/// An inert [`ConsumerContext`] that can be used when no customizations are
/// needed.
#[derive(Clone, Debug, Default)]
pub struct DefaultConsumerContext;

impl ClientContext for DefaultConsumerContext {}
impl ConsumerContext for DefaultConsumerContext {}

/// Specifies whether a commit should be performed synchronously or asynchronously.
///
/// Regardless of the `CommitMode`, the commit APIs enqueue the commit request in
/// a local work queue. A separate worker thread picks up this commit request and forwards
/// it to the Kafka broker over the network. [1]
///
/// The difference between CommitMode::Sync and CommitMode::Async is in whether the caller
/// waits for the Kafka broker to signal that it finished handling the commit request. [2]
///
/// Note that the commit APIs are not async in the Rust sense due to the lack of a
/// callback-based interface exposed by librdkafka. [3]
///
/// [1]: https://github.com/edenhill/librdkafka/blob/e3d9515e396615b57674a93b39be2ca60355f4f4/src/rdkafka_cgrp.c#L3161
/// [2]: https://github.com/edenhill/librdkafka/blob/f092c290995ca81b3afb4015fcc3350ba02caa96/src/rdkafka_offset.c#L387
/// [3]: https://github.com/edenhill/librdkafka/issues/3212
/// [4]: https://github.com/edenhill/librdkafka/blob/e3d9515e396615b57674a93b39be2ca60355f4f4/src/rdkafka_cgrp.c#L2846
#[derive(Clone, Copy, Debug)]
pub enum CommitMode {
    /// In `Sync` mode, the caller blocks until the Kafka broker finishes processing
    /// the commit request. [4]
    Sync = 0,

    /// In `Async` mode, the caller enqueues the commit request in a local
    /// work queue and returns immediately. [2]
    Async = 1,
}

/// Consumer group metadata.
///
/// For use with [`Producer::send_offsets_to_transaction`].
///
/// [`Producer::send_offsets_to_transaction`]: crate::producer::Producer::send_offsets_to_transaction
pub struct ConsumerGroupMetadata(NativePtr<RDKafkaConsumerGroupMetadata>);

impl ConsumerGroupMetadata {
    pub(crate) fn ptr(&self) -> *const RDKafkaConsumerGroupMetadata {
        self.0.ptr()
    }
}

unsafe impl KafkaDrop for RDKafkaConsumerGroupMetadata {
    const TYPE: &'static str = "consumer_group_metadata";
    const DROP: unsafe extern "C" fn(*mut Self) = rdsys::rd_kafka_consumer_group_metadata_destroy;
}

unsafe impl Send for ConsumerGroupMetadata {}
unsafe impl Sync for ConsumerGroupMetadata {}

/// The rebalance protocol for a consumer.
pub enum RebalanceProtocol {
    /// The consumer has not (yet) joined a group.
    None,
    /// Eager rebalance protocol.
    Eager,
    /// Cooperative rebalance protocol.
    Cooperative,
}

/// Common trait for all consumers.
///
/// # Note about object safety
///
/// Doing type erasure on consumers is expected to be rare (eg. `Box<dyn
/// Consumer>`). Therefore, the API is optimised for the case where a concrete
/// type is available. As a result, some methods are not available on trait
/// objects, since they are generic.
pub trait Consumer<C = DefaultConsumerContext>
where
    C: ConsumerContext,
{
    /// Returns the [`Client`] underlying this consumer.
    fn client(&self) -> &Client<C>;

    /// Returns a reference to the [`ConsumerContext`] used to create this
    /// consumer.
    fn context(&self) -> &Arc<C> {
        self.client().context()
    }

    /// Returns the current consumer group metadata associated with the
    /// consumer.
    ///
    /// If the consumer was not configured with a `group.id`, returns `None`.
    /// For use with [`Producer::send_offsets_to_transaction`].
    ///
    /// [`Producer::send_offsets_to_transaction`]: crate::producer::Producer::send_offsets_to_transaction
    fn group_metadata(&self) -> Option<ConsumerGroupMetadata>;

    /// Subscribes the consumer to a list of topics.
    fn subscribe(&self, topics: &[&str]) -> KafkaResult<()>;

    /// Unsubscribes the current subscription list.
    fn unsubscribe(&self);

    /// Manually assigns topics and partitions to the consumer. If used,
    /// automatic consumer rebalance won't be activated.
    fn assign(&self, assignment: &TopicPartitionList) -> KafkaResult<()>;

    /// Seeks to `offset` for the specified `topic` and `partition`. After a
    /// successful call to `seek`, the next poll of the consumer will return the
    /// message with `offset`.
    fn seek<T: Into<Timeout>>(
        &self,
        topic: &str,
        partition: i32,
        offset: Offset,
        timeout: T,
    ) -> KafkaResult<()>;

    /// Commits the offset of the specified message. The commit can be sync
    /// (blocking), or async. Notice that when a specific offset is committed,
    /// all the previous offsets are considered committed as well. Use this
    /// method only if you are processing messages in order.
    fn commit(
        &self,
        topic_partition_list: &TopicPartitionList,
        mode: CommitMode,
    ) -> KafkaResult<()>;

    /// Commits the current consumer state. Notice that if the consumer fails
    /// after a message has been received, but before the message has been
    /// processed by the user code, this might lead to data loss. Check the
    /// "at-least-once delivery" section in the readme for more information.
    fn commit_consumer_state(&self, mode: CommitMode) -> KafkaResult<()>;

    /// Commit the provided message. Note that this will also automatically
    /// commit every message with lower offset within the same partition.
    fn commit_message(&self, message: &BorrowedMessage<'_>, mode: CommitMode) -> KafkaResult<()>;

    /// Stores offset to be used on the next (auto)commit. When
    /// using this `enable.auto.offset.store` should be set to `false` in the
    /// config.
    fn store_offset(&self, topic: &str, partition: i32, offset: i64) -> KafkaResult<()>;

    /// Like [`Consumer::store_offset`], but the offset to store is derived from
    /// the provided message.
    fn store_offset_from_message(&self, message: &BorrowedMessage<'_>) -> KafkaResult<()>;

    /// Store offsets to be used on the next (auto)commit. When using this
    /// `enable.auto.offset.store` should be set to `false` in the config.
    fn store_offsets(&self, tpl: &TopicPartitionList) -> KafkaResult<()>;

    /// Returns the current topic subscription.
    fn subscription(&self) -> KafkaResult<TopicPartitionList>;

    /// Returns the current partition assignment.
    fn assignment(&self) -> KafkaResult<TopicPartitionList>;

    /// Retrieves the committed offsets for topics and partitions.
    fn committed<T>(&self, timeout: T) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Retrieves the committed offsets for specified topics and partitions.
    fn committed_offsets<T>(
        &self,
        tpl: TopicPartitionList,
        timeout: T,
    ) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>;

    /// Looks up the offsets for this consumer's partitions by timestamp.
    fn offsets_for_timestamp<T>(
        &self,
        timestamp: i64,
        timeout: T,
    ) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Looks up the offsets for the specified partitions by timestamp.
    fn offsets_for_times<T>(
        &self,
        timestamps: TopicPartitionList,
        timeout: T,
    ) -> KafkaResult<TopicPartitionList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Retrieve current positions (offsets) for topics and partitions.
    fn position(&self) -> KafkaResult<TopicPartitionList>;

    /// Returns the metadata information for the specified topic, or for all
    /// topics in the cluster if no topic is specified.
    fn fetch_metadata<T>(&self, topic: Option<&str>, timeout: T) -> KafkaResult<Metadata>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Returns the low and high watermarks for a specific topic and partition.
    fn fetch_watermarks<T>(
        &self,
        topic: &str,
        partition: i32,
        timeout: T,
    ) -> KafkaResult<(i64, i64)>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Returns the group membership information for the given group. If no group is
    /// specified, all groups will be returned.
    fn fetch_group_list<T>(&self, group: Option<&str>, timeout: T) -> KafkaResult<GroupList>
    where
        T: Into<Timeout>,
        Self: Sized;

    /// Pauses consumption for the provided list of partitions.
    fn pause(&self, partitions: &TopicPartitionList) -> KafkaResult<()>;

    /// Resumes consumption for the provided list of partitions.
    fn resume(&self, partitions: &TopicPartitionList) -> KafkaResult<()>;

    /// Reports the rebalance protocol in use.
    fn rebalance_protocol(&self) -> RebalanceProtocol;
}
<|endoftext|>


<commit_before>pub enum Flag {
    Set(&'static str),
    Clear(&'static str),
    NotAffected(&'static str),
    Undefined,
}

pub struct FlagsDesc {
    pub x: Flag,
    pub n: Flag,
    pub z: Flag,
    pub v: Flag,
    pub c: Flag,
}

const FLAGS_ARC: FlagsDesc = FlagsDesc {
    x: Flag::Set("X - Set the same as carry"),
    n: Flag::Set("N - Set if the result is negative; cleared otherwise."),
    z: Flag::Set("Z — Set if the result is zero; cleared otherwise."),
    v: Flag::Set("V — Set if an overflow is generated; cleared otherwise."),
    c: Flag::Set("C — Set if a carry is generated; cleared otherwise."),
};

const FLAGS_X: FlagsDesc = FlagsDesc {
    x: Flag::Set("X - Set the same as carry"),
    n: Flag::Set("N - Set if the result is negative; cleared otherwise."),
    z: Flag::Set("Z — Cleared if the result is non-zero; unchanged otherwise."),
    v: Flag::Set("V — Set if an overflow is generated; cleared otherwise."),
    c: Flag::Set("C — Set if a carry is generated; cleared otherwise."),
};

const FLAGS_ABCD: FlagsDesc = FlagsDesc {
    x: Flag::Set("X - Set the same as carry"),
    n: Flag::Undefined,
    z: Flag::Clear("Z — Cleared if the result is nonzero; unchanged otherwise"),
    v: Flag::Undefined,
    c: Flag::Set("C — Set if a decimal carry was generated; cleared otherwise."),
};

const FLAGS_AND: FlagsDesc = FlagsDesc {
    x: Flag::NotAffected("X - Not Affected"),
    n: Flag::Set("N — Set if the most significant bit of the result is set; cleared otherwise."),
    z: Flag::Set("Z — Set if the result is zero; cleared otherwise."),
    v: Flag::Clear("V — Always cleared."),
    c: Flag::Clear("C — Always cleared."),
};

pub struct Description {
    pub description: &'static str,
    pub operation: &'static str,
    pub assembler: &'static [&'static str],
    pub attributes: &'static str,
    pub flags: &'static FlagsDesc,
}

pub const ABCD_DESC: Description = Description {
    description: "Adds the source operand to the destination operand along with the extend bit, and stores the result in the destination location. The addition is performed using binary- coded decimal arithmetic. The operands, which are packed binary-coded decimal numbers, can be addressed in two different ways\n
        1. Data Register to Data Register: The operands are contained in the data registers specified in the instruction.\n
        2. Memory to Memory: The operands are addressed with the predecrement addressing mode using the address registers specified in the instruction.\n
        This operation is a byte operation only.",
    operation: "Source10 + Destination10 + X → Destination",
    assembler: &["abcd < ea > ,Dn", "Add Dn, < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_ABCD,
};

pub const ADD_DESC: Description = Description {
    description: "Adds the source operand to the destination operand using binary addition and stores the result in the destination location. The size of the operation may be specified as byte, word, or long. The mode of the instruction indicates which operand is the source and which is the destination, as well as the operand size.",
    operation: "Source + Destination → Destination",
    assembler: &["Add < ea > ,Dn", "Add Dn, < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_ARC,
};

pub const ADDQ_DESC: Description = Description {
    description: "Adds an immediate value of one to eight to the operand at the destination location. The size of the operation may be specified as byte, word, or long. Word and long operations are also allowed on the address registers. When adding to address registers, the condition codes are not altered, and the entire destination address register is used regardless of the operation size.",
    operation: "Immidate + Destination → Destination",
    assembler: &["addq # < data > , < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_ARC,
};

pub const ADDX_DESC: Description = Description {
    description: "Adds the source operand and the extend bit to the destination operand and stores the result in the destination location. The operands can be addressed in two different ways:
                    1. Data register to data register—The data registers specified in the instruction contain the operands.
                    2. Memory to memory—The address registers specified in the instruction address the operands using the predecrement addressing mode.
                    The size of the operation can be specified as byte, word, or long.";
    operation: "Source + Destination + X → Destination",
    assembler: &["addx Dy,Dx", "addx -(Ay),-(Ax)"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_X,
};

pub const AND_DESC: Description = Description {
    description: "Performs an AND operation of the source operand with the destination operand and stores the result in the destination location. The size of the operation can be specified as byte, word, or long. The contents of an address register may not be used as an operand.",
    operation: "Source & Destination → Destination",
    assembler: &["and < ea > ,Dn", "Add Dn, < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_AND,
};

<commit_msg>Added more desc<commit_after>pub enum Flag {
    Set(&'static str),
    Clear(&'static str),
    NotAffected(&'static str),
    Undefined,
}

pub struct FlagsDesc {
    pub x: Flag,
    pub n: Flag,
    pub z: Flag,
    pub v: Flag,
    pub c: Flag,
}

const FLAGS_ARC: FlagsDesc = FlagsDesc {
    x: Flag::Set("X - Set the same as carry"),
    n: Flag::Set("N - Set if the result is negative; cleared otherwise."),
    z: Flag::Set("Z — Set if the result is zero; cleared otherwise."),
    v: Flag::Set("V — Set if an overflow is generated; cleared otherwise."),
    c: Flag::Set("C — Set if a carry is generated; cleared otherwise."),
};

const FLAGS_X: FlagsDesc = FlagsDesc {
    x: Flag::Set("X - Set the same as carry"),
    n: Flag::Set("N - Set if the result is negative; cleared otherwise."),
    z: Flag::Set("Z — Cleared if the result is non-zero; unchanged otherwise."),
    v: Flag::Set("V — Set if an overflow is generated; cleared otherwise."),
    c: Flag::Set("C — Set if a carry is generated; cleared otherwise."),
};

const FLAGS_ABCD: FlagsDesc = FlagsDesc {
    x: Flag::Set("X - Set the same as carry"),
    n: Flag::Undefined,
    z: Flag::Clear("Z — Cleared if the result is nonzero; unchanged otherwise"),
    v: Flag::Undefined,
    c: Flag::Set("C — Set if a decimal carry was generated; cleared otherwise."),
};

const FLAGS_AND: FlagsDesc = FlagsDesc {
    x: Flag::NotAffected("X - Not Affected"),
    n: Flag::Set("N — Set if the most significant bit of the result is set; cleared otherwise."),
    z: Flag::Set("Z — Set if the result is zero; cleared otherwise."),
    v: Flag::Clear("V — Always cleared."),
    c: Flag::Clear("C — Always cleared."),
};

const FLAGS_SHIFT: FlagsDesc = FlagsDesc {
    x: Flag::Set("X — Set according to the last bit shifted out of the operand; unaffected for a shift count of zero."),
    n: Flag::Set("N — Set if the most significant bit of the result is set; cleared otherwise."),
    z: Flag::Set("Z — Set if the result is zero; cleared otherwise."),
    v: Flag::Set("V — Set if the most significant bit is changed at any time during the shift operation; cleared otherwise."),
    c: Flag::Set("C — Set according to the last bit shifted out of the operand; cleared for a shift count of zero."),
};

const FLAGS_BINST: FlagsDesc = FlagsDesc {
    x: Flag::NotAffected("X — Not Affected."),
    n: Flag::NotAffected("N — Not Affected."),
    z: Flag::Set("Z — Set if the bit tested is zero; cleared otherwise."),
    v: Flag::NotAffected("V — Not Affected."),
    c: Flag::NotAffected("C — Not Affected."),
};

const FLAGS_NOT_AFFECTED: FlagsDesc = FlagsDesc {
    x: Flag::NotAffected("X — Not Affected."),
    n: Flag::NotAffected("N — Not Affected."),
    z: Flag::NotAffected("Z — Not Affected."),
    v: Flag::NotAffected("V — Not Affected."),
    c: Flag::NotAffected("C — Not Affected."),
};

const FLAGS_CLR: FlagsDesc = FlagsDesc {
    x: Flag::NotAffected("X — Not Affected."),
    n: Flag::Clear("N — Always cleared."),
    z: Flag::Set("Z — Always set."),
    v: Flag::Clear("V — Always cleared."),
    c: Flag::Clear("C — Always cleared."),
};

const FLAGS_CMP: FlagsDesc = FlagsDesc {
    x: Flag::NotAffected("X — Not Affected."),
    n: Flag::Set("N — Set if the result is negative; cleared otherwise."),
    z: Flag::Set("Z — Set if the result is zero; cleared otherwise."),
    v: Flag::Set("V — Set if an overflow occurs; cleared otherwise."),
    c: Flag::Set("C — Set if a borrow occurs; cleared otherwise."),
};

const FLAGS_DIV: FlagsDesc = FlagsDesc {
    x: Flag::NotAffected("X — Not Affected."),
    n: Flag::Set("N — Set if the quotient is negative; cleared otherwise; undefined if overflow or divide by zero occurs."),
    z: Flag::Set("Z — Set if the quotient is zero; cleared otherwise; undefined if overflow or divide by zero occurs."),
    v: Flag::Set("V — Set if division overflow occurs; undefined if divide by zero occurs; cleared otherwise."),
    c: Flag::Set("C — Always cleared."),
};

pub struct Description {
    pub description: &'static str,
    pub operation: &'static str,
    pub assembler: &'static [&'static str],
    pub attributes: &'static str,
    pub flags: &'static FlagsDesc,
}

pub const ABCD_DESC: Description = Description {
    description: "Adds the source operand to the destination operand along with the extend bit, and stores the result in the destination location. The addition is performed using binary- coded decimal arithmetic. The operands, which are packed binary-coded decimal numbers, can be addressed in two different ways\n
        1. Data Register to Data Register: The operands are contained in the data registers specified in the instruction.\n
        2. Memory to Memory: The operands are addressed with the predecrement addressing mode using the address registers specified in the instruction.\n
        This operation is a byte operation only.",
    operation: "Source10 + Destination10 + X → Destination",
    assembler: &["abcd < ea > ,Dn", "Add Dn, < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_ABCD,
};

pub const ADD_DESC: Description = Description {
    description: "Adds the source operand to the destination operand using binary addition and stores the result in the destination location. The size of the operation may be specified as byte, word, or long. The mode of the instruction indicates which operand is the source and which is the destination, as well as the operand size.",
    operation: "Source + Destination → Destination",
    assembler: &["Add < ea > ,Dn", "Add Dn, < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_ARC,
};

pub const ADDQ_DESC: Description = Description {
    description: "Adds an immediate value of one to eight to the operand at the destination location. The size of the operation may be specified as byte, word, or long. Word and long operations are also allowed on the address registers. When adding to address registers, the condition codes are not altered, and the entire destination address register is used regardless of the operation size.",
    operation: "Immidate + Destination → Destination",
    assembler: &["addq # < data > , < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_ARC,
};

pub const ADDX_DESC: Description = Description {
    description: "Adds the source operand and the extend bit to the destination operand and stores the result in the destination location. The operands can be addressed in two different ways:
                    1. Data register to data register—The data registers specified in the instruction contain the operands.
                    2. Memory to memory—The address registers specified in the instruction address the operands using the predecrement addressing mode.
                    The size of the operation can be specified as byte, word, or long.",
    operation: "Source + Destination + X → Destination",
    assembler: &["addx Dy,Dx", "addx -(Ay),-(Ax)"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_X,
};

pub const AND_DESC: Description = Description {
    description: "Performs an AND operation of the source operand with the destination operand and stores the result in the destination location. The size of the operation can be specified as byte, word, or long. The contents of an address register may not be used as an operand.",
    operation: "Source & Destination → Destination",
    assembler: &["and < ea > ,Dn", "Add Dn, < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_AND,
};

pub const ASL_ASR_DESC: Description = Description {
    description: "Arithmetically shifts the bits of the operand in the direction (L or R) specified. The carry bit receives the last bit shifted out of the operand. The shift count for the shifting of a register may be specified in two different ways:
                    1. Immediate—The shift count is specified in the instruction (shift range, 1 – 8).
                    2. Register—The shift count is the value in the data register specified in instruction modulo 64.
                    The size of the operation can be specified as byte, word, or long. An operand in mem- ory can be shifted one bit only, and the operand size is restricted to a word.
                    For ASL, the operand is shifted left; the number of positions shifted is the shift count. Bits shifted out of the high-order bit go to both the carry and the extend bits; zeros are shifted into the low-order bit. The overflow bit indicates if any sign changes occur dur- ing the shift.",
    operation: "Destination Shifted By Count → Destination",
    assembler: &["ASd Dx,Dy", "ASd # < data > ,Dy", "ASd < ea >", "where d is direction, L or R"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_SHIFT,
};

pub const BCC_DESC: Description = Description {
    description: "If the specified condition is true, program execution continues at location (PC) + displacement.
                  The program counter contains the address of the instruction word for the Bcc instruction plus two.
                  The displacement is a twos-complement integer that represents the relative distance in bytes from the current program counter to the destination program counter.
                  If the 8-bit displacement field in the instruction word is zero, a 16-bit displacement (the word immediately following the instruction) is used.
                  If the 8-bit displacement field in the instruction word is all ones ($FF), the 32-bit displacement (long word immediately following the instruction) is used.
                  Condition code cc specifies one of the following conditional tests.",
    operation: "If Condition True Then PC + dn → PC",
    assembler: &["bcc < label >"],
    attributes: "Byte, Word",
    flags: &FLAGS_NOT_AFFECTED,
};

pub const BCHG_DESC: Description = Description {
    description: "Tests a bit in the destination operand and sets the Z condition code appropriately, then inverts the specified bit in the destination. When the destination is a data register, any of the 32 bits can be specified by the modulo 32-bit number. When the destination is a memory location, the operation is a byte operation, and the bit number is modulo 8. In all cases, bit zero refers to the least significant bit. The bit number for this operation may be specified in either of two ways:
                    1. Immediate—The bit number is specified in a second word of the instruction.
                    2. Register—The specified data register contains the bit number.",
    operation: "TEST ( < number > of Destination) → Z;
                TEST ( < number > of Destination) → < bit number > of Destination",
    assembler: &["bchg dn, < ea >", "bchg # < data > , < ea >"],
    attributes: "Byte, Long",
    flags: &FLAGS_BINST,
};

pub const BCLR_DESC: Description = Description {
    description: "Tests a bit in the destination operand and sets the Z condition code appropriately, then clears the specified bit in the destination. When a data register is the destination, any of the 32 bits can be specified by a modulo 32-bit number. When a memory location is the destination, the operation is a byte operation, and the bit number is modulo 8. In all cases, bit zero refers to the least significant bit. The bit number for this operation can be specified in either of two ways:
                    1. Immediate—The bit number is specified in a second word of the instruction.
                    2. Register—The specified data register contains the bit number.",
    operation: "TEST ( < bit number > of Destination) → Z; 0 → < bit number > of Destination",
    assembler: &["bclr dn, < ea >", "bclr # < data > , < ea >"],
    attributes: "Byte, Long",
    flags: &FLAGS_BINST,
};

pub const BRA_DESC: Description = Description {
    description: "Program execution continues at location (PC) + displacement. The program counter contains the address of the instruction word of the BRA instruction plus two. The displacement is a twos complement integer that represents the relative distance in bytes from the current program counter to the destination program counter. If the 8-bit displacement field in the instruction word is zero, a 16-bit displacement (the word immediately following the instruction) is used. If the 8-bit displacement field in the instruction word is all ones ($FF), the 32-bit displacement (long word immediately following the instruction) is used.",
    operation: "PC + dn → PC",
    assembler: &["bra < label >"],
    attributes: "Byte, Long",
    flags: &FLAGS_NOT_AFFECTED,
};

pub const BSET_DESC: Description = Description {
    description: "Description: Tests a bit in the destination operand and sets the Z condition code appropriately, then sets the specified bit in the destination operand. When a data register is the destination, any of the 32 bits can be specified by a modulo 32-bit number. When a memory location is the destination, the operation is a byte operation, and the bit number is modulo 8. In all cases, bit zero refers to the least significant bit. The bit number for this operation can be specified in either of two ways:
                    1. Immediate—The bit number is specified in the second word of the instruction.
                    2. Register—The specified data register contains the bit number.",
    operation: "TEST ( < bit number > of Destination) → Z; 1 → < bit number > of Destination",
    assembler: &["btest dn, < ea >", "btest # < data > , < ea >"],
    attributes: "Byte, Long",
    flags: &FLAGS_BINST,
};

pub const BSR_DESC: Description = Description {
    description: "Pushes the long-word address of the instruction immediately following the BSR instruction onto the system stack. The program counter contains the address of the instruction word plus two. Program execution then continues at location (PC) + displacement. The displacement is a twos complement integer that represents the relative distance in bytes from the current program counter to the destination program counter. If the 8-bit displacement field in the instruction word is zero, a 16-bit displacement (the word immediately following the instruction) is used. If the 8-bit displacement field in the instruction word is all ones ($FF), the 32-bit displacement (long word immediately following the instruction) is used.",
    operation: "SP – 4 → SP; PC → (SP); PC + dn → PC",
    assembler: &["bsr < label >"],
    attributes: "Byte, Word",
    flags: &FLAGS_NOT_AFFECTED,
};

pub const BTST_DESC: Description = Description {
    description: "Tests a bit in the destination operand and sets the Z condition code appropriately. When a data register is the destination, any of the 32 bits can be specified by a modulo 32- bit number. When a memory location is the destination, the operation is a byte operation, and the bit number is modulo 8. In all cases, bit zero refers to the least significant bit. The bit number for this operation can be specified in either of two ways:
                 1. Immediate—The bit number is specified in a second word of the instruction.
                 2. Register—The specified data register contains the bit number.",
    operation: "TEST ( < bit number > of Destination) → Z",
    assembler: &["btest dn, < ea >", "btest # < data > , < ea >"],
    attributes: "Byte, Long",
    flags: &FLAGS_BINST,
};

pub const CLR_DESC: Description = Description {
    description: "Clears the destination operand to zero. The size of the operation may be specified as byte, word, or long.",
    operation: " 0 → Destination",
    assembler: &["clr < ea >"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_CLR,
};

pub const CMP_DESC: Description = Description {
    description: "Subtracts the source operand from the destination data register and sets the condition codes according to the result; the data register is not changed. The size of the operation can be byte, word, or long.",
    operation: "Destination – Source → cc",
    assembler: &["cmp < ea > , Dn"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_CMP,
};

pub const CMPM_DESC: Description = Description {
    description: "Subtracts the source operand from the destination operand and sets the condition codes according to the results; the destination location is not changed. The operands are always addressed with the postincrement addressing mode, using the address registers specified in the instruction. The size of the operation may be specified as byte, word, or long.",
    operation: "Destination – Source → cc",
    assembler: &["cmpm (Ay) + ,(Ax) +"],
    attributes: "Byte, Word, Long",
    flags: &FLAGS_CMP,
};

pub const DBCC_DESC: Description = Description {
    description: "Description: Controls a loop of instructions. The parameters are a condition code, a data register (counter), and a displacement value. The instruction first tests the condition for termination; if it is true, no operation is performed. If the termination condition is not true, the low-order 16 bits of the counter data register decrement by one. If the result is – 1, execution continues with the next instruction. If the result is not equal to – 1, execution continues at the location indicated by the current value of the program counter plus the sign-extended 16-bit displacement. The value in the program counter is the address of the instruction word of the DBcc instruction plus two. The displacement is a twos complement integer that represents the relative distance in bytes from the current program counter to the destination program counter. Condition code cc specifies one of the following conditional tests:",
    operation: " If Condition False
                    Then (Dn – 1 → Dn; If Dn not equal – 1 Then PC + dn → PC)",
    assembler: &["dbcc dn, < label >"],
    attributes: "Word",
    flags: &FLAGS_NOT_AFFECTED,
};

pub const DIVS_DIVU_DESC: Description = Description {
    description: "Divides the signed destination operand by the signed source operand and stores the signed result in the destination. The result is a quotient in the lower word (least significant 16 bits) and a remainder in the upper word (most significant 16 bits). The sign of the remainder is the same as the sign of the dividend.
                    Two special conditions may arise during the operation:
                    1. Division by zero causes a trap.
                    2. Overflow may be detected and set before the instruction completes. If the instruction detects an overflow, it sets the overflow condition code, and the operands are unaffected.",
    operation: "Destination / Source → Destination",
    assembler: &["DIVS.W < ea > ,Dn32/16 → 16r – 16q"],
    attributes: "Word",
    flags: &FLAGS_DIV,
};

<|endoftext|>


<commit_before>use std::io::{BufReader, Read};

use byteorder::{ReadBytesExt, BigEndian};

use types::{Result, Dimensions};
use traits::Metadata as BaseMetadata;
use traits::LoadableMetadata;
use utils::BufReadExt;

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Metadata {
    pub dimensions: Dimensions,
    // TODO: something else?
}

impl BaseMetadata for Metadata {
    #[inline]
    fn mime_type(&self) -> &'static str { "image/jpeg" }

    #[inline]
    fn dimensions(&self) -> Dimensions {
        self.dimensions
    }
}

impl LoadableMetadata for Metadata {
    fn load<R: ?Sized + Read>(r: &mut R) -> Result<Metadata> {
        let mut r = &mut BufReader::new(r);
        loop {
            let bytes_read = try!(r.skip_until(0xff));
            if bytes_read == 0 {
                return Err(unexpected_eof!("when searching for a marker"));
            }

            let marker_type = try!(r.read_u8().map_err(if_eof!("when reading marker type")));
            if marker_type == 0 { continue; }  // skip "stuffed" byte

            let has_size = match marker_type {
                0xd0...0xd9 => false,
                _ => true
            };

            let size = if has_size {
                try!(r.read_u16::<BigEndian>()
                    .map_err(if_eof!("when reading marker payload size"))) - 2
            } else { 0 };

            let dimensions = match marker_type {
                0xc0 | 0xc2 => {  // maybe others?
                    // skip one byte
                    try!(r.read_u8().map_err(if_eof!("when skipping to dimensions data")));
                    let h = try!(r.read_u16::<BigEndian>().map_err(if_eof!("when reading height")));
                    let w = try!(r.read_u16::<BigEndian>().map_err(if_eof!("when reading width")));
                    Some((w, h))
                }
                _ => None
            };

            if let Some(dimensions) = dimensions {
                return Ok(Metadata {
                    dimensions: dimensions.into()
                });
            }

            let size = size as u64;
            if try!(r.skip_exact(size)) != size {
                return Err(unexpected_eof!("when skipping marker payload"));
            }
        }
    }
}

<commit_msg>Improved jpeg metadata parser<commit_after>use std::io::{BufReader, Read};

use byteorder::{ReadBytesExt, BigEndian};

use types::{Result, Dimensions};
use traits::Metadata as BaseMetadata;
use traits::LoadableMetadata;
use utils::BufReadExt;

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Metadata {
    pub dimensions: Dimensions,
    // TODO: something else?
}

impl BaseMetadata for Metadata {
    #[inline]
    fn mime_type(&self) -> &'static str { "image/jpeg" }

    #[inline]
    fn dimensions(&self) -> Dimensions {
        self.dimensions
    }
}

impl LoadableMetadata for Metadata {
    fn load<R: ?Sized + Read>(r: &mut R) -> Result<Metadata> {
        let mut r = &mut BufReader::new(r);
        loop {
            if try!(r.skip_until(0xff)) == 0 {
                return Err(unexpected_eof!("when searching for a marker"));
            }

            let marker_type = try_if_eof!(r.read_u8(), "when reading marker type");
            if marker_type == 0 { continue; }  // skip "stuffed" byte

            let has_size = match marker_type {
                0xd0...0xd9 => false,
                _ => true
            };

            let size = if has_size {
                try_if_eof!(r.read_u16::<BigEndian>(), "when reading marker payload size") - 2
            } else { 0 };

            let dimensions = match marker_type {
                0xc0 | 0xc2 => {  // maybe others?
                    // skip one byte
                    let _ = try_if_eof!(r.read_u8(), "when skipping to dimensions data");
                    let h = try_if_eof!(r.read_u16::<BigEndian>(), "when reading height");
                    let w = try_if_eof!(r.read_u16::<BigEndian>(), "when reading width");
                    Some((w, h))
                }
                _ => None
            };

            if let Some(dimensions) = dimensions {
                return Ok(Metadata {
                    dimensions: dimensions.into()
                });
            }

            let size = size as u64;
            if try!(r.skip_exact(size)) != size {
                return Err(unexpected_eof!("when skipping marker payload"));
            }
        }
    }
}

<|endoftext|>


<commit_before>//! This reader composes frames of bytes started with a 4 byte frame header indicating the size of
//! the buffer. An exact size buffer will be allocated once the 4 byte frame header is received.

use std::io::{self, Read};
use std::collections::VecDeque;
use std::mem;

#[derive(Debug)]
pub struct FrameReader {
    frames: Frames
}

impl FrameReader {
    pub fn new(max_frame_size: u32) -> FrameReader {
        FrameReader {
            frames: Frames::new(max_frame_size)
        }
    }

    pub fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        self.frames.read(reader)
    }

    pub fn iter_mut(&mut self) -> Iter {
        Iter {
            frames: &mut self.frames
        }
    }
}

pub struct Iter<'a> {
    frames: &'a mut Frames
}

impl<'a> Iterator for Iter<'a> {
    type Item = Vec<u8>;

    fn next(&mut self) -> Option<Vec<u8>> {
        self.frames.completed_frames.pop_front()
    }
}

#[derive(Debug)]
struct Frames {
    max_frame_size: u32,
    bytes_read: usize,
    header: [u8; 4],
    reading_header: bool,
    current: Vec<u8>,
    completed_frames: VecDeque<Vec<u8>>
}

impl Frames {
    pub fn new(max_frame_size: u32) -> Frames {
        Frames {
            max_frame_size: max_frame_size,
            bytes_read: 0,
            header: [0; 4],
            reading_header: true,
            current: Vec::with_capacity(0),
            completed_frames: VecDeque::new()
        }
    }

    fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let mut total_bytes_read = 0;
        loop {
            let bytes_read = try!(self.do_read(reader));
            total_bytes_read += bytes_read;
            // We either got EOF or we read a bunch of data, and didn't exaust our buffer.
            if bytes_read == 0 || self.bytes_read != 0 {
                return Ok(total_bytes_read)
            }
        }
    }

    fn do_read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        if self.reading_header {
            self.read_header(reader)
        } else {
            self.read_value(reader)
        }
    }

    // TODO: Return an error if size is greater than max_frame_size
    fn read_header<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.header[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == 4 {
           let len = unsafe { u32::from_be(mem::transmute(self.header)) };
           self.bytes_read = 0;
           self.reading_header = false;
           self.current = Vec::with_capacity(len as usize);
           unsafe { self.current.set_len(len as usize); }
        }
        Ok(bytes_read)
    }

    fn read_value<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.current[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == self.current.len() {
           self.completed_frames.push_back(mem::replace(&mut self.current, Vec::new()));
           self.bytes_read = 0;
           self.reading_header = true;
        }
        Ok(bytes_read)
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use std::io::Cursor;
    use std::io::Write;
    use super::FrameReader;

    #[test]
    fn partial_and_complete_reads() {
        let buf1 = String::from("Hello World").into_bytes();
        let buf2 = String::from("Hi.").into_bytes();
        let header1: [u8; 4] = unsafe { mem::transmute((buf1.len() as u32).to_be()) };
        let header2: [u8; 4] = unsafe { mem::transmute((buf2.len() as u32).to_be()) };

        let mut reader = FrameReader::new(1024);

        // Write a partial header
        let mut header = Cursor::new(&header1[0..2]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing just the header
        let mut header = Cursor::new(&header1[2..]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Write a partial value
        let mut data = Cursor::new(&buf1[0..5]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(5, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing the first value
        let mut data = Cursor::new(&buf1[5..]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(6, bytes_read);
        let val = reader.iter_mut().next().unwrap();
        assert_eq!(buf1, val);

        // Write an entire header and value
        let mut data = Cursor::new(Vec::with_capacity(7));
        assert_eq!(4, data.write(&header2).unwrap());
        assert_eq!(3, data.write(&buf2).unwrap());
        data.set_position(0);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(7, bytes_read);
        assert_eq!(buf2, reader.iter_mut().next().unwrap());
    }
}
<commit_msg>Report an error on eof in FrameReader<commit_after>//! This reader composes frames of bytes started with a 4 byte frame header indicating the size of
//! the buffer. An exact size buffer will be allocated once the 4 byte frame header is received.

use std::io::{self, Read, Error, ErrorKind};
use std::collections::VecDeque;
use std::mem;

#[derive(Debug)]
pub struct FrameReader {
    frames: Frames
}

impl FrameReader {
    pub fn new(max_frame_size: u32) -> FrameReader {
        FrameReader {
            frames: Frames::new(max_frame_size)
        }
    }

    pub fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        self.frames.read(reader)
    }

    pub fn iter_mut(&mut self) -> Iter {
        Iter {
            frames: &mut self.frames
        }
    }
}

pub struct Iter<'a> {
    frames: &'a mut Frames
}

impl<'a> Iterator for Iter<'a> {
    type Item = Vec<u8>;

    fn next(&mut self) -> Option<Vec<u8>> {
        self.frames.completed_frames.pop_front()
    }
}

#[derive(Debug)]
struct Frames {
    max_frame_size: u32,
    bytes_read: usize,
    header: [u8; 4],
    reading_header: bool,
    current: Vec<u8>,
    completed_frames: VecDeque<Vec<u8>>
}

impl Frames {
    pub fn new(max_frame_size: u32) -> Frames {
        Frames {
            max_frame_size: max_frame_size,
            bytes_read: 0,
            header: [0; 4],
            reading_header: true,
            current: Vec::with_capacity(0),
            completed_frames: VecDeque::new()
        }
    }

    fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let mut total_bytes_read = 0;
        loop {
            let bytes_read = try!(self.do_read(reader));
            total_bytes_read += bytes_read;

            if total_bytes_read == 0 {
                return Error::new(ErrorKind::UnexpectedEof, "Read 0 bytes");
            }

            // We either are done reading or we read a bunch of data, and didn't exaust our buffer.
            if bytes_read == 0 || self.bytes_read != 0 {
                return Ok(total_bytes_read)
            }
        }
    }

    fn do_read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        if self.reading_header {
            self.read_header(reader)
        } else {
            self.read_value(reader)
        }
    }

    // TODO: Return an error if size is greater than max_frame_size
    fn read_header<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.header[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == 4 {
           let len = unsafe { u32::from_be(mem::transmute(self.header)) };
           self.bytes_read = 0;
           self.reading_header = false;
           self.current = Vec::with_capacity(len as usize);
           unsafe { self.current.set_len(len as usize); }
        }
        Ok(bytes_read)
    }

    fn read_value<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.current[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == self.current.len() {
           self.completed_frames.push_back(mem::replace(&mut self.current, Vec::new()));
           self.bytes_read = 0;
           self.reading_header = true;
        }
        Ok(bytes_read)
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use std::io::Cursor;
    use std::io::Write;
    use super::FrameReader;

    #[test]
    fn partial_and_complete_reads() {
        let buf1 = String::from("Hello World").into_bytes();
        let buf2 = String::from("Hi.").into_bytes();
        let header1: [u8; 4] = unsafe { mem::transmute((buf1.len() as u32).to_be()) };
        let header2: [u8; 4] = unsafe { mem::transmute((buf2.len() as u32).to_be()) };

        let mut reader = FrameReader::new(1024);

        // Write a partial header
        let mut header = Cursor::new(&header1[0..2]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing just the header
        let mut header = Cursor::new(&header1[2..]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Write a partial value
        let mut data = Cursor::new(&buf1[0..5]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(5, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing the first value
        let mut data = Cursor::new(&buf1[5..]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(6, bytes_read);
        let val = reader.iter_mut().next().unwrap();
        assert_eq!(buf1, val);

        // Write an entire header and value
        let mut data = Cursor::new(Vec::with_capacity(7));
        assert_eq!(4, data.write(&header2).unwrap());
        assert_eq!(3, data.write(&buf2).unwrap());
        data.set_position(0);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(7, bytes_read);
        assert_eq!(buf2, reader.iter_mut().next().unwrap());
    }
}
<|endoftext|>