<commit_before>use std::ops::{Add, AddAssign, Sub, Div, Mul, Index, IndexMut};
use std::fmt;

use na;
use num::{Zero, One};

use ::{lerp, find_interval};
use cie;

/// Represents a linear RGB spectrum.
/// TODO Rename this to `RGBSpectrum` and make `Spectrum` a type alias to this so we can also support
/// full spectral rendering.
#[derive(Debug,Copy,PartialEq,Clone,Default)]
pub struct Spectrum {
    pub r: f32,
    pub g: f32,
    pub b: f32,
}

impl Spectrum {
    /// Create an RGB spectrum from its components
    pub fn rgb(r: f32, g: f32, b: f32) -> Spectrum {
        Spectrum { r: r, g: g, b: b }
    }

    /// Create an RGB spectrum where all the components have the same value.
    pub fn grey(v: f32) -> Spectrum {
        Spectrum { r: v, g: v, b: v }
    }

    pub fn white() -> Spectrum {
        Spectrum::rgb(1.0, 1.0, 1.0)
    }

    pub fn black() -> Spectrum {
        Spectrum::rgb(0.0, 0.0, 0.0)
    }

    pub fn red() -> Spectrum {
        Spectrum::rgb(1.0, 0.0, 0.0)
    }

    pub fn green() -> Spectrum {
        Spectrum::rgb(0.0, 1.0, 0.0)
    }

    pub fn blue() -> Spectrum {
        Spectrum::rgb(0.0, 0.0, 1.0)
    }

    /// Convert this linear RGB spectrum to non-linear sRGB and return the result as an array of
    /// bytes.
    pub fn to_srgb(&self) -> [u8; 3] {
        let a = 0.055f32;
        let b = 1f32 / 2.4;
        let mut srgb = [0; 3];
        for i in 0..3 {
            let v = if self[i] <= 0.0031308 {
                12.92 * self[i]
            } else {
                (1.0 + a) * f32::powf(self[i], b) - a
            };
            srgb[i] = na::clamp(v * 255.0, 0.0, 255.0) as u8;
        }
        srgb
    }

    /// Convert a non-linear sRGB value to a linear RGB spectrum.
    pub fn from_srgb(rgb: &[u8; 3]) -> Spectrum {
        fn convert(v: u8) -> f32 {
            let value = v as f32 / 255.0;
            if value <= 0.04045 {
                value / 12.92
            } else {
                ((value + 0.055) * 1.0 / 1.055).powf(2.4)
            }
        }

        Spectrum::rgb(convert(rgb[0]), convert(rgb[1]), convert(rgb[2]))
    }

    /// Convert a linear spectrum in XYZ format to a linear RGB format.
    pub fn from_xyz(xyz: &[f32; 3]) -> Spectrum {
        let r = 3.240479 * xyz[0] - 1.537150 * xyz[1] - 0.498535 * xyz[2];
        let g = -0.969256 * xyz[0] + 1.875991 * xyz[1] + 0.041556 * xyz[2];
        let b = 0.055648 * xyz[0] - 0.204043 * xyz[1] + 1.057311 * xyz[2];
        Spectrum::rgb(r, g, b)
    }

    /// Create a spectrum from a series of (wavelength, value) samples from an SPD (Spectral Power
    /// Distribution).
    pub fn from_sampled(lambda: &[f32], v: &[f32], n: usize) -> Spectrum {
        // TODO sort by wavelength if needed
        let mut xyz = [0.0; 3];
        for i in 0..cie::N_CIE_SAMPLES {
            let val = interpolate_spectrum_samples(lambda, v, n, cie::CIE_LAMBDA[i]);
            xyz[0] += val * cie::CIE_X[i];
            xyz[1] += val * cie::CIE_Y[i];
            xyz[2] += val * cie::CIE_Z[i];
        }
        let scale = (cie::CIE_LAMBDA[cie::N_CIE_SAMPLES - 1] - cie::CIE_LAMBDA[0]) /
                    (cie::CIE_Y_INTEGRAL * cie::N_CIE_SAMPLES as f32);
        xyz[0] *= scale;
        xyz[1] *= scale;
        xyz[2] *= scale;

        Self::from_xyz(&xyz)
    }

    /// Return true if the colour is black i.e (0, 0 ,0).
    pub fn is_black(&self) -> bool {
        self.r == 0.0 && self.g == 0.0 && self.b == 0.0
    }

    /// Return true if any of the components is NaN. Useful for debugging.
    pub fn has_nan(&self) -> bool {
        self.r.is_nan() || self.g.is_nan() || self.b.is_nan()
    }

    /// Return true if any of the components is infinite. Useful for debugging.
    pub fn is_infinite(&self) -> bool {
        self.r.is_infinite() || self.g.is_infinite() || self.b.is_infinite()
    }

    /// Return a spectrum where each component is the square root of the original component.
    pub fn sqrt(&self) -> Spectrum {
        Spectrum::rgb(self.r.sqrt(), self.g.sqrt(), self.b.sqrt())
    }

    /// Return the luminance of the Spectrum
    pub fn y(&self) -> f32 {
        let y_height: [f32; 3] = [0.212671, 0.715160, 0.072169];
        return y_height[0] * self[0] + y_height[1] * self[1] + y_height[2] * self[2];

    }
}

fn interpolate_spectrum_samples(lambda: &[f32], vals: &[f32], n: usize, l: f32) -> f32 {
    for i in 0..n - 1 {
        assert!(lambda[i + 1] > lambda[i]);
    }
    if l <= lambda[0] {
        return vals[0];
    }
    if l >= lambda[n - 1] {
        return vals[n - 1];
    }
    let offset = find_interval(n, |index| lambda[index] <= l);
    assert!(l >= lambda[offset] && l <= lambda[offset + 1]);
    let t = (l - lambda[offset]) / (lambda[offset + 1] - lambda[offset]);
    lerp(t, vals[offset], vals[offset + 1])
}

// Operators

impl Add<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn add(self, rhs: Spectrum) -> Spectrum {
        Spectrum {
            r: self.r + rhs.r,
            g: self.g + rhs.g,
            b: self.b + rhs.b,
        }
    }
}

impl Sub<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn sub(self, rhs: Spectrum) -> Spectrum {
        Spectrum {
            r: self.r - rhs.r,
            g: self.g - rhs.g,
            b: self.b - rhs.b,
        }
    }
}

impl AddAssign<Spectrum> for Spectrum {
    fn add_assign(&mut self, rhs: Spectrum) {
        self.r += rhs.r;
        self.g += rhs.g;
        self.b += rhs.b;
    }
}

impl Mul<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn mul(self, rhs: Spectrum) -> Spectrum {
        Spectrum::rgb(self.r * rhs.r, self.g * rhs.g, self.b * rhs.b)
    }
}

impl Div<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn div(self, rhs: Spectrum) -> Spectrum {
        Spectrum::rgb(self.r / rhs.r, self.g / rhs.g, self.b / rhs.b)
    }
}

impl Add<f32> for Spectrum {
    type Output = Spectrum;

    fn add(self, rhs: f32) -> Spectrum {
        Spectrum {
            r: self.r + rhs,
            g: self.g + rhs,
            b: self.b + rhs,
        }
    }
}

impl Sub<f32> for Spectrum {
    type Output = Spectrum;

    fn sub(self, rhs: f32) -> Spectrum {
        Spectrum {
            r: self.r - rhs,
            g: self.g - rhs,
            b: self.b - rhs,
        }
    }
}

impl Mul<f32> for Spectrum {
    type Output = Spectrum;

    fn mul(self, rhs: f32) -> Spectrum {
        Spectrum::rgb(self.r * rhs, self.g * rhs, self.b * rhs)
    }
}

impl Mul<Spectrum> for f32 {
    type Output = Spectrum;

    fn mul(self, rhs: Spectrum) -> Spectrum {
        Spectrum::rgb(self * rhs.r, self * rhs.g, self * rhs.b)
    }
}

impl Div<f32> for Spectrum {
    type Output = Spectrum;

    fn div(self, rhs: f32) -> Spectrum {
        Spectrum::rgb(self.r / rhs, self.g / rhs, self.b / rhs)
    }
}

impl Index<usize> for Spectrum {
    type Output = f32;
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index(&self, i: usize) -> &f32 {
        match i {
            0 => &self.r,
            1 => &self.g,
            2 => &self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}

impl IndexMut<usize> for Spectrum {
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index_mut(&mut self, i: usize) -> &mut f32 {
        match i {
            0 => &mut self.r,
            1 => &mut self.g,
            2 => &mut self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}

impl Zero for Spectrum {
    fn zero() -> Spectrum {
        Spectrum::black()
    }

    fn is_zero(&self) -> bool {
        self.is_black()
    }
}

impl One for Spectrum {
    fn one() -> Spectrum {
        Spectrum::white()
    }
}

impl fmt::Display for Spectrum {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[ {}, {}, {} ]", self.r, self.g, self.b)
    }
}
<commit_msg>Implement Spectrum::max_component_value()<commit_after>use std::ops::{Add, AddAssign, Sub, Div, Mul, Index, IndexMut};
use std::fmt;

use na;
use num::{Zero, One};

use ::{lerp, find_interval};
use cie;

/// Represents a linear RGB spectrum.
/// TODO Rename this to `RGBSpectrum` and make `Spectrum` a type alias to this so we can also support
/// full spectral rendering.
#[derive(Debug,Copy,PartialEq,Clone,Default)]
pub struct Spectrum {
    pub r: f32,
    pub g: f32,
    pub b: f32,
}

impl Spectrum {
    /// Create an RGB spectrum from its components
    pub fn rgb(r: f32, g: f32, b: f32) -> Spectrum {
        Spectrum { r: r, g: g, b: b }
    }

    /// Create an RGB spectrum where all the components have the same value.
    pub fn grey(v: f32) -> Spectrum {
        Spectrum { r: v, g: v, b: v }
    }

    pub fn white() -> Spectrum {
        Spectrum::rgb(1.0, 1.0, 1.0)
    }

    pub fn black() -> Spectrum {
        Spectrum::rgb(0.0, 0.0, 0.0)
    }

    pub fn red() -> Spectrum {
        Spectrum::rgb(1.0, 0.0, 0.0)
    }

    pub fn green() -> Spectrum {
        Spectrum::rgb(0.0, 1.0, 0.0)
    }

    pub fn blue() -> Spectrum {
        Spectrum::rgb(0.0, 0.0, 1.0)
    }

    /// Convert this linear RGB spectrum to non-linear sRGB and return the result as an array of
    /// bytes.
    pub fn to_srgb(&self) -> [u8; 3] {
        let a = 0.055f32;
        let b = 1f32 / 2.4;
        let mut srgb = [0; 3];
        for i in 0..3 {
            let v = if self[i] <= 0.0031308 {
                12.92 * self[i]
            } else {
                (1.0 + a) * f32::powf(self[i], b) - a
            };
            srgb[i] = na::clamp(v * 255.0, 0.0, 255.0) as u8;
        }
        srgb
    }

    /// Convert a non-linear sRGB value to a linear RGB spectrum.
    pub fn from_srgb(rgb: &[u8; 3]) -> Spectrum {
        fn convert(v: u8) -> f32 {
            let value = v as f32 / 255.0;
            if value <= 0.04045 {
                value / 12.92
            } else {
                ((value + 0.055) * 1.0 / 1.055).powf(2.4)
            }
        }

        Spectrum::rgb(convert(rgb[0]), convert(rgb[1]), convert(rgb[2]))
    }

    /// Convert a linear spectrum in XYZ format to a linear RGB format.
    pub fn from_xyz(xyz: &[f32; 3]) -> Spectrum {
        let r = 3.240479 * xyz[0] - 1.537150 * xyz[1] - 0.498535 * xyz[2];
        let g = -0.969256 * xyz[0] + 1.875991 * xyz[1] + 0.041556 * xyz[2];
        let b = 0.055648 * xyz[0] - 0.204043 * xyz[1] + 1.057311 * xyz[2];
        Spectrum::rgb(r, g, b)
    }

    /// Create a spectrum from a series of (wavelength, value) samples from an SPD (Spectral Power
    /// Distribution).
    pub fn from_sampled(lambda: &[f32], v: &[f32], n: usize) -> Spectrum {
        // TODO sort by wavelength if needed
        let mut xyz = [0.0; 3];
        for i in 0..cie::N_CIE_SAMPLES {
            let val = interpolate_spectrum_samples(lambda, v, n, cie::CIE_LAMBDA[i]);
            xyz[0] += val * cie::CIE_X[i];
            xyz[1] += val * cie::CIE_Y[i];
            xyz[2] += val * cie::CIE_Z[i];
        }
        let scale = (cie::CIE_LAMBDA[cie::N_CIE_SAMPLES - 1] - cie::CIE_LAMBDA[0]) /
                    (cie::CIE_Y_INTEGRAL * cie::N_CIE_SAMPLES as f32);
        xyz[0] *= scale;
        xyz[1] *= scale;
        xyz[2] *= scale;

        Self::from_xyz(&xyz)
    }

    /// Return true if the colour is black i.e (0, 0 ,0).
    pub fn is_black(&self) -> bool {
        self.r == 0.0 && self.g == 0.0 && self.b == 0.0
    }

    /// Return true if any of the components is NaN. Useful for debugging.
    pub fn has_nan(&self) -> bool {
        self.r.is_nan() || self.g.is_nan() || self.b.is_nan()
    }

    /// Return true if any of the components is infinite. Useful for debugging.
    pub fn is_infinite(&self) -> bool {
        self.r.is_infinite() || self.g.is_infinite() || self.b.is_infinite()
    }

    /// Return a spectrum where each component is the square root of the original component.
    pub fn sqrt(&self) -> Spectrum {
        Spectrum::rgb(self.r.sqrt(), self.g.sqrt(), self.b.sqrt())
    }

    /// Return the luminance of the Spectrum
    pub fn y(&self) -> f32 {
        let y_height: [f32; 3] = [0.212671, 0.715160, 0.072169];
        return y_height[0] * self[0] + y_height[1] * self[1] + y_height[2] * self[2];

    }

    pub fn max_component_value(&self) -> f32 {
        self.r.max(self.g).max(self.b)
    }
}

fn interpolate_spectrum_samples(lambda: &[f32], vals: &[f32], n: usize, l: f32) -> f32 {
    for i in 0..n - 1 {
        assert!(lambda[i + 1] > lambda[i]);
    }
    if l <= lambda[0] {
        return vals[0];
    }
    if l >= lambda[n - 1] {
        return vals[n - 1];
    }
    let offset = find_interval(n, |index| lambda[index] <= l);
    assert!(l >= lambda[offset] && l <= lambda[offset + 1]);
    let t = (l - lambda[offset]) / (lambda[offset + 1] - lambda[offset]);
    lerp(t, vals[offset], vals[offset + 1])
}

// Operators

impl Add<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn add(self, rhs: Spectrum) -> Spectrum {
        Spectrum {
            r: self.r + rhs.r,
            g: self.g + rhs.g,
            b: self.b + rhs.b,
        }
    }
}

impl Sub<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn sub(self, rhs: Spectrum) -> Spectrum {
        Spectrum {
            r: self.r - rhs.r,
            g: self.g - rhs.g,
            b: self.b - rhs.b,
        }
    }
}

impl AddAssign<Spectrum> for Spectrum {
    fn add_assign(&mut self, rhs: Spectrum) {
        self.r += rhs.r;
        self.g += rhs.g;
        self.b += rhs.b;
    }
}

impl Mul<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn mul(self, rhs: Spectrum) -> Spectrum {
        Spectrum::rgb(self.r * rhs.r, self.g * rhs.g, self.b * rhs.b)
    }
}

impl Div<Spectrum> for Spectrum {
    type Output = Spectrum;

    fn div(self, rhs: Spectrum) -> Spectrum {
        Spectrum::rgb(self.r / rhs.r, self.g / rhs.g, self.b / rhs.b)
    }
}

impl Add<f32> for Spectrum {
    type Output = Spectrum;

    fn add(self, rhs: f32) -> Spectrum {
        Spectrum {
            r: self.r + rhs,
            g: self.g + rhs,
            b: self.b + rhs,
        }
    }
}

impl Sub<f32> for Spectrum {
    type Output = Spectrum;

    fn sub(self, rhs: f32) -> Spectrum {
        Spectrum {
            r: self.r - rhs,
            g: self.g - rhs,
            b: self.b - rhs,
        }
    }
}

impl Mul<f32> for Spectrum {
    type Output = Spectrum;

    fn mul(self, rhs: f32) -> Spectrum {
        Spectrum::rgb(self.r * rhs, self.g * rhs, self.b * rhs)
    }
}

impl Mul<Spectrum> for f32 {
    type Output = Spectrum;

    fn mul(self, rhs: Spectrum) -> Spectrum {
        Spectrum::rgb(self * rhs.r, self * rhs.g, self * rhs.b)
    }
}

impl Div<f32> for Spectrum {
    type Output = Spectrum;

    fn div(self, rhs: f32) -> Spectrum {
        Spectrum::rgb(self.r / rhs, self.g / rhs, self.b / rhs)
    }
}

impl Index<usize> for Spectrum {
    type Output = f32;
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index(&self, i: usize) -> &f32 {
        match i {
            0 => &self.r,
            1 => &self.g,
            2 => &self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}

impl IndexMut<usize> for Spectrum {
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index_mut(&mut self, i: usize) -> &mut f32 {
        match i {
            0 => &mut self.r,
            1 => &mut self.g,
            2 => &mut self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}

impl Zero for Spectrum {
    fn zero() -> Spectrum {
        Spectrum::black()
    }

    fn is_zero(&self) -> bool {
        self.is_black()
    }
}

impl One for Spectrum {
    fn one() -> Spectrum {
        Spectrum::white()
    }
}

impl fmt::Display for Spectrum {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[ {}, {}, {} ]", self.r, self.g, self.b)
    }
}
<|endoftext|>


<commit_before>use ::rand::SeedableRng;

pub struct SplitMixRng {
    state : u64,
    carry : Option<u32>
}

impl ::rand::Rng for SplitMixRng {
    fn next_u32(&mut self) -> u32 {
        match self.carry {
            None => {
                let x = self.next_u64();
                self.carry = Some((x >> 32) as u32);
                x as u32
            },
            Some(x) => { self.carry = None; x }
        }
    }

    fn next_u64(&mut self) -> u64 {
        self.state = self.state.wrapping_add(0x9E3779B97F4A7C15);
        let mut z : u64 = self.state;
        z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
        z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
        z ^ (z >> 31)
    }
}

impl ::rand::SeedableRng<u64> for SplitMixRng {
    fn reseed(&mut self, seed: u64) {
        self.state = seed
    }

    fn from_seed(seed: u64) -> Self {
        SplitMixRng { state: seed, carry: None }
    }
}

impl ::rand::Rand for SplitMixRng {
    fn rand<R: ::rand::Rng>(rng: &mut R) -> SplitMixRng {
        SplitMixRng::from_seed(rng.gen::<u64>())
    }
}
<commit_msg>Also allow SplitMix to be seeded automatically.<commit_after>use ::rand::SeedableRng;
use ::time::get_time;

pub struct SplitMixRng {
    state : u64,
    carry : Option<u32>
}

impl SplitMixRng {
    pub fn new() -> Self {
        SplitMixRng::from_seed(())
    }
}

impl ::rand::Rng for SplitMixRng {
    fn next_u32(&mut self) -> u32 {
        match self.carry {
            None => {
                let x = self.next_u64();
                self.carry = Some((x >> 32) as u32);
                x as u32
            },
            Some(x) => { self.carry = None; x }
        }
    }

    fn next_u64(&mut self) -> u64 {
        self.state = self.state.wrapping_add(0x9E3779B97F4A7C15);
        let mut z : u64 = self.state;
        z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
        z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
        z ^ (z >> 31)
    }
}

impl ::rand::SeedableRng<u64> for SplitMixRng {
    fn reseed(&mut self, seed: u64) {
        self.state = seed
    }

    fn from_seed(seed: u64) -> Self {
        SplitMixRng { state: seed, carry: None }
    }
}

impl ::rand::SeedableRng<()> for SplitMixRng {
    fn reseed(&mut self, _: ()) {
        self.reseed(get_time().sec as u64)
    }

    fn from_seed(_: ()) -> Self {
        SplitMixRng::from_seed(get_time().sec as u64)
    }
}

impl ::rand::Rand for SplitMixRng {
    fn rand<R: ::rand::Rng>(rng: &mut R) -> SplitMixRng {
        SplitMixRng::from_seed(rng.gen::<u64>())
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;

use criterion::{
    black_box, AxisScale, Bencher, Criterion, ParameterizedBenchmark, PlotConfiguration, Throughput,
};
use rand::{Rng, RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;

use exonum_crypto::Hash;
use exonum_merkledb::{
    proof_map_index::PROOF_MAP_KEY_SIZE as KEY_SIZE, Database, MapIndex, ProofListIndex,
    ProofMapIndex, TemporaryDB,
};

const NAME: &str = "name";
const FAMILY: &str = "index_family";
const SAMPLE_SIZE: usize = 10;
const CHUNK_SIZE: usize = 64;
const SEED: [u8; 16] = [100; 16];
const ITEM_COUNTS: [usize; 4] = [1_000, 10_000, 100_000, 1_000_000];

fn generate_random_kv(len: usize) -> Vec<(Hash, Vec<u8>)> {
    let mut rng = XorShiftRng::from_seed(SEED);
    let mut exists_keys = HashSet::new();
    let mut base = [0; KEY_SIZE];
    rng.fill_bytes(&mut base);
    let base = base;

    let kv_generator = |_| {
        let mut v = vec![0; CHUNK_SIZE];
        // Generate only unique keys.
        let mut k = base;
        let byte: usize = rng.gen_range(0, 31);
        k[byte] = rng.gen::<u8>();

        rng.fill_bytes(&mut v);
        while exists_keys.contains(&k) {
            rng.fill_bytes(&mut k);
        }
        exists_keys.insert(k);
        (Hash::new(k), v)
    };

    (0..len).map(kv_generator).collect::<Vec<_>>()
}

fn map_index_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = MapIndex::new(NAME, &mut fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn map_index_with_family_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = MapIndex::new_in_family(NAME, FAMILY, &mut fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn map_index_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let mut fork = db.fork();

    let mut table = ProofMapIndex::new(NAME, &mut fork);
    assert!(table.keys().next().is_none());
    for item in data {
        table.put(&item.0, item.1);
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: ProofMapIndex<_, Vec<u8>, Vec<u8>> = ProofMapIndex::new(NAME, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn map_index_with_family_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let mut fork = db.fork();

    let mut table = ProofMapIndex::new_in_family(NAME, FAMILY, &mut fork);
    assert!(table.keys().next().is_none());
    for item in data {
        table.put(&item.0, item.1);
    }
    db.merge(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: ProofMapIndex<_, Vec<u8>, Vec<u8>> =
                ProofMapIndex::new_in_family(NAME, FAMILY, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn proof_list_append(b: &mut Bencher, len: usize) {
    let mut rng = XorShiftRng::from_seed(SEED);
    let data = (0..len)
        .map(|_| {
            let mut chunk = vec![0; CHUNK_SIZE];
            rng.fill_bytes(&mut chunk);
            chunk
        })
        .collect::<Vec<_>>();

    let db = TemporaryDB::default();
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(mut storage, data)| {
            let mut table = ProofListIndex::new(NAME, &mut storage);
            assert!(table.is_empty());
            for item in data {
                table.push(item);
            }
        },
    );
}

fn proof_map_insert_without_merge(b: &mut Bencher, len: usize) {
    let db = TemporaryDB::default();
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(mut storage, data)| {
            let mut table = ProofMapIndex::new(NAME, &mut storage);
            for item in data {
                table.put(&item.0, item.1);
            }
        },
    );
}

fn proof_map_insert_with_merge(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = ProofMapIndex::new(NAME, &mut fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn proof_map_index_build_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let mut proofs = Vec::with_capacity(data.len());

    b.iter(|| {
        proofs.clear();
        proofs.extend(data.iter().map(|item| table.get_proof(item.0)));
    });

    for (i, proof) in proofs.into_iter().enumerate() {
        let checked_proof = proof.check().unwrap();
        assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
        assert_eq!(checked_proof.merkle_root(), table_merkle_root);
    }
}

fn proof_map_index_verify_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let proofs: Vec<_> = data.iter().map(|item| table.get_proof(item.0)).collect();

    b.iter(|| {
        for (i, proof) in proofs.iter().enumerate() {
            let checked_proof = proof.clone().check().unwrap();
            assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
            assert_eq!(checked_proof.merkle_root(), table_merkle_root);
        }
    });
}

fn bench_fn<F>(c: &mut Criterion, name: &str, benchmark: F)
where
    F: Fn(&mut Bencher, usize) + 'static,
{
    let item_counts = ITEM_COUNTS.iter().cloned();
    c.bench(
        name,
        ParameterizedBenchmark::new(
            "items",
            move |b: &mut Bencher, &len: &usize| benchmark(b, len),
            item_counts,
        )
        .throughput(|s| Throughput::Elements(*s as u32))
        .plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic))
        .sample_size(SAMPLE_SIZE),
    );
}

pub fn bench_storage(c: &mut Criterion) {
    exonum_crypto::init();

    bench_fn(c, "plain_map/insert", map_index_insert);
    bench_fn(c, "plain_map/iter", map_index_iter);
    bench_fn(
        c,
        "plain_map_with_family/insert",
        map_index_with_family_insert,
    );
    bench_fn(c, "plain_map_with_family/iter", map_index_with_family_iter);
    bench_fn(c, "proof_list/append", proof_list_append);
    bench_fn(
        c,
        "proof_map/insert/no_merge",
        proof_map_insert_without_merge,
    );
    bench_fn(c, "proof_map/insert/merge", proof_map_insert_with_merge);
    bench_fn(c, "proof_map/proofs/build", proof_map_index_build_proofs);
    bench_fn(
        c,
        "proof_map/proofs/validate",
        proof_map_index_verify_proofs,
    );
}
<commit_msg>Clarify benchmark names<commit_after>// Copyright 2019 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;

use criterion::{
    black_box, AxisScale, Bencher, Criterion, ParameterizedBenchmark, PlotConfiguration, Throughput,
};
use rand::{Rng, RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;

use exonum_crypto::Hash;
use exonum_merkledb::{
    proof_map_index::PROOF_MAP_KEY_SIZE as KEY_SIZE, Database, MapIndex, ProofListIndex,
    ProofMapIndex, TemporaryDB,
};

const NAME: &str = "name";
const FAMILY: &str = "index_family";
const SAMPLE_SIZE: usize = 10;
const CHUNK_SIZE: usize = 64;
const SEED: [u8; 16] = [100; 16];
const ITEM_COUNTS: [usize; 4] = [1_000, 10_000, 100_000, 1_000_000];

fn generate_random_kv(len: usize) -> Vec<(Hash, Vec<u8>)> {
    let mut rng = XorShiftRng::from_seed(SEED);
    let mut exists_keys = HashSet::new();
    let mut base = [0; KEY_SIZE];
    rng.fill_bytes(&mut base);
    let base = base;

    let kv_generator = |_| {
        let mut v = vec![0; CHUNK_SIZE];
        // Generate only unique keys.
        let mut k = base;
        let byte: usize = rng.gen_range(0, 31);
        k[byte] = rng.gen::<u8>();

        rng.fill_bytes(&mut v);
        while exists_keys.contains(&k) {
            rng.fill_bytes(&mut k);
        }
        exists_keys.insert(k);
        (Hash::new(k), v)
    };

    (0..len).map(kv_generator).collect::<Vec<_>>()
}

fn plain_map_index_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = MapIndex::new(NAME, &mut fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn plain_map_index_with_family_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = MapIndex::new_in_family(NAME, FAMILY, &mut fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn plain_map_index_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let mut fork = db.fork();

    let mut table = MapIndex::new(NAME, &mut fork);
    assert!(table.keys().next().is_none());
    for item in data {
        table.put(&item.0, item.1);
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Vec<u8>, Vec<u8>> = MapIndex::new(NAME, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn plain_map_index_with_family_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let mut fork = db.fork();

    let mut table = MapIndex::new_in_family(NAME, FAMILY, &mut fork);
    assert!(table.keys().next().is_none());
    for item in data {
        table.put(&item.0, item.1);
    }
    db.merge(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Vec<u8>, Vec<u8>> =
                MapIndex::new_in_family(NAME, FAMILY, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn proof_list_append(b: &mut Bencher, len: usize) {
    let mut rng = XorShiftRng::from_seed(SEED);
    let data = (0..len)
        .map(|_| {
            let mut chunk = vec![0; CHUNK_SIZE];
            rng.fill_bytes(&mut chunk);
            chunk
        })
        .collect::<Vec<_>>();

    let db = TemporaryDB::default();
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(mut storage, data)| {
            let mut table = ProofListIndex::new(NAME, &mut storage);
            assert!(table.is_empty());
            for item in data {
                table.push(item);
            }
        },
    );
}

fn proof_map_insert_without_merge(b: &mut Bencher, len: usize) {
    let db = TemporaryDB::default();
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(mut storage, data)| {
            let mut table = ProofMapIndex::new(NAME, &mut storage);
            for item in data {
                table.put(&item.0, item.1);
            }
        },
    );
}

fn proof_map_insert_with_merge(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = ProofMapIndex::new(NAME, &mut fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn proof_map_index_build_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let mut proofs = Vec::with_capacity(data.len());

    b.iter(|| {
        proofs.clear();
        proofs.extend(data.iter().map(|item| table.get_proof(item.0)));
    });

    for (i, proof) in proofs.into_iter().enumerate() {
        let checked_proof = proof.check().unwrap();
        assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
        assert_eq!(checked_proof.merkle_root(), table_merkle_root);
    }
}

fn proof_map_index_verify_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let proofs: Vec<_> = data.iter().map(|item| table.get_proof(item.0)).collect();

    b.iter(|| {
        for (i, proof) in proofs.iter().enumerate() {
            let checked_proof = proof.clone().check().unwrap();
            assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
            assert_eq!(checked_proof.merkle_root(), table_merkle_root);
        }
    });
}

fn bench_fn<F>(c: &mut Criterion, name: &str, benchmark: F)
where
    F: Fn(&mut Bencher, usize) + 'static,
{
    let item_counts = ITEM_COUNTS.iter().cloned();
    c.bench(
        name,
        ParameterizedBenchmark::new(
            "items",
            move |b: &mut Bencher, &len: &usize| benchmark(b, len),
            item_counts,
        )
        .throughput(|s| Throughput::Elements(*s as u32))
        .plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic))
        .sample_size(SAMPLE_SIZE),
    );
}

pub fn bench_storage(c: &mut Criterion) {
    exonum_crypto::init();

    bench_fn(c, "plain_map/insert", plain_map_index_insert);
    bench_fn(c, "plain_map/iter", plain_map_index_iter);
    bench_fn(
        c,
        "plain_map_with_family/insert",
        plain_map_index_with_family_insert,
    );
    bench_fn(
        c,
        "plain_map_with_family/iter",
        plain_map_index_with_family_iter,
    );
    bench_fn(c, "proof_list/append", proof_list_append);
    bench_fn(
        c,
        "proof_map/insert/no_merge",
        proof_map_insert_without_merge,
    );
    bench_fn(c, "proof_map/insert/merge", proof_map_insert_with_merge);
    bench_fn(c, "proof_map/proofs/build", proof_map_index_build_proofs);
    bench_fn(
        c,
        "proof_map/proofs/validate",
        proof_map_index_verify_proofs,
    );
}
<|endoftext|>


<commit_before>use color::Color;
use dimensions::Dimensions;
use graphics;
use graphics::{
    Graphics,
};
use graphics::character::CharacterCache;
use label;
use label::FontSize;
use mouse::Mouse;
use piston::input::keyboard::Key::{
    Backspace,
    Left,
    Right,
    Return,
};
use point::Point;
use rectangle;
use std::num::Float;
use clock_ticks::precise_time_s;
use ui_context::{
    Id,
    UIID,
    UiContext,
};
use vecmath::{
    vec2_add,
    vec2_sub,
};
use widget::{ DefaultWidgetState, Widget };
use std::cmp;
use Callback;
use FrameColor;
use FrameWidth;
use Position;
use Size;

pub type Idx = usize;
pub type CursorX = f64;

/// Represents the state of the text_box widget.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct State(DrawState, Capturing);

/// Represents the next tier of state.
#[derive(Debug, PartialEq, Clone,Copy)]
pub enum DrawState {
    Normal,
    Highlighted(Element),
    Clicked(Element),
}

/// Whether the textbox is currently captured or not.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Capturing {
    Uncaptured,
    Captured(Idx),
}

/// Represents an element of the TextBox widget.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Element {
    Nill,
    Rect,
    Text(Idx, CursorX),
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State(state, capturing) => match capturing {
                Capturing::Captured(_) => rectangle::State::Normal,
                Capturing::Uncaptured => match state {
                    DrawState::Normal => rectangle::State::Normal,
                    DrawState::Highlighted(_) => rectangle::State::Highlighted,
                    DrawState::Clicked(_) => rectangle::State::Clicked,
                },
            }
        }
    }
}

widget_fns!(TextBox, State, Widget::TextBox(State(DrawState::Normal, Capturing::Uncaptured)));

static TEXT_PADDING: f64 = 5f64;

// if the index is 0 return (0, the start of the text)
// until we reach the each index we start with iterate over the characters character in the string
//   if the

/// Calculate the index and x position for a text box cursor.
fn update_cursor_position<C: CharacterCache>(uic: &mut UiContext<C>,
                          mut idx: usize,
                          mut text_x: f64,
                          font_size: FontSize,
                          text: &str) -> (Idx, CursorX) {
    if idx == 0 { return (0, text_x); }
    let text_len = text.len();
    if idx > text_len { idx = text_len; }
    for (i, ch) in text.chars().enumerate() {
        if i >= idx { break; }
        text_x += uic.get_character(font_size, ch).width();
    }
    (idx, text_x)
}

/// Check if cursor is over the pad and if so, which
fn over_elem<C: CharacterCache>(uic: &mut UiContext<C>,
             pos: Point,
             mouse_pos: Point,
             rect_dim: Dimensions,
             pad_pos: Point,
             pad_dim: Dimensions,
             text_pos: Point,
             text_w: f64,
             font_size: FontSize,
             text: &str) -> Element {
    match rectangle::is_over(pos, mouse_pos, rect_dim) {
        false => Element::Nill,
        true => match rectangle::is_over(pad_pos, mouse_pos, pad_dim) {
            false => Element::Rect,
            true => {
                let (idx, cursor_x) = closest_idx(uic, mouse_pos, text_pos[0], text_w, font_size, text);
                Element::Text(idx, cursor_x)
            },
        },
    }
}

/// Check which character is closest to the mouse cursor.
fn closest_idx<C: CharacterCache>(uic: &mut UiContext<C>,
               mouse_pos: Point,
               text_x: f64,
               text_w: f64,
               font_size: FontSize,
               text: &str) -> (Idx, f64) {
    if mouse_pos[0] <= text_x { return (0, text_x) }
    let mut x = text_x;
    let mut prev_x = x;
    let mut left_x = text_x;
    for (i, ch) in text.chars().enumerate() {
        let character = uic.get_character(font_size, ch);
        let char_w = character.width();
        x += char_w;
        let right_x = prev_x + char_w / 2.0;
        if mouse_pos[0] > left_x && mouse_pos[0] < right_x { return (i, prev_x) }
        prev_x = x;
        left_x = right_x;
    }
    (text.len(), text_x + text_w)
}

/// Check and return the current state of the TextBox.
fn get_new_state(over_elem: Element,
                 prev_box_state: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::Capturing::{Uncaptured, Captured};
    use self::DrawState::{Normal, Highlighted, Clicked};
    use self::Element::{Nill, Text};
    match prev_box_state {
        State(prev, Uncaptured) => {
            match (over_elem, prev, mouse.left) {
                (_, Normal, Down)                       => State(Normal, Uncaptured),
                (Nill, Normal, Up)                      |
                (Nill, Highlighted(_), Up)              => State(Normal, Uncaptured),
                (_, Normal, Up)                         |
                (_, Highlighted(_), Up)                 => State(Highlighted(over_elem), Uncaptured),
                (_, Highlighted(p_elem), Down)          |
                (_, Clicked(p_elem), Down)              => State(Clicked(p_elem), Uncaptured),
                (Text(idx, _), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx)),
                (Nill, _, _)                            => State(Normal, Uncaptured),
                _                                       => prev_box_state,
            }
        },
        State(prev, Captured(p_idx)) => {
            match (over_elem, prev, mouse.left) {
                (Nill, Clicked(Nill), Up)               => State(Normal, Uncaptured),
                (Text(idx, _), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx)),
                (_, Normal, Up)                         |
                (_, Highlighted(_), Up)                 |
                (_, Clicked(_), Up)                     => State(Highlighted(over_elem), Captured(p_idx)),
                (_, Highlighted(p_elem), Down)          |
                (_, Clicked(p_elem), Down)              => State(Clicked(p_elem), Captured(p_idx)),
                _                                       => prev_box_state,
            }
        },
    }
}

/// Draw the text cursor.
fn draw_cursor<B: Graphics>(
    win_w: f64,
    win_h: f64,
    graphics: &mut B,
    color: Color,
    cursor_x: f64,
    pad_pos_y: f64,
    pad_h: f64
) {
    let draw_state = graphics::default_draw_state();
    let transform = graphics::abs_transform(win_w, win_h);
    let Color(color) = color.plain_contrast();
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    graphics::Line::round([r, g, b, (a * (precise_time_s() * 2.5).sin() as f32).abs()], 0.5f64)
        .draw(
            [cursor_x, pad_pos_y, cursor_x, pad_pos_y + pad_h],
            draw_state,
            transform,
            graphics
        );
}

/// A context on which the builder pattern can be implemented.
pub struct TextBox<'a, F> {
    ui_id: UIID,
    text: &'a mut String,
    font_size: u32,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
}

impl<'a, F> TextBox<'a, F> {
    pub fn font_size(self, font_size: FontSize) -> TextBox<'a, F> {
        TextBox { font_size: font_size, ..self }
    }
}

impl<'a, F> TextBox<'a, F> {
    /// Initialise a TextBoxContext.
    pub fn new(ui_id: UIID, text: &'a mut String) -> TextBox<'a, F> {
        TextBox {
            ui_id: ui_id,
            text: text,
            font_size: 24, // Default font_size.
            pos: [0.0, 0.0],
            dim: [192.0, 48.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
        }
    }
}

quack! {
    tb: TextBox['a, F]
    get:
        fn () -> Size [] { Size(tb.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(
                Widget::TextBox(State(DrawState::Normal, Capturing::Uncaptured))
            )
        }
        fn () -> Id [] { Id(tb.ui_id) }
    set:
        fn (val: Color) [] { tb.maybe_color = Some(val) }
        fn (val: Callback<F>) [where F: FnMut(&mut String) + 'a] {
            tb.maybe_callback = Some(val.0)
        }
        fn (val: FrameColor) [] { tb.maybe_frame_color = Some(val.0) }
        fn (val: FrameWidth) [] { tb.maybe_frame = Some(val.0) }
        fn (val: Position) [] { tb.pos = val.0 }
        fn (val: Size) [] { tb.dim = val.0 }
    action:
}

impl<'a, F> ::draw::Drawable for TextBox<'a, F>
    where
        F: FnMut(&mut String) + 'a
{

    #[inline]
    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {
        let mouse = uic.get_mouse_state();
        let state = *get_state(uic, self.ui_id);

        // Rect.
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let frame_w2 = frame_w * 2.0;
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };
        let pad_pos = vec2_add(self.pos, [frame_w; 2]);
        let pad_dim = vec2_sub(self.dim, [frame_w2; 2]);
        let text_x = pad_pos[0] + TEXT_PADDING;
        let text_y = pad_pos[1] + (pad_dim[1] - self.font_size as f64) / 2.0;
        let text_pos = [text_x, text_y];
        let text_w = label::width(uic, self.font_size, &self.text);
        let over_elem = over_elem(uic, self.pos, mouse.pos, self.dim,
                                  pad_pos, pad_dim, text_pos, text_w,
                                  self.font_size, &self.text);
        let new_state = get_new_state(over_elem, state, mouse);

        rectangle::draw(uic.win_w, uic.win_h, graphics, new_state.as_rectangle_state(),
                        self.pos, self.dim, maybe_frame, color);
        uic.draw_text(graphics, text_pos, self.font_size,
                           color.plain_contrast(), &self.text);

        let new_state = match new_state { State(w_state, capturing) => match capturing {
            Capturing::Uncaptured => new_state,
            Capturing::Captured(idx) => {
                let (idx, cursor_x) = update_cursor_position(uic, idx, text_x, self.font_size, &self.text);
                draw_cursor(uic.win_w, uic.win_h, graphics, color,
                            cursor_x, pad_pos[1], pad_dim[1]);
                let mut new_idx = idx;
                let mut new_cursor_x = cursor_x;

                // Check for entered text.
                let entered_text = uic.get_entered_text();
                for t in entered_text.iter() {
                    let mut entered_text_width = 0.0;
                    for ch in t[..].chars() {
                        let c = uic.get_character(self.font_size, ch);
                        entered_text_width += c.width();
                    }
                    if new_cursor_x + entered_text_width < pad_pos[0] + pad_dim[0] - TEXT_PADDING {
                        new_cursor_x += entered_text_width;
                    }
                    else {
                        break;
                    }
                    let new_text = format!("{}{}{}", &self.text[..idx], t, &self.text[idx..]);
                    *self.text = new_text;
                    new_idx += t.len();
                }

                // Check for control keys.
                let pressed_keys = uic.get_pressed_keys();
                for key in pressed_keys.iter() {
                    match *key {
                        Backspace => {
                            if self.text.len() > 0
                            && self.text.len() >= idx
                            && idx > 0 {
                                let rem_idx = idx - 1;
                                new_cursor_x -= uic.get_character_w(
                                    self.font_size, self.text[..].char_at(rem_idx)
                                );
                                let new_text = format!("{}{}", &self.text[..rem_idx], &self.text[idx..]);
                                *self.text = new_text;
                                new_idx = rem_idx;
                            }
                        },
                        Left => {
                            if idx > 0 {
                                new_cursor_x -= uic.get_character_w(
                                    self.font_size, self.text[..].char_at(idx - 1)
                                );
                                new_idx -= 1;
                            }
                        },
                        Right => {
                            if self.text.len() > idx {
                                new_cursor_x += uic.get_character_w(
                                    self.font_size, self.text[..].char_at(idx)
                                );
                                new_idx += 1;
                            }
                        },
                        Return => if self.text.len() > 0 {
                            let TextBox { // borrowck
                                ref mut maybe_callback,
                                ref font_size,
                                ref mut text,
                                ..
                            } = *self;
                            match *maybe_callback {
                                Some(ref mut callback) => {
                                    (*callback)(*text);

                                    new_idx = cmp::min(new_idx, text.len());
                                    let text = &*text;
                                    new_cursor_x = text.chars()
                                                       // Add text_pos.x for padding
                                                       .fold(text_pos[0], |acc, c| {
                                        acc + uic.get_character_w(*font_size, c)
                                    });
                                },
                                None => (),
                            }
                        },
                        _ => (),
                    }
                }

                State(w_state, Capturing::Captured(new_idx))
            },
        }};

        set_state(uic, self.ui_id, Widget::TextBox(new_state), self.pos, self.dim);

    }
}
<commit_msg>remove comments I forgot to delete<commit_after>use color::Color;
use dimensions::Dimensions;
use graphics;
use graphics::{
    Graphics,
};
use graphics::character::CharacterCache;
use label;
use label::FontSize;
use mouse::Mouse;
use piston::input::keyboard::Key::{
    Backspace,
    Left,
    Right,
    Return,
};
use point::Point;
use rectangle;
use std::num::Float;
use clock_ticks::precise_time_s;
use ui_context::{
    Id,
    UIID,
    UiContext,
};
use vecmath::{
    vec2_add,
    vec2_sub,
};
use widget::{ DefaultWidgetState, Widget };
use std::cmp;
use Callback;
use FrameColor;
use FrameWidth;
use Position;
use Size;

pub type Idx = usize;
pub type CursorX = f64;

/// Represents the state of the text_box widget.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct State(DrawState, Capturing);

/// Represents the next tier of state.
#[derive(Debug, PartialEq, Clone,Copy)]
pub enum DrawState {
    Normal,
    Highlighted(Element),
    Clicked(Element),
}

/// Whether the textbox is currently captured or not.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Capturing {
    Uncaptured,
    Captured(Idx),
}

/// Represents an element of the TextBox widget.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Element {
    Nill,
    Rect,
    Text(Idx, CursorX),
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State(state, capturing) => match capturing {
                Capturing::Captured(_) => rectangle::State::Normal,
                Capturing::Uncaptured => match state {
                    DrawState::Normal => rectangle::State::Normal,
                    DrawState::Highlighted(_) => rectangle::State::Highlighted,
                    DrawState::Clicked(_) => rectangle::State::Clicked,
                },
            }
        }
    }
}

widget_fns!(TextBox, State, Widget::TextBox(State(DrawState::Normal, Capturing::Uncaptured)));

static TEXT_PADDING: f64 = 5f64;

/// Calculate the index and x position for a text box cursor.
fn update_cursor_position<C: CharacterCache>(uic: &mut UiContext<C>,
                          mut idx: usize,
                          mut text_x: f64,
                          font_size: FontSize,
                          text: &str) -> (Idx, CursorX) {
    if idx == 0 { return (0, text_x); }
    let text_len = text.len();
    if idx > text_len { idx = text_len; }
    for (i, ch) in text.chars().enumerate() {
        if i >= idx { break; }
        text_x += uic.get_character(font_size, ch).width();
    }
    (idx, text_x)
}

/// Check if cursor is over the pad and if so, which
fn over_elem<C: CharacterCache>(uic: &mut UiContext<C>,
             pos: Point,
             mouse_pos: Point,
             rect_dim: Dimensions,
             pad_pos: Point,
             pad_dim: Dimensions,
             text_pos: Point,
             text_w: f64,
             font_size: FontSize,
             text: &str) -> Element {
    match rectangle::is_over(pos, mouse_pos, rect_dim) {
        false => Element::Nill,
        true => match rectangle::is_over(pad_pos, mouse_pos, pad_dim) {
            false => Element::Rect,
            true => {
                let (idx, cursor_x) = closest_idx(uic, mouse_pos, text_pos[0], text_w, font_size, text);
                Element::Text(idx, cursor_x)
            },
        },
    }
}

/// Check which character is closest to the mouse cursor.
fn closest_idx<C: CharacterCache>(uic: &mut UiContext<C>,
               mouse_pos: Point,
               text_x: f64,
               text_w: f64,
               font_size: FontSize,
               text: &str) -> (Idx, f64) {
    if mouse_pos[0] <= text_x { return (0, text_x) }
    let mut x = text_x;
    let mut prev_x = x;
    let mut left_x = text_x;
    for (i, ch) in text.chars().enumerate() {
        let character = uic.get_character(font_size, ch);
        let char_w = character.width();
        x += char_w;
        let right_x = prev_x + char_w / 2.0;
        if mouse_pos[0] > left_x && mouse_pos[0] < right_x { return (i, prev_x) }
        prev_x = x;
        left_x = right_x;
    }
    (text.len(), text_x + text_w)
}

/// Check and return the current state of the TextBox.
fn get_new_state(over_elem: Element,
                 prev_box_state: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::Capturing::{Uncaptured, Captured};
    use self::DrawState::{Normal, Highlighted, Clicked};
    use self::Element::{Nill, Text};
    match prev_box_state {
        State(prev, Uncaptured) => {
            match (over_elem, prev, mouse.left) {
                (_, Normal, Down)                       => State(Normal, Uncaptured),
                (Nill, Normal, Up)                      |
                (Nill, Highlighted(_), Up)              => State(Normal, Uncaptured),
                (_, Normal, Up)                         |
                (_, Highlighted(_), Up)                 => State(Highlighted(over_elem), Uncaptured),
                (_, Highlighted(p_elem), Down)          |
                (_, Clicked(p_elem), Down)              => State(Clicked(p_elem), Uncaptured),
                (Text(idx, _), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx)),
                (Nill, _, _)                            => State(Normal, Uncaptured),
                _                                       => prev_box_state,
            }
        },
        State(prev, Captured(p_idx)) => {
            match (over_elem, prev, mouse.left) {
                (Nill, Clicked(Nill), Up)               => State(Normal, Uncaptured),
                (Text(idx, _), Clicked(Text(_, _)), Up) => State(Highlighted(over_elem), Captured(idx)),
                (_, Normal, Up)                         |
                (_, Highlighted(_), Up)                 |
                (_, Clicked(_), Up)                     => State(Highlighted(over_elem), Captured(p_idx)),
                (_, Highlighted(p_elem), Down)          |
                (_, Clicked(p_elem), Down)              => State(Clicked(p_elem), Captured(p_idx)),
                _                                       => prev_box_state,
            }
        },
    }
}

/// Draw the text cursor.
fn draw_cursor<B: Graphics>(
    win_w: f64,
    win_h: f64,
    graphics: &mut B,
    color: Color,
    cursor_x: f64,
    pad_pos_y: f64,
    pad_h: f64
) {
    let draw_state = graphics::default_draw_state();
    let transform = graphics::abs_transform(win_w, win_h);
    let Color(color) = color.plain_contrast();
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    graphics::Line::round([r, g, b, (a * (precise_time_s() * 2.5).sin() as f32).abs()], 0.5f64)
        .draw(
            [cursor_x, pad_pos_y, cursor_x, pad_pos_y + pad_h],
            draw_state,
            transform,
            graphics
        );
}

/// A context on which the builder pattern can be implemented.
pub struct TextBox<'a, F> {
    ui_id: UIID,
    text: &'a mut String,
    font_size: u32,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
}

impl<'a, F> TextBox<'a, F> {
    pub fn font_size(self, font_size: FontSize) -> TextBox<'a, F> {
        TextBox { font_size: font_size, ..self }
    }
}

impl<'a, F> TextBox<'a, F> {
    /// Initialise a TextBoxContext.
    pub fn new(ui_id: UIID, text: &'a mut String) -> TextBox<'a, F> {
        TextBox {
            ui_id: ui_id,
            text: text,
            font_size: 24, // Default font_size.
            pos: [0.0, 0.0],
            dim: [192.0, 48.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
        }
    }
}

quack! {
    tb: TextBox['a, F]
    get:
        fn () -> Size [] { Size(tb.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(
                Widget::TextBox(State(DrawState::Normal, Capturing::Uncaptured))
            )
        }
        fn () -> Id [] { Id(tb.ui_id) }
    set:
        fn (val: Color) [] { tb.maybe_color = Some(val) }
        fn (val: Callback<F>) [where F: FnMut(&mut String) + 'a] {
            tb.maybe_callback = Some(val.0)
        }
        fn (val: FrameColor) [] { tb.maybe_frame_color = Some(val.0) }
        fn (val: FrameWidth) [] { tb.maybe_frame = Some(val.0) }
        fn (val: Position) [] { tb.pos = val.0 }
        fn (val: Size) [] { tb.dim = val.0 }
    action:
}

impl<'a, F> ::draw::Drawable for TextBox<'a, F>
    where
        F: FnMut(&mut String) + 'a
{

    #[inline]
    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {
        let mouse = uic.get_mouse_state();
        let state = *get_state(uic, self.ui_id);

        // Rect.
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let frame_w2 = frame_w * 2.0;
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };
        let pad_pos = vec2_add(self.pos, [frame_w; 2]);
        let pad_dim = vec2_sub(self.dim, [frame_w2; 2]);
        let text_x = pad_pos[0] + TEXT_PADDING;
        let text_y = pad_pos[1] + (pad_dim[1] - self.font_size as f64) / 2.0;
        let text_pos = [text_x, text_y];
        let text_w = label::width(uic, self.font_size, &self.text);
        let over_elem = over_elem(uic, self.pos, mouse.pos, self.dim,
                                  pad_pos, pad_dim, text_pos, text_w,
                                  self.font_size, &self.text);
        let new_state = get_new_state(over_elem, state, mouse);

        rectangle::draw(uic.win_w, uic.win_h, graphics, new_state.as_rectangle_state(),
                        self.pos, self.dim, maybe_frame, color);
        uic.draw_text(graphics, text_pos, self.font_size,
                           color.plain_contrast(), &self.text);

        let new_state = match new_state { State(w_state, capturing) => match capturing {
            Capturing::Uncaptured => new_state,
            Capturing::Captured(idx) => {
                let (idx, cursor_x) = update_cursor_position(uic, idx, text_x, self.font_size, &self.text);
                draw_cursor(uic.win_w, uic.win_h, graphics, color,
                            cursor_x, pad_pos[1], pad_dim[1]);
                let mut new_idx = idx;
                let mut new_cursor_x = cursor_x;

                // Check for entered text.
                let entered_text = uic.get_entered_text();
                for t in entered_text.iter() {
                    let mut entered_text_width = 0.0;
                    for ch in t[..].chars() {
                        let c = uic.get_character(self.font_size, ch);
                        entered_text_width += c.width();
                    }
                    if new_cursor_x + entered_text_width < pad_pos[0] + pad_dim[0] - TEXT_PADDING {
                        new_cursor_x += entered_text_width;
                    }
                    else {
                        break;
                    }
                    let new_text = format!("{}{}{}", &self.text[..idx], t, &self.text[idx..]);
                    *self.text = new_text;
                    new_idx += t.len();
                }

                // Check for control keys.
                let pressed_keys = uic.get_pressed_keys();
                for key in pressed_keys.iter() {
                    match *key {
                        Backspace => {
                            if self.text.len() > 0
                            && self.text.len() >= idx
                            && idx > 0 {
                                let rem_idx = idx - 1;
                                new_cursor_x -= uic.get_character_w(
                                    self.font_size, self.text[..].char_at(rem_idx)
                                );
                                let new_text = format!("{}{}", &self.text[..rem_idx], &self.text[idx..]);
                                *self.text = new_text;
                                new_idx = rem_idx;
                            }
                        },
                        Left => {
                            if idx > 0 {
                                new_cursor_x -= uic.get_character_w(
                                    self.font_size, self.text[..].char_at(idx - 1)
                                );
                                new_idx -= 1;
                            }
                        },
                        Right => {
                            if self.text.len() > idx {
                                new_cursor_x += uic.get_character_w(
                                    self.font_size, self.text[..].char_at(idx)
                                );
                                new_idx += 1;
                            }
                        },
                        Return => if self.text.len() > 0 {
                            let TextBox { // borrowck
                                ref mut maybe_callback,
                                ref font_size,
                                ref mut text,
                                ..
                            } = *self;
                            match *maybe_callback {
                                Some(ref mut callback) => {
                                    (*callback)(*text);

                                    new_idx = cmp::min(new_idx, text.len());
                                    let text = &*text;
                                    new_cursor_x = text.chars()
                                                       // Add text_pos.x for padding
                                                       .fold(text_pos[0], |acc, c| {
                                        acc + uic.get_character_w(*font_size, c)
                                    });
                                },
                                None => (),
                            }
                        },
                        _ => (),
                    }
                }

                State(w_state, Capturing::Captured(new_idx))
            },
        }};

        set_state(uic, self.ui_id, Widget::TextBox(new_state), self.pos, self.dim);

    }
}
<|endoftext|>


<commit_before>mod screen;

use self::screen::Screen;
use data::{Editable, Named, Selectable, Undoable};
use std::{cmp, iter};
use syntect::easy::HighlightLines;
use syntect::highlighting::Theme;
use syntect::highlighting::{Color, FontStyle, Style};
use syntect::parsing::{SyntaxReference, SyntaxSet};
use termion::terminal_size;
pub struct View<'a> {
    message: Option<String>,
    is_prompt: bool,
    line_offset: usize,
    screen: Screen,
    highlighter: HighlightLines<'a>,
    theme: &'a Theme,
    syntax_set: &'a SyntaxSet,
}

const TAB_LENGTH: usize = 4;

impl<'a> View<'a> {
    pub fn new(theme: &'a Theme, syntax: &'a SyntaxReference, syntax_set: &'a SyntaxSet) -> Self {
        let default_style = Style {
            foreground: theme.settings.foreground.unwrap_or(Color::WHITE),
            background: theme.settings.background.unwrap_or(Color::BLACK),
            font_style: FontStyle::empty(),
        };
        View {
            message: None,
            is_prompt: false,
            line_offset: 0,
            screen: Screen::with_default_style(default_style),
            highlighter: HighlightLines::new(syntax, theme),
            theme,
            syntax_set,
        }
    }

    pub fn message(&mut self, message: &str) {
        self.is_prompt = false;
        self.message = Some(String::from(message));
    }

    pub fn prompt(&mut self, prompt: &str, message: &str) {
        self.is_prompt = true;
        let msg = String::from(prompt) + message;
        self.message = Some(msg);
    }

    pub fn quiet(&mut self) {
        self.is_prompt = false;
        self.message = None;
    }

    pub fn center_view(&mut self, line: usize) {
        self.line_offset = line
            .checked_sub(self.lines_height() as usize / 2)
            .unwrap_or(0);
    }

    pub fn adjust_view(&mut self, line: usize) {
        if line < self.line_offset {
            self.line_offset = line;
        } else if line + 1 >= self.line_offset + self.lines_height() {
            self.line_offset = 1 + line - self.lines_height();
        }
    }

    pub fn scroll_view<T: Editable>(&mut self, offset: isize, content: &T) {
        self.line_offset = cmp::min(
            cmp::max((self.line_offset as isize) + offset, 0),
            (content.line_count() as isize) - 1,
        ) as usize;
    }

    pub fn render<T>(&mut self, content: &T)
    where
        T: Editable + Named + Selectable + Undoable,
    {
        self.screen.clear();
        self.paint_lines(content);
        self.paint_status(content);
        self.paint_message();
        self.paint_cursor(content);
        self.screen.present();
    }

    pub fn translate_coordinates<T>(&self, content: &T, x: u16, y: u16) -> (usize, usize)
    where
        T: Editable,
    {
        let line = cmp::min(
            (y as isize + self.line_offset as isize - 1) as usize,
            content.line_count() - 1,
        );
        let visual_col = (cmp::max(
            0,
            x as isize - self.line_number_width(content.line_count()) as isize - 2,
        )) as usize;
        // find out if we clicked through a tab
        let col = content
            .iter_line(line)
            .scan(0, |state, x| {
                *state += if x == '\t' { TAB_LENGTH } else { 1 };
                Some(*state)
            }).take_while(|&x| x <= visual_col)
            .count();
        (line, col)
    }

    fn paint_message(&self) {
        if let Some(ref message) = self.message {
            let y = self.lines_height() + 1;
            self.screen.draw(0, y, message);
        }
    }

    fn paint_cursor<T>(&mut self, content: &T)
    where
        T: Editable + Selectable,
    {
        // FIXME: don't print the cursor if off screen, though we should in the future for long
        // lines
        if (content.line()) < self.line_offset
            || content.line() >= self.line_offset + self.lines_height()
            || content.col() >= self.lines_width(content.line_count())
            || content.sel().is_some()
        {
            self.screen.hide_cursor();
            return;
        }

        // in the case of a prompt, the cursor should be drawn in the message line
        let (x, y) = if self.is_prompt {
            (
                self.message.clone().unwrap().chars().count(),
                self.lines_height() + 1,
            )
        } else {
            let (a, b) = self.cursor_pos(content);
            (a, b)
        };
        self.screen.move_cursor(x, y);
        self.screen.show_cursor();
    }

    fn paint_status<T>(&self, content: &T)
    where
        T: Editable + Named + Undoable,
    {
        let line = content.line();
        let column = content.col();
        let line_count = content.line_count();
        let advance = ((line + 1) as f64 / line_count as f64 * 100.0).floor();

        let (screen_width, _) = terminal_size().unwrap();
        let empty_line = (0..screen_width).map(|_| ' ').collect::<String>();
        let y = self.lines_height();

        let style = Style {
            background: self.theme.settings.background.unwrap_or(Color::BLACK),
            foreground: self.theme.settings.foreground.unwrap_or(Color::WHITE),
            font_style: FontStyle::empty(),
        };

        self.screen.draw_with_style(0, y, style, &empty_line);
        let mut filename = content.name().clone();
        if !content.no_changes_since_save() {
            filename.push_str(" *");
        }
        self.screen.draw_with_style(0, y, style, &filename);

        let position_info = format!("{}% {}/{}: {}", advance, line + 1, line_count, column);
        let x = screen_width as usize - position_info.len();
        self.screen.draw_with_style(x, y, style, &position_info);
    }

    fn paint_lines<T>(&mut self, content: &T)
    where
        T: Editable + Selectable,
    {
        let line_offset = self.line_offset as usize;
        let lines_height = self.lines_height() as usize;
        let line_count = content.line_count();

        let line_start = self.line_number_width(line_count) as usize + 1;

        for (y, line) in content
            .lines()
            .skip(line_offset)
            .take(cmp::min(lines_height, line_count))
            .enumerate()
        {
            // paint line number and initialize display for this line
            let line_index = line_offset + y;
            let line_number_style = Style {
                background: self.theme.settings.gutter.unwrap_or(Color {
                    r: 40,
                    g: 40,
                    b: 40,
                    a: 255,
                }),
                foreground: self.theme.settings.gutter_foreground.unwrap_or(Color {
                    r: 146,
                    g: 131,
                    b: 116,
                    a: 255,
                }),
                font_style: FontStyle::empty(),
            };
            self.screen
                .draw_with_style(0, y, line_number_style, &format!("{}", 1 + line_index));

            let mut line_str = line
                .chars()
                .flat_map(|c| {
                    if c == '\t' {
                        iter::repeat(' ').take(TAB_LENGTH) // FIXME: selection should consider tabs
                    } else {
                        iter::repeat(c).take(1)
                    }
                }).collect::<String>();
            line_str.pop();

            let ranges: Vec<(Style, &str)> = self.highlighter.highlight(&line_str, self.syntax_set);
            self.screen.draw_ranges(line_start, y, ranges);

            // draw selection over
            if let Some((selbeg, selend)) = content.sel() {
                let selection_style = Style {
                    foreground: self
                        .theme
                        .settings
                        .selection_foreground
                        .unwrap_or(Color::WHITE),
                    background: self.theme.settings.selection.unwrap_or(Color::BLACK),
                    font_style: FontStyle::empty(),
                };
                let beg = content.line_index_to_char_index(line_index);
                let end = beg + line_str.len();

                if *selbeg <= beg && *selend >= end {
                    // line is fully inside the selection
                    if line_str.is_empty() {
                        self.screen
                            .draw_with_style(line_start, y, selection_style, " ");
                    } else {
                        self.screen
                            .draw_with_style(line_start, y, selection_style, &line_str);
                    }
                } else if *selbeg >= beg && *selbeg <= end && *selend <= end && *selend >= beg {
                    // selection is inside the line
                    self.screen.draw_with_style(
                        line_start + (*selbeg - beg),
                        y,
                        selection_style,
                        &line_str[(*selbeg - beg)..=(*selend - beg)],
                    );
                } else if *selbeg <= end && *selbeg >= beg {
                    // line contains the beginning of the selection
                    self.screen.draw_with_style(
                        line_start + (*selbeg - beg),
                        y,
                        selection_style,
                        &line_str[(*selbeg - beg)..],
                    );
                } else if *selend <= end && *selend >= beg {
                    // line contains the end of the selection
                    self.screen.draw_with_style(
                        line_start,
                        y,
                        selection_style,
                        &line_str[..=(*selend - beg)],
                    );
                }
            }
        }
    }

    fn cursor_pos<T: Editable>(&self, content: &T) -> (usize, usize) {
        // TODO: column offsetting for long lines
        let line = content.line();
        let first_line = self.line_offset;
        let y = line - first_line as usize;
        // we can't trust the actual column because tabs have variable length
        let visual_col = content.col();
        let column: usize = content
            .iter_line(line)
            .map(|x| if x == '\t' { TAB_LENGTH } else { 1 })
            .take(visual_col)
            .sum();
        (
            (self.line_number_width(content.line_count()) as usize + 1 + column),
            y,
        )
    }

    fn line_number_width(&self, line_count: usize) -> u16 {
        line_count.to_string().len() as u16
    }

    fn status_height(&self) -> u16 {
        2
    }

    pub fn lines_height(&self) -> usize {
        let (_, screen_height) = terminal_size().unwrap();
        let incompressible = self.status_height() as usize;
        cmp::max(screen_height as usize, incompressible) - incompressible
    }

    pub fn lines_width(&self, line_count: usize) -> usize {
        let (screen_width, _) = terminal_size().unwrap();
        let incompressible = self.line_number_width(line_count) as usize + 1;
        cmp::max(screen_width as usize, incompressible) - incompressible
    }
}
<commit_msg>Do a full syntax pass<commit_after>mod screen;

use self::screen::Screen;
use data::{Editable, Named, Selectable, Undoable};
use std::{cmp, iter};
use syntect::easy::HighlightLines;
use syntect::highlighting::Theme;
use syntect::highlighting::{Color, FontStyle, Style};
use syntect::parsing::{SyntaxReference, SyntaxSet};
use termion::terminal_size;
pub struct View<'a> {
    message: Option<String>,
    is_prompt: bool,
    line_offset: usize,
    screen: Screen,
    theme: &'a Theme,
    syntax_set: &'a SyntaxSet,
    syntax_ref: &'a SyntaxReference,
}

const TAB_LENGTH: usize = 4;

impl<'a> View<'a> {
    pub fn new(
        theme: &'a Theme,
        syntax_ref: &'a SyntaxReference,
        syntax_set: &'a SyntaxSet,
    ) -> Self {
        let default_style = Style {
            foreground: theme.settings.foreground.unwrap_or(Color::WHITE),
            background: theme.settings.background.unwrap_or(Color::BLACK),
            font_style: FontStyle::empty(),
        };
        View {
            message: None,
            is_prompt: false,
            line_offset: 0,
            screen: Screen::with_default_style(default_style),
            theme,
            syntax_set,
            syntax_ref,
        }
    }

    pub fn message(&mut self, message: &str) {
        self.is_prompt = false;
        self.message = Some(String::from(message));
    }

    pub fn prompt(&mut self, prompt: &str, message: &str) {
        self.is_prompt = true;
        let msg = String::from(prompt) + message;
        self.message = Some(msg);
    }

    pub fn quiet(&mut self) {
        self.is_prompt = false;
        self.message = None;
    }

    pub fn center_view(&mut self, line: usize) {
        self.line_offset = line
            .checked_sub(self.lines_height() as usize / 2)
            .unwrap_or(0);
    }

    pub fn adjust_view(&mut self, line: usize) {
        if line < self.line_offset {
            self.line_offset = line;
        } else if line + 1 >= self.line_offset + self.lines_height() {
            self.line_offset = 1 + line - self.lines_height();
        }
    }

    pub fn scroll_view<T: Editable>(&mut self, offset: isize, content: &T) {
        self.line_offset = cmp::min(
            cmp::max((self.line_offset as isize) + offset, 0),
            (content.line_count() as isize) - 1,
        ) as usize;
    }

    pub fn render<T>(&mut self, content: &T)
    where
        T: Editable + Named + Selectable + Undoable,
    {
        self.screen.clear();
        self.paint_lines(content);
        self.paint_status(content);
        self.paint_message();
        self.paint_cursor(content);
        self.screen.present();
    }

    pub fn translate_coordinates<T>(&self, content: &T, x: u16, y: u16) -> (usize, usize)
    where
        T: Editable,
    {
        let line = cmp::min(
            (y as isize + self.line_offset as isize - 1) as usize,
            content.line_count() - 1,
        );
        let visual_col = (cmp::max(
            0,
            x as isize - self.line_number_width(content.line_count()) as isize - 2,
        )) as usize;
        // find out if we clicked through a tab
        let col = content
            .iter_line(line)
            .scan(0, |state, x| {
                *state += if x == '\t' { TAB_LENGTH } else { 1 };
                Some(*state)
            }).take_while(|&x| x <= visual_col)
            .count();
        (line, col)
    }

    fn paint_message(&self) {
        if let Some(ref message) = self.message {
            let y = self.lines_height() + 1;
            self.screen.draw(0, y, message);
        }
    }

    fn paint_cursor<T>(&mut self, content: &T)
    where
        T: Editable + Selectable,
    {
        // FIXME: don't print the cursor if off screen, though we should in the future for long
        // lines
        if (content.line()) < self.line_offset
            || content.line() >= self.line_offset + self.lines_height()
            || content.col() >= self.lines_width(content.line_count())
            || content.sel().is_some()
        {
            self.screen.hide_cursor();
            return;
        }

        // in the case of a prompt, the cursor should be drawn in the message line
        let (x, y) = if self.is_prompt {
            (
                self.message.clone().unwrap().chars().count(),
                self.lines_height() + 1,
            )
        } else {
            let (a, b) = self.cursor_pos(content);
            (a, b)
        };
        self.screen.move_cursor(x, y);
        self.screen.show_cursor();
    }

    fn paint_status<T>(&self, content: &T)
    where
        T: Editable + Named + Undoable,
    {
        let line = content.line();
        let column = content.col();
        let line_count = content.line_count();
        let advance = ((line + 1) as f64 / line_count as f64 * 100.0).floor();

        let (screen_width, _) = terminal_size().unwrap();
        let empty_line = (0..screen_width).map(|_| ' ').collect::<String>();
        let y = self.lines_height();

        let style = Style {
            background: self.theme.settings.background.unwrap_or(Color::BLACK),
            foreground: self.theme.settings.foreground.unwrap_or(Color::WHITE),
            font_style: FontStyle::empty(),
        };

        self.screen.draw_with_style(0, y, style, &empty_line);
        let mut filename = content.name().clone();
        if !content.no_changes_since_save() {
            filename.push_str(" *");
        }
        self.screen.draw_with_style(0, y, style, &filename);

        let position_info = format!("{}% {}/{}: {}", advance, line + 1, line_count, column);
        let x = screen_width as usize - position_info.len();
        self.screen.draw_with_style(x, y, style, &position_info);
    }

    fn paint_lines<T>(&mut self, content: &T)
    where
        T: Editable + Selectable,
    {
        let line_offset = self.line_offset as usize;
        let lines_height = self.lines_height() as usize;
        let line_count = content.line_count();

        let line_start = self.line_number_width(line_count) as usize + 1;

        let mut highlighter = HighlightLines::new(self.syntax_ref, self.theme);

        for (i, line) in content.lines().enumerate() {
            let mut line_str = line
                .chars()
                .flat_map(|c| {
                    if c == '\t' {
                        iter::repeat(' ').take(TAB_LENGTH) // FIXME: selection should consider tabs
                    } else {
                        iter::repeat(c).take(1)
                    }
                }).collect::<String>();
            line_str.pop();

            if i < line_offset {
                continue;
            }
            let y = i - line_offset;
            if y >= cmp::min(lines_height, line_count) {
                break;
            }
            let ranges: Vec<(Style, &str)> = highlighter.highlight(&line_str, self.syntax_set);

            // paint line number and initialize display for this line
            let line_index = line_offset + y;
            let line_number_style = Style {
                background: self.theme.settings.gutter.unwrap_or(Color {
                    r: 40,
                    g: 40,
                    b: 40,
                    a: 255,
                }),
                foreground: self.theme.settings.gutter_foreground.unwrap_or(Color {
                    r: 146,
                    g: 131,
                    b: 116,
                    a: 255,
                }),
                font_style: FontStyle::empty(),
            };
            self.screen
                .draw_with_style(0, y, line_number_style, &format!("{}", 1 + line_index));

            self.screen.draw_ranges(line_start, y, ranges);

            // draw selection over
            if let Some((selbeg, selend)) = content.sel() {
                let selection_style = Style {
                    foreground: self
                        .theme
                        .settings
                        .selection_foreground
                        .unwrap_or(Color::WHITE),
                    background: self.theme.settings.selection.unwrap_or(Color::BLACK),
                    font_style: FontStyle::empty(),
                };
                let beg = content.line_index_to_char_index(line_index);
                let end = beg + line_str.len();

                if *selbeg <= beg && *selend >= end {
                    // line is fully inside the selection
                    if line_str.is_empty() {
                        self.screen
                            .draw_with_style(line_start, y, selection_style, " ");
                    } else {
                        self.screen
                            .draw_with_style(line_start, y, selection_style, &line_str);
                    }
                } else if *selbeg >= beg && *selbeg <= end && *selend <= end && *selend >= beg {
                    // selection is inside the line
                    self.screen.draw_with_style(
                        line_start + (*selbeg - beg),
                        y,
                        selection_style,
                        &line_str[(*selbeg - beg)..=(*selend - beg)],
                    );
                } else if *selbeg <= end && *selbeg >= beg {
                    // line contains the beginning of the selection
                    self.screen.draw_with_style(
                        line_start + (*selbeg - beg),
                        y,
                        selection_style,
                        &line_str[(*selbeg - beg)..],
                    );
                } else if *selend <= end && *selend >= beg {
                    // line contains the end of the selection
                    self.screen.draw_with_style(
                        line_start,
                        y,
                        selection_style,
                        &line_str[..=(*selend - beg)],
                    );
                }
            }
        }
    }

    fn cursor_pos<T: Editable>(&self, content: &T) -> (usize, usize) {
        // TODO: column offsetting for long lines
        let line = content.line();
        let first_line = self.line_offset;
        let y = line - first_line as usize;
        // we can't trust the actual column because tabs have variable length
        let visual_col = content.col();
        let column: usize = content
            .iter_line(line)
            .map(|x| if x == '\t' { TAB_LENGTH } else { 1 })
            .take(visual_col)
            .sum();
        (
            (self.line_number_width(content.line_count()) as usize + 1 + column),
            y,
        )
    }

    fn line_number_width(&self, line_count: usize) -> u16 {
        line_count.to_string().len() as u16
    }

    fn status_height(&self) -> u16 {
        2
    }

    pub fn lines_height(&self) -> usize {
        let (_, screen_height) = terminal_size().unwrap();
        let incompressible = self.status_height() as usize;
        cmp::max(screen_height as usize, incompressible) - incompressible
    }

    pub fn lines_width(&self, line_count: usize) -> usize {
        let (screen_width, _) = terminal_size().unwrap();
        let incompressible = self.line_number_width(line_count) as usize + 1;
        cmp::max(screen_width as usize, incompressible) - incompressible
    }
}
<|endoftext|>


<commit_before>use std::io;
use byteorder::{WriteBytesExt, NetworkEndian};

/// ts: i64 | id: u16
pub struct RemoveEntry {
    timestamp: i64,
    id: u16,
}

impl RemoveEntry {
    pub fn new(timestamp: i64, id: u16) -> Self {
        RemoveEntry { timestamp, id }
    }

    pub fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_i64::<NetworkEndian>(self.timestamp)?;
        target.write_u16::<NetworkEndian>(self.id)?;

        Ok(())
    }
}

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

    #[test]
    fn test_remove_entry() {
        let cmd = Message::RemoveEntry(RemoveEntry::new(12345, 23));
        let mut vec = Vec::<u8>::new();

        assert!(cmd.write_to(&mut vec).is_ok());

        assert_eq!(
        vec![
            /* cmd */ 4,
            /* ts  */ 0, 0, 0, 0, 0, 0, 48, 57,
            /* id  */ 0, 23,
        ],
        vec
        );
    }
}<commit_msg>Implement ReadFrom, WriteTo for RemoveEntry<commit_after>use std::io;
use byteorder::{ReadBytesExt, WriteBytesExt, NetworkEndian};
use super::super::{ReadFrom, WriteTo, ReadError};

/// ts: i64 | id: u16
pub struct RemoveEntry {
    timestamp: i64,
    id: u16,
}

impl RemoveEntry {
    pub fn new(timestamp: i64, id: u16) -> Self {
        RemoveEntry { timestamp, id }
    }

    pub fn timestamp(&self) -> i64 {
        self.timestamp
    }

    pub fn id(&self) -> u16 {
        self.id
    }
}

impl ReadFrom for RemoveEntry {
    fn read_from<R: io::Read>(source: &mut R) -> Result<Self, ReadError> {
        let timestamp = source.read_i64::<NetworkEndian>()?;
        let id = source.read_u16::<NetworkEndian>()?;

        Ok(RemoveEntry::new(timestamp, id))
    }
}

impl WriteTo for RemoveEntry {
    fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_i64::<NetworkEndian>(self.timestamp)?;
        target.write_u16::<NetworkEndian>(self.id)?;

        Ok(())
    }
}

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

    #[test]
    fn test_write() {
        let msg = Message::RemoveEntry(RemoveEntry::new(12345, 23));
        let mut vec = Vec::<u8>::new();

        assert!(msg.write_to(&mut vec).is_ok());

        assert_eq!(
            vec![
                /* cmd */ 4,
                /* ts  */ 0, 0, 0, 0, 0, 0, 48, 57,
                /* id  */ 0, 23,
            ],
            vec
        );
    }

    #[test]
    fn test_read() {
        let mut vec: Vec<u8> = vec![
            /* ts  */ 0, 0, 0, 0, 0, 0, 48, 57,
            /* id  */ 0, 23,
        ];

        let msg = RemoveEntry::read_from(&mut vec.as_mut_slice().as_ref()).unwrap();

        assert_eq!(12345, msg.timestamp());
        assert_eq!(23, msg.id());
    }
}<|endoftext|>