<commit_before>pub mod line;

use std::cmp::{max, min};
use std::mem;
use std::str;

use termbox_simple::Termbox;

pub use self::line::Line;
use config::Style;

pub struct MsgArea {
    lines       : Vec<Line>,

    // Rendering related
    width       : i32,
    height      : i32,

    /// Vertical scroll: An offset from the last visible line.
    /// E.g. when this is 0, `self.lines[self.lines.len() - 1]` is drawn at the
    /// bottom of screen.
    scroll      : i32,

    line_buf    : Line,
}

impl MsgArea {
    pub fn new(width : i32, height : i32) -> MsgArea {
        MsgArea {
            lines: Vec::new(),
            width: width,
            height: height,
            scroll: 0,
            line_buf: Line::new(),
        }
    }

    pub fn resize(&mut self, width : i32, height : i32) {
        self.width = width;
        self.height = height;
    }

    pub fn draw(&self, tb : &mut Termbox, pos_x : i32, pos_y : i32) {
        let mut row = pos_y + self.height - 1;

        // Draw lines in reverse order
        let mut line_idx = ((self.lines.len() as i32) - 1) - self.scroll;
        while line_idx >= 0 && row >= pos_y {
            let line = unsafe { self.lines.get_unchecked(line_idx as usize) };

            // Where to start rendering this line?
            let line_height = line.rendered_height(self.width);
            let line_row = row - line_height + 1;

            // Do we have enough space to render this line?
            if line_row >= pos_y {
                // Render it
                line.draw(tb, pos_x, line_row, self.width);
                row = line_row - 1;
                line_idx -= 1;
            } else {
                // Maybe we can still render some part of it
                let render_from = pos_y - line_row;
                line.draw_from(tb, pos_x, line_row, render_from, self.width);
                break;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Scrolling

impl MsgArea {
    pub fn scroll_up(&mut self) {
        if self.scroll < (self.lines.len() as i32) - 1 {
            self.scroll += 1;
        }
    }

    pub fn scroll_down(&mut self) {
        if self.scroll > 0 {
            self.scroll -= 1;
        }
    }

    pub fn scroll_top(&mut self) {
        if self.lines.is_empty() { return; }

        let mut height_left = self.height;
        let mut i = 0;
        loop {
            let rendered = self.lines[i].rendered_height(self.width);
            if height_left < rendered {
                break;
            }
            height_left -= rendered;
            i += 1;
        }

        self.scroll = (self.lines.len() - i) as i32;
    }

    pub fn scroll_bottom(&mut self) {
        self.scroll = 0;
    }

    pub fn page_up(&mut self) {
        self.scroll = max(0, min((self.lines.len() as i32) - 1, self.scroll + 10));
    }

    pub fn page_down(&mut self) {
        self.scroll = max(0, self.scroll - 10);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Adding text

impl MsgArea {
    pub fn set_style(&mut self, style: Style) {
        self.line_buf.set_style(style);
    }

    pub fn add_text(&mut self, str : &str) {
        self.line_buf.add_text(str);
    }

    pub fn add_char(&mut self, char : char) {
        self.line_buf.add_char(char);
    }

    pub fn flush_line(&mut self) -> usize {
        self.lines.push(mem::replace(&mut self.line_buf, Line::new()));
        if self.scroll != 0 {
            self.scroll += 1;
        }
        self.lines.len() - 1
    }

    #[inline]
    pub fn modify_line<F>(&mut self, idx : usize, f : F) where F : Fn(&mut Line) {
        f(&mut self.lines[idx]);
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn newline_scrolling() {
        let mut msg_area = MsgArea::new(100, 1);
        // Adding a new line when scroll is 0 should not change it
        assert_eq!(msg_area.scroll, 0);
        msg_area.add_text("line1");
        msg_area.flush_line();
        assert_eq!(msg_area.scroll, 0);

        msg_area.add_text("line2");
        msg_area.flush_line();
        assert_eq!(msg_area.scroll, 0);

        msg_area.scroll_up();
        assert_eq!(msg_area.scroll, 1);
        msg_area.add_text("line3");
        msg_area.flush_line();
        assert_eq!(msg_area.scroll, 2);
    }
}
<commit_msg>Fix out of bounds err introduces in prev commit<commit_after>pub mod line;

use std::cmp::{max, min};
use std::mem;
use std::str;

use termbox_simple::Termbox;

pub use self::line::Line;
use config::Style;

pub struct MsgArea {
    lines       : Vec<Line>,

    // Rendering related
    width       : i32,
    height      : i32,

    /// Vertical scroll: An offset from the last visible line.
    /// E.g. when this is 0, `self.lines[self.lines.len() - 1]` is drawn at the
    /// bottom of screen.
    scroll      : i32,

    line_buf    : Line,
}

impl MsgArea {
    pub fn new(width : i32, height : i32) -> MsgArea {
        MsgArea {
            lines: Vec::new(),
            width: width,
            height: height,
            scroll: 0,
            line_buf: Line::new(),
        }
    }

    pub fn resize(&mut self, width : i32, height : i32) {
        self.width = width;
        self.height = height;
    }

    pub fn draw(&self, tb : &mut Termbox, pos_x : i32, pos_y : i32) {
        let mut row = pos_y + self.height - 1;

        // Draw lines in reverse order
        let mut line_idx = ((self.lines.len() as i32) - 1) - self.scroll;
        while line_idx >= 0 && row >= pos_y {
            let line = unsafe { self.lines.get_unchecked(line_idx as usize) };

            // Where to start rendering this line?
            let line_height = line.rendered_height(self.width);
            let line_row = row - line_height + 1;

            // Do we have enough space to render this line?
            if line_row >= pos_y {
                // Render it
                line.draw(tb, pos_x, line_row, self.width);
                row = line_row - 1;
                line_idx -= 1;
            } else {
                // Maybe we can still render some part of it
                let render_from = pos_y - line_row;
                line.draw_from(tb, pos_x, line_row, render_from, self.width);
                break;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Scrolling

impl MsgArea {
    pub fn scroll_up(&mut self) {
        if self.scroll < (self.lines.len() as i32) - 1 {
            self.scroll += 1;
        }
    }

    pub fn scroll_down(&mut self) {
        if self.scroll > 0 {
            self.scroll -= 1;
        }
    }

    pub fn scroll_top(&mut self) {
        if self.lines.is_empty() { return; }

        let mut height_left = self.height;
        let mut i = 0;
        while i < self.lines.len() {
            let rendered = self.lines[i].rendered_height(self.width);
            if height_left < rendered {
                break;
            }
            height_left -= rendered;
            i += 1;
        }

        self.scroll = (self.lines.len() - i) as i32;
    }

    pub fn scroll_bottom(&mut self) {
        self.scroll = 0;
    }

    pub fn page_up(&mut self) {
        self.scroll = max(0, min((self.lines.len() as i32) - 1, self.scroll + 10));
    }

    pub fn page_down(&mut self) {
        self.scroll = max(0, self.scroll - 10);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Adding text

impl MsgArea {
    pub fn set_style(&mut self, style: Style) {
        self.line_buf.set_style(style);
    }

    pub fn add_text(&mut self, str : &str) {
        self.line_buf.add_text(str);
    }

    pub fn add_char(&mut self, char : char) {
        self.line_buf.add_char(char);
    }

    pub fn flush_line(&mut self) -> usize {
        self.lines.push(mem::replace(&mut self.line_buf, Line::new()));
        if self.scroll != 0 {
            self.scroll += 1;
        }
        self.lines.len() - 1
    }

    #[inline]
    pub fn modify_line<F>(&mut self, idx : usize, f : F) where F : Fn(&mut Line) {
        f(&mut self.lines[idx]);
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn newline_scrolling() {
        let mut msg_area = MsgArea::new(100, 1);
        // Adding a new line when scroll is 0 should not change it
        assert_eq!(msg_area.scroll, 0);
        msg_area.add_text("line1");
        msg_area.flush_line();
        assert_eq!(msg_area.scroll, 0);

        msg_area.add_text("line2");
        msg_area.flush_line();
        assert_eq!(msg_area.scroll, 0);

        msg_area.scroll_up();
        assert_eq!(msg_area.scroll, 1);
        msg_area.add_text("line3");
        msg_area.flush_line();
        assert_eq!(msg_area.scroll, 2);
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use crate::message_dyn::MessageDyn;
use crate::reflect::reflect_eq::ReflectEqMode;
use crate::reflect::MessageDescriptor;
use crate::reflect::ProtobufValue;
use crate::Message;

/// Trait implemented for all the generated messages, except when lite runtime is enabled.
///
/// When lite runtime is enabled, only `MessageLite` is implemented.
///
/// * Generated messages are generated from `.proto` files
/// * Dynamic messages can be created without code generation using only parsed proto files
///   (see [FileDescriptor::new_dynamic](crate::reflect::FileDescriptor::new_dynamic)).
///
/// Also, generated messages implement `Default + PartialEq`
///
/// This trait is sized, there's accompanying [`MessageDyn`](crate::MessageDyn) trait
/// which is implemented for all messages which can be used in functions
/// without making message a function type parameter.
///
/// ## `Display`
///
/// [`Display`](fmt::Display) implementation for messages does protobuf text format.
/// See [`text_format`](crate::text_format) for more details.
pub trait MessageFull: Message + ProtobufValue + fmt::Debug + fmt::Display {
    /// Get message descriptor for message type.
    ///
    /// ```
    /// # use protobuf::MessageFull;
    /// # fn foo<MyMessage: MessageFull>() {
    /// let descriptor = MyMessage::descriptor();
    /// assert_eq!("MyMessage", descriptor.name());
    /// # }
    /// ```
    // TODO: rename to `descriptor`.
    fn descriptor() -> MessageDescriptor;

    /// Reflective equality.
    ///
    /// # See also
    ///
    /// [`dyn Message::reflect_eq_dyn()`], `dyn` version of this function.
    fn reflect_eq(&self, other: &Self, mode: &ReflectEqMode) -> bool {
        <dyn MessageDyn>::reflect_eq_dyn(self, other, mode)
    }
}
<commit_msg>Outdated todo<commit_after>use std::fmt;

use crate::message_dyn::MessageDyn;
use crate::reflect::reflect_eq::ReflectEqMode;
use crate::reflect::MessageDescriptor;
use crate::reflect::ProtobufValue;
use crate::Message;

/// Trait implemented for all the generated messages, except when lite runtime is enabled.
///
/// When lite runtime is enabled, only `MessageLite` is implemented.
///
/// * Generated messages are generated from `.proto` files
/// * Dynamic messages can be created without code generation using only parsed proto files
///   (see [FileDescriptor::new_dynamic](crate::reflect::FileDescriptor::new_dynamic)).
///
/// Also, generated messages implement `Default + PartialEq`
///
/// This trait is sized, there's accompanying [`MessageDyn`](crate::MessageDyn) trait
/// which is implemented for all messages which can be used in functions
/// without making message a function type parameter.
///
/// ## `Display`
///
/// [`Display`](fmt::Display) implementation for messages does protobuf text format.
/// See [`text_format`](crate::text_format) for more details.
pub trait MessageFull: Message + ProtobufValue + fmt::Debug + fmt::Display {
    /// Get message descriptor for message type.
    ///
    /// ```
    /// # use protobuf::MessageFull;
    /// # fn foo<MyMessage: MessageFull>() {
    /// let descriptor = MyMessage::descriptor();
    /// assert_eq!("MyMessage", descriptor.name());
    /// # }
    /// ```
    fn descriptor() -> MessageDescriptor;

    /// Reflective equality.
    ///
    /// # See also
    ///
    /// [`dyn Message::reflect_eq_dyn()`], `dyn` version of this function.
    fn reflect_eq(&self, other: &Self, mode: &ReflectEqMode) -> bool {
        <dyn MessageDyn>::reflect_eq_dyn(self, other, mode)
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 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 std::mem;

use raftstore::store::{keys, util, Msg};
use rocksdb::DB;
use util::transport::{RetryableSendCh, Sender};

use super::super::metrics::*;
use super::super::{Coprocessor, KeyEntry, ObserverContext, SplitCheckObserver, SplitChecker};
use super::Host;

pub struct Checker {
    max_keys_count: u64,
    split_threshold: u64,
    current_count: u64,
    split_keys: Vec<Vec<u8>>,
    batch_split_limit: u64,
}

impl Checker {
    pub fn new(max_keys_count: u64, split_threshold: u64, batch_split_limit: u64) -> Checker {
        Checker {
            max_keys_count,
            split_threshold,
            current_count: 0,
            split_keys: Vec::with_capacity(1),
            batch_split_limit,
        }
    }
}

impl SplitChecker for Checker {
    fn on_kv(&mut self, _: &mut ObserverContext, key: &KeyEntry) -> bool {
        if !key.is_commit_version() {
            return false;
        }
        self.current_count += 1;

        let mut over_limit = self.split_keys.len() as u64 >= self.batch_split_limit;
        if self.current_count > self.split_threshold && !over_limit {
            self.split_keys.push(keys::origin_key(key.key()).to_vec());
            // if for previous on_kv() self.current_count == self.split_threshold,
            // the split key would be pushed this time, but the entry for this time should not be ignored.
            self.current_count = 1;
            over_limit = self.split_keys.len() as u64 >= self.batch_split_limit;
        }

        // For a large region, scan over the range maybe cost too much time,
        // so limit the number of produced split_key for one batch.
        // Also need to scan over self.max_keys_count for last part.
        over_limit && self.current_count + self.split_threshold >= self.max_keys_count
    }

    fn split_keys(&mut self) -> Vec<Vec<u8>> {
        // make sure not to split when less than max_keys_count for last part
        if self.current_count + self.split_threshold < self.max_keys_count {
            self.split_keys.pop();
        }
        if !self.split_keys.is_empty() {
            mem::replace(&mut self.split_keys, vec![])
        } else {
            vec![]
        }
    }
}

pub struct KeysCheckObserver<C> {
    region_max_keys: u64,
    split_keys: u64,
    batch_split_limit: u64,
    ch: RetryableSendCh<Msg, C>,
}

impl<C: Sender<Msg>> KeysCheckObserver<C> {
    pub fn new(
        region_max_keys: u64,
        split_keys: u64,
        batch_split_limit: u64,
        ch: RetryableSendCh<Msg, C>,
    ) -> KeysCheckObserver<C> {
        KeysCheckObserver {
            region_max_keys,
            split_keys,
            batch_split_limit,
            ch,
        }
    }
}

impl<C> Coprocessor for KeysCheckObserver<C> {}

impl<C: Sender<Msg> + Send> SplitCheckObserver for KeysCheckObserver<C> {
    fn add_checker(&self, ctx: &mut ObserverContext, host: &mut Host, engine: &DB) {
        let region = ctx.region();
        let region_id = region.get_id();
        let region_keys = match util::get_region_approximate_keys(engine, region) {
            Ok(keys) => keys,
            Err(e) => {
                warn!(
                    "[region {}] failed to get approximate keys: {}",
                    region_id, e
                );
                // Need to check keys.
                host.add_checker(Box::new(Checker::new(
                    self.region_max_keys,
                    self.split_keys,
                    self.batch_split_limit,
                )));
                return;
            }
        };

        let res = Msg::RegionApproximateKeys {
            region_id,
            keys: region_keys,
        };
        if let Err(e) = self.ch.try_send(res) {
            warn!(
                "[region {}] failed to send approximate region keys: {}",
                region_id, e
            );
        }

        REGION_KEYS_HISTOGRAM.observe(region_keys as f64);
        if region_keys >= self.region_max_keys {
            info!(
                "[region {}] approximate keys {} >= {}, need to do split check",
                region.get_id(),
                region_keys,
                self.region_max_keys
            );
            // Need to check keys.
            host.add_checker(Box::new(Checker::new(
                self.region_max_keys,
                self.split_keys,
                self.batch_split_limit,
            )));
        } else {
            // Does not need to check keys.
            debug!(
                "[region {}] approximate keys {} < {}, does not need to do split check",
                region.get_id(),
                region_keys,
                self.region_max_keys
            );
        }
    }
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc;
    use std::sync::Arc;

    use kvproto::metapb::Peer;
    use kvproto::metapb::Region;
    use kvproto::pdpb::CheckPolicy;
    use rocksdb::Writable;
    use rocksdb::{ColumnFamilyOptions, DBOptions};
    use tempdir::TempDir;

    use raftstore::store::{keys, Msg, SplitCheckRunner, SplitCheckTask};
    use storage::mvcc::{Write, WriteType};
    use storage::{Key, ALL_CFS, CF_DEFAULT, CF_WRITE};
    use util::properties::RangePropertiesCollectorFactory;
    use util::rocksdb::{new_engine_opt, CFOptions};
    use util::transport::RetryableSendCh;
    use util::worker::Runnable;

    use raftstore::coprocessor::{Config, CoprocessorHost};

    use super::super::size::tests::must_split_at;

    #[test]
    fn test_split_check() {
        let path = TempDir::new("test-raftstore").unwrap();
        let path_str = path.path().to_str().unwrap();
        let db_opts = DBOptions::new();
        let mut cf_opts = ColumnFamilyOptions::new();
        let f = Box::new(RangePropertiesCollectorFactory::default());
        cf_opts.add_table_properties_collector_factory("tikv.range-properties-collector", f);

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| CFOptions::new(cf, cf_opts.clone()))
            .collect();
        let engine = Arc::new(new_engine_opt(path_str, db_opts, cfs_opts).unwrap());

        let mut region = Region::new();
        region.set_id(1);
        region.set_start_key(vec![]);
        region.set_end_key(vec![]);
        region.mut_peers().push(Peer::new());
        region.mut_region_epoch().set_version(2);
        region.mut_region_epoch().set_conf_ver(5);

        let (tx, rx) = mpsc::sync_channel(100);
        let ch = RetryableSendCh::new(tx, "test-split");
        let mut cfg = Config::default();
        cfg.region_max_keys = 100;
        cfg.region_split_keys = 80;
        cfg.batch_split_limit = 1;

        let mut runnable = SplitCheckRunner::new(
            Arc::clone(&engine),
            ch.clone(),
            Arc::new(CoprocessorHost::new(cfg, ch.clone())),
        );

        // so split key will be z0080
        for i in 0..90 {
            let key = keys::data_key(
                Key::from_raw(format!("{:04}", i).as_bytes())
                    .append_ts(2)
                    .as_encoded(),
            );
            let write_value = Write::new(WriteType::Put, 0, None).to_bytes();
            let write_cf = engine.cf_handle(CF_WRITE).unwrap();
            engine.put_cf(write_cf, &key, &write_value).unwrap();
            engine.flush_cf(write_cf, true).unwrap();
            let default_cf = engine.cf_handle(CF_DEFAULT).unwrap();
            engine.put_cf(default_cf, &key, &[0; 1024]).unwrap();
            engine.flush_cf(default_cf, true).unwrap();
        }

        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        // keys has not reached the max_keys 100 yet.
        match rx.try_recv() {
            Ok(Msg::RegionApproximateSize { region_id, .. })
            | Ok(Msg::RegionApproximateKeys { region_id, .. }) => {
                assert_eq!(region_id, region.get_id());
            }
            others => panic!("expect recv empty, but got {:?}", others),
        }

        for i in 90..160 {
            let key = keys::data_key(
                Key::from_raw(format!("{:04}", i).as_bytes())
                    .append_ts(2)
                    .as_encoded(),
            );

            let write_value =
                Write::new(WriteType::Put, 0, Some(b"shortvalue".to_vec())).to_bytes();
            let write_cf = engine.cf_handle(CF_WRITE).unwrap();
            engine.put_cf(write_cf, &key, &write_value).unwrap();
            engine.flush_cf(write_cf, true).unwrap();
            let default_cf = engine.cf_handle(CF_DEFAULT).unwrap();
            engine.put_cf(default_cf, &key, &[0; 1024]).unwrap();
            engine.flush_cf(default_cf, true).unwrap();
        }
        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![Key::from_raw(b"0080").append_ts(2).into_encoded()],
        );

        drop(rx);
        // It should be safe even the result can't be sent back.
        runnable.run(SplitCheckTask::new(region, true, CheckPolicy::SCAN));
    }

    #[test]
    fn test_batch_split_check() {
        let path = TempDir::new("test-raftstore").unwrap();
        let path_str = path.path().to_str().unwrap();
        let db_opts = DBOptions::new();
        let mut cf_opts = ColumnFamilyOptions::new();
        let f = Box::new(RangePropertiesCollectorFactory::default());
        cf_opts.add_table_properties_collector_factory("tikv.range-properties-collector", f);

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| CFOptions::new(cf, cf_opts.clone()))
            .collect();
        let engine = Arc::new(new_engine_opt(path_str, db_opts, cfs_opts).unwrap());

        let mut region = Region::new();
        region.set_id(1);
        region.set_start_key(vec![]);
        region.set_end_key(vec![]);
        region.mut_peers().push(Peer::new());
        region.mut_region_epoch().set_version(2);
        region.mut_region_epoch().set_conf_ver(5);

        let (tx, rx) = mpsc::sync_channel(100);
        let ch = RetryableSendCh::new(tx, "test-batch-split");
        let mut cfg = Config::default();
        cfg.region_max_keys = 100;
        cfg.region_split_keys = 80;
        cfg.batch_split_limit = 5;

        let mut runnable = SplitCheckRunner::new(
            Arc::clone(&engine),
            ch.clone(),
            Arc::new(CoprocessorHost::new(cfg, ch.clone())),
        );

        // so split key will be z0080
        for i in 0..90 {
            let key = keys::data_key(
                Key::from_raw(format!("{:04}", i).as_bytes())
                    .append_ts(2)
                    .as_encoded(),
            );
            let write_value = Write::new(WriteType::Put, 0, None).to_bytes();
            let write_cf = engine.cf_handle(CF_WRITE).unwrap();
            engine.put_cf(write_cf, &key, &write_value).unwrap();
            engine.flush_cf(write_cf, true).unwrap();
            let default_cf = engine.cf_handle(CF_DEFAULT).unwrap();
            engine.put_cf(default_cf, &key, &[0; 1024]).unwrap();
            engine.flush_cf(default_cf, true).unwrap();
        }

        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        // keys has not reached the max_keys 100 yet.
        match rx.try_recv() {
            Ok(Msg::RegionApproximateSize { region_id, .. })
            | Ok(Msg::RegionApproximateKeys { region_id, .. }) => {
                assert_eq!(region_id, region.get_id());
            }
            others => panic!("expect recv empty, but got {:?}", others),
        }

        for i in 90..160 {
            let key = keys::data_key(
                Key::from_raw(format!("{:04}", i).as_bytes())
                    .append_ts(2)
                    .as_encoded(),
            );

            let write_value =
                Write::new(WriteType::Put, 0, Some(b"shortvalue".to_vec())).to_bytes();
            let write_cf = engine.cf_handle(CF_WRITE).unwrap();
            engine.put_cf(write_cf, &key, &write_value).unwrap();
            engine.flush_cf(write_cf, true).unwrap();
            let default_cf = engine.cf_handle(CF_DEFAULT).unwrap();
            engine.put_cf(default_cf, &key, &[0; 1024]).unwrap();
            engine.flush_cf(default_cf, true).unwrap();
        }

        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![Key::from_raw(b"0080").append_ts(2).into_encoded()],
        );

        for i in 160..300 {
            let key = keys::data_key(
                Key::from_raw(format!("{:04}", i).as_bytes())
                    .append_ts(2)
                    .as_encoded(),
            );

            let write_value = Write::new(WriteType::Put, 0, None).to_bytes();
            let write_cf = engine.cf_handle(CF_WRITE).unwrap();
            engine.put_cf(write_cf, &key, &write_value).unwrap();
            engine.flush_cf(write_cf, true).unwrap();
            let default_cf = engine.cf_handle(CF_DEFAULT).unwrap();
            engine.put_cf(default_cf, &key, &[0; 1024]).unwrap();
            engine.flush_cf(default_cf, true).unwrap();
        }

        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![
                Key::from_raw(b"0080").append_ts(2).into_encoded(),
                Key::from_raw(b"0160").append_ts(2).into_encoded(),
                Key::from_raw(b"0240").append_ts(2).into_encoded(),
            ],
        );

        for i in 300..500 {
            let key = keys::data_key(
                Key::from_raw(format!("{:04}", i).as_bytes())
                    .append_ts(2)
                    .as_encoded(),
            );

            let write_value = Write::new(WriteType::Put, 0, None).to_bytes();
            let write_cf = engine.cf_handle(CF_WRITE).unwrap();
            engine.put_cf(write_cf, &key, &write_value).unwrap();
            engine.flush_cf(write_cf, true).unwrap();
            let default_cf = engine.cf_handle(CF_DEFAULT).unwrap();
            engine.put_cf(default_cf, &key, &[0; 1024]).unwrap();
            engine.flush_cf(default_cf, true).unwrap();
        }

        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![
                Key::from_raw(b"0080").append_ts(2).into_encoded(),
                Key::from_raw(b"0160").append_ts(2).into_encoded(),
                Key::from_raw(b"0240").append_ts(2).into_encoded(),
                Key::from_raw(b"0320").append_ts(2).into_encoded(),
                Key::from_raw(b"0400").append_ts(2).into_encoded(),
            ],
        );

        drop(rx);
        // It should be safe even the result can't be sent back.
        runnable.run(SplitCheckTask::new(region, true, CheckPolicy::SCAN));
    }
}
<commit_msg>tests/split_observer: fix defective kv flush (#3505)<commit_after>// Copyright 2018 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 std::mem;

use raftstore::store::{keys, util, Msg};
use rocksdb::DB;
use util::transport::{RetryableSendCh, Sender};

use super::super::metrics::*;
use super::super::{Coprocessor, KeyEntry, ObserverContext, SplitCheckObserver, SplitChecker};
use super::Host;

pub struct Checker {
    max_keys_count: u64,
    split_threshold: u64,
    current_count: u64,
    split_keys: Vec<Vec<u8>>,
    batch_split_limit: u64,
}

impl Checker {
    pub fn new(max_keys_count: u64, split_threshold: u64, batch_split_limit: u64) -> Checker {
        Checker {
            max_keys_count,
            split_threshold,
            current_count: 0,
            split_keys: Vec::with_capacity(1),
            batch_split_limit,
        }
    }
}

impl SplitChecker for Checker {
    fn on_kv(&mut self, _: &mut ObserverContext, key: &KeyEntry) -> bool {
        if !key.is_commit_version() {
            return false;
        }
        self.current_count += 1;

        let mut over_limit = self.split_keys.len() as u64 >= self.batch_split_limit;
        if self.current_count > self.split_threshold && !over_limit {
            self.split_keys.push(keys::origin_key(key.key()).to_vec());
            // if for previous on_kv() self.current_count == self.split_threshold,
            // the split key would be pushed this time, but the entry for this time should not be ignored.
            self.current_count = 1;
            over_limit = self.split_keys.len() as u64 >= self.batch_split_limit;
        }

        // For a large region, scan over the range maybe cost too much time,
        // so limit the number of produced split_key for one batch.
        // Also need to scan over self.max_keys_count for last part.
        over_limit && self.current_count + self.split_threshold >= self.max_keys_count
    }

    fn split_keys(&mut self) -> Vec<Vec<u8>> {
        // make sure not to split when less than max_keys_count for last part
        if self.current_count + self.split_threshold < self.max_keys_count {
            self.split_keys.pop();
        }
        if !self.split_keys.is_empty() {
            mem::replace(&mut self.split_keys, vec![])
        } else {
            vec![]
        }
    }
}

pub struct KeysCheckObserver<C> {
    region_max_keys: u64,
    split_keys: u64,
    batch_split_limit: u64,
    ch: RetryableSendCh<Msg, C>,
}

impl<C: Sender<Msg>> KeysCheckObserver<C> {
    pub fn new(
        region_max_keys: u64,
        split_keys: u64,
        batch_split_limit: u64,
        ch: RetryableSendCh<Msg, C>,
    ) -> KeysCheckObserver<C> {
        KeysCheckObserver {
            region_max_keys,
            split_keys,
            batch_split_limit,
            ch,
        }
    }
}

impl<C> Coprocessor for KeysCheckObserver<C> {}

impl<C: Sender<Msg> + Send> SplitCheckObserver for KeysCheckObserver<C> {
    fn add_checker(&self, ctx: &mut ObserverContext, host: &mut Host, engine: &DB) {
        let region = ctx.region();
        let region_id = region.get_id();
        let region_keys = match util::get_region_approximate_keys(engine, region) {
            Ok(keys) => keys,
            Err(e) => {
                warn!(
                    "[region {}] failed to get approximate keys: {}",
                    region_id, e
                );
                // Need to check keys.
                host.add_checker(Box::new(Checker::new(
                    self.region_max_keys,
                    self.split_keys,
                    self.batch_split_limit,
                )));
                return;
            }
        };

        let res = Msg::RegionApproximateKeys {
            region_id,
            keys: region_keys,
        };
        if let Err(e) = self.ch.try_send(res) {
            warn!(
                "[region {}] failed to send approximate region keys: {}",
                region_id, e
            );
        }

        REGION_KEYS_HISTOGRAM.observe(region_keys as f64);
        if region_keys >= self.region_max_keys {
            info!(
                "[region {}] approximate keys {} >= {}, need to do split check",
                region.get_id(),
                region_keys,
                self.region_max_keys
            );
            // Need to check keys.
            host.add_checker(Box::new(Checker::new(
                self.region_max_keys,
                self.split_keys,
                self.batch_split_limit,
            )));
        } else {
            // Does not need to check keys.
            debug!(
                "[region {}] approximate keys {} < {}, does not need to do split check",
                region.get_id(),
                region_keys,
                self.region_max_keys
            );
        }
    }
}

#[cfg(test)]
mod tests {
    use std::cmp;
    use std::sync::{mpsc, Arc};

    use kvproto::metapb::{Peer, Region};
    use kvproto::pdpb::CheckPolicy;
    use rocksdb::{ColumnFamilyOptions, DBOptions, Writable, DB};
    use tempdir::TempDir;

    use raftstore::store::{keys, Msg, SplitCheckRunner, SplitCheckTask};
    use storage::mvcc::{Write, WriteType};
    use storage::{Key, ALL_CFS, CF_DEFAULT, CF_WRITE};
    use util::properties::RangePropertiesCollectorFactory;
    use util::rocksdb::{new_engine_opt, CFOptions};
    use util::transport::RetryableSendCh;
    use util::worker::Runnable;

    use raftstore::coprocessor::{Config, CoprocessorHost};

    use super::super::size::tests::must_split_at;

    fn put_data(engine: &DB, mut start_idx: u64, end_idx: u64, fill_short_value: bool) {
        let write_cf = engine.cf_handle(CF_WRITE).unwrap();
        let default_cf = engine.cf_handle(CF_DEFAULT).unwrap();
        let write_value = if fill_short_value {
            Write::new(WriteType::Put, 0, Some(b"shortvalue".to_vec()))
        } else {
            Write::new(WriteType::Put, 0, None)
        }.to_bytes();

        while start_idx < end_idx {
            let batch_idx = cmp::min(start_idx + 20, end_idx);
            for i in start_idx..batch_idx {
                let key = keys::data_key(
                    Key::from_raw(format!("{:04}", i).as_bytes())
                        .append_ts(2)
                        .as_encoded(),
                );
                engine.put_cf(write_cf, &key, &write_value).unwrap();
                engine.put_cf(default_cf, &key, &[0; 1024]).unwrap();
            }
            // Flush to generate SST files, so that properties can be utilized.
            engine.flush_cf(write_cf, true).unwrap();
            engine.flush_cf(default_cf, true).unwrap();
            start_idx = batch_idx;
        }
    }

    #[test]
    fn test_split_check() {
        let path = TempDir::new("test-raftstore").unwrap();
        let path_str = path.path().to_str().unwrap();
        let db_opts = DBOptions::new();
        let mut cf_opts = ColumnFamilyOptions::new();
        let f = Box::new(RangePropertiesCollectorFactory::default());
        cf_opts.add_table_properties_collector_factory("tikv.range-properties-collector", f);

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| CFOptions::new(cf, cf_opts.clone()))
            .collect();
        let engine = Arc::new(new_engine_opt(path_str, db_opts, cfs_opts).unwrap());

        let mut region = Region::new();
        region.set_id(1);
        region.set_start_key(vec![]);
        region.set_end_key(vec![]);
        region.mut_peers().push(Peer::new());
        region.mut_region_epoch().set_version(2);
        region.mut_region_epoch().set_conf_ver(5);

        let (tx, rx) = mpsc::sync_channel(100);
        let ch = RetryableSendCh::new(tx, "test-split");
        let mut cfg = Config::default();
        cfg.region_max_keys = 100;
        cfg.region_split_keys = 80;
        cfg.batch_split_limit = 1;

        let mut runnable = SplitCheckRunner::new(
            Arc::clone(&engine),
            ch.clone(),
            Arc::new(CoprocessorHost::new(cfg, ch.clone())),
        );

        // so split key will be z0080
        put_data(&engine, 0, 90, false);

        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        // keys has not reached the max_keys 100 yet.
        match rx.try_recv() {
            Ok(Msg::RegionApproximateSize { region_id, .. })
            | Ok(Msg::RegionApproximateKeys { region_id, .. }) => {
                assert_eq!(region_id, region.get_id());
            }
            others => panic!("expect recv empty, but got {:?}", others),
        }

        put_data(&engine, 90, 160, true);
        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![Key::from_raw(b"0080").append_ts(2).into_encoded()],
        );

        drop(rx);
        // It should be safe even the result can't be sent back.
        runnable.run(SplitCheckTask::new(region, true, CheckPolicy::SCAN));
    }

    #[test]
    fn test_batch_split_check() {
        let path = TempDir::new("test-raftstore").unwrap();
        let path_str = path.path().to_str().unwrap();
        let db_opts = DBOptions::new();
        let mut cf_opts = ColumnFamilyOptions::new();
        let f = Box::new(RangePropertiesCollectorFactory::default());
        cf_opts.add_table_properties_collector_factory("tikv.range-properties-collector", f);

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| CFOptions::new(cf, cf_opts.clone()))
            .collect();
        let engine = Arc::new(new_engine_opt(path_str, db_opts, cfs_opts).unwrap());

        let mut region = Region::new();
        region.set_id(1);
        region.set_start_key(vec![]);
        region.set_end_key(vec![]);
        region.mut_peers().push(Peer::new());
        region.mut_region_epoch().set_version(2);
        region.mut_region_epoch().set_conf_ver(5);

        let (tx, rx) = mpsc::sync_channel(100);
        let ch = RetryableSendCh::new(tx, "test-batch-split");
        let mut cfg = Config::default();
        cfg.region_max_keys = 100;
        cfg.region_split_keys = 80;
        cfg.batch_split_limit = 5;

        let mut runnable = SplitCheckRunner::new(
            Arc::clone(&engine),
            ch.clone(),
            Arc::new(CoprocessorHost::new(cfg, ch.clone())),
        );

        // so split key will be z0080
        put_data(&engine, 0, 90, false);
        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        // keys has not reached the max_keys 100 yet.
        match rx.try_recv() {
            Ok(Msg::RegionApproximateSize { region_id, .. })
            | Ok(Msg::RegionApproximateKeys { region_id, .. }) => {
                assert_eq!(region_id, region.get_id());
            }
            others => panic!("expect recv empty, but got {:?}", others),
        }

        put_data(&engine, 90, 160, true);
        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![Key::from_raw(b"0080").append_ts(2).into_encoded()],
        );

        put_data(&engine, 160, 300, false);
        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![
                Key::from_raw(b"0080").append_ts(2).into_encoded(),
                Key::from_raw(b"0160").append_ts(2).into_encoded(),
                Key::from_raw(b"0240").append_ts(2).into_encoded(),
            ],
        );

        put_data(&engine, 300, 500, false);
        runnable.run(SplitCheckTask::new(region.clone(), true, CheckPolicy::SCAN));
        must_split_at(
            &rx,
            &region,
            vec![
                Key::from_raw(b"0080").append_ts(2).into_encoded(),
                Key::from_raw(b"0160").append_ts(2).into_encoded(),
                Key::from_raw(b"0240").append_ts(2).into_encoded(),
                Key::from_raw(b"0320").append_ts(2).into_encoded(),
                Key::from_raw(b"0400").append_ts(2).into_encoded(),
            ],
        );

        drop(rx);
        // It should be safe even the result can't be sent back.
        runnable.run(SplitCheckTask::new(region, true, CheckPolicy::SCAN));
    }
}
<|endoftext|>


<commit_before>use std::f64;

/// Used to build a constraint, specifying additional details.
#[derive(Default)]
pub struct ConstraintBuilder {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

/// Used to build a constraint, specifying additional details.
impl ConstraintBuilder {
    /// Creates a new `ConstraintBuilder` with default values.
    pub fn new() -> Self {
        ConstraintBuilder::default()
    }

    /// Inserts a new width.
    pub fn width(mut self, width: impl Into<f64>) -> Self {
        self.width = width.into();
        self
    }

    /// Inserts a new height.
    pub fn height(mut self, height: impl Into<f64>) -> Self {
        self.height = height.into();
        self
    }

    /// Inserts a new size.
    pub fn size(mut self, width: impl Into<f64>, height: impl Into<f64>) -> Self {
        self.width = width.into();
        self.height = height.into();
        self
    }

    /// Inserts a new min_width.
    pub fn min_width(mut self, min_width: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self
    }

    /// Inserts a new min_height.
    pub fn min_height(mut self, min_height: impl Into<f64>) -> Self {
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn min_size(mut self, min_width: impl Into<f64>, min_height: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new max_width.
    pub fn max_width(mut self, max_width: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self
    }

    /// Inserts a new max_height.
    pub fn max_height(mut self, max_height: impl Into<f64>) -> Self {
        self.max_height = max_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn max_size(mut self, max_width: impl Into<f64>, max_height: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self.max_height = max_height.into();
        self
    }

    /// Builds the constraint.
    pub fn build(self) -> Constraint {
        Constraint {
            width: self.width,
            height: self.height,
            min_width: self.min_width,
            min_height: self.min_height,
            max_width: self.max_width,
            max_height: self.max_height,
        }
    }
}

/// `Constraint` describes a box constraint.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Constraint {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

impl Default for Constraint {
    fn default() -> Self {
        Constraint {
            width: 0.0,
            height: 0.0,
            min_width: 0.0,
            min_height: 0.0,
            max_width: f64::MAX,
            max_height: f64::MAX,
        }
    }
}

impl Constraint {
    /// Returns a constraint builder.
    #[inline]
    pub fn create() -> ConstraintBuilder {
        ConstraintBuilder::new()
    }

    /// Gets width.
    pub fn width(&self) -> f64 {
        self.width
    }

    /// Sets width.
    pub fn set_width(&mut self, width: f64) {
        self.width = width;

        // adjust min and max
        if self.min_width > width {
            self.min_width = width;
        }

        if self.max_width < width {
            self.max_width = width;
        }
    }

    /// Gets height.
    pub fn height(&self) -> f64 {
        self.height
    }

    /// Sets height.
    pub fn set_height(&mut self, height: f64) {
        self.height = height;

        // adjust min and max
        if self.min_height > height {
            self.min_height = height;
        }

        if self.max_height < height {
            self.max_height = height;
        }
    }

    /// Gets the size.
    pub fn size(&self) -> (f64, f64) {
        (self.width, self.height)
    }

    /// Sets the size.
    pub fn set_size(&mut self, width: f64, height: f64) {
        self.set_width(width);
        self.set_height(height);
    }

    /// Gets min_width.
    pub fn min_width(&self) -> f64 {
        self.min_width
    }

    /// Sets min_width and set width to 0.0.
    pub fn set_min_width(&mut self, min_width: f64) {
        self.min_width = min_width;

        self.width = 0.;
    }

    /// Gets min_height.
    pub fn min_height(&self) -> f64 {
        self.min_height
    }

    /// Sets min_height and set height to min_height if height < min_height.
    pub fn set_min_height(&mut self, min_height: f64) {
        self.min_height = min_height;

        self.height = 0.;
    }

    /// Gets the min_size.
    pub fn min_size(&self) -> (f64, f64) {
        (self.min_width, self.min_height)
    }

    /// Sets the min size.
    pub fn set_min_size(&mut self, min_width: f64, min_height: f64) {
        self.set_min_width(min_width);
        self.set_min_height(min_height);
    }

    /// Gets max_width.
    pub fn max_width(&self) -> f64 {
        self.max_width
    }

    /// Sets max_width and set width to 0.0.
    pub fn set_max_width(&mut self, max_width: f64) {
        self.max_width = max_width;

        self.width = 0.;
    }

    /// Gets max_height.
    pub fn max_height(&self) -> f64 {
        self.max_height
    }

    /// Sets max_height and set height to 0.0.
    pub fn set_max_height(&mut self, max_height: f64) {
        self.max_height = max_height;

        self.height = 0.;
    }

    /// Gets the max_size.
    pub fn max_size(&self) -> (f64, f64) {
        (self.max_width, self.max_height)
    }

    /// Sets the max size.
    pub fn set_max_size(&mut self, max_width: f64, max_height: f64) {
        self.set_max_width(max_width);
        self.set_max_height(max_height);
    }

    /// Adjust the given `size` to match the constraint.
    pub fn perform(&self, size: (f64, f64)) -> (f64, f64) {
        let size = {
            let width = if self.width > 0.0 { self.width } else { size.0 };
            let height = if self.height > 0.0 {
                self.height
            } else {
                size.1
            };

            (width, height)
        };

        (
            constrain(size.0, self.min_width, self.max_width, self.width),
            constrain(size.1, self.min_height, self.max_height, self.height),
        )
    }
}

// Check constraint for the given
fn constrain(val: f64, min: f64, max: f64, size: f64) -> f64 {
    if min == 0.0 && max == 0.0 && size > 0.0 {
        size
    } else if val < min && min > 0.0 {
        min
    } else if val > max && max > 0.0 {
        max
    } else {
        val
    }
}

impl From<ConstraintBuilder> for Constraint {
    fn from(builder: ConstraintBuilder) -> Self {
        builder.build()
    }
}

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

    const ERROR: f64 = f64::EPSILON;

    #[test]
    fn test_builder_width() {
        let width = 12.0;

        let constraint = Constraint::create().width(width).build();

        assert!((constraint.width() - width).abs() < ERROR);
    }

    #[test]
    fn test_builder_height() {
        let height = 12.0;

        let constraint = Constraint::create().height(height).build();

        assert!((constraint.height() - height).abs() < ERROR);
    }

    #[test]
    fn test_builder_min_width() {
        let width = 12.0;

        let constraint = Constraint::create().min_width(width).build();

        assert!((constraint.min_width() - width).abs() < ERROR);
    }

    #[test]
    fn test_builder_min_height() {
        let height = 12.0;

        let constraint = Constraint::create().min_height(height).build();

        assert!((constraint.min_height() - height).abs() < ERROR);
    }

    #[test]
    fn test_builder_max_width() {
        let width = 12.0;

        let constraint = Constraint::create().max_width(width).build();

        assert!((constraint.max_width() - width).abs() < ERROR);
    }

    #[test]
    fn test_builder_max_height() {
        let height = 12.0;

        let constraint = Constraint::create().max_height(height).build();

        assert!((constraint.max_height() - height).abs() < ERROR);
    }

    #[test]
    fn test_set_width() {
        let width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_width(width);
        assert!((constraint.width() - width).abs() < ERROR);
    }

    #[test]
    fn test_set_height() {
        let height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_height(height);

        assert!((constraint.height() - height).abs() < ERROR);
    }

    #[test]
    fn test_set_size() {
        let width = 12.0;
        let height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_size(width, height);

        assert_eq!(constraint.size(), (width, height));
    }

    #[test]
    fn test_set_min_width() {
        let min_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_width(min_width);

        assert!((constraint.min_width() - min_width).abs() < ERROR);
    }

    #[test]
    fn test_set_min_height() {
        let min_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_height(min_height);

        assert!((constraint.min_height() - min_height).abs() < ERROR);
    }

    #[test]
    fn test_set_min_size() {
        let min_width = 12.0;
        let min_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_min_size(min_width, min_height);

        assert_eq!(constraint.min_size(), (min_width, min_height));
    }

    #[test]
    fn test_set_max_width() {
        let max_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_width(max_width);

        assert!((constraint.max_width() - max_width).abs() < ERROR);
    }

    #[test]
    fn test_set_max_height() {
        let max_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_height(max_height);

        assert!((constraint.max_height() - max_height).abs() < ERROR);
    }

    #[test]
    fn test_set_max_size() {
        let max_width = 12.0;
        let max_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_max_size(max_width, max_height);

        assert_eq!(constraint.max_size(), (max_width, max_height));
    }

    #[test]
    fn test_perform() {
        let mut constraint = Constraint::default();

        constraint.set_min_width(10.0);
        constraint.set_min_height(10.0);
        constraint.set_max_width(50.0);
        constraint.set_max_height(60.0);

        assert_eq!(constraint.perform((10.0, 59.0)), (10.0, 59.0));
        assert_eq!(constraint.perform((5.0, 40.0)), (10.0, 40.0));
        assert_eq!(constraint.perform((10.0, 70.0)), (10.0, 60.0));
    }
}
<commit_msg>utils: update constraints documentation<commit_after>use std::f64;

/// Used to capture constraint properties.
#[derive(Default)]
pub struct ConstraintBuilder {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

/// The `ConstraintBuilder` is used to crate the constraint porperties of an entity.
///
/// Constraints will provide properties that are used to determine the
/// size requirements of an entity. It will store `minimum` and
/// `maximim` values for its height and width, next to the current values.
impl ConstraintBuilder {
    /// Creates a new `ConstraintBuilder` with default values.
    pub fn new() -> Self {
        ConstraintBuilder::default()
    }

    /// Inserts a new width.
    pub fn width(mut self, width: impl Into<f64>) -> Self {
        self.width = width.into();
        self
    }

    /// Inserts a new height.
    pub fn height(mut self, height: impl Into<f64>) -> Self {
        self.height = height.into();
        self
    }

    /// Inserts a new size.
    pub fn size(mut self, width: impl Into<f64>, height: impl Into<f64>) -> Self {
        self.width = width.into();
        self.height = height.into();
        self
    }

    /// Inserts a new min_width.
    pub fn min_width(mut self, min_width: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self
    }

    /// Inserts a new min_height.
    pub fn min_height(mut self, min_height: impl Into<f64>) -> Self {
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn min_size(mut self, min_width: impl Into<f64>, min_height: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new max_width.
    pub fn max_width(mut self, max_width: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self
    }

    /// Inserts a new max_height.
    pub fn max_height(mut self, max_height: impl Into<f64>) -> Self {
        self.max_height = max_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn max_size(mut self, max_width: impl Into<f64>, max_height: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self.max_height = max_height.into();
        self
    }

    /// Builds the constraint.
    pub fn build(self) -> Constraint {
        Constraint {
            width: self.width,
            height: self.height,
            min_width: self.min_width,
            min_height: self.min_height,
            max_width: self.max_width,
            max_height: self.max_height,
        }
    }
}

/// `Constraint` describes the constraints properties of a `box` entity.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Constraint {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

impl Default for Constraint {
    fn default() -> Self {
        Constraint {
            width: 0.0,
            height: 0.0,
            min_width: 0.0,
            min_height: 0.0,
            max_width: f64::MAX,
            max_height: f64::MAX,
        }
    }
}

/// The `Constraint` is used to manage the constraint porperties of an entity.
///
/// Constraints will provide properties that are used to determine the
/// size requirements of an entity. It will store `minimum` and
/// `maximim` values for its height and width, next to the current values.
impl Constraint {
    /// Returns a constraint builder.
    #[inline]
    pub fn create() -> ConstraintBuilder {
        ConstraintBuilder::new()
    }

    /// Gets width.
    pub fn width(&self) -> f64 {
        self.width
    }

    /// Sets width.
    pub fn set_width(&mut self, width: f64) {
        self.width = width;

        // adjust min and max
        if self.min_width > width {
            self.min_width = width;
        }

        if self.max_width < width {
            self.max_width = width;
        }
    }

    /// Gets height.
    pub fn height(&self) -> f64 {
        self.height
    }

    /// Sets height.
    pub fn set_height(&mut self, height: f64) {
        self.height = height;

        // adjust min and max
        if self.min_height > height {
            self.min_height = height;
        }

        if self.max_height < height {
            self.max_height = height;
        }
    }

    /// Gets the size.
    pub fn size(&self) -> (f64, f64) {
        (self.width, self.height)
    }

    /// Sets the size.
    pub fn set_size(&mut self, width: f64, height: f64) {
        self.set_width(width);
        self.set_height(height);
    }

    /// Gets min_width.
    pub fn min_width(&self) -> f64 {
        self.min_width
    }

    /// Sets min_width and set width to 0.0.
    pub fn set_min_width(&mut self, min_width: f64) {
        self.min_width = min_width;

        self.width = 0.;
    }

    /// Gets min_height.
    pub fn min_height(&self) -> f64 {
        self.min_height
    }

    /// Sets min_height and set height to min_height if height < min_height.
    pub fn set_min_height(&mut self, min_height: f64) {
        self.min_height = min_height;

        self.height = 0.;
    }

    /// Gets the min_size.
    pub fn min_size(&self) -> (f64, f64) {
        (self.min_width, self.min_height)
    }

    /// Sets the min size.
    pub fn set_min_size(&mut self, min_width: f64, min_height: f64) {
        self.set_min_width(min_width);
        self.set_min_height(min_height);
    }

    /// Gets max_width.
    pub fn max_width(&self) -> f64 {
        self.max_width
    }

    /// Sets max_width and set width to 0.0.
    pub fn set_max_width(&mut self, max_width: f64) {
        self.max_width = max_width;

        self.width = 0.;
    }

    /// Gets max_height.
    pub fn max_height(&self) -> f64 {
        self.max_height
    }

    /// Sets max_height and set height to 0.0.
    pub fn set_max_height(&mut self, max_height: f64) {
        self.max_height = max_height;

        self.height = 0.;
    }

    /// Gets the max_size.
    pub fn max_size(&self) -> (f64, f64) {
        (self.max_width, self.max_height)
    }

    /// Sets the max size.
    pub fn set_max_size(&mut self, max_width: f64, max_height: f64) {
        self.set_max_width(max_width);
        self.set_max_height(max_height);
    }

    /// Adjust the given `size`.
    ///
    /// Asures that size will respect the defined `box` values for min
    /// and max constraints. The value will be adapted if outside of a bound.
    pub fn perform(&self, size: (f64, f64)) -> (f64, f64) {
        let size = {
            let width = if self.width > 0.0 { self.width } else { size.0 };
            let height = if self.height > 0.0 {
                self.height
            } else {
                size.1
            };

            (width, height)
        };

        (
            // check `width` value to meet the constraint requirement
            constrain(size.0, self.min_width, self.max_width, self.width),

            // check `height` value to meet the constraint requirement
            constrain(size.1, self.min_height, self.max_height, self.height),
        )
    }
}

// Check constraint for the given
fn constrain(val: f64, min: f64, max: f64, size: f64) -> f64 {
    if min == 0.0 && max == 0.0 && size > 0.0 {
        size
    } else if val < min && min > 0.0 {
        min
    } else if val > max && max > 0.0 {
        max
    } else {
        val
    }
}

impl From<ConstraintBuilder> for Constraint {
    fn from(builder: ConstraintBuilder) -> Self {
        builder.build()
    }
}

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

    const ERROR: f64 = f64::EPSILON;

    #[test]
    fn test_builder_width() {
        let width = 12.0;

        let constraint = Constraint::create().width(width).build();

        assert!((constraint.width() - width).abs() < ERROR);
    }

    #[test]
    fn test_builder_height() {
        let height = 12.0;

        let constraint = Constraint::create().height(height).build();

        assert!((constraint.height() - height).abs() < ERROR);
    }

    #[test]
    fn test_builder_min_width() {
        let width = 12.0;

        let constraint = Constraint::create().min_width(width).build();

        assert!((constraint.min_width() - width).abs() < ERROR);
    }

    #[test]
    fn test_builder_min_height() {
        let height = 12.0;

        let constraint = Constraint::create().min_height(height).build();

        assert!((constraint.min_height() - height).abs() < ERROR);
    }

    #[test]
    fn test_builder_max_width() {
        let width = 12.0;

        let constraint = Constraint::create().max_width(width).build();

        assert!((constraint.max_width() - width).abs() < ERROR);
    }

    #[test]
    fn test_builder_max_height() {
        let height = 12.0;

        let constraint = Constraint::create().max_height(height).build();

        assert!((constraint.max_height() - height).abs() < ERROR);
    }

    #[test]
    fn test_set_width() {
        let width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_width(width);
        assert!((constraint.width() - width).abs() < ERROR);
    }

    #[test]
    fn test_set_height() {
        let height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_height(height);

        assert!((constraint.height() - height).abs() < ERROR);
    }

    #[test]
    fn test_set_size() {
        let width = 12.0;
        let height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_size(width, height);

        assert_eq!(constraint.size(), (width, height));
    }

    #[test]
    fn test_set_min_width() {
        let min_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_width(min_width);

        assert!((constraint.min_width() - min_width).abs() < ERROR);
    }

    #[test]
    fn test_set_min_height() {
        let min_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_height(min_height);

        assert!((constraint.min_height() - min_height).abs() < ERROR);
    }

    #[test]
    fn test_set_min_size() {
        let min_width = 12.0;
        let min_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_min_size(min_width, min_height);

        assert_eq!(constraint.min_size(), (min_width, min_height));
    }

    #[test]
    fn test_set_max_width() {
        let max_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_width(max_width);

        assert!((constraint.max_width() - max_width).abs() < ERROR);
    }

    #[test]
    fn test_set_max_height() {
        let max_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_height(max_height);

        assert!((constraint.max_height() - max_height).abs() < ERROR);
    }

    #[test]
    fn test_set_max_size() {
        let max_width = 12.0;
        let max_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_max_size(max_width, max_height);

        assert_eq!(constraint.max_size(), (max_width, max_height));
    }

    #[test]
    fn test_perform() {
        let mut constraint = Constraint::default();

        constraint.set_min_width(10.0);
        constraint.set_min_height(10.0);
        constraint.set_max_width(50.0);
        constraint.set_max_height(60.0);

        assert_eq!(constraint.perform((10.0, 59.0)), (10.0, 59.0));
        assert_eq!(constraint.perform((5.0, 40.0)), (10.0, 40.0));
        assert_eq!(constraint.perform((10.0, 70.0)), (10.0, 60.0));
    }
}
<|endoftext|>


<commit_before><commit_msg>Replace pSubGroupTspace pUniSubGroups pTmpMembers<commit_after><|endoftext|>


<commit_before><commit_msg>Handle number conversion errors<commit_after><|endoftext|>


<commit_before>use super::{Document,Root,RootChild,Element,ElementChild};

#[allow(dead_code)]
struct Parser;

#[allow(dead_code)]
struct ParsedElement<'a> {
    name: &'a str,
    attributes: Vec<ParsedAttribute<'a>>,
    children: Vec<ParsedElement<'a>>,
}

#[allow(dead_code)]
struct ParsedAttribute<'a> {
    name: &'a str,
    value: &'a str,
}

macro_rules! try_parse(
    ($e:expr) => ({
        match $e {
            None => return None,
            Some(x) => x,
        }
    })
)

// Pattern: 0-or-1
macro_rules! optional_parse(
    ($f:expr, $start:expr) => ({
        match $f {
            None => (None, $start),
            Some((value, next)) => (Some(value), next),
        }
    })
)

#[allow(dead_code)]
impl Parser {
    fn new() -> Parser {
        Parser
    }

    fn parse_preamble<'a>(&self, xml: &'a str) -> &'a str {
        // Parse the preamble
        let idx = xml.find_str("?>").expect("No preamble end");
        let end_of_preamble = idx + "?>".len();
        xml.slice_from(end_of_preamble)
    }

    fn optional_space<'a>(&self, xml: &'a str) -> &'a str {
        match xml.slice_space() {
            Some((_, next_xml)) => next_xml,
            None => xml,
        }
    }

    fn parse_attribute_value_quote<'a>(&self, xml: &'a str, quote: &str) -> Option<(&'a str, &'a str)> {
        let (_, xml) = match xml.slice_literal(quote) {
            None => return None,
            Some(x) => x,
        };
        // TODO: don't consume & or <
        let (value, xml) = xml.slice_until(quote).expect("No value");
        let (_, xml) = xml.slice_literal(quote).expect("No quote");

        Some((value, xml))
    }

    fn parse_attribute<'a>(&self, xml: &'a str) -> Option<(ParsedAttribute<'a>, &'a str)> {
        let (name, xml) = match xml.slice_name() {
            Some(x) => x,
            None => return None,
        };

        let xml = self.optional_space(xml);
        let (_, xml) = xml.slice_literal("=").expect("No equal sign");
        let xml = self.optional_space(xml);

        // Pattern: alternate
        let (value, xml) = match self.parse_attribute_value_quote(xml, "'") {
            Some(x) => x,
            None => match self.parse_attribute_value_quote(xml, "\"") {
                Some(x) => x,
                None => fail!("No attribute value"),
            },
        };

        Some((ParsedAttribute{name: name, value: value}, xml))
    }

    fn parse_attributes<'a>(&self, xml: &'a str) -> (Vec<ParsedAttribute<'a>>, &'a str) {
        let mut xml = xml;
        let mut attrs = Vec::new();

        // Pattern: zero-or-more
        // On failure, return the end of the last successful parse
        loop {
            let (_, after_space) = match xml.slice_space() {
                None => return (attrs, xml),
                Some(x) => x,
            };

            xml = match self.parse_attribute(after_space) {
                None => return (attrs, xml),
                Some((attr, after_attr)) => {
                    attrs.push(attr);
                    after_attr
                },
            };
        }
    }

    fn parse_empty_element<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        let (_, xml) = try_parse!(xml.slice_literal("<"));
        let (name, xml) = try_parse!(xml.slice_name());
        let (attrs, xml) = self.parse_attributes(xml);
        let xml = self.optional_space(xml);
        let (_, xml) = try_parse!(xml.slice_literal("/>"));

        Some((ParsedElement{name: name, attributes: attrs, children: Vec::new()}, xml))
    }

    fn parse_element_start<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        let (_, xml) = try_parse!(xml.slice_literal("<"));
        let (name, xml) = try_parse!(xml.slice_name());
        let (attrs, xml) = self.parse_attributes(xml);
        let xml = self.optional_space(xml);
        let (_, xml) = try_parse!(xml.slice_literal(">"));

        Some((ParsedElement{name: name, attributes: attrs, children: Vec::new()}, xml))
    }

    fn parse_element_end<'a>(&self, xml: &'a str) -> Option<(&'a str, &'a str)> {
        let (_, xml) = try_parse!(xml.slice_literal("</"));
        let (name, xml) = try_parse!(xml.slice_name());
        let xml = self.optional_space(xml);
        let (_, xml) = try_parse!(xml.slice_literal(">"));
        Some((name, xml))
    }

    fn parse_non_empty_element<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        let (mut element, xml) = try_parse!(self.parse_element_start(xml));
        let (child, xml) = optional_parse!(self.parse_element(xml), xml);
        let (name, xml) = try_parse!(self.parse_element_end(xml));

        if element.name != name {
            fail!("tags do not match!");
        }

        child.map(|c| element.children.push(c));

        Some((element, xml))
    }

    fn parse_element<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        // Pattern: alternate
        match self.parse_empty_element(xml) {
            Some(x) => Some(x),
            None => match self.parse_non_empty_element(xml) {
                Some(x) => Some(x),
                None => None,
            },
        }
    }

    fn hydrate_element(&self, doc: Document, element_data: ParsedElement) -> Element {
        let element = doc.new_element(element_data.name.to_string());
        for attr in element_data.attributes.iter() {
            element.set_attribute(attr.name.to_string(), attr.value.to_string());
        }
        for child in element_data.children.move_iter() {
            element.append_child(self.hydrate_element(doc.clone(), child));
        }
        element
    }

    fn hydrate_parsed_data(&self, element_data: ParsedElement) -> Document {
        let doc = Document::new();

        doc.root().append_child(self.hydrate_element(doc.clone(), element_data));

        doc
    }

    fn parse(&self, xml: &str) -> Document {
        let after_preamble = self.parse_preamble(xml);

        let (element, _tail) = self.parse_element(after_preamble).expect("no element");

        self.hydrate_parsed_data(element)
    }
}

trait XmlStr<'a> {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str);
    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)>;
    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)>;
    fn slice_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<(&'a str, &'a str)>;
    fn slice_name(&self) -> Option<(&'a str, &'a str)>;
    fn slice_space(&self) -> Option<(&'a str, &'a str)>;
}

impl<'a> XmlStr<'a> for &'a str {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str) {
        (self.slice_to(position), self.slice_from(position))
    }

    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)> {
        match self.find_str(s) {
            Some(position) => Some(self.slice_at(position)),
            None => None
        }
    }

    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)> {
        if self.starts_with(expected) {
            Some(self.slice_at(expected.len()))
        } else {
            None
        }
    }

    fn slice_start_rest(&self,
                        is_first: |char| -> bool,
                        is_rest: |char| -> bool)
                        -> Option<(&'a str, &'a str)>
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(self.slice_at(offset)),
            None => Some((self.clone(), "")),
        }
    }

    fn slice_name(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn slice_space(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }
}

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                        |
            'A'..'Z'                   |
            '_'                        |
            'a'..'z'                   |
            '\U000000C0'..'\U000000D6' |
            '\U000000D8'..'\U000000F6' |
            '\U000000F8'..'\U000002FF' |
            '\U00000370'..'\U0000037D' |
            '\U0000037F'..'\U00001FFF' |
            '\U0000200C'..'\U0000200D' |
            '\U00002070'..'\U0000218F' |
            '\U00002C00'..'\U00002FEF' |
            '\U00003001'..'\U0000D7FF' |
            '\U0000F900'..'\U0000FDCF' |
            '\U0000FDF0'..'\U0000FFFD' |
            '\U00010000'..'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                |
            '.'                |
            '0'..'9'           |
            '\u00B7'           |
            '\u0300'..'\u036F' |
            '\u203F'..'\u2040' => true,
            _ => false
        }
    }

    fn is_space_char(&self) -> bool {
        match *self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }
}

trait Hax {
    fn first_child(&self) -> Option<RootChild>;
}

impl Hax for Root {
    fn first_child(&self) -> Option<RootChild> {
        self.children().remove(0)
    }
}

trait Hax2 {
    fn first_child(&self) -> Option<ElementChild>;
}

impl Hax2 for Element {
    fn first_child(&self) -> Option<ElementChild> {
        self.children().remove(0)
    }
}

#[test]
fn parses_a_document_with_a_single_element() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello />");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.name().as_slice(), "hello");
}

#[test]
fn parses_an_element_with_an_attribute() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope='world'/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
}

#[test]
fn parses_an_element_with_an_attribute_using_double_quotes() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope=\"world\"/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
}

#[test]
fn parses_an_element_with_multiple_attributes() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope=\"world\" happy='true'/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
    assert_eq!(top.get_attribute("happy").unwrap().as_slice(), "true");
}

#[test]
fn parses_an_element_that_is_not_self_closing() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello></hello>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.name().as_slice(), "hello");
}

#[test]
fn parses_nested_elements() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello><world/></hello>");
    let nested = doc.root().first_child().unwrap().element().unwrap().first_child().unwrap().element().unwrap();

    assert_eq!(nested.name().as_slice(), "world");
}

#[test]
fn parses_multiply_nested_elements() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello><awesome><world/></awesome></hello>");
    let hello = doc.root().first_child().unwrap().element().unwrap();
    let awesome = hello.first_child().unwrap().element().unwrap();
    let world = awesome.first_child().unwrap().element().unwrap();

    assert_eq!(world.name().as_slice(), "world");
}

#[test]
fn parses_nested_elements_with_attributes() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello><world name='Earth'/></hello>");
    let hello = doc.root().first_child().unwrap().element().unwrap();
    let world = hello.first_child().unwrap().element().unwrap();

    assert_eq!(world.get_attribute("name").unwrap().as_slice(), "Earth");
}
<commit_msg>Add a comment to remember to do entities in attributes<commit_after>use super::{Document,Root,RootChild,Element,ElementChild};

#[allow(dead_code)]
struct Parser;

#[allow(dead_code)]
struct ParsedElement<'a> {
    name: &'a str,
    attributes: Vec<ParsedAttribute<'a>>,
    children: Vec<ParsedElement<'a>>,
}

#[allow(dead_code)]
struct ParsedAttribute<'a> {
    name: &'a str,
    value: &'a str,
}

macro_rules! try_parse(
    ($e:expr) => ({
        match $e {
            None => return None,
            Some(x) => x,
        }
    })
)

// Pattern: 0-or-1
macro_rules! optional_parse(
    ($f:expr, $start:expr) => ({
        match $f {
            None => (None, $start),
            Some((value, next)) => (Some(value), next),
        }
    })
)

#[allow(dead_code)]
impl Parser {
    fn new() -> Parser {
        Parser
    }

    fn parse_preamble<'a>(&self, xml: &'a str) -> &'a str {
        // Parse the preamble
        let idx = xml.find_str("?>").expect("No preamble end");
        let end_of_preamble = idx + "?>".len();
        xml.slice_from(end_of_preamble)
    }

    fn optional_space<'a>(&self, xml: &'a str) -> &'a str {
        match xml.slice_space() {
            Some((_, next_xml)) => next_xml,
            None => xml,
        }
    }

    fn parse_attribute_value_quote<'a>(&self, xml: &'a str, quote: &str) -> Option<(&'a str, &'a str)> {
        let (_, xml) = match xml.slice_literal(quote) {
            None => return None,
            Some(x) => x,
        };
        // TODO: don't consume & or <
        // TODO: support references
        let (value, xml) = xml.slice_until(quote).expect("No value");
        let (_, xml) = xml.slice_literal(quote).expect("No quote");

        Some((value, xml))
    }

    fn parse_attribute<'a>(&self, xml: &'a str) -> Option<(ParsedAttribute<'a>, &'a str)> {
        let (name, xml) = match xml.slice_name() {
            Some(x) => x,
            None => return None,
        };

        let xml = self.optional_space(xml);
        let (_, xml) = xml.slice_literal("=").expect("No equal sign");
        let xml = self.optional_space(xml);

        // Pattern: alternate
        let (value, xml) = match self.parse_attribute_value_quote(xml, "'") {
            Some(x) => x,
            None => match self.parse_attribute_value_quote(xml, "\"") {
                Some(x) => x,
                None => fail!("No attribute value"),
            },
        };

        Some((ParsedAttribute{name: name, value: value}, xml))
    }

    fn parse_attributes<'a>(&self, xml: &'a str) -> (Vec<ParsedAttribute<'a>>, &'a str) {
        let mut xml = xml;
        let mut attrs = Vec::new();

        // Pattern: zero-or-more
        // On failure, return the end of the last successful parse
        loop {
            let (_, after_space) = match xml.slice_space() {
                None => return (attrs, xml),
                Some(x) => x,
            };

            xml = match self.parse_attribute(after_space) {
                None => return (attrs, xml),
                Some((attr, after_attr)) => {
                    attrs.push(attr);
                    after_attr
                },
            };
        }
    }

    fn parse_empty_element<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        let (_, xml) = try_parse!(xml.slice_literal("<"));
        let (name, xml) = try_parse!(xml.slice_name());
        let (attrs, xml) = self.parse_attributes(xml);
        let xml = self.optional_space(xml);
        let (_, xml) = try_parse!(xml.slice_literal("/>"));

        Some((ParsedElement{name: name, attributes: attrs, children: Vec::new()}, xml))
    }

    fn parse_element_start<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        let (_, xml) = try_parse!(xml.slice_literal("<"));
        let (name, xml) = try_parse!(xml.slice_name());
        let (attrs, xml) = self.parse_attributes(xml);
        let xml = self.optional_space(xml);
        let (_, xml) = try_parse!(xml.slice_literal(">"));

        Some((ParsedElement{name: name, attributes: attrs, children: Vec::new()}, xml))
    }

    fn parse_element_end<'a>(&self, xml: &'a str) -> Option<(&'a str, &'a str)> {
        let (_, xml) = try_parse!(xml.slice_literal("</"));
        let (name, xml) = try_parse!(xml.slice_name());
        let xml = self.optional_space(xml);
        let (_, xml) = try_parse!(xml.slice_literal(">"));
        Some((name, xml))
    }

    fn parse_non_empty_element<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        let (mut element, xml) = try_parse!(self.parse_element_start(xml));
        let (child, xml) = optional_parse!(self.parse_element(xml), xml);
        let (name, xml) = try_parse!(self.parse_element_end(xml));

        if element.name != name {
            fail!("tags do not match!");
        }

        child.map(|c| element.children.push(c));

        Some((element, xml))
    }

    fn parse_element<'a>(&self, xml: &'a str) -> Option<(ParsedElement<'a>, &'a str)> {
        // Pattern: alternate
        match self.parse_empty_element(xml) {
            Some(x) => Some(x),
            None => match self.parse_non_empty_element(xml) {
                Some(x) => Some(x),
                None => None,
            },
        }
    }

    fn hydrate_element(&self, doc: Document, element_data: ParsedElement) -> Element {
        let element = doc.new_element(element_data.name.to_string());
        for attr in element_data.attributes.iter() {
            element.set_attribute(attr.name.to_string(), attr.value.to_string());
        }
        for child in element_data.children.move_iter() {
            element.append_child(self.hydrate_element(doc.clone(), child));
        }
        element
    }

    fn hydrate_parsed_data(&self, element_data: ParsedElement) -> Document {
        let doc = Document::new();

        doc.root().append_child(self.hydrate_element(doc.clone(), element_data));

        doc
    }

    fn parse(&self, xml: &str) -> Document {
        let after_preamble = self.parse_preamble(xml);

        let (element, _tail) = self.parse_element(after_preamble).expect("no element");

        self.hydrate_parsed_data(element)
    }
}

trait XmlStr<'a> {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str);
    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)>;
    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)>;
    fn slice_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<(&'a str, &'a str)>;
    fn slice_name(&self) -> Option<(&'a str, &'a str)>;
    fn slice_space(&self) -> Option<(&'a str, &'a str)>;
}

impl<'a> XmlStr<'a> for &'a str {
    fn slice_at(&self, position: uint) -> (&'a str, &'a str) {
        (self.slice_to(position), self.slice_from(position))
    }

    fn slice_until(&self, s: &str) -> Option<(&'a str, &'a str)> {
        match self.find_str(s) {
            Some(position) => Some(self.slice_at(position)),
            None => None
        }
    }

    fn slice_literal(&self, expected: &str) -> Option<(&'a str, &'a str)> {
        if self.starts_with(expected) {
            Some(self.slice_at(expected.len()))
        } else {
            None
        }
    }

    fn slice_start_rest(&self,
                        is_first: |char| -> bool,
                        is_rest: |char| -> bool)
                        -> Option<(&'a str, &'a str)>
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(self.slice_at(offset)),
            None => Some((self.clone(), "")),
        }
    }

    fn slice_name(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn slice_space(&self) -> Option<(&'a str, &'a str)> {
        self.slice_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }
}

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                        |
            'A'..'Z'                   |
            '_'                        |
            'a'..'z'                   |
            '\U000000C0'..'\U000000D6' |
            '\U000000D8'..'\U000000F6' |
            '\U000000F8'..'\U000002FF' |
            '\U00000370'..'\U0000037D' |
            '\U0000037F'..'\U00001FFF' |
            '\U0000200C'..'\U0000200D' |
            '\U00002070'..'\U0000218F' |
            '\U00002C00'..'\U00002FEF' |
            '\U00003001'..'\U0000D7FF' |
            '\U0000F900'..'\U0000FDCF' |
            '\U0000FDF0'..'\U0000FFFD' |
            '\U00010000'..'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                |
            '.'                |
            '0'..'9'           |
            '\u00B7'           |
            '\u0300'..'\u036F' |
            '\u203F'..'\u2040' => true,
            _ => false
        }
    }

    fn is_space_char(&self) -> bool {
        match *self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }
}

trait Hax {
    fn first_child(&self) -> Option<RootChild>;
}

impl Hax for Root {
    fn first_child(&self) -> Option<RootChild> {
        self.children().remove(0)
    }
}

trait Hax2 {
    fn first_child(&self) -> Option<ElementChild>;
}

impl Hax2 for Element {
    fn first_child(&self) -> Option<ElementChild> {
        self.children().remove(0)
    }
}

#[test]
fn parses_a_document_with_a_single_element() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello />");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.name().as_slice(), "hello");
}

#[test]
fn parses_an_element_with_an_attribute() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope='world'/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
}

#[test]
fn parses_an_element_with_an_attribute_using_double_quotes() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope=\"world\"/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
}

#[test]
fn parses_an_element_with_multiple_attributes() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello scope=\"world\" happy='true'/>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.get_attribute("scope").unwrap().as_slice(), "world");
    assert_eq!(top.get_attribute("happy").unwrap().as_slice(), "true");
}

#[test]
fn parses_an_element_that_is_not_self_closing() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello></hello>");
    let top = doc.root().first_child().unwrap().element().unwrap();

    assert_eq!(top.name().as_slice(), "hello");
}

#[test]
fn parses_nested_elements() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello><world/></hello>");
    let nested = doc.root().first_child().unwrap().element().unwrap().first_child().unwrap().element().unwrap();

    assert_eq!(nested.name().as_slice(), "world");
}

#[test]
fn parses_multiply_nested_elements() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello><awesome><world/></awesome></hello>");
    let hello = doc.root().first_child().unwrap().element().unwrap();
    let awesome = hello.first_child().unwrap().element().unwrap();
    let world = awesome.first_child().unwrap().element().unwrap();

    assert_eq!(world.name().as_slice(), "world");
}

#[test]
fn parses_nested_elements_with_attributes() {
    let parser = Parser::new();
    let doc = parser.parse("<?xml version='1.0' ?><hello><world name='Earth'/></hello>");
    let hello = doc.root().first_child().unwrap().element().unwrap();
    let world = hello.first_child().unwrap().element().unwrap();

    assert_eq!(world.get_attribute("name").unwrap().as_slice(), "Earth");
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::BindingDeclarations::ProgressEventBinding;
use dom::bindings::codegen::InheritTypes::ProgressEventDerived;
use dom::bindings::error::Fallible;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::event::{Event, ProgressEventTypeId};
use dom::window::Window;
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct ProgressEvent {
    event: Event,
    length_computable: bool,
    loaded: u64,
    total: u64
}

impl ProgressEventDerived for Event {
    fn is_progressevent(&self) -> bool {
        self.type_id == ProgressEventTypeId
    }
}

impl ProgressEvent {
    pub fn new_inherited(length_computable: bool, loaded: u64, total: u64) -> ProgressEvent {
        ProgressEvent {
            event: Event::new_inherited(ProgressEventTypeId),
            length_computable: length_computable,
            loaded: loaded,
            total: total
        }
    }
    pub fn new(window: &JSRef<Window>, length_computable: bool,
                loaded: u64, total: u64) -> Temporary<ProgressEvent> {
        reflect_dom_object(~ProgressEvent::new_inherited(length_computable, loaded, total),
                           window,
                           ProgressEventBinding::Wrap)
    }
    pub fn Constructor(owner: &JSRef<Window>,
                       type_: DOMString,
                       init: &ProgressEventBinding::ProgressEventInit)
                       -> Fallible<Temporary<ProgressEvent>> {
        let ev = ProgressEvent::new(owner, init.lengthComputable, init.loaded, init.total);
        Ok(ev)
    }
}

pub trait ProgressEventMethods {
    fn LengthComputable(&self) -> bool;
    fn Loaded(&self) -> u64;
    fn Total(&self) -> u64;
}

impl<'a> ProgressEventMethods for JSRef<'a, ProgressEvent> {
    fn LengthComputable(&self) -> bool {
        self.length_computable
    }
    fn Loaded(&self) -> u64{
        self.loaded
    }
    fn Total(&self) -> u64 {
        self.total
    }
}

impl Reflectable for ProgressEvent {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.event.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.event.mut_reflector()
    }
}<commit_msg>auto merge of #2375 : zwarich/servo/progressevent-rename, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::BindingDeclarations::ProgressEventBinding;
use dom::bindings::codegen::InheritTypes::ProgressEventDerived;
use dom::bindings::error::Fallible;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::event::{Event, ProgressEventTypeId};
use dom::window::Window;
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct ProgressEvent {
    event: Event,
    length_computable: bool,
    loaded: u64,
    total: u64
}

impl ProgressEventDerived for Event {
    fn is_progressevent(&self) -> bool {
        self.type_id == ProgressEventTypeId
    }
}

impl ProgressEvent {
    pub fn new_inherited(length_computable: bool, loaded: u64, total: u64) -> ProgressEvent {
        ProgressEvent {
            event: Event::new_inherited(ProgressEventTypeId),
            length_computable: length_computable,
            loaded: loaded,
            total: total
        }
    }
    pub fn new(window: &JSRef<Window>, length_computable: bool,
                loaded: u64, total: u64) -> Temporary<ProgressEvent> {
        reflect_dom_object(~ProgressEvent::new_inherited(length_computable, loaded, total),
                           window,
                           ProgressEventBinding::Wrap)
    }
    pub fn Constructor(owner: &JSRef<Window>,
                       _type: DOMString,
                       init: &ProgressEventBinding::ProgressEventInit)
                       -> Fallible<Temporary<ProgressEvent>> {
        let ev = ProgressEvent::new(owner, init.lengthComputable, init.loaded, init.total);
        Ok(ev)
    }
}

pub trait ProgressEventMethods {
    fn LengthComputable(&self) -> bool;
    fn Loaded(&self) -> u64;
    fn Total(&self) -> u64;
}

impl<'a> ProgressEventMethods for JSRef<'a, ProgressEvent> {
    fn LengthComputable(&self) -> bool {
        self.length_computable
    }
    fn Loaded(&self) -> u64{
        self.loaded
    }
    fn Total(&self) -> u64 {
        self.total
    }
}

impl Reflectable for ProgressEvent {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.event.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.event.mut_reflector()
    }
}<|endoftext|>