<commit_before>use gl;
use gl::types::*;
use gl_context::GLContext;
use num;
use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;
use std::ptr;

use shader::*;

/// Gets the id number for a given input of the shader program.
#[allow(non_snake_case)]
pub fn glGetAttribLocation(shader_program: GLuint, name: &str) -> GLint {
  let c_str = CString::new(name.as_bytes()).unwrap();
  let ptr = c_str.as_ptr() as *const i8;
  unsafe {
    gl::GetAttribLocation(shader_program, ptr)
  }
}

pub struct BufferHandle<'a> {
  pub gl_id: GLuint,
  phantom: PhantomData<&'a ()>,
}

impl<'a> BufferHandle<'a> {
  pub fn new<'b:'a>(_gl: &'a GLContext) -> BufferHandle<'b> {
    let mut gl_id = 0;

    unsafe {
      gl::GenBuffers(1, &mut gl_id);
    }

    assert!(gl_id != 0);

    BufferHandle {
      gl_id: gl_id,
      phantom: PhantomData,
    }
  }
}

impl<'a> Drop for BufferHandle<'a> {
  fn drop(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.gl_id);
    }
  }
}

/// Fixed-size VRAM buffer for individual bytes.
pub struct GLByteBuffer<'a> {
  pub handle: BufferHandle<'a>,
  /// number of bytes in the buffer.
  pub length: usize,
  /// maximum number of bytes in the buffer.
  pub capacity: usize,
}

impl<'a> GLByteBuffer<'a> {
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new<'b:'a>(
    gl: &'a mut GLContext,
    capacity: usize,
  ) -> GLByteBuffer<'b> {
    let handle = BufferHandle::new(gl);

    unsafe {
      gl::BindBuffer(gl::ARRAY_BUFFER, handle.gl_id);

      gl::BufferData(
        gl::ARRAY_BUFFER,
        capacity as GLsizeiptr,
        ptr::null(),
        gl::DYNAMIC_DRAW,
      );
    }

    match gl.get_error() {
      gl::NO_ERROR => {},
      gl::OUT_OF_MEMORY => panic!("Out of VRAM"),
      err => warn!("OpenGL error 0x{:x}", err),
    }

    GLByteBuffer {
      handle: handle,
      length: 0,
      capacity: capacity,
    }
  }

  pub fn bind(&self, _: &mut GLContext) {
    unsafe {
      gl::BindBuffer(gl::ARRAY_BUFFER, self.handle.gl_id);
    }
  }

  /// Add more data into this buffer.
  /// Returns false and does nothing if this would exceed the capacity of the buffer.
  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub unsafe fn push(&mut self, gl: &mut GLContext, vs: *const u8, count: usize) -> bool {
    if self.length + count > self.capacity {
      // This would overflow the buffer.
      return false;
    }

    self.update_inner(gl, self.length, vs, count);
    self.length += count;

    true
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn swap_remove(&mut self, _gl: &mut GLContext, i: usize, count: usize) {
    assert!(count <= self.length);
    self.length -= count;
    assert!(i <= self.length);

    // In the `i == self.length` case, we don't bother with the swap;
    // decreasing `self.length` is enough.

    if i < self.length {
      assert!(
        i <= self.length - count,
        "GLByteBuffer::swap_remove would cause copy in overlapping regions"
      );

      unsafe {
        gl::CopyBufferSubData(
          gl::ARRAY_BUFFER,
          gl::ARRAY_BUFFER,
          self.length as GLintptr,
          i as GLintptr,
          count as GLsizeiptr,
        );
      }
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub unsafe fn update(&self, gl: &mut GLContext, idx: usize, vs: *const u8, count: usize) {
    assert!(idx + count <= self.length);
    self.update_inner(gl, idx, vs, count);
  }

  unsafe fn update_inner(
    &self,
    _gl: &mut GLContext,
    idx: usize,
    vs: *const u8,
    count: usize,
  ) {
    assert!(idx + count <= self.capacity);

    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      idx as GLintptr,
      count as GLsizeiptr,
      mem::transmute(vs),
    );
  }
}

/// Fixed-size typed VRAM buffer, optimized for bulk inserts.
pub struct GLBuffer<'a, T> {
  pub byte_buffer: GLByteBuffer<'a>,
  phantom: PhantomData<Vec<T>>,
}

impl<'a, T> GLBuffer<'a, T> {
  pub fn new<'b:'a>(
    gl: &'a mut GLContext,
    capacity: usize,
  ) -> GLBuffer<'b, T> {
    GLBuffer {
      byte_buffer: GLByteBuffer::new(gl, capacity * mem::size_of::<T>()),
      phantom: PhantomData,
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn push(&mut self, gl: &mut GLContext, vs: &[T]) -> bool {
    unsafe {
      self.byte_buffer.push(
        gl,
        mem::transmute(vs.as_ptr()),
        mem::size_of::<T>() * vs.len()
      )
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn update(&mut self, gl: &mut GLContext, idx: usize, vs: &[T]) {
    unsafe {
      self.byte_buffer.update(
        gl,
        mem::size_of::<T>() * idx,
        mem::transmute(vs.as_ptr()),
        mem::size_of::<T>() * vs.len(),
      );
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn swap_remove(&mut self, gl: &mut GLContext, idx: usize, count: usize) {
    self.byte_buffer.swap_remove(
      gl,
      mem::size_of::<T>() * idx,
      mem::size_of::<T>() * count,
    );
  }
}

#[derive(Debug)]
#[derive(Copy, Clone)]
pub enum DrawMode {
  Lines,
  Triangles,
  Points,
}

impl DrawMode {
  fn to_enum(&self) -> GLenum {
    match *self {
      DrawMode::Lines     => gl::LINES,
      DrawMode::Triangles => gl::TRIANGLES,
      DrawMode::Points    => gl::POINTS,
    }
  }
}

#[derive(Debug)]
#[derive(Copy, Clone)]
pub enum GLType {
  Float,
  UInt,
  Int,
}

impl GLType {
  pub fn size(&self) -> u32 {
    num::NumCast::from(
      match *self {
        GLType::Float => mem::size_of::<GLfloat>(),
        GLType::UInt  => mem::size_of::<GLuint>(),
        GLType::Int   => mem::size_of::<GLint>(),
      })
    .unwrap()
  }

  pub fn gl_enum(&self) -> GLenum {
    match *self {
      GLType::Float => gl::FLOAT,
      GLType::UInt  => gl::UNSIGNED_INT,
      GLType::Int   => gl::INT,
    }
  }

  pub fn is_integral(&self) -> bool {
    match *self {
      GLType::Float => false,
      GLType::UInt  => true,
      GLType::Int   => true,
    }
  }
}

#[derive(Debug)]
/// Specifies how to pass data from OpenGL to the vertex shaders.
pub struct VertexAttribData<'a> {
  /// Cooresponds to the shader's `input variable`.
  pub name: &'a str,
  /// The size of this attribute, in the provided units.
  pub size: u32,
  pub unit: GLType,
}

pub struct ArrayHandle<'a> {
  pub gl_id: GLuint,
  phantom: PhantomData<&'a ()>,
}

impl<'a> ArrayHandle<'a> {
  pub fn new<'b:'a>(_gl: &'a GLContext) -> ArrayHandle<'b> {
    let mut gl_id = 0;
    unsafe {
      gl::GenVertexArrays(1, &mut gl_id);
    }

    ArrayHandle {
      gl_id: gl_id,
      phantom: PhantomData,
    }
  }
}

impl<'a> Drop for ArrayHandle<'a> {
  fn drop(&mut self) {
    unsafe {
      gl::DeleteVertexArrays(1, &self.gl_id);
    }
  }
}

/// A fixed-capacity array of bytes passed to OpenGL.
pub struct GLArray<'a, T> {
  pub buffer: GLBuffer<'a, T>,
  pub handle: ArrayHandle<'a>,
  /// How to draw this buffer. Ex: gl::LINES, gl::TRIANGLES, etc.
  pub mode: GLenum,
  /// length in `T`s.
  pub length: usize,
}

impl<'a, T> GLArray<'a, T> {
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new<'b:'a>(
    gl: &'a mut GLContext,
    shader_program: &Shader<'b>,
    attribs: &[VertexAttribData],
    mode: DrawMode,
    buffer: GLBuffer<'b, T>,
  ) -> GLArray<'b, T> {
    let handle = ArrayHandle::new(gl);

    unsafe {
      gl::BindVertexArray(handle.gl_id);
    }

    let mut offset = 0;
    let attrib_span = {
      let mut attrib_span = 0;
      for attrib in attribs.iter() {
        attrib_span += attrib.size * attrib.unit.size();
      }
      attrib_span
    };
    for attrib in attribs.iter() {
      let shader_attrib =
        glGetAttribLocation(
          shader_program.handle.gl_id,
          attrib.name
        );
      assert!(shader_attrib != -1, "shader attribute \"{}\" not found", attrib.name);
      let shader_attrib = shader_attrib as GLuint;

      unsafe {
        gl::EnableVertexAttribArray(shader_attrib);

        if attrib.unit.is_integral() {
          gl::VertexAttribIPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        } else {
          gl::VertexAttribPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            gl::FALSE as GLboolean,
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        }
      }
      offset += (attrib.size * attrib.unit.size()) as isize;
    }

    match unsafe { gl::GetError() } {
      gl::NO_ERROR => {},
      err => warn!("OpenGL error 0x{:x}", err),
    }

    assert_eq!(attrib_span as usize, mem::size_of::<T>());

    let length = buffer.byte_buffer.length / mem::size_of::<T>();

    GLArray {
      buffer: buffer,
      handle: handle,
      mode: mode.to_enum(),
      length: length,
    }
  }

  pub fn bind(&self, _: &mut GLContext) {
    unsafe {
      gl::BindVertexArray(self.handle.gl_id);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.buffer.byte_buffer.handle.gl_id);
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  /// The array doesn't need to be bound.
  pub fn push(&mut self, gl: &mut GLContext, vs: &[T]) -> bool {
    let r = self.buffer.push(gl, vs);
    if r {
      self.length += vs.len();
    }
    r
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  /// The array doesn't need to be bound.
  pub fn swap_remove(&mut self, gl: &mut GLContext, idx: usize, count: usize) {
    self.buffer.swap_remove(gl, idx, count);
    self.length -= count;
  }

  /// Draws all the queued triangles to the screen.
  /// N.B. For performance reasons, this does NOT bind the array.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn draw(&self, gl: &mut GLContext) {
    self.draw_slice(gl, 0, self.length);
  }

  /// Draw some subset of the triangle array.
  /// N.B. For performance reasons, this does NOT bind the array.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn draw_slice(&self, _gl: &mut GLContext, start: usize, len: usize) {
    assert!(start + len <= self.length);

    unsafe {
      gl::DrawArrays(self.mode, start as i32, len as i32);
    }
  }
}
<commit_msg>better error message<commit_after>use gl;
use gl::types::*;
use gl_context::GLContext;
use num;
use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;
use std::ptr;

use shader::*;

/// Gets the id number for a given input of the shader program.
#[allow(non_snake_case)]
pub fn glGetAttribLocation(shader_program: GLuint, name: &str) -> GLint {
  let c_str = CString::new(name.as_bytes()).unwrap();
  let ptr = c_str.as_ptr() as *const i8;
  unsafe {
    gl::GetAttribLocation(shader_program, ptr)
  }
}

pub struct BufferHandle<'a> {
  pub gl_id: GLuint,
  phantom: PhantomData<&'a ()>,
}

impl<'a> BufferHandle<'a> {
  pub fn new<'b:'a>(_gl: &'a GLContext) -> BufferHandle<'b> {
    let mut gl_id = 0;

    unsafe {
      gl::GenBuffers(1, &mut gl_id);
    }

    assert!(gl_id != 0);

    BufferHandle {
      gl_id: gl_id,
      phantom: PhantomData,
    }
  }
}

impl<'a> Drop for BufferHandle<'a> {
  fn drop(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.gl_id);
    }
  }
}

/// Fixed-size VRAM buffer for individual bytes.
pub struct GLByteBuffer<'a> {
  pub handle: BufferHandle<'a>,
  /// number of bytes in the buffer.
  pub length: usize,
  /// maximum number of bytes in the buffer.
  pub capacity: usize,
}

impl<'a> GLByteBuffer<'a> {
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new<'b:'a>(
    gl: &'a mut GLContext,
    capacity: usize,
  ) -> GLByteBuffer<'b> {
    let handle = BufferHandle::new(gl);

    unsafe {
      gl::BindBuffer(gl::ARRAY_BUFFER, handle.gl_id);

      gl::BufferData(
        gl::ARRAY_BUFFER,
        capacity as GLsizeiptr,
        ptr::null(),
        gl::DYNAMIC_DRAW,
      );
    }

    match gl.get_error() {
      gl::NO_ERROR => {},
      gl::OUT_OF_MEMORY => panic!("Out of VRAM"),
      err => warn!("OpenGL error 0x{:x}", err),
    }

    GLByteBuffer {
      handle: handle,
      length: 0,
      capacity: capacity,
    }
  }

  pub fn bind(&self, _: &mut GLContext) {
    unsafe {
      gl::BindBuffer(gl::ARRAY_BUFFER, self.handle.gl_id);
    }
  }

  /// Add more data into this buffer.
  /// Returns false and does nothing if this would exceed the capacity of the buffer.
  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub unsafe fn push(&mut self, gl: &mut GLContext, vs: *const u8, count: usize) -> bool {
    if self.length + count > self.capacity {
      // This would overflow the buffer.
      return false;
    }

    self.update_inner(gl, self.length, vs, count);
    self.length += count;

    true
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn swap_remove(&mut self, _gl: &mut GLContext, i: usize, count: usize) {
    assert!(count <= self.length);
    self.length -= count;
    assert!(i <= self.length);

    // In the `i == self.length` case, we don't bother with the swap;
    // decreasing `self.length` is enough.

    if i < self.length {
      assert!(
        i <= self.length - count,
        "GLByteBuffer::swap_remove would cause copy in overlapping regions"
      );

      unsafe {
        gl::CopyBufferSubData(
          gl::ARRAY_BUFFER,
          gl::ARRAY_BUFFER,
          self.length as GLintptr,
          i as GLintptr,
          count as GLsizeiptr,
        );
      }
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub unsafe fn update(&self, gl: &mut GLContext, idx: usize, vs: *const u8, count: usize) {
    assert!(idx + count <= self.length);
    self.update_inner(gl, idx, vs, count);
  }

  unsafe fn update_inner(
    &self,
    _gl: &mut GLContext,
    idx: usize,
    vs: *const u8,
    count: usize,
  ) {
    assert!(idx + count <= self.capacity);

    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      idx as GLintptr,
      count as GLsizeiptr,
      mem::transmute(vs),
    );
  }
}

/// Fixed-size typed VRAM buffer, optimized for bulk inserts.
pub struct GLBuffer<'a, T> {
  pub byte_buffer: GLByteBuffer<'a>,
  phantom: PhantomData<Vec<T>>,
}

impl<'a, T> GLBuffer<'a, T> {
  pub fn new<'b:'a>(
    gl: &'a mut GLContext,
    capacity: usize,
  ) -> GLBuffer<'b, T> {
    GLBuffer {
      byte_buffer: GLByteBuffer::new(gl, capacity * mem::size_of::<T>()),
      phantom: PhantomData,
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn push(&mut self, gl: &mut GLContext, vs: &[T]) -> bool {
    unsafe {
      self.byte_buffer.push(
        gl,
        mem::transmute(vs.as_ptr()),
        mem::size_of::<T>() * vs.len()
      )
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn update(&mut self, gl: &mut GLContext, idx: usize, vs: &[T]) {
    unsafe {
      self.byte_buffer.update(
        gl,
        mem::size_of::<T>() * idx,
        mem::transmute(vs.as_ptr()),
        mem::size_of::<T>() * vs.len(),
      );
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn swap_remove(&mut self, gl: &mut GLContext, idx: usize, count: usize) {
    self.byte_buffer.swap_remove(
      gl,
      mem::size_of::<T>() * idx,
      mem::size_of::<T>() * count,
    );
  }
}

#[derive(Debug)]
#[derive(Copy, Clone)]
pub enum DrawMode {
  Lines,
  Triangles,
  Points,
}

impl DrawMode {
  fn to_enum(&self) -> GLenum {
    match *self {
      DrawMode::Lines     => gl::LINES,
      DrawMode::Triangles => gl::TRIANGLES,
      DrawMode::Points    => gl::POINTS,
    }
  }
}

#[derive(Debug)]
#[derive(Copy, Clone)]
pub enum GLType {
  Float,
  UInt,
  Int,
}

impl GLType {
  pub fn size(&self) -> u32 {
    num::NumCast::from(
      match *self {
        GLType::Float => mem::size_of::<GLfloat>(),
        GLType::UInt  => mem::size_of::<GLuint>(),
        GLType::Int   => mem::size_of::<GLint>(),
      })
    .unwrap()
  }

  pub fn gl_enum(&self) -> GLenum {
    match *self {
      GLType::Float => gl::FLOAT,
      GLType::UInt  => gl::UNSIGNED_INT,
      GLType::Int   => gl::INT,
    }
  }

  pub fn is_integral(&self) -> bool {
    match *self {
      GLType::Float => false,
      GLType::UInt  => true,
      GLType::Int   => true,
    }
  }
}

#[derive(Debug)]
/// Specifies how to pass data from OpenGL to the vertex shaders.
pub struct VertexAttribData<'a> {
  /// Cooresponds to the shader's `input variable`.
  pub name: &'a str,
  /// The size of this attribute, in the provided units.
  pub size: u32,
  pub unit: GLType,
}

pub struct ArrayHandle<'a> {
  pub gl_id: GLuint,
  phantom: PhantomData<&'a ()>,
}

impl<'a> ArrayHandle<'a> {
  pub fn new<'b:'a>(_gl: &'a GLContext) -> ArrayHandle<'b> {
    let mut gl_id = 0;
    unsafe {
      gl::GenVertexArrays(1, &mut gl_id);
    }

    ArrayHandle {
      gl_id: gl_id,
      phantom: PhantomData,
    }
  }
}

impl<'a> Drop for ArrayHandle<'a> {
  fn drop(&mut self) {
    unsafe {
      gl::DeleteVertexArrays(1, &self.gl_id);
    }
  }
}

/// A fixed-capacity array of bytes passed to OpenGL.
pub struct GLArray<'a, T> {
  pub buffer: GLBuffer<'a, T>,
  pub handle: ArrayHandle<'a>,
  /// How to draw this buffer. Ex: gl::LINES, gl::TRIANGLES, etc.
  pub mode: GLenum,
  /// length in `T`s.
  pub length: usize,
}

impl<'a, T> GLArray<'a, T> {
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new<'b:'a>(
    gl: &'a mut GLContext,
    shader_program: &Shader<'b>,
    attribs: &[VertexAttribData],
    mode: DrawMode,
    buffer: GLBuffer<'b, T>,
  ) -> GLArray<'b, T> {
    let handle = ArrayHandle::new(gl);

    unsafe {
      gl::BindVertexArray(handle.gl_id);
    }

    let mut offset = 0;
    let attrib_span = {
      let mut attrib_span = 0;
      for attrib in attribs.iter() {
        attrib_span += attrib.size * attrib.unit.size();
      }
      attrib_span
    };
    for attrib in attribs.iter() {
      let shader_attrib =
        glGetAttribLocation(
          shader_program.handle.gl_id,
          attrib.name
        );
      assert!(shader_attrib != -1, "shader attribute \"{}\" not found", attrib.name);
      let shader_attrib = shader_attrib as GLuint;

      unsafe {
        gl::EnableVertexAttribArray(shader_attrib);

        if attrib.unit.is_integral() {
          gl::VertexAttribIPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        } else {
          gl::VertexAttribPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            gl::FALSE as GLboolean,
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        }
      }
      offset += (attrib.size * attrib.unit.size()) as isize;
    }

    match unsafe { gl::GetError() } {
      gl::NO_ERROR => {},
      err => warn!("OpenGL error 0x{:x}", err),
    }

    if attrib_span as usize != mem::size_of::<T>() {
      panic!("GLArray attribs don't describe the right number of bytes");
    }

    let length = buffer.byte_buffer.length / mem::size_of::<T>();

    GLArray {
      buffer: buffer,
      handle: handle,
      mode: mode.to_enum(),
      length: length,
    }
  }

  pub fn bind(&self, _: &mut GLContext) {
    unsafe {
      gl::BindVertexArray(self.handle.gl_id);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.buffer.byte_buffer.handle.gl_id);
    }
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  /// The array doesn't need to be bound.
  pub fn push(&mut self, gl: &mut GLContext, vs: &[T]) -> bool {
    let r = self.buffer.push(gl, vs);
    if r {
      self.length += vs.len();
    }
    r
  }

  /// N.B. For performance reasons, this does NOT bind the buffer.
  /// It will do the wrong thing if `bind` has not been correctly called.
  /// The array doesn't need to be bound.
  pub fn swap_remove(&mut self, gl: &mut GLContext, idx: usize, count: usize) {
    self.buffer.swap_remove(gl, idx, count);
    self.length -= count;
  }

  /// Draws all the queued triangles to the screen.
  /// N.B. For performance reasons, this does NOT bind the array.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn draw(&self, gl: &mut GLContext) {
    self.draw_slice(gl, 0, self.length);
  }

  /// Draw some subset of the triangle array.
  /// N.B. For performance reasons, this does NOT bind the array.
  /// It will do the wrong thing if `bind` has not been correctly called.
  pub fn draw_slice(&self, _gl: &mut GLContext, start: usize, len: usize) {
    assert!(start + len <= self.length);

    unsafe {
      gl::DrawArrays(self.mode, start as i32, len as i32);
    }
  }
}
<|endoftext|>


<commit_before>
use dimensions::Dimensions;
use point::Point;

/// Callback params.
pub type WidgetNum = usize;
pub type ColNum = usize;
pub type RowNum = usize;
pub type Width = f64;
pub type Height = f64;
pub type PosX = f64;
pub type PosY = f64;

/// Draw a matrix of any rectangular widget type, where the
/// matrix will provide a callback with the widget number,
/// it's `rows` and `cols` position, the width and height
/// for the widget and the location at which the widget
/// should be drawn.
#[derive(Copy, Clone)]
pub struct WidgetMatrix {
    cols: usize,
    rows: usize,
    pos: Point,
    dim: Dimensions,
    cell_pad_w: f64,
    cell_pad_h: f64,
}

/*
/// A cell to be returned via the cell callback.
pub struct MatrixCell<'a>(&'a mut UiContext, WidgetNum, ColNum, RowNum, PosX, PosY, Width, Height);
*/

impl WidgetMatrix {

    /// The callback called for each widget in the matrix.
    /// This should be called following all builder methods.
    pub fn each_widget<F>(&mut self, mut callback: F)
        where
            F: FnMut(WidgetNum, ColNum, RowNum, Point, Dimensions)
    {
        let widget_w = self.dim[0] / self.cols as f64;
        let widget_h = self.dim[1] / self.rows as f64;
        let mut widget_num = 0;
        for col in 0..self.cols {
            for row in 0..self.rows {
                callback(
                    widget_num,
                    col,
                    row,
                    [self.pos[0] + (widget_w * col as f64) + self.cell_pad_w,
                     self.pos[1] + (widget_h * row as f64) + self.cell_pad_h],
                    [widget_w - self.cell_pad_w * 2.0,
                     widget_h - self.cell_pad_h * 2.0],
                );
                widget_num += 1;
            }
        }
    }

    /// A builder method for adding padding to the cell.
    pub fn cell_padding(self, w: f64, h: f64) -> WidgetMatrix {
        WidgetMatrix { cell_pad_w: w, cell_pad_h: h, ..self }
    }

    /*
    /// Create an iterator over the matrix cells.
    fn iter_cells(&mut self) -> CellIterator {
    }
    */

}

/*
/// A struct used for iterating over the cells of a WidgetMatrix.
pub struct CellIterator {
    row: usize,
    col: usize,
    rows: usize,
    cols: usize,
}

impl Iterator for CellIterator {
    fn next
    */

impl WidgetMatrix {

    /// Create a widget matrix context.
    pub fn new(cols: usize, rows: usize) -> WidgetMatrix {
        WidgetMatrix {
            cols: cols,
            rows: rows,
            pos: [0.0, 0.0],
            dim: [256.0, 256.0],
            cell_pad_w: 0.0,
            cell_pad_h: 0.0,
        }
    }
}

/*
quack! {
    wm: WidgetMatrix[]
    get:
        fn () -> Size [] { Size(wm.pos) }
    set:
        fn (val: Position) [] { wm.pos = val.0 }
        fn (val: Size) [] { wm.dim = val.0 }
    action:
}
*/
<commit_msg>Implemented `Positionable` for `WidgetMatrix`<commit_after>
use dimensions::Dimensions;
use point::Point;
use position::Positionable;

/// Callback params.
pub type WidgetNum = usize;
pub type ColNum = usize;
pub type RowNum = usize;
pub type Width = f64;
pub type Height = f64;
pub type PosX = f64;
pub type PosY = f64;

/// Draw a matrix of any rectangular widget type, where the
/// matrix will provide a callback with the widget number,
/// it's `rows` and `cols` position, the width and height
/// for the widget and the location at which the widget
/// should be drawn.
#[derive(Copy, Clone)]
pub struct WidgetMatrix {
    cols: usize,
    rows: usize,
    pos: Point,
    dim: Dimensions,
    cell_pad_w: f64,
    cell_pad_h: f64,
}

/*
/// A cell to be returned via the cell callback.
pub struct MatrixCell<'a>(&'a mut UiContext, WidgetNum, ColNum, RowNum, PosX, PosY, Width, Height);
*/

impl WidgetMatrix {

    /// The callback called for each widget in the matrix.
    /// This should be called following all builder methods.
    pub fn each_widget<F>(&mut self, mut callback: F)
        where
            F: FnMut(WidgetNum, ColNum, RowNum, Point, Dimensions)
    {
        let widget_w = self.dim[0] / self.cols as f64;
        let widget_h = self.dim[1] / self.rows as f64;
        let mut widget_num = 0;
        for col in 0..self.cols {
            for row in 0..self.rows {
                callback(
                    widget_num,
                    col,
                    row,
                    [self.pos[0] + (widget_w * col as f64) + self.cell_pad_w,
                     self.pos[1] + (widget_h * row as f64) + self.cell_pad_h],
                    [widget_w - self.cell_pad_w * 2.0,
                     widget_h - self.cell_pad_h * 2.0],
                );
                widget_num += 1;
            }
        }
    }

    /// A builder method for adding padding to the cell.
    pub fn cell_padding(self, w: f64, h: f64) -> WidgetMatrix {
        WidgetMatrix { cell_pad_w: w, cell_pad_h: h, ..self }
    }

    /*
    /// Create an iterator over the matrix cells.
    fn iter_cells(&mut self) -> CellIterator {
    }
    */

}

/*
/// A struct used for iterating over the cells of a WidgetMatrix.
pub struct CellIterator {
    row: usize,
    col: usize,
    rows: usize,
    cols: usize,
}

impl Iterator for CellIterator {
    fn next
    */

impl WidgetMatrix {

    /// Create a widget matrix context.
    pub fn new(cols: usize, rows: usize) -> WidgetMatrix {
        WidgetMatrix {
            cols: cols,
            rows: rows,
            pos: [0.0, 0.0],
            dim: [256.0, 256.0],
            cell_pad_w: 0.0,
            cell_pad_h: 0.0,
        }
    }
}

impl Positionable for WidgetMatrix {
    fn point(mut self, pos: Point) -> Self {
        self.pos = pos;
        self
    }
}

/*
quack! {
    wm: WidgetMatrix[]
    get:
        fn () -> Size [] { Size(wm.pos) }
    set:
        fn (val: Size) [] { wm.dim = val.0 }
    action:
}
*/
<|endoftext|>


<commit_before>use regex::Regex;

const INPUT: &'static str = include_str!("data/day16.txt");

pub fn part1() -> u16 {
    calculate_part1(INPUT)
}

pub fn part2() -> u32 {
    calculate_part2(INPUT)
}

fn calculate_part1(input: &str) -> u16 {
    // let re = Regex::new("Sue (?P<sue>\\d+?):\
    //                     (,? children: (?P<children>\\d))\
    //                     |(,? cats: (?P<cats>\\d))\
    //                     |(,? samoyeds: (?P<samoyeds>\\d))\
    //                     |(,? pomeranians: (?P<pomeranians>\\d))\
    //                     |(,? akitas: (?P<akitas>\\d))\
    //                     |(,? vizslas: (?P<vizslas>\\d))\
    //                     |(,? goldfish: (?P<goldfish>\\d))\
    //                     |(,? trees: (?P<trees>\\d))\
    //                     |(,? cars: (?P<cars>\\d))\
    //                     |(,? perfumes: (?P<perfumes>\\d))"
    // ).expect("Invalid regex");

    let re_sue = Regex::new("Sue (\\d+):").expect("Invalid regex (Sue)");

    let tests = [
        Regex::new("children: 3").expect("Invalid regex(children)"),
        Regex::new("cats: 7").expect("Invalid regex(cats)"),
        Regex::new("samoyeds: 2").expect("Invalid regex(samoyeds)"),
        Regex::new("pomeranians: 3").expect("Invalid regex(pomeranians)"),
        Regex::new("akitas: 0").expect("Invalid regex(akitas)"),
        Regex::new("vizslas: 0").expect("Invalid regex(vizslas)"),
        Regex::new("goldfish: 5").expect("Invalid regex(goldfish)"),
        Regex::new("trees: 3").expect("Invalid regex(trees)"),
        Regex::new("cars: 2").expect("Invalid regex(cars)"),
        Regex::new("perfumes: 1").expect("Invalid regex(perfumes)"),
    ];

    let mut max_points = 0;
    let mut sue: u16 = 0;

    for line in input.lines() {
        let mut current_points = 0;
        let cap = re_sue.captures(line).expect(&format!("Invalid input: {}", line));
        let current_sue = cap.at(1).expect("Invalid input 2");

        for test in tests.iter() {
            if test.is_match(line) {
                current_points += 1;
            }
        }

        if current_points > max_points {
            max_points = current_points;
            sue = current_sue.parse().expect(&format!("Could not parse aunt Sue number: {}", current_sue));
        }
    }

    sue
}

fn calculate_part2(_: &str) -> u32 {
    0
}
<commit_msg>day 16 part 2<commit_after>use regex::Regex;

const INPUT: &'static str = include_str!("data/day16.txt");

pub fn part1() -> u16 {
    calculate_part1(INPUT)
}

pub fn part2() -> u16 {
    calculate_part2(INPUT)
}

fn calculate_part1(input: &str) -> u16 {
    let re_sue = Regex::new("Sue (\\d+):").expect("Invalid regex (Sue)");

    let tests = [
        Regex::new("children: 3").expect("Invalid regex(children)"),
        Regex::new("cats: 7").expect("Invalid regex(cats)"),
        Regex::new("samoyeds: 2").expect("Invalid regex(samoyeds)"),
        Regex::new("pomeranians: 3").expect("Invalid regex(pomeranians)"),
        Regex::new("akitas: 0").expect("Invalid regex(akitas)"),
        Regex::new("vizslas: 0").expect("Invalid regex(vizslas)"),
        Regex::new("goldfish: 5").expect("Invalid regex(goldfish)"),
        Regex::new("trees: 3").expect("Invalid regex(trees)"),
        Regex::new("cars: 2").expect("Invalid regex(cars)"),
        Regex::new("perfumes: 1").expect("Invalid regex(perfumes)"),
    ];

    let mut max_points = 0;
    let mut sue: u16 = 0;

    for line in input.lines() {
        let mut current_points = 0;
        let cap = re_sue.captures(line).expect(&format!("Invalid input: {}", line));
        let current_sue = cap.at(1).unwrap();

        for test in tests.iter() {
            if test.is_match(line) {
                current_points += 1;
            }
        }

        if current_points > max_points {
            max_points = current_points;
            sue = current_sue.parse().expect(&format!("Could not parse aunt Sue number: {}", current_sue));
        }
    }

    sue
}

fn calculate_part2(input: &str) -> u16 {
    let re_sue = Regex::new("Sue (\\d+):").expect("Invalid regex (Sue)");

    let tests = [
        Regex::new("children: 3").expect("Invalid regex(children)"),
        Regex::new("samoyeds: 2").expect("Invalid regex(samoyeds)"),
        Regex::new("akitas: 0").expect("Invalid regex(akitas)"),
        Regex::new("vizslas: 0").expect("Invalid regex(vizslas)"),
        Regex::new("cars: 2").expect("Invalid regex(cars)"),
        Regex::new("perfumes: 1").expect("Invalid regex(perfumes)"),
    ];

    let re_cats = Regex::new("cats: (\\d+?)").expect("Invalid regex(cats)");
    let re_trees = Regex::new("trees: (\\d+?)").expect("Invalid regex(trees)");
    let re_pomeranians = Regex::new("pomeranians: (\\d+?)").expect("Invalid regex(pomeranians)");
    let re_goldfish = Regex::new("goldfish: (\\d+?)").expect("Invalid regex(goldfish)");

    let mut max_points = 0;
    let mut sue: u16 = 0;

    for line in input.lines() {
        let mut current_points = 0;
        let cap = re_sue.captures(line).expect(&format!("Invalid input: {}", line));
        let current_sue = cap.at(1).unwrap();

        for test in tests.iter() {
            if test.is_match(line) {
                current_points += 1;
            }
        }

        if let Some(cap) = re_cats.captures(line) {
            let val: u16 = cap.at(1).unwrap().parse().expect("Could not parse value");
            if val > 7 {
                current_points += 1;
            }
        }

        if let Some(cap) = re_trees.captures(line) {
            let val: u16 = cap.at(1).unwrap().parse().expect("Could not parse value");
            if val > 3 {
                current_points += 1;
            }
        }

        if let Some(cap) = re_pomeranians.captures(line) {
            let val: u16 = cap.at(1).unwrap().parse().expect("Could not parse value");
            if val < 3 {
                current_points += 1;
            }
        }

        if let Some(cap) = re_goldfish.captures(line) {
            let val: u16 = cap.at(1).unwrap().parse().expect("Could not parse value");
            if val < 5 {
                current_points += 1;
            }
        }

        if current_points > max_points {
            max_points = current_points;
            sue = current_sue.parse().expect(&format!("Could not parse aunt Sue number: {}", current_sue));
        }
    }

    sue
}
<|endoftext|>


<commit_before><commit_msg>Remove unused import of std::ascii::AsciiExt (#142)<commit_after><|endoftext|>


<commit_before><commit_msg>Hack: Prevent ICE<commit_after><|endoftext|>


<commit_before>use libc::{c_int, c_void};

use super::*;

//int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
extern "C" fn put_glyph(info: *mut ffi::VTermGlyphInfo, pos: ffi::VTermPos, vterm: *mut c_void) -> c_int {
    let vterm: &mut VTerm = unsafe { &mut *(vterm as *mut VTerm) };
    match vterm.state_event_tx.as_ref() {
        Some(tx) => {
            let event = StateEvent::PutGlyph {
                glyph_info: ::GlyphInfo::from_ptr(info),
                pos: pos.as_pos(),
            };

            match tx.send(event) {
                Ok(_) => 1,
                Err(_) => 0,
            }
        }
        None => 0,
    }
}
//int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
extern "C" fn move_cursor(new: ffi::VTermPos, old: ffi::VTermPos, visible: c_int, vterm: *mut c_void) -> c_int {
    let vterm: &mut VTerm = unsafe { &mut *(vterm as *mut VTerm) };
    match vterm.state_event_tx.as_ref() {
        Some(tx) => {
            let event = StateEvent::MoveCursor {
                new: new.as_pos(),
                old: old.as_pos(),
                is_visible: int_to_bool(visible),
            };

            match tx.send(event) {
                Ok(_) => 1,
                Err(_) => 0,
            }
        }
        None => 0,
    }
}

//int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
extern "C" fn scroll_rect(rect: ffi::VTermRect, downward: c_int, rightward: c_int, vterm: *mut c_void) -> c_int {
    0
}
//int (*moverect)(VTermRect dest, VTermRect src, void *user);
extern "C" fn move_rect(dest: ffi::VTermRect, srd: ffi::VTermRect, vterm: *mut c_void) -> c_int {
    0
}
//int (*erase)(VTermRect rect, int selective, void *user);
extern "C" fn erase(rect: ffi::VTermRect, selective: c_int, vterm: *mut c_void) -> c_int {
    0
}
//int (*initpen)(void *user);
extern "C" fn init_pen(vterm: *mut c_void) -> c_int {
    0
}
//int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
extern "C" fn set_pen_attr(attr: ffi::VTermAttr, val: *mut ffi::VTermValue, vterm: *mut c_void) -> c_int {
    0
}
//int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
extern "C" fn set_term_prop(prop: ffi::VTermProp, val: *mut ffi::VTermValue, vterm: *mut c_void) -> c_int {
    0
}
//int (*bell)(void *user);
extern "C" fn bell(vterm: *mut c_void) -> c_int {
    0
}
//int (*resize)(int rows, int cols, VTermPos *delta, void *user);
extern "C" fn resize(rows: c_int, cols: c_int, delta: *mut ffi::VTermPos, vterm: *mut c_void) -> c_int {
    0
}
//int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
extern "C" fn set_line_info(row: c_int, new: *const ffi::VTermLineInfo, old: *const ffi::VTermLineInfo, vterm: *mut c_void) -> c_int {
    0
}

pub static STATE_CALLBACKS: ffi::VTermStateCallbacks = ffi::VTermStateCallbacks {
      put_glyph: put_glyph,
      move_cursor: move_cursor,
      scroll_rect: scroll_rect,
      move_rect: move_rect,
      erase: erase,
      init_pen: init_pen,
      set_pen_attr: set_pen_attr,
      set_term_prop: set_term_prop,
      bell: bell,
      resize: resize,
      set_line_info: set_line_info,
};

mod tests {
    #![allow(unused_imports)]
    use super::super::*;
    use std::io::prelude::*;

    #[test]
    fn state_can_generate_put_glyph_events() {
        let mut vterm: VTerm = VTerm::new(&Size {
            height: 2,
            width: 2,
        });
        vterm.generate_state_events().unwrap();
        vterm.write(b"a");

        let rx = vterm.state_event_rx.take().unwrap();

        let mut found_it = false;
        while let Ok(e) = rx.try_recv() {
            match e {
                StateEvent::PutGlyph{glyph_info, pos} => {
                    found_it = true;

                    assert_eq!(glyph_info.chars[0], b'a');
                    assert_eq!(pos.x, 0);
                    assert_eq!(pos.y, 0);

                    break
                },
                _ => {}
            }
        }

        assert!(found_it);
    }

    #[test]
    fn state_can_generate_move_cursor_events() {
        let mut vterm: VTerm = VTerm::new(&Size {
            height: 2,
            width: 2,
        });
        vterm.generate_state_events().unwrap();
        vterm.write(b"\x1b[1;2H");

        let rx = vterm.state_event_rx.take().unwrap();

        let mut found_it = false;
        while let Ok(e) = rx.try_recv() {
            match e {
                StateEvent::MoveCursor{new, old, is_visible} => {
                    found_it = true;

                    assert_eq!(new, Pos { x: 1, y: 0 });
                    assert_eq!(old, Pos { x: 0, y: 0 });
                    assert_eq!(is_visible, true);

                    break
                },
                _ => {}
            }
        }

        assert!(found_it);
    }
}
<commit_msg>Refactor<commit_after>use libc::{c_int, c_void};
use std::sync::mpsc::Sender;

use super::*;

/// Call the given closure with the vterms sender, if it exists.
fn with_sender<F>(vterm: *mut c_void, closure: F) -> c_int
    where F: Fn(&Sender<StateEvent>) -> c_int
{
    let vterm: &mut VTerm = unsafe { &mut *(vterm as *mut VTerm) };
    match vterm.state_event_tx.as_ref() {
        Some(tx) => closure(tx),
        None => 0,
    }
}

// int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
extern "C" fn put_glyph(info: *mut ffi::VTermGlyphInfo,
                        pos: ffi::VTermPos,
                        vterm: *mut c_void)
                        -> c_int {
    with_sender(vterm, |tx| {
        let event = StateEvent::PutGlyph {
            glyph_info: ::GlyphInfo::from_ptr(info),
            pos: pos.as_pos(),
        };

        match tx.send(event) {
            Ok(_) => 1,
            Err(_) => 0,
        }
    })
}

// int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
extern "C" fn move_cursor(new: ffi::VTermPos,
                          old: ffi::VTermPos,
                          visible: c_int,
                          vterm: *mut c_void)
                          -> c_int {
      with_sender(vterm, |tx| {
          let event = StateEvent::MoveCursor {
              new: new.as_pos(),
              old: old.as_pos(),
              is_visible: int_to_bool(visible),
          };

          match tx.send(event) {
              Ok(_) => 1,
              Err(_) => 0,
          }
      })
}

// int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
extern "C" fn scroll_rect(rect: ffi::VTermRect,
                          downward: c_int,
                          rightward: c_int,
                          vterm: *mut c_void)
                          -> c_int {
    0
}
// int (*moverect)(VTermRect dest, VTermRect src, void *user);
extern "C" fn move_rect(dest: ffi::VTermRect, src: ffi::VTermRect, vterm: *mut c_void) -> c_int {
    0
}
// int (*erase)(VTermRect rect, int selective, void *user);
extern "C" fn erase(rect: ffi::VTermRect, selective: c_int, vterm: *mut c_void) -> c_int {
    0
}
// int (*initpen)(void *user);
extern "C" fn init_pen(vterm: *mut c_void) -> c_int {
    0
}
// int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
extern "C" fn set_pen_attr(attr: ffi::VTermAttr,
                           val: *mut ffi::VTermValue,
                           vterm: *mut c_void)
                           -> c_int {
    0
}
// int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
extern "C" fn set_term_prop(prop: ffi::VTermProp,
                            val: *mut ffi::VTermValue,
                            vterm: *mut c_void)
                            -> c_int {
    0
}
// int (*bell)(void *user);
extern "C" fn bell(vterm: *mut c_void) -> c_int {
    0
}
// int (*resize)(int rows, int cols, VTermPos *delta, void *user);
extern "C" fn resize(rows: c_int,
                     cols: c_int,
                     delta: *mut ffi::VTermPos,
                     vterm: *mut c_void)
                     -> c_int {
    0
}
// int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
extern "C" fn set_line_info(row: c_int,
                            new: *const ffi::VTermLineInfo,
                            old: *const ffi::VTermLineInfo,
                            vterm: *mut c_void)
                            -> c_int {
    0
}

pub static STATE_CALLBACKS: ffi::VTermStateCallbacks = ffi::VTermStateCallbacks {
    put_glyph: put_glyph,
    move_cursor: move_cursor,
    scroll_rect: scroll_rect,
    move_rect: move_rect,
    erase: erase,
    init_pen: init_pen,
    set_pen_attr: set_pen_attr,
    set_term_prop: set_term_prop,
    bell: bell,
    resize: resize,
    set_line_info: set_line_info,
};

mod tests {
    #![allow(unused_imports)]
    use super::super::*;
    use std::io::prelude::*;

    #[test]
    fn state_can_generate_put_glyph_events() {
        let mut vterm: VTerm = VTerm::new(&Size {
            height: 2,
            width: 2,
        });
        vterm.generate_state_events().unwrap();
        vterm.write(b"a");

        let rx = vterm.state_event_rx.take().unwrap();

        let mut found_it = false;
        while let Ok(e) = rx.try_recv() {
            match e {
                StateEvent::PutGlyph{glyph_info, pos} => {
                    found_it = true;

                    assert_eq!(glyph_info.chars[0], b'a');
                    assert_eq!(pos.x, 0);
                    assert_eq!(pos.y, 0);

                    break;
                }
                _ => {}
            }
        }

        assert!(found_it);
    }

    #[test]
    fn state_can_generate_move_cursor_events() {
        let mut vterm: VTerm = VTerm::new(&Size {
            height: 2,
            width: 2,
        });
        vterm.generate_state_events().unwrap();
        vterm.write(b"\x1b[1;2H");

        let rx = vterm.state_event_rx.take().unwrap();

        let mut found_it = false;
        while let Ok(e) = rx.try_recv() {
            match e {
                StateEvent::MoveCursor{new, old, is_visible} => {
                    found_it = true;

                    assert_eq!(new, Pos { x: 1, y: 0 });
                    assert_eq!(old, Pos { x: 0, y: 0 });
                    assert_eq!(is_visible, true);

                    break;
                }
                _ => {}
            }
        }

        assert!(found_it);
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use prometheus::*;

lazy_static! {
    pub static ref KV_COMMAND_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_storage_command_total",
            "Total number of commands received.",
            &["type"]
        ).unwrap();

    pub static ref SCHED_STAGE_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_stage_total",
            "Total number of commands on each stage.",
            &["type", "stage"]
        ).unwrap();

    pub static ref SCHED_CONTEX_GAUGE: Gauge =
        register_gauge!(
            "tikv_scheduler_contex_total",
            "Total number of pending commands."
        ).unwrap();

    pub static ref SCHED_WORKER_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_worker_command_total",
            "Total number of commands executed by worker pool.",
            &["type", "rw_type"]
        ).unwrap();

    pub static ref SCHED_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_command_duration_seconds",
            "Bucketed histogram of command execution",
            &["type"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref SCHED_LATCH_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_latch_wait_duration_seconds",
            "Bucketed histogram of latch wait",
            &["type"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref SCHED_TOO_BUSY_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_too_busy_total",
            "Total count of scheduler too busy",
            &["type"]
        ).unwrap();

    pub static ref SCHED_COMMANDS_PRI_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_commands_pri_total",
            "Total count of different priority commands",
            &["priority"]
        ).unwrap();

    pub static ref KV_COMMAND_KEYREAD_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_kv_command_key_read",
            "Bucketed histogram of keys read of a kv command",
            &["type"],
            exponential_buckets(1.0, 2.0, 21).unwrap()
        ).unwrap();

    pub static ref KV_COMMAND_SCAN_DETAILS: CounterVec =
        register_counter_vec!(
            "tikv_scheudler_kv_scan_details",
            "Bucketed counter of kv keys scan details for each cf",
            &["req","cf","tag"]
        ).unwrap();

    pub static ref RAWKV_COMMAND_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_storage_rawkv_command_total",
            "Total number of rawkv commands received.",
            &["type"]
        ).unwrap();

    pub static ref KV_COMMAND_GC_EMPTY_RANGE_COUNTER: Counter =
        register_counter!(
            "tikv_storage_gc_empty_range_total",
            "Total number of empty range found by gc"
        ).unwrap();

    pub static ref KV_COMMAND_GC_SKIPPED_COUNTER: Counter =
        register_counter!(
            "tikv_storage_gc_skipped_counter",
            "Total number of gc command skipped owing to optimization"
        ).unwrap();

    pub static ref BATCH_COMMANDS: HistogramVec =
        register_histogram_vec!(
            "tikv_storage_batch_commands_total",
            "Bucketed histogram of total number of a batch of commands",
            &["type"],
            vec![1.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0,
            20.0, 24.0, 28.0, 32.0, 48.0, 64.0, 96.0, 128.0, 192.0, 256.0]
        ).unwrap();

    pub static ref KV_COMMAND_KEYWRITE_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_kv_command_key_write",
            "Bucketed histogram of keys write of a kv command",
            &["type"],
            exponential_buckets(1.0, 2.0, 21).unwrap()
        ).unwrap();
}
<commit_msg>Fix some metrics (#2342)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use prometheus::*;

lazy_static! {
    pub static ref KV_COMMAND_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_storage_command_total",
            "Total number of commands received.",
            &["type"]
        ).unwrap();

    pub static ref SCHED_STAGE_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_stage_total",
            "Total number of commands on each stage.",
            &["type", "stage"]
        ).unwrap();

    pub static ref SCHED_CONTEX_GAUGE: Gauge =
        register_gauge!(
            "tikv_scheduler_contex_total",
            "Total number of pending commands."
        ).unwrap();

    pub static ref SCHED_WORKER_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_worker_command_total",
            "Total number of commands executed by worker pool.",
            &["type", "rw_type"]
        ).unwrap();

    pub static ref SCHED_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_command_duration_seconds",
            "Bucketed histogram of command execution",
            &["type"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref SCHED_LATCH_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_latch_wait_duration_seconds",
            "Bucketed histogram of latch wait",
            &["type"],
            exponential_buckets(0.0005, 2.0, 20).unwrap()
        ).unwrap();

    pub static ref SCHED_TOO_BUSY_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_too_busy_total",
            "Total count of scheduler too busy",
            &["type"]
        ).unwrap();

    pub static ref SCHED_COMMANDS_PRI_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_commands_pri_total",
            "Total count of different priority commands",
            &["priority"]
        ).unwrap();

    pub static ref KV_COMMAND_KEYREAD_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_kv_command_key_read",
            "Bucketed histogram of keys read of a kv command",
            &["type"],
            exponential_buckets(1.0, 2.0, 21).unwrap()
        ).unwrap();

    pub static ref KV_COMMAND_SCAN_DETAILS: CounterVec =
        register_counter_vec!(
            "tikv_scheduler_kv_scan_details",
            "Bucketed counter of kv keys scan details for each cf",
            &["req","cf","tag"]
        ).unwrap();

    pub static ref RAWKV_COMMAND_COUNTER_VEC: CounterVec =
        register_counter_vec!(
            "tikv_storage_rawkv_command_total",
            "Total number of rawkv commands received.",
            &["type"]
        ).unwrap();

    pub static ref KV_COMMAND_GC_EMPTY_RANGE_COUNTER: Counter =
        register_counter!(
            "tikv_storage_gc_empty_range_total",
            "Total number of empty range found by gc"
        ).unwrap();

    pub static ref KV_COMMAND_GC_SKIPPED_COUNTER: Counter =
        register_counter!(
            "tikv_storage_gc_skipped_counter",
            "Total number of gc command skipped owing to optimization"
        ).unwrap();

    pub static ref BATCH_COMMANDS: HistogramVec =
        register_histogram_vec!(
            "tikv_storage_batch_commands_total",
            "Bucketed histogram of total number of a batch of commands",
            &["type"],
            vec![1.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0,
            20.0, 24.0, 28.0, 32.0, 48.0, 64.0, 96.0, 128.0, 192.0, 256.0]
        ).unwrap();

    pub static ref KV_COMMAND_KEYWRITE_HISTOGRAM_VEC: HistogramVec =
        register_histogram_vec!(
            "tikv_scheduler_kv_command_key_write",
            "Bucketed histogram of keys write of a kv command",
            &["type"],
            exponential_buckets(1.0, 2.0, 21).unwrap()
        ).unwrap();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate exonum;
#[macro_use]
extern crate log;

use std::fmt;

use exonum::blockchain::{Service, Transaction, Schema, NodeState};
use exonum::crypto::{PublicKey, Hash};
use exonum::messages::{RawMessage, Message, FromRaw, RawTransaction, Error as MessageError};
use exonum::storage::{MerkleTable, MemoryDB, Map, List, View, MapTable, MerklePatriciaTable,
                      Result as StorageResult};

pub const CONFIG_SERVICE: u16 = 1;
pub const CONFIG_PROPOSE_MESSAGE_ID: u16 = 0;
pub const CONFIG_VOTE_MESSAGE_ID: u16 = 1;

message! {
    TxConfigPropose {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_PROPOSE_MESSAGE_ID;
        const SIZE = 56;

        from:           &PublicKey  [00 => 32]
        height:         u64         [32 => 40]
        config:        &[u8]        [40 => 48] // serialized config bytes
        actual_from_height: u64     [48 => 56] // с какой высоты становится актуальным
    }
}

message! {
    TxConfigVote {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_VOTE_MESSAGE_ID;
        const SIZE = 81;

        from:           &PublicKey  [00 => 32]
        height:         u64         [32 => 40]
        hash_propose:   &Hash       [40 => 72] // hash of transacion we're voting for
        seed:           u64         [72 => 80] // incremental (1, 2, 3, 4, 5, 6, 7) проверять +1
        revoke:         bool        [80 => 81] // голос_за=false / отозвать=true
    }
}

#[derive(Clone, PartialEq)]
pub enum ConfigTx {
    ConfigPropose(TxConfigPropose),
    ConfigVote(TxConfigVote),
}

#[derive(Default)]
pub struct ConfigurationService {}

pub struct ConfigurationSchema<'a> {
    view: &'a View,
}

impl ConfigTx {
    pub fn from(&self) -> &PublicKey {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.from(),
            ConfigTx::ConfigVote(ref msg) => msg.from(),
        }
    }

    pub fn height(&self) -> u64 {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.height(),
            ConfigTx::ConfigVote(ref msg) => msg.height(),
        }
    }
}

impl fmt::Debug for ConfigTx {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => write!(fmt, "{:?}", msg),
            ConfigTx::ConfigVote(ref msg) => write!(fmt, "{:?}", msg),
        }
    }
}

impl FromRaw for ConfigTx {
    fn from_raw(raw: RawMessage) -> Result<ConfigTx, MessageError> {
        match raw.message_type() {
            CONFIG_PROPOSE_MESSAGE_ID => {
                Ok(ConfigTx::ConfigPropose(TxConfigPropose::from_raw(raw)?))
            }
            CONFIG_VOTE_MESSAGE_ID => Ok(ConfigTx::ConfigVote(TxConfigVote::from_raw(raw)?)),
            _ => Err(MessageError::IncorrectMessageType { message_type: raw.message_type() }),
        }
    }
}

impl Message for ConfigTx {
    fn raw(&self) -> &RawMessage {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.raw(),
            ConfigTx::ConfigVote(ref msg) => msg.raw(),
        }
    }

    fn verify_signature(&self, public_key: &PublicKey) -> bool {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.verify_signature(public_key),
            ConfigTx::ConfigVote(ref msg) => msg.verify_signature(public_key),
        }
    }

    fn hash(&self) -> Hash {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.hash(),
            ConfigTx::ConfigVote(ref msg) => msg.hash(),
        }
    }
}

impl<'a> ConfigurationSchema<'a> {
    pub fn new(view: &'a View) -> ConfigurationSchema {
        ConfigurationSchema { view: view }
    }

    fn config_proposes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, Hash, TxConfigPropose> {
        // config_propose paricia merkletree <hash_tx> транзакция пропоз
        MerklePatriciaTable::new(MapTable::new(vec![04], self.view))
    }

    fn config_votes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, PublicKey, TxConfigVote> {
        // config_votes patricia merkletree <pub_key> последний голос
        MerklePatriciaTable::new(MapTable::new(vec![05], self.view))
    }

    pub fn state_hash(&self) -> StorageResult<Hash> {
        let db = MemoryDB::new();
        let hashes: MerkleTable<MemoryDB, u64, Hash> = MerkleTable::new(db);

        hashes.append(self.config_proposes().root_hash()?)?;
        hashes.append(self.config_votes().root_hash()?)?;
        hashes.root_hash()
    }
    pub fn get_config_propose(&self,
                              hash: &Hash)
                              -> Result<Option<TxConfigPropose>, ::storage::Error> {
        self.config_proposes().get(hash)
    }

    pub fn get_vote(&self, pub_key: &PublicKey) -> Result<Option<TxConfigVote>, ::storage::Error> {
        self.config_votes().get(pub_key)
    }
}

impl TxConfigPropose {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;
        if !config.validators.contains(self.from()) {
            error!("ConfigPropose from unknown validator: {:?}", self.from());
            return Ok(());
        }

        let hash = self.hash();
        if config_schema.config_proposes().get(&hash)?.is_some() {
            error!("Received config_propose has already been handled, msg={:?}",
                   self);
            return Ok(());
        }

        trace!("Handle ConfigPropose");
        config_schema.config_proposes().put(&hash, self.clone())?;
        Ok(())
    }
}

impl TxConfigVote {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;

        if !config.validators.contains(self.from()) {
            error!("ConfigVote from unknown validator: {:?}", self.from());
            return Ok(());
        }

        if config_schema.config_proposes().get(self.hash_propose())?.is_none() {
            error!("Received config_vote for unknown transaciton, msg={:?}",
                   self);
            return Ok(());
        }

        if let Some(vote) = config_schema.config_votes().get(self.from())? {
            if vote.seed() != self.seed() - 1 {
                error!("Received config_vote with wrong seed, msg={:?}", self);
                return Ok(());
            }
        }

        let msg = self.clone();
        config_schema.config_votes().put(msg.from(), self.clone())?;

        let mut votes_count = 0;
        for pub_key in config.validators.clone() {
            if let Some(vote) = config_schema.config_votes().get(&pub_key)? {
                if !vote.revoke() {
                    votes_count += 1;
                }
            }
        }

        if votes_count > 2 / 3 * config.validators.len() {
            if let Some(config_propose) =
                config_schema.config_proposes()
                    .get(self.hash_propose())? {
                blockchain_schema.commit_actual_configuration(config_propose.actual_from_height(),
                                                 config_propose.config())?;
            }
        }
        Ok(())
    }
}

impl Transaction for ConfigTx {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, view: &View) -> StorageResult<()> {
        match *self {
            ConfigTx::ConfigPropose(ref tx) => tx.execute(view),
            ConfigTx::ConfigVote(ref tx) => tx.execute(view),
        }
    }
}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_id(&self) -> u16 {
        CONFIG_SERVICE
    }

    fn state_hash(&self, view: &View) -> Option<StorageResult<Hash>> {
        let schema = ConfigurationSchema::new(view);
        Some(schema.state_hash())
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError> {
        ConfigTx::from_raw(raw).map(|tx| Box::new(tx) as Box<Transaction>)
    }

    fn handle_commit(&self, state: &mut NodeState) -> StorageResult<()> {
        let actual_config = {
            let schema = Schema::new(state.view());
            let height = state.height();
            schema.get_configuration_at_height(height)?
        };

        if let Some(config) = actual_config {
            info!("Updated node config={:#?}", config);
            state.update_config(config);
        }
        Ok(())
    }
}<commit_msg>After rebase fix #2<commit_after>#[macro_use]
extern crate exonum;
#[macro_use]
extern crate log;

use std::fmt;

use exonum::blockchain::{Service, Transaction, Schema, NodeState};
use exonum::crypto::{PublicKey, Hash};
use exonum::messages::{RawMessage, Message, FromRaw, RawTransaction, Error as MessageError};
use exonum::storage::{MerkleTable, MemoryDB, Map, List, View, MapTable, MerklePatriciaTable,
                      Result as StorageResult};

pub const CONFIG_SERVICE: u16 = 1;
pub const CONFIG_PROPOSE_MESSAGE_ID: u16 = 0;
pub const CONFIG_VOTE_MESSAGE_ID: u16 = 1;

message! {
    TxConfigPropose {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_PROPOSE_MESSAGE_ID;
        const SIZE = 56;

        from:           &PublicKey  [00 => 32]
        height:         u64         [32 => 40]
        config:        &[u8]        [40 => 48] // serialized config bytes
        actual_from_height: u64     [48 => 56] // с какой высоты становится актуальным
    }
}

message! {
    TxConfigVote {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_VOTE_MESSAGE_ID;
        const SIZE = 81;

        from:           &PublicKey  [00 => 32]
        height:         u64         [32 => 40]
        hash_propose:   &Hash       [40 => 72] // hash of transacion we're voting for
        seed:           u64         [72 => 80] // incremental (1, 2, 3, 4, 5, 6, 7) проверять +1
        revoke:         bool        [80 => 81] // голос_за=false / отозвать=true
    }
}

#[derive(Clone, PartialEq)]
pub enum ConfigTx {
    ConfigPropose(TxConfigPropose),
    ConfigVote(TxConfigVote),
}

#[derive(Default)]
pub struct ConfigurationService {}

pub struct ConfigurationSchema<'a> {
    view: &'a View,
}

impl ConfigTx {
    pub fn from(&self) -> &PublicKey {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.from(),
            ConfigTx::ConfigVote(ref msg) => msg.from(),
        }
    }

    pub fn height(&self) -> u64 {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.height(),
            ConfigTx::ConfigVote(ref msg) => msg.height(),
        }
    }
}

impl fmt::Debug for ConfigTx {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => write!(fmt, "{:?}", msg),
            ConfigTx::ConfigVote(ref msg) => write!(fmt, "{:?}", msg),
        }
    }
}

impl FromRaw for ConfigTx {
    fn from_raw(raw: RawMessage) -> Result<ConfigTx, MessageError> {
        match raw.message_type() {
            CONFIG_PROPOSE_MESSAGE_ID => {
                Ok(ConfigTx::ConfigPropose(TxConfigPropose::from_raw(raw)?))
            }
            CONFIG_VOTE_MESSAGE_ID => Ok(ConfigTx::ConfigVote(TxConfigVote::from_raw(raw)?)),
            _ => Err(MessageError::IncorrectMessageType { message_type: raw.message_type() }),
        }
    }
}

impl Message for ConfigTx {
    fn raw(&self) -> &RawMessage {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.raw(),
            ConfigTx::ConfigVote(ref msg) => msg.raw(),
        }
    }

    fn verify_signature(&self, public_key: &PublicKey) -> bool {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.verify_signature(public_key),
            ConfigTx::ConfigVote(ref msg) => msg.verify_signature(public_key),
        }
    }

    fn hash(&self) -> Hash {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.hash(),
            ConfigTx::ConfigVote(ref msg) => msg.hash(),
        }
    }
}

impl<'a> ConfigurationSchema<'a> {
    pub fn new(view: &'a View) -> ConfigurationSchema {
        ConfigurationSchema { view: view }
    }

    fn config_proposes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, Hash, TxConfigPropose> {
        // config_propose paricia merkletree <hash_tx> транзакция пропоз
        MerklePatriciaTable::new(MapTable::new(vec![04], self.view))
    }

    fn config_votes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, PublicKey, TxConfigVote> {
        // config_votes patricia merkletree <pub_key> последний голос
        MerklePatriciaTable::new(MapTable::new(vec![05], self.view))
    }

    pub fn state_hash(&self) -> StorageResult<Hash> {
        let db = MemoryDB::new();
        let hashes: MerkleTable<MemoryDB, u64, Hash> = MerkleTable::new(db);

        hashes.append(self.config_proposes().root_hash()?)?;
        hashes.append(self.config_votes().root_hash()?)?;
        hashes.root_hash()
    }
    pub fn get_config_propose(&self,
                              hash: &Hash)
                              -> Result<Option<TxConfigPropose>, exonum::storage::Error> {
        self.config_proposes().get(hash)
    }

    pub fn get_vote(&self,
                    pub_key: &PublicKey)
                    -> Result<Option<TxConfigVote>, exonum::storage::Error> {
        self.config_votes().get(pub_key)
    }
}

impl TxConfigPropose {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;
        if !config.validators.contains(self.from()) {
            error!("ConfigPropose from unknown validator: {:?}", self.from());
            return Ok(());
        }

        let hash = self.hash();
        if config_schema.config_proposes().get(&hash)?.is_some() {
            error!("Received config_propose has already been handled, msg={:?}",
                   self);
            return Ok(());
        }

        trace!("Handle ConfigPropose");
        config_schema.config_proposes().put(&hash, self.clone())?;
        Ok(())
    }
}

impl TxConfigVote {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;

        if !config.validators.contains(self.from()) {
            error!("ConfigVote from unknown validator: {:?}", self.from());
            return Ok(());
        }

        if config_schema.config_proposes().get(self.hash_propose())?.is_none() {
            error!("Received config_vote for unknown transaciton, msg={:?}",
                   self);
            return Ok(());
        }

        if let Some(vote) = config_schema.config_votes().get(self.from())? {
            if vote.seed() != self.seed() - 1 {
                error!("Received config_vote with wrong seed, msg={:?}", self);
                return Ok(());
            }
        }

        let msg = self.clone();
        config_schema.config_votes().put(msg.from(), self.clone())?;

        let mut votes_count = 0;
        for pub_key in config.validators.clone() {
            if let Some(vote) = config_schema.config_votes().get(&pub_key)? {
                if !vote.revoke() {
                    votes_count += 1;
                }
            }
        }

        if votes_count > 2 / 3 * config.validators.len() {
            if let Some(config_propose) =
                config_schema.config_proposes()
                    .get(self.hash_propose())? {
                blockchain_schema.commit_actual_configuration(config_propose.actual_from_height(),
                                                 config_propose.config())?;
            }
        }
        Ok(())
    }
}

impl Transaction for ConfigTx {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, view: &View) -> StorageResult<()> {
        match *self {
            ConfigTx::ConfigPropose(ref tx) => tx.execute(view),
            ConfigTx::ConfigVote(ref tx) => tx.execute(view),
        }
    }
}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_id(&self) -> u16 {
        CONFIG_SERVICE
    }

    fn state_hash(&self, view: &View) -> Option<StorageResult<Hash>> {
        let schema = ConfigurationSchema::new(view);
        Some(schema.state_hash())
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError> {
        ConfigTx::from_raw(raw).map(|tx| Box::new(tx) as Box<Transaction>)
    }

    fn handle_commit(&self, state: &mut NodeState) -> StorageResult<()> {
        let actual_config = {
            let schema = Schema::new(state.view());
            let height = state.height();
            schema.get_configuration_at_height(height)?
        };

        if let Some(config) = actual_config {
            info!("Updated node config={:#?}", config);
            state.update_config(config);
        }
        Ok(())
    }
}<|endoftext|>