<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::Arc;

use kvproto::coprocessor::KeyRange;
use tipb::executor::{self, ExecType};
use tipb::select::DAGRequest;

use crate::storage::Store;

use super::batch::executors::*;
use super::batch::interface::*;
use super::executor::{
    Executor, HashAggExecutor, LimitExecutor, ScanExecutor, SelectionExecutor, StreamAggExecutor,
    TopNExecutor,
};
use crate::coprocessor::dag::exec_summary::*;
use crate::coprocessor::dag::expr::{EvalConfig, Flag, SqlMode};
use crate::coprocessor::metrics::*;
use crate::coprocessor::*;

/// Utilities to build an executor DAG.
///
/// Currently all executors are executed in sequence and there is no task scheduler, so this
/// builder is in fact a pipeline builder. The builder will finally build a `Box<Executor>` which
/// may contain another executor as its source, embedded in the field, one after another. These
/// nested executors together form an executor pipeline that a single iteration at the out-most
/// executor (i.e. calling `next()`) will drive the whole pipeline.
pub struct DAGBuilder;

impl DAGBuilder {
    /// Given a list of executor descriptors and checks whether all executor descriptors can
    /// be used to build batch executors.
    pub fn check_build_batch(exec_descriptors: &[executor::Executor]) -> Result<()> {
        for ed in exec_descriptors {
            match ed.get_tp() {
                ExecType::TypeTableScan => {
                    let descriptor = ed.get_tbl_scan();
                    BatchTableScanExecutor::check_supported(&descriptor)?;
                }
                ExecType::TypeIndexScan => {
                    let descriptor = ed.get_idx_scan();
                    BatchIndexScanExecutor::check_supported(&descriptor)?;
                }
                ExecType::TypeSelection => {
                    let descriptor = ed.get_selection();
                    BatchSelectionExecutor::check_supported(&descriptor)?;
                }
                ExecType::TypeLimit => {}
                _ => {
                    return Err(box_err!("Unsupported executor {:?}", ed.get_tp()));
                }
            }
        }

        Ok(())
    }

    // Note: `S` is `'static` because we have trait objects `Executor`.
    pub fn build_batch<S: Store + 'static, C: ExecSummaryCollector + 'static>(
        executor_descriptors: Vec<executor::Executor>,
        store: S,
        ranges: Vec<KeyRange>,
        config: Arc<EvalConfig>,
    ) -> Result<Box<dyn BatchExecutor>> {
        let mut executor_descriptors = executor_descriptors.into_iter();
        let mut first_ed = executor_descriptors
            .next()
            .ok_or_else(|| Error::Other(box_err!("No executors")))?;

        let mut executor: Box<dyn BatchExecutor>;
        let mut summary_slot_index = 0;

        match first_ed.get_tp() {
            ExecType::TypeTableScan => {
                // TODO: Use static metrics.
                COPR_EXECUTOR_COUNT.with_label_values(&["tblscan"]).inc();

                let mut descriptor = first_ed.take_tbl_scan();
                let columns_info = descriptor.take_columns().into_vec();
                executor = Box::new(BatchTableScanExecutor::new(
                    C::new(summary_slot_index),
                    store,
                    config.clone(),
                    columns_info,
                    ranges,
                    descriptor.get_desc(),
                )?);
            }
            ExecType::TypeIndexScan => {
                COPR_EXECUTOR_COUNT.with_label_values(&["idxscan"]).inc();

                let mut descriptor = first_ed.take_idx_scan();
                let columns_info = descriptor.take_columns().into_vec();
                executor = Box::new(BatchIndexScanExecutor::new(
                    C::new(summary_slot_index),
                    store,
                    config.clone(),
                    columns_info,
                    ranges,
                    descriptor.get_desc(),
                    descriptor.get_unique(),
                )?);
            }
            _ => {
                return Err(Error::Other(box_err!(
                    "Unexpected first executor {:?}",
                    first_ed.get_tp()
                )));
            }
        }

        for mut ed in executor_descriptors {
            summary_slot_index += 1;

            let new_executor: Box<dyn BatchExecutor> = match ed.get_tp() {
                ExecType::TypeSelection => {
                    COPR_EXECUTOR_COUNT.with_label_values(&["selection"]).inc();

                    Box::new(BatchSelectionExecutor::new(
                        C::new(summary_slot_index),
                        config.clone(),
                        executor,
                        ed.take_selection().take_conditions().into_vec(),
                    )?)
                }
                ExecType::TypeLimit => {
                    COPR_EXECUTOR_COUNT.with_label_values(&["limit"]).inc();

                    Box::new(BatchLimitExecutor::new(
                        C::new(summary_slot_index),
                        executor,
                        ed.get_limit().get_limit() as usize,
                    )?)
                }
                _ => {
                    return Err(Error::Other(box_err!(
                        "Unexpected non-first executor {:?}",
                        first_ed.get_tp()
                    )));
                }
            };
            executor = new_executor;
        }

        Ok(executor)
    }

    /// Builds a normal executor pipeline.
    ///
    /// Normal executors iterate rows one by one.
    pub fn build_normal<S: Store + 'static, C: ExecSummaryCollector + 'static>(
        exec_descriptors: Vec<executor::Executor>,
        store: S,
        ranges: Vec<KeyRange>,
        ctx: Arc<EvalConfig>,
        collect: bool,
    ) -> Result<Box<dyn Executor + Send>> {
        let mut exec_descriptors = exec_descriptors.into_iter();
        let first = exec_descriptors
            .next()
            .ok_or_else(|| Error::Other(box_err!("has no executor")))?;

        let mut src = Self::build_normal_first_executor::<_, C>(first, store, ranges, collect)?;
        let mut summary_slot_index = 0;

        for mut exec in exec_descriptors {
            summary_slot_index += 1;

            let curr: Box<dyn Executor + Send> = match exec.get_tp() {
                ExecType::TypeTableScan | ExecType::TypeIndexScan => {
                    return Err(box_err!("got too much *scan exec, should be only one"));
                }
                ExecType::TypeSelection => Box::new(SelectionExecutor::new(
                    exec.take_selection(),
                    Arc::clone(&ctx),
                    src,
                )?),
                ExecType::TypeAggregation => Box::new(HashAggExecutor::new(
                    exec.take_aggregation(),
                    Arc::clone(&ctx),
                    src,
                )?),
                ExecType::TypeStreamAgg => Box::new(StreamAggExecutor::new(
                    Arc::clone(&ctx),
                    src,
                    exec.take_aggregation(),
                )?),
                ExecType::TypeTopN => {
                    Box::new(TopNExecutor::new(exec.take_topN(), Arc::clone(&ctx), src)?)
                }
                ExecType::TypeLimit => Box::new(LimitExecutor::new(
                    C::new(summary_slot_index),
                    exec.take_limit(),
                    src,
                )),
            };
            src = curr;
        }
        Ok(src)
    }

    /// Builds the inner-most executor for the normal executor pipeline, which can produce rows to
    /// other executors and never receive rows from other executors.
    ///
    /// The inner-most executor must be a table scan executor or an index scan executor.
    fn build_normal_first_executor<S: Store + 'static, C: ExecSummaryCollector + 'static>(
        mut first: executor::Executor,
        store: S,
        ranges: Vec<KeyRange>,
        collect: bool,
    ) -> Result<Box<dyn Executor + Send>> {
        match first.get_tp() {
            ExecType::TypeTableScan => {
                let ex = Box::new(ScanExecutor::table_scan(
                    C::new(0),
                    first.take_tbl_scan(),
                    ranges,
                    store,
                    collect,
                )?);
                Ok(ex)
            }
            ExecType::TypeIndexScan => {
                let unique = first.get_idx_scan().get_unique();
                let ex = Box::new(ScanExecutor::index_scan(
                    C::new(0),
                    first.take_idx_scan(),
                    ranges,
                    store,
                    unique,
                    collect,
                )?);
                Ok(ex)
            }
            _ => Err(box_err!(
                "first exec type should be *Scan, but get {:?}",
                first.get_tp()
            )),
        }
    }

    fn build_dag<S: Store + 'static>(
        eval_cfg: EvalConfig,
        mut req: DAGRequest,
        ranges: Vec<KeyRange>,
        store: S,
        deadline: Deadline,
        batch_row_limit: usize,
    ) -> Result<super::DAGRequestHandler> {
        let executors_len = req.get_executors().len();

        let executor = if req.get_collect_execution_summaries() {
            Self::build_normal::<_, ExecSummaryCollectorEnabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                Arc::new(eval_cfg),
                req.get_collect_range_counts(),
            )?
        } else {
            Self::build_normal::<_, ExecSummaryCollectorDisabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                Arc::new(eval_cfg),
                req.get_collect_range_counts(),
            )?
        };
        Ok(super::DAGRequestHandler::new(
            deadline,
            executor,
            req.take_output_offsets(),
            batch_row_limit,
            executors_len,
            req.get_collect_execution_summaries(),
        ))
    }

    fn build_batch_dag<S: Store + 'static>(
        deadline: Deadline,
        config: EvalConfig,
        mut req: DAGRequest,
        ranges: Vec<KeyRange>,
        store: S,
    ) -> Result<super::batch_handler::BatchDAGHandler> {
        let ranges_len = ranges.len();
        let executors_len = req.get_executors().len();
        let collect_exec_summary = req.get_collect_execution_summaries();

        let config = Arc::new(config);
        let out_most_executor = if collect_exec_summary {
            super::builder::DAGBuilder::build_batch::<_, ExecSummaryCollectorEnabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                config.clone(),
            )?
        } else {
            super::builder::DAGBuilder::build_batch::<_, ExecSummaryCollectorDisabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                config.clone(),
            )?
        };

        // Check output offsets
        let output_offsets = req.take_output_offsets();
        let schema_len = out_most_executor.schema().len();
        for offset in &output_offsets {
            if (*offset as usize) >= schema_len {
                return Err(box_err!(
                    "Invalid output offset (schema has {} columns, access index {})",
                    schema_len,
                    offset
                ));
            }
        }

        Ok(super::batch_handler::BatchDAGHandler::new(
            deadline,
            out_most_executor,
            output_offsets,
            config,
            BatchExecuteStatistics::new(
                if collect_exec_summary {
                    executors_len
                } else {
                    0 // Avoid allocation for executor summaries when it is not needed
                },
                ranges_len,
            ),
            collect_exec_summary,
        ))
    }

    pub fn build<S: Store + 'static>(
        req: DAGRequest,
        ranges: Vec<KeyRange>,
        store: S,
        deadline: Deadline,
        batch_row_limit: usize,
        is_streaming: bool,
        enable_batch_if_possible: bool,
    ) -> Result<Box<dyn RequestHandler>> {
        let mut eval_cfg = EvalConfig::from_flag(Flag::from_bits_truncate(req.get_flags()));
        // We respect time zone name first, then offset.
        if req.has_time_zone_name() && !req.get_time_zone_name().is_empty() {
            box_try!(eval_cfg.set_time_zone_by_name(req.get_time_zone_name()));
        } else if req.has_time_zone_offset() {
            box_try!(eval_cfg.set_time_zone_by_offset(req.get_time_zone_offset()));
        } else {
            // This should not be reachable. However we will not panic here in case
            // of compatibility issues.
        }
        if req.has_max_warning_count() {
            eval_cfg.set_max_warning_cnt(req.get_max_warning_count() as usize);
        }
        if req.has_sql_mode() {
            eval_cfg.set_sql_mode(SqlMode::from_bits_truncate(req.get_sql_mode()));
        }

        let mut is_batch = false;
        if enable_batch_if_possible && !is_streaming {
            let build_batch_result =
                super::builder::DAGBuilder::check_build_batch(req.get_executors());
            if let Err(e) = build_batch_result {
                info!("Coprocessor request cannot be batched"; "reason" => %e);
            } else {
                is_batch = true;
            }
        }

        if is_batch {
            Ok(Self::build_batch_dag(deadline, eval_cfg, req, ranges, store)?.into_boxed())
        } else {
            Ok(
                Self::build_dag(eval_cfg, req, ranges, store, deadline, batch_row_limit)?
                    .into_boxed(),
            )
        }
    }
}
<commit_msg>Output start_ts so that tracing requests that cannot be batched can be easier (#4665)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::Arc;

use kvproto::coprocessor::KeyRange;
use tipb::executor::{self, ExecType};
use tipb::select::DAGRequest;

use crate::storage::Store;

use super::batch::executors::*;
use super::batch::interface::*;
use super::executor::{
    Executor, HashAggExecutor, LimitExecutor, ScanExecutor, SelectionExecutor, StreamAggExecutor,
    TopNExecutor,
};
use crate::coprocessor::dag::exec_summary::*;
use crate::coprocessor::dag::expr::{EvalConfig, Flag, SqlMode};
use crate::coprocessor::metrics::*;
use crate::coprocessor::*;

/// Utilities to build an executor DAG.
///
/// Currently all executors are executed in sequence and there is no task scheduler, so this
/// builder is in fact a pipeline builder. The builder will finally build a `Box<Executor>` which
/// may contain another executor as its source, embedded in the field, one after another. These
/// nested executors together form an executor pipeline that a single iteration at the out-most
/// executor (i.e. calling `next()`) will drive the whole pipeline.
pub struct DAGBuilder;

impl DAGBuilder {
    /// Given a list of executor descriptors and checks whether all executor descriptors can
    /// be used to build batch executors.
    pub fn check_build_batch(exec_descriptors: &[executor::Executor]) -> Result<()> {
        for ed in exec_descriptors {
            match ed.get_tp() {
                ExecType::TypeTableScan => {
                    let descriptor = ed.get_tbl_scan();
                    BatchTableScanExecutor::check_supported(&descriptor)?;
                }
                ExecType::TypeIndexScan => {
                    let descriptor = ed.get_idx_scan();
                    BatchIndexScanExecutor::check_supported(&descriptor)?;
                }
                ExecType::TypeSelection => {
                    let descriptor = ed.get_selection();
                    BatchSelectionExecutor::check_supported(&descriptor)?;
                }
                ExecType::TypeLimit => {}
                _ => {
                    return Err(box_err!("Unsupported executor {:?}", ed.get_tp()));
                }
            }
        }

        Ok(())
    }

    // Note: `S` is `'static` because we have trait objects `Executor`.
    pub fn build_batch<S: Store + 'static, C: ExecSummaryCollector + 'static>(
        executor_descriptors: Vec<executor::Executor>,
        store: S,
        ranges: Vec<KeyRange>,
        config: Arc<EvalConfig>,
    ) -> Result<Box<dyn BatchExecutor>> {
        let mut executor_descriptors = executor_descriptors.into_iter();
        let mut first_ed = executor_descriptors
            .next()
            .ok_or_else(|| Error::Other(box_err!("No executors")))?;

        let mut executor: Box<dyn BatchExecutor>;
        let mut summary_slot_index = 0;

        match first_ed.get_tp() {
            ExecType::TypeTableScan => {
                // TODO: Use static metrics.
                COPR_EXECUTOR_COUNT.with_label_values(&["tblscan"]).inc();

                let mut descriptor = first_ed.take_tbl_scan();
                let columns_info = descriptor.take_columns().into_vec();
                executor = Box::new(BatchTableScanExecutor::new(
                    C::new(summary_slot_index),
                    store,
                    config.clone(),
                    columns_info,
                    ranges,
                    descriptor.get_desc(),
                )?);
            }
            ExecType::TypeIndexScan => {
                COPR_EXECUTOR_COUNT.with_label_values(&["idxscan"]).inc();

                let mut descriptor = first_ed.take_idx_scan();
                let columns_info = descriptor.take_columns().into_vec();
                executor = Box::new(BatchIndexScanExecutor::new(
                    C::new(summary_slot_index),
                    store,
                    config.clone(),
                    columns_info,
                    ranges,
                    descriptor.get_desc(),
                    descriptor.get_unique(),
                )?);
            }
            _ => {
                return Err(Error::Other(box_err!(
                    "Unexpected first executor {:?}",
                    first_ed.get_tp()
                )));
            }
        }

        for mut ed in executor_descriptors {
            summary_slot_index += 1;

            let new_executor: Box<dyn BatchExecutor> = match ed.get_tp() {
                ExecType::TypeSelection => {
                    COPR_EXECUTOR_COUNT.with_label_values(&["selection"]).inc();

                    Box::new(BatchSelectionExecutor::new(
                        C::new(summary_slot_index),
                        config.clone(),
                        executor,
                        ed.take_selection().take_conditions().into_vec(),
                    )?)
                }
                ExecType::TypeLimit => {
                    COPR_EXECUTOR_COUNT.with_label_values(&["limit"]).inc();

                    Box::new(BatchLimitExecutor::new(
                        C::new(summary_slot_index),
                        executor,
                        ed.get_limit().get_limit() as usize,
                    )?)
                }
                _ => {
                    return Err(Error::Other(box_err!(
                        "Unexpected non-first executor {:?}",
                        first_ed.get_tp()
                    )));
                }
            };
            executor = new_executor;
        }

        Ok(executor)
    }

    /// Builds a normal executor pipeline.
    ///
    /// Normal executors iterate rows one by one.
    pub fn build_normal<S: Store + 'static, C: ExecSummaryCollector + 'static>(
        exec_descriptors: Vec<executor::Executor>,
        store: S,
        ranges: Vec<KeyRange>,
        ctx: Arc<EvalConfig>,
        collect: bool,
    ) -> Result<Box<dyn Executor + Send>> {
        let mut exec_descriptors = exec_descriptors.into_iter();
        let first = exec_descriptors
            .next()
            .ok_or_else(|| Error::Other(box_err!("has no executor")))?;

        let mut src = Self::build_normal_first_executor::<_, C>(first, store, ranges, collect)?;
        let mut summary_slot_index = 0;

        for mut exec in exec_descriptors {
            summary_slot_index += 1;

            let curr: Box<dyn Executor + Send> = match exec.get_tp() {
                ExecType::TypeTableScan | ExecType::TypeIndexScan => {
                    return Err(box_err!("got too much *scan exec, should be only one"));
                }
                ExecType::TypeSelection => Box::new(SelectionExecutor::new(
                    exec.take_selection(),
                    Arc::clone(&ctx),
                    src,
                )?),
                ExecType::TypeAggregation => Box::new(HashAggExecutor::new(
                    exec.take_aggregation(),
                    Arc::clone(&ctx),
                    src,
                )?),
                ExecType::TypeStreamAgg => Box::new(StreamAggExecutor::new(
                    Arc::clone(&ctx),
                    src,
                    exec.take_aggregation(),
                )?),
                ExecType::TypeTopN => {
                    Box::new(TopNExecutor::new(exec.take_topN(), Arc::clone(&ctx), src)?)
                }
                ExecType::TypeLimit => Box::new(LimitExecutor::new(
                    C::new(summary_slot_index),
                    exec.take_limit(),
                    src,
                )),
            };
            src = curr;
        }
        Ok(src)
    }

    /// Builds the inner-most executor for the normal executor pipeline, which can produce rows to
    /// other executors and never receive rows from other executors.
    ///
    /// The inner-most executor must be a table scan executor or an index scan executor.
    fn build_normal_first_executor<S: Store + 'static, C: ExecSummaryCollector + 'static>(
        mut first: executor::Executor,
        store: S,
        ranges: Vec<KeyRange>,
        collect: bool,
    ) -> Result<Box<dyn Executor + Send>> {
        match first.get_tp() {
            ExecType::TypeTableScan => {
                let ex = Box::new(ScanExecutor::table_scan(
                    C::new(0),
                    first.take_tbl_scan(),
                    ranges,
                    store,
                    collect,
                )?);
                Ok(ex)
            }
            ExecType::TypeIndexScan => {
                let unique = first.get_idx_scan().get_unique();
                let ex = Box::new(ScanExecutor::index_scan(
                    C::new(0),
                    first.take_idx_scan(),
                    ranges,
                    store,
                    unique,
                    collect,
                )?);
                Ok(ex)
            }
            _ => Err(box_err!(
                "first exec type should be *Scan, but get {:?}",
                first.get_tp()
            )),
        }
    }

    fn build_dag<S: Store + 'static>(
        eval_cfg: EvalConfig,
        mut req: DAGRequest,
        ranges: Vec<KeyRange>,
        store: S,
        deadline: Deadline,
        batch_row_limit: usize,
    ) -> Result<super::DAGRequestHandler> {
        let executors_len = req.get_executors().len();

        let executor = if req.get_collect_execution_summaries() {
            Self::build_normal::<_, ExecSummaryCollectorEnabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                Arc::new(eval_cfg),
                req.get_collect_range_counts(),
            )?
        } else {
            Self::build_normal::<_, ExecSummaryCollectorDisabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                Arc::new(eval_cfg),
                req.get_collect_range_counts(),
            )?
        };
        Ok(super::DAGRequestHandler::new(
            deadline,
            executor,
            req.take_output_offsets(),
            batch_row_limit,
            executors_len,
            req.get_collect_execution_summaries(),
        ))
    }

    fn build_batch_dag<S: Store + 'static>(
        deadline: Deadline,
        config: EvalConfig,
        mut req: DAGRequest,
        ranges: Vec<KeyRange>,
        store: S,
    ) -> Result<super::batch_handler::BatchDAGHandler> {
        let ranges_len = ranges.len();
        let executors_len = req.get_executors().len();
        let collect_exec_summary = req.get_collect_execution_summaries();

        let config = Arc::new(config);
        let out_most_executor = if collect_exec_summary {
            super::builder::DAGBuilder::build_batch::<_, ExecSummaryCollectorEnabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                config.clone(),
            )?
        } else {
            super::builder::DAGBuilder::build_batch::<_, ExecSummaryCollectorDisabled>(
                req.take_executors().into_vec(),
                store,
                ranges,
                config.clone(),
            )?
        };

        // Check output offsets
        let output_offsets = req.take_output_offsets();
        let schema_len = out_most_executor.schema().len();
        for offset in &output_offsets {
            if (*offset as usize) >= schema_len {
                return Err(box_err!(
                    "Invalid output offset (schema has {} columns, access index {})",
                    schema_len,
                    offset
                ));
            }
        }

        Ok(super::batch_handler::BatchDAGHandler::new(
            deadline,
            out_most_executor,
            output_offsets,
            config,
            BatchExecuteStatistics::new(
                if collect_exec_summary {
                    executors_len
                } else {
                    0 // Avoid allocation for executor summaries when it is not needed
                },
                ranges_len,
            ),
            collect_exec_summary,
        ))
    }

    pub fn build<S: Store + 'static>(
        req: DAGRequest,
        ranges: Vec<KeyRange>,
        store: S,
        deadline: Deadline,
        batch_row_limit: usize,
        is_streaming: bool,
        enable_batch_if_possible: bool,
    ) -> Result<Box<dyn RequestHandler>> {
        let mut eval_cfg = EvalConfig::from_flag(Flag::from_bits_truncate(req.get_flags()));
        // We respect time zone name first, then offset.
        if req.has_time_zone_name() && !req.get_time_zone_name().is_empty() {
            box_try!(eval_cfg.set_time_zone_by_name(req.get_time_zone_name()));
        } else if req.has_time_zone_offset() {
            box_try!(eval_cfg.set_time_zone_by_offset(req.get_time_zone_offset()));
        } else {
            // This should not be reachable. However we will not panic here in case
            // of compatibility issues.
        }
        if req.has_max_warning_count() {
            eval_cfg.set_max_warning_cnt(req.get_max_warning_count() as usize);
        }
        if req.has_sql_mode() {
            eval_cfg.set_sql_mode(SqlMode::from_bits_truncate(req.get_sql_mode()));
        }

        let mut is_batch = false;
        if enable_batch_if_possible && !is_streaming {
            let build_batch_result =
                super::builder::DAGBuilder::check_build_batch(req.get_executors());
            if let Err(e) = build_batch_result {
                info!("Coprocessor request cannot be batched"; "start_ts" => req.get_start_ts(), "reason" => %e);
            } else {
                is_batch = true;
            }
        }

        if is_batch {
            Ok(Self::build_batch_dag(deadline, eval_cfg, req, ranges, store)?.into_boxed())
        } else {
            Ok(
                Self::build_dag(eval_cfg, req, ranges, store, deadline, batch_row_limit)?
                    .into_boxed(),
            )
        }
    }
}
<|endoftext|>


<commit_before>#![crate_id(name="seq", vers="1.0.0", author="Daniel MacDougall")]

#![feature(macro_rules)]

// TODO: Make -w flag work with decimals
// TODO: Support -f flag

extern crate getopts;
extern crate libc;

use std::os;

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "seq";

fn print_usage(opts: &[getopts::OptGroup]) {
    println!("seq 1.0.0\n");
    println!("Usage:\n  seq [-w] [-s string] [-t string] [first [step]] last\n");
    println!("{:s}", getopts::usage("Print sequences of numbers", opts));
}

fn parse_float(s: &str) -> Result<f32, String>{
    match from_str(s) {
        Some(n) => Ok(n),
        None => Err(format!("seq: invalid floating point argument: {:s}", s))
    }
}

fn escape_sequences(s: &str) -> String {
    s.replace("\\n", "\n").
        replace("\\t", "\t")
}

#[allow(dead_code)]
fn main() { os::set_exit_status(uumain(os::args())); }

pub fn uumain(args: Vec<String>) -> int {
    let opts = [
        getopts::optopt("s", "separator", "Separator character (defaults to \\n)", ""),
        getopts::optopt("t", "terminator", "Terminator character (defaults to separator)", ""),
        getopts::optflag("w", "widths", "Equalize widths of all numbers by padding with zeros"),
        getopts::optflag("h", "help", "print this help text and exit"),
        getopts::optflag("V", "version", "print version and exit"),
    ];
    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => {
            show_error!("{}", f);
            print_usage(opts);
            return 1;
        }
    };
    if matches.opt_present("help") {
        print_usage(opts);
        return 0;
    }
    if matches.opt_present("version") {
        println!("seq 1.0.0");
        return 0;
    }
    if matches.free.len() < 1 || matches.free.len() > 3 {
        print_usage(opts);
        return 1;
    }
    let first = if matches.free.len() > 1 {
        match parse_float(matches.free.get(0).as_slice()) {
            Ok(n) => n,
            Err(s) => { show_error!("{:s}", s); return 1; }
        }
    } else {
        1.0
    };
    let step = if matches.free.len() > 2 {
        match parse_float(matches.free.get(1).as_slice()) {
            Ok(n) => n,
            Err(s) => { show_error!("{:s}", s); return 1; }
        }
    } else {
        1.0
    };
    let last = match parse_float(matches.free.get(matches.free.len()-1).as_slice()) {
        Ok(n) => n,
        Err(s) => { show_error!("{:s}", s); return 1; }
    };
    let separator = escape_sequences(matches.opt_str("s").unwrap_or("\n".to_string()).as_slice());
    let terminator = escape_sequences(matches.opt_str("t").unwrap_or(separator.to_string()).as_slice());
    print_seq(first, step, last, separator, terminator, matches.opt_present("w"));

    0
}

fn done_printing(next: f32, step: f32, last: f32) -> bool {
    if step > 0f32 {
        next > last
    } else {
        next < last
    }
}

fn print_seq(first: f32, step: f32, last: f32, separator: String, terminator: String, pad: bool) {
    let mut i = first;
    let maxlen = first.max(last).to_str().len();
    while !done_printing(i, step, last) {
        let ilen = i.to_str().len();
        if pad && ilen < maxlen {
            for _ in range(0, maxlen - ilen) {
                print!("0");
            }
        }
        print!("{:f}", i);
        i += step;
        if !done_printing(i, step, last) {
            print!("{:s}", separator);
        }
    }
    print!("{:s}", terminator);
}
<commit_msg>seq: now passes about half of the Busybox tests<commit_after>#![crate_id(name="seq", vers="1.0.0", author="Daniel MacDougall")]

#![feature(macro_rules)]

// TODO: Make -w flag work with decimals
// TODO: Support -f flag

extern crate getopts;
extern crate libc;

use std::os;

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "seq";

#[deriving(Clone)]
struct SeqOptions {
    separator: String,
    terminator: Option<String>,
    widths: bool
}

fn parse_float(s: &str) -> Result<f64, String>{
    match from_str(s) {
        Some(n) => Ok(n),
        None => Err(format!("seq: invalid floating point argument: {:s}", s))
    }
}

fn escape_sequences(s: &str) -> String {
    s.replace("\\n", "\n").
        replace("\\t", "\t")
}

fn parse_options(args: Vec<String>, options: &mut SeqOptions) -> Result<Vec<String>, int> {
    let mut seq_args = vec!();
    let program = args.get(0).clone();
    let mut iter = args.move_iter().skip(1);
    loop {
        match iter.next() {
            Some(arg) => match arg.as_slice() {
                "--help" | "-h" => {
                    print_help(&program);
                    return Err(0);
                }
                "--version" | "-V" => {
                    print_version();
                    return Err(0);
                }
                "-s" | "--separator" => match iter.next() {
                    Some(sep) => options.separator = sep,
                    None => {
                        show_error!("expected a separator after {}", arg);
                        return Err(1);
                    }
                },
                "-t" | "--terminator" => match iter.next() {
                    Some(term) => options.terminator = Some(term),
                    None => {
                        show_error!("expected a terminator after '{}'", arg);
                        return Err(1);
                    }
                },
                "-w" | "--widths" => options.widths = true,
                _ => {
                    if arg.len() > 1 && arg.as_slice().char_at(0) == '-' {
                        let argptr: *String = &arg;  // escape from the borrow checker
                        let mut chiter = unsafe { (*argptr).as_slice() }.chars().skip(1);
                        let mut ch = ' ';
                        while match chiter.next() { Some(m) => { ch = m; true } None => false } {
                            match ch {
                                'h' => {
                                    print_help(&program);
                                    return Err(0);
                                }
                                'V' => {
                                    print_version();
                                    return Err(0);
                                }
                                's' => match iter.next() {
                                    Some(sep) => {
                                        options.separator = sep;
                                        let next = chiter.next();
                                        if next.is_some() {
                                            show_error!("unexpected character ('{}')", next.unwrap());
                                            return Err(1);
                                        }
                                    }
                                    None => {
                                        show_error!("expected a separator after {}", arg);
                                        return Err(1);
                                    }
                                },
                                't' => match iter.next() {
                                    Some(term) => {
                                        options.terminator = Some(term);
                                        let next = chiter.next();
                                        if next.is_some() {
                                            show_error!("unexpected character ('{}')", next.unwrap());
                                            return Err(1);
                                        }
                                    }
                                    None => {
                                        show_error!("expected a terminator after {}", arg);
                                        return Err(1);
                                    }
                                },
                                'w' => options.widths = true,
                                _ => { seq_args.push(arg); break }
                            }
                        }
                    } else {
                        seq_args.push(arg);
                    }
                }
            },
            None => break
        }
    }
    Ok(seq_args)
}

fn print_help(program: &String) {
    let opts = [
        getopts::optopt("s", "separator", "Separator character (defaults to \\n)", ""),
        getopts::optopt("t", "terminator", "Terminator character (defaults to separator)", ""),
        getopts::optflag("w", "widths", "Equalize widths of all numbers by padding with zeros"),
        getopts::optflag("h", "help", "print this help text and exit"),
        getopts::optflag("V", "version", "print version and exit"),
    ];
    println!("seq 1.0.0\n");
    println!("Usage:\n  {} [-w] [-s string] [-t string] [first [step]] last\n", *program);
    println!("{:s}", getopts::usage("Print sequences of numbers", opts));
}

fn print_version() {
    println!("seq 1.0.0\n");
}

#[allow(dead_code)]
fn main() { os::set_exit_status(uumain(os::args())); }

pub fn uumain(args: Vec<String>) -> int {
    let program = args.get(0).clone();
    let mut options = SeqOptions {
        separator: "\n".to_string(),
        terminator: None,
        widths: false
    };
    let free = match parse_options(args, &mut options) {
        Ok(m) => m,
        Err(f) => return f
    };
    if free.len() < 1 || free.len() > 3 {
        crash!(1, "too {} operands.\nTry '{} --help' for more information.",
               if free.len() < 1 { "few" } else { "many" }, program);
    }
    let first = if free.len() > 1 {
        match parse_float(free.get(0).as_slice()) {
            Ok(n) => n,
            Err(s) => { show_error!("{:s}", s); return 1; }
        }
    } else {
        1.0
    };
    let step = if free.len() > 2 {
        match parse_float(free.get(1).as_slice()) {
            Ok(n) => n,
            Err(s) => { show_error!("{:s}", s); return 1; }
        }
    } else {
        1.0
    };
    let last = match parse_float(free.get(free.len()-1).as_slice()) {
        Ok(n) => n,
        Err(s) => { show_error!("{:s}", s); return 1; }
    };
    let separator = escape_sequences(options.separator.as_slice());
    let terminator = match options.terminator {
        Some(term) => escape_sequences(term.as_slice()),
        None => separator.clone()
    };
    print_seq(first, step, last, separator, terminator, options.widths);

    0
}

#[inline(always)]
fn done_printing(next: f64, step: f64, last: f64) -> bool {
    if step > 0f64 {
        next > last
    } else {
        next < last
    }
}

fn print_seq(first: f64, step: f64, last: f64, separator: String, terminator: String, pad: bool) {
    let mut i = first;
    let maxlen = first.max(last).to_str().len();
    while !done_printing(i, step, last) {
        let ilen = i.to_str().len();
        if pad && ilen < maxlen {
            for _ in range(0, maxlen - ilen) {
                print!("0");
            }
        }
        print!("{:f}", i);
        i += step;
        if !done_printing(i, step, last) {
            print!("{:s}", separator);
        }
    }
    if i != first {
        print!("{:s}", terminator);
    }
}
<|endoftext|>


<commit_before>use parking_lot::RwLock;
use std::any::Any;
use std::rc::Rc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;
use crate::core::codepointinfo::TextStyle;

use crate::core::editor::InputStageActionMap;
use crate::core::modes::core_mode::split_with_direction;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::view::ContentFilter;

use crate::core::view::FilterIo;
use crate::core::view::LayoutDirection;
use crate::core::view::LayoutEnv;
use crate::core::view::LayoutOperation;

use crate::core::view::View;

use crate::core::document::DocumentBuilder;

pub struct BasicEditorMode {
    // add common fields
}
pub struct BasicEditorModeContext {
    // add per view fields
}

impl<'a> Mode for BasicEditorMode {
    fn name(&self) -> &'static str {
        &"basic-editor"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        Self::register_input_stage_actions(&mut map);
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc BasicEditorMode-mode ctx");
        let ctx = BasicEditorModeContext {};
        Box::new(ctx)
    }

    fn configure_view(
        &mut self,
        mut editor: &mut Editor<'static>,
        mut env: &mut EditorEnv<'static>,
        mut view: &mut View<'static>,
    ) {
        let doc = view.document();

        let status_doc = DocumentBuilder::new()
            .document_name("")
            .internal(true)
            //           .use_buffer_log(false)
            .finalize();

        let status_doc = status_doc;

        // hsplit

        // children_layout_and_modes
        let ops_modes = vec![
            (
                LayoutOperation::Fixed {
                    size: 1 + 0, /* nano-like */
                },
                doc.clone(),
                vec![], // TODO(ceg): title-mode
            ),
            (
                LayoutOperation::RemainMinus { minus: 2 },
                doc.clone(),
                vec!["simple-view".to_owned()],
            ),
            (
                LayoutOperation::Fixed { size: 1 },
                None,
                vec!["hsplit-mode".to_owned()],
            ),
            (
                LayoutOperation::RemainPercent { p: 100.0 },
                status_doc,
                vec!["status-mode".to_owned()],
            ),
        ];

        let mut layout_ops = vec![];
        let mut docs = vec![];
        let mut modes = vec![];

        for e in &ops_modes {
            layout_ops.push(e.0.clone());
            docs.push(e.1.clone());
            modes.push(e.2.clone());
        }

        let (width, height) = view.dimension();
        dbg_println!("width {}  height {}", width, height);

        split_with_direction(
            &mut editor,
            &mut env,
            &mut view,
            width,
            height,
            LayoutDirection::Vertical,
            &layout_ops,
            &docs,
            &modes,
        );

        // mark children as non destroyable
        for i in 0..view.children.len() {
            let vid = view.children[i].id;
            let v = editor.view_map.get(&vid).unwrap();
            v.write().destroyable = false;
        }

        // TODO(ceg): put some kind of label/name? on
        // like view.label = 'text-view'
        // like view.label = 'status-line'
        // view.children_by_label<String, (vid, index)>
        view.destroyable = false; // root view

        // set focus on text view : TODO(ceg): title mode + configure
        let title_vid = view.children[0].id;
        let v = editor.view_map.get(&title_vid).unwrap();
        v.write()
            .compose_content_filters
            .borrow_mut()
            .push(Box::new(BasicEditorTitle::new()));

        // set focus on text view (simple-view mode)
        let simple_view_idx = 1;
        let simple_view_id = view.children[simple_view_idx].id;
        view.main_child = Some(simple_view_idx); // index in children
        view.focus_to = Some(simple_view_id); // TODO(ceg):
        env.focus_changed_to = Some(simple_view_id); // TODO(ceg):

        // TODO(ceg): status mode + configure
        // setup status view
        let status_view_idx = view.children.len() - 1;
        let status_view_id = view.children[status_view_idx].id;

        // set status_vid
        view.status_view_id = Some(status_view_id);
        env.status_view_id = Some(status_view_id);
    }
}

impl BasicEditorMode {
    pub fn new() -> Self {
        dbg_println!("BasicEditorMode");
        BasicEditorMode {}
    }

    pub fn register_input_stage_actions<'a>(_map: &'a mut InputStageActionMap<'a>) {}
}

///////////////////////////////////////////////////////////////////////////////////////////////////

struct BasicEditorTitle {
    title: String,
    width: usize,
    height: usize,
}

impl BasicEditorTitle {
    pub fn new() -> Self {
        BasicEditorTitle {
            title: String::new(),
            width: 0,
            height: 0,
        }
    }
}

use crate::core::VERSION;

impl ContentFilter<'_> for BasicEditorTitle {
    fn name(&self) -> &'static str {
        &"editor-title"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.width = env.screen.width();
        self.height = env.screen.height();

        let mut w = self.width;
        self.title = format!("unlimitED {} ", VERSION);
        w = w.saturating_sub(self.title.len());

        let view = view.read();
        let d = view.document().unwrap();
        let d = d.read();
        let mut doc_info = format!("{}", d.name);
        let dlen = doc_info.len();
        if w > dlen {
            let margin = 1; // w / 2 - dlen / 2;
            let margin = (0..margin).map(|_| " ").collect::<String>();
            self.title.push_str(&margin);
        }

        if d.changed {
            doc_info.push_str("* ");
        } else {
            doc_info.push_str("  ");
        }
        if d.is_syncing {
            doc_info.push_str("(sync)");
        }

        doc_info.push_str(&format!(" size {:<12}", d.size()));

        self.title.push_str(&doc_info);
    }

    fn run(
        &mut self,
        _view: &View,
        env: &mut LayoutEnv,
        _filter_in: &Vec<FilterIo>,
        _filter_out: &mut Vec<FilterIo>,
    ) {
        let _bg_color = (113, 114, 123);

        let len = self.title.len();
        for c in self.title.chars() {
            let mut cpi = CodepointInfo::new();
            cpi.displayed_cp = c;
            cpi.style.color = TextStyle::default_bg_color();
            cpi.style.bg_color = TextStyle::default_color();
            let (b, _) = env.screen.push(cpi.clone());
            if !b {
                break;
            }
        }

        if len >= self.width {
            env.quit = true;
            return;
        }
        let remain = self.width - len;

        let _fill = ' ' as char;
        for _i in 0..remain {
            let mut cpi = CodepointInfo::new();
            cpi.style.color = TextStyle::default_bg_color(); // remove ?
            cpi.style.bg_color = TextStyle::default_color(); // remove ?

            let (b, _) = env.screen.push(cpi.clone());
            if !b {
                env.quit = true;
                return;
            }
        }

        env.quit = true;
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {}
}
<commit_msg>status bar : add document name<commit_after>use parking_lot::RwLock;
use std::any::Any;
use std::rc::Rc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;
use crate::core::codepointinfo::TextStyle;

use crate::core::editor::InputStageActionMap;
use crate::core::modes::core_mode::split_with_direction;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::view::ContentFilter;

use crate::core::view::FilterIo;
use crate::core::view::LayoutDirection;
use crate::core::view::LayoutEnv;
use crate::core::view::LayoutOperation;

use crate::core::view::View;

use crate::core::document::DocumentBuilder;

pub struct BasicEditorMode {
    // add common fields
}
pub struct BasicEditorModeContext {
    // add per view fields
}

impl<'a> Mode for BasicEditorMode {
    fn name(&self) -> &'static str {
        &"basic-editor"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        Self::register_input_stage_actions(&mut map);
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc BasicEditorMode-mode ctx");
        let ctx = BasicEditorModeContext {};
        Box::new(ctx)
    }

    fn configure_view(
        &mut self,
        mut editor: &mut Editor<'static>,
        mut env: &mut EditorEnv<'static>,
        mut view: &mut View<'static>,
    ) {
        let doc = view.document();

        let status_doc = DocumentBuilder::new()
            .document_name("status-bar")
            .internal(true)
            //           .use_buffer_log(false)
            .finalize();

        let status_doc = status_doc;

        // hsplit

        // children_layout_and_modes
        let ops_modes = vec![
            (
                LayoutOperation::Fixed {
                    size: 1 + 0, /* nano-like */
                },
                doc.clone(),
                vec![], // TODO(ceg): title-mode
            ),
            (
                LayoutOperation::RemainMinus { minus: 2 },
                doc.clone(),
                vec!["simple-view".to_owned()],
            ),
            (
                LayoutOperation::Fixed { size: 1 },
                None,
                vec!["hsplit-mode".to_owned()],
            ),
            (
                LayoutOperation::RemainPercent { p: 100.0 },
                status_doc,
                vec!["status-mode".to_owned()],
            ),
        ];

        let mut layout_ops = vec![];
        let mut docs = vec![];
        let mut modes = vec![];

        for e in &ops_modes {
            layout_ops.push(e.0.clone());
            docs.push(e.1.clone());
            modes.push(e.2.clone());
        }

        let (width, height) = view.dimension();
        dbg_println!("width {}  height {}", width, height);

        split_with_direction(
            &mut editor,
            &mut env,
            &mut view,
            width,
            height,
            LayoutDirection::Vertical,
            &layout_ops,
            &docs,
            &modes,
        );

        // mark children as non destroyable
        for i in 0..view.children.len() {
            let vid = view.children[i].id;
            let v = editor.view_map.get(&vid).unwrap();
            v.write().destroyable = false;
        }

        // TODO(ceg): put some kind of label/name? on
        // like view.label = 'text-view'
        // like view.label = 'status-line'
        // view.children_by_label<String, (vid, index)>
        view.destroyable = false; // root view

        // set focus on text view : TODO(ceg): title mode + configure
        let title_vid = view.children[0].id;
        let v = editor.view_map.get(&title_vid).unwrap();
        v.write()
            .compose_content_filters
            .borrow_mut()
            .push(Box::new(BasicEditorTitle::new()));

        // set focus on text view (simple-view mode)
        let simple_view_idx = 1;
        let simple_view_id = view.children[simple_view_idx].id;
        view.main_child = Some(simple_view_idx); // index in children
        view.focus_to = Some(simple_view_id); // TODO(ceg):
        env.focus_changed_to = Some(simple_view_id); // TODO(ceg):

        // TODO(ceg): status mode + configure
        // setup status view
        let status_view_idx = view.children.len() - 1;
        let status_view_id = view.children[status_view_idx].id;

        // set status_vid
        view.status_view_id = Some(status_view_id);
        env.status_view_id = Some(status_view_id);
    }
}

impl BasicEditorMode {
    pub fn new() -> Self {
        dbg_println!("BasicEditorMode");
        BasicEditorMode {}
    }

    pub fn register_input_stage_actions<'a>(_map: &'a mut InputStageActionMap<'a>) {}
}

///////////////////////////////////////////////////////////////////////////////////////////////////

struct BasicEditorTitle {
    title: String,
    width: usize,
    height: usize,
}

impl BasicEditorTitle {
    pub fn new() -> Self {
        BasicEditorTitle {
            title: String::new(),
            width: 0,
            height: 0,
        }
    }
}

use crate::core::VERSION;

impl ContentFilter<'_> for BasicEditorTitle {
    fn name(&self) -> &'static str {
        &"editor-title"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.width = env.screen.width();
        self.height = env.screen.height();

        let mut w = self.width;
        self.title = format!("unlimitED {} ", VERSION);
        w = w.saturating_sub(self.title.len());

        let view = view.read();
        let d = view.document().unwrap();
        let d = d.read();
        let mut doc_info = format!("{}", d.name);
        let dlen = doc_info.len();
        if w > dlen {
            let margin = 1; // w / 2 - dlen / 2;
            let margin = (0..margin).map(|_| " ").collect::<String>();
            self.title.push_str(&margin);
        }

        if d.changed {
            doc_info.push_str("* ");
        } else {
            doc_info.push_str("  ");
        }
        if d.is_syncing {
            doc_info.push_str("(sync)");
        }

        doc_info.push_str(&format!(" size {:<12}", d.size()));

        self.title.push_str(&doc_info);
    }

    fn run(
        &mut self,
        _view: &View,
        env: &mut LayoutEnv,
        _filter_in: &Vec<FilterIo>,
        _filter_out: &mut Vec<FilterIo>,
    ) {
        let _bg_color = (113, 114, 123);

        let len = self.title.len();
        for c in self.title.chars() {
            let mut cpi = CodepointInfo::new();
            cpi.displayed_cp = c;
            cpi.style.color = TextStyle::default_bg_color();
            cpi.style.bg_color = TextStyle::default_color();
            let (b, _) = env.screen.push(cpi.clone());
            if !b {
                break;
            }
        }

        if len >= self.width {
            env.quit = true;
            return;
        }
        let remain = self.width - len;

        let _fill = ' ' as char;
        for _i in 0..remain {
            let mut cpi = CodepointInfo::new();
            cpi.style.color = TextStyle::default_bg_color(); // remove ?
            cpi.style.bg_color = TextStyle::default_color(); // remove ?

            let (b, _) = env.screen.push(cpi.clone());
            if !b {
                env.quit = true;
                return;
            }
        }

        env.quit = true;
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {}
}
<|endoftext|>


<commit_before>use crate::{
    exporter::{ExportResult, Exporter},
    texture::Texture,
};
use image::{DynamicImage, ImageBuffer, Rgba};
use std::marker::PhantomData;

/// Exporter type for images.
#[derive(Copy, Clone)]
pub struct ImageExporter<T>(PhantomData<T>);

impl<T: Texture<Pixel = Rgba<u8>>> ImageExporter<T> {
    /// Export a texture to an image type.
    pub fn export(texture: &T) -> ExportResult<DynamicImage> {
        <Self as Exporter<T>>::export(texture)
    }
}

impl<T: Texture<Pixel = Rgba<u8>>> Exporter<T> for ImageExporter<T> {
    type Output = DynamicImage;

    fn export(texture: &T) -> ExportResult<DynamicImage> {
        let width = texture.width();
        let height = texture.height();

        if width == 0 || height == 0 {
            return Err("Width or height of this texture is zero".to_string());
        }

        let mut pixels = Vec::new();

        for row in 0..height {
            for col in 0..width {
                if let Some(pixel) = texture.get(col, row) {
                    pixels.push(pixel[0]);
                    pixels.push(pixel[1]);
                    pixels.push(pixel[2]);
                    pixels.push(pixel[3]);
                } else {
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                }
            }
        }

        if let Some(image_buffer) =
            ImageBuffer::<Rgba<u8>, Vec<u8>>::from_raw(width, height, pixels)
        {
            Ok(DynamicImage::ImageRgba8(image_buffer))
        } else {
            Err("Can't export texture".to_string())
        }
    }
}
<commit_msg>Pre-allocate the Export Vec<commit_after>use crate::{
    exporter::{ExportResult, Exporter},
    texture::Texture,
};
use image::{DynamicImage, ImageBuffer, Rgba};
use std::marker::PhantomData;

/// Exporter type for images.
#[derive(Copy, Clone)]
pub struct ImageExporter<T>(PhantomData<T>);

impl<T: Texture<Pixel = Rgba<u8>>> ImageExporter<T> {
    /// Export a texture to an image type.
    pub fn export(texture: &T) -> ExportResult<DynamicImage> {
        <Self as Exporter<T>>::export(texture)
    }
}

impl<T: Texture<Pixel = Rgba<u8>>> Exporter<T> for ImageExporter<T> {
    type Output = DynamicImage;

    fn export(texture: &T) -> ExportResult<DynamicImage> {
        let width = texture.width();
        let height = texture.height();

        if width == 0 || height == 0 {
            return Err("Width or height of this texture is zero".to_string());
        }

        let mut pixels = Vec::with_capacity((width * height * 4) as usize);

        for row in 0..height {
            for col in 0..width {
                if let Some(pixel) = texture.get(col, row) {
                    pixels.push(pixel[0]);
                    pixels.push(pixel[1]);
                    pixels.push(pixel[2]);
                    pixels.push(pixel[3]);
                } else {
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                }
            }
        }

        if let Some(image_buffer) =
            ImageBuffer::<Rgba<u8>, Vec<u8>>::from_raw(width, height, pixels)
        {
            Ok(DynamicImage::ImageRgba8(image_buffer))
        } else {
            Err("Can't export texture".to_string())
        }
    }
}
<|endoftext|>


<commit_before>use core::mem;

use futures_core::{Future, IntoFuture, Async, Poll, Stream};

/// Creates a `Stream` from a seed and a closure returning a `Future`.
///
/// This function is the dual for the `Stream::fold()` adapter: while
/// `Stream::fold()` reduces a `Stream` to one single value, `unfold()` creates a
/// `Stream` from a seed value.
///
/// `unfold()` will call the provided closure with the provided seed, then wait
/// for the returned `Future` to complete with `(a, b)`. It will then yield the
/// value `a`, and use `b` as the next internal state.
///
/// If the closure returns `None` instead of `Some(Future)`, then the `unfold()`
/// will stop producing items and return `Ok(Async::Ready(None))` in future
/// calls to `poll()`.
///
/// In case of error generated by the returned `Future`, the error will be
/// returned by the `Stream`.  The `Stream` will then yield
/// `Ok(Async::Ready(None))` in future calls to `poll()`.
///
/// This function can typically be used when wanting to go from the "world of
/// futures" to the "world of streams": the provided closure can build a
/// `Future` using other library functions working on futures, and `unfold()`
/// will turn it into a `Stream` by repeating the operation.
///
/// # Example
///
/// ```rust
/// # extern crate futures;
/// # extern crate futures_executor;
///
/// use futures::prelude::*;
/// use futures::stream;
/// use futures::future;
/// use futures_executor::current_thread::run;
///
/// # fn main() {
/// let mut stream = stream::unfold(0, |state| {
///     if state <= 2 {
///         let next_state = state + 1;
///         let yielded = state  * 2;
///         let fut = future::ok::<_, u32>((yielded, next_state));
///         Some(fut)
///     } else {
///         None
///     }
/// });
///
/// let result = run(|c| c.block_on(stream.collect()));
/// assert_eq!(result, Ok(vec![0, 2, 4]));
/// # }
/// ```
pub fn unfold<T, F, Fut, It>(init: T, f: F) -> Unfold<T, F, Fut>
    where F: FnMut(T) -> Option<Fut>,
          Fut: IntoFuture<Item = (It, T)>,
{
    Unfold {
        f: f,
        state: State::Ready(init),
    }
}

/// A stream which creates futures, polls them and return their result
///
/// This stream is returned by the `futures::stream::unfold` method
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Unfold<T, F, Fut> where Fut: IntoFuture {
    f: F,
    state: State<T, Fut::Future>,
}

impl <T, F, Fut, It> Stream for Unfold<T, F, Fut>
    where F: FnMut(T) -> Option<Fut>,
          Fut: IntoFuture<Item = (It, T)>,
{
    type Item = It;
    type Error = Fut::Error;

    fn poll(&mut self) -> Poll<Option<It>, Fut::Error> {
        loop {
            match mem::replace(&mut self.state, State::Empty) {
                // State::Empty may happen if the future returned an error
                State::Empty => { return Ok(Async::Ready(None)); }
                State::Ready(state) => {
                    match (self.f)(state) {
                        Some(fut) => { self.state = State::Processing(fut.into_future()); }
                        None => { return Ok(Async::Ready(None)); }
                    }
                }
                State::Processing(mut fut) => {
                    match fut.poll()? {
                        Async:: Ready((item, next_state)) => {
                            self.state = State::Ready(next_state);
                            return Ok(Async::Ready(Some(item)));
                        }
                        Async::Pending => {
                            self.state = State::Processing(fut);
                            return Ok(Async::Pending);
                        }
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
enum State<T, F> where F: Future {
    /// Placeholder state when doing work, or when the returned Future generated an error
    Empty,

    /// Ready to generate new future; current internal state is the `T`
    Ready(T),

    /// Working on a future generated previously
    Processing(F),
}
<commit_msg>Increate unfold's flexibility<commit_after>use core::mem;

use futures_core::{Future, IntoFuture, Async, Poll, Stream};

/// Creates a `Stream` from a seed and a closure returning a `Future`.
///
/// This function is the dual for the `Stream::fold()` adapter: while
/// `Stream::fold()` reduces a `Stream` to one single value, `unfold()` creates a
/// `Stream` from a seed value.
///
/// `unfold()` will call the provided closure with the provided seed, then wait
/// for the returned `Future` to complete with `(a, b)`. It will then yield the
/// value `a`, and use `b` as the next internal state.
///
/// If the closure returns `None` instead of `Some(Future)`, then the `unfold()`
/// will stop producing items and return `Ok(Async::Ready(None))` in future
/// calls to `poll()`.
///
/// In case of error generated by the returned `Future`, the error will be
/// returned by the `Stream`.  The `Stream` will then yield
/// `Ok(Async::Ready(None))` in future calls to `poll()`.
///
/// This function can typically be used when wanting to go from the "world of
/// futures" to the "world of streams": the provided closure can build a
/// `Future` using other library functions working on futures, and `unfold()`
/// will turn it into a `Stream` by repeating the operation.
///
/// # Example
///
/// ```rust
/// # extern crate futures;
/// # extern crate futures_executor;
///
/// use futures::prelude::*;
/// use futures::stream;
/// use futures::future;
/// use futures_executor::current_thread::run;
///
/// # fn main() {
/// let mut stream = stream::unfold(0, |state| {
///     if state <= 2 {
///         let next_state = state + 1;
///         let yielded = state  * 2;
///         let fut = future::ok::<_, u32>((yielded, next_state));
///         Some(fut)
///     } else {
///         None
///     }
/// });
///
/// let result = run(|c| c.block_on(stream.collect()));
/// assert_eq!(result, Ok(vec![0, 2, 4]));
/// # }
/// ```
pub fn unfold<T, F, Fut, It>(init: T, f: F) -> Unfold<T, F, Fut>
    where F: FnMut(T) -> Fut,
          Fut: IntoFuture<Item = Option<(It, T)>>,
{
    Unfold {
        f: f,
        state: State::Ready(init),
    }
}

/// A stream which creates futures, polls them and return their result
///
/// This stream is returned by the `futures::stream::unfold` method
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Unfold<T, F, Fut> where Fut: IntoFuture {
    f: F,
    state: State<T, Fut::Future>,
}

impl <T, F, Fut, It> Stream for Unfold<T, F, Fut>
    where F: FnMut(T) -> Fut,
          Fut: IntoFuture<Item = Option<(It, T)>>,
{
    type Item = It;
    type Error = Fut::Error;

    fn poll(&mut self) -> Poll<Option<It>, Fut::Error> {
        loop {
            match mem::replace(&mut self.state, State::Empty) {
                // State::Empty may happen if the future returned an error
                State::Empty => { return Ok(Async::Ready(None)); }
                State::Ready(state) => {
                    self.state = State::Processing((self.f)(state).into_future());
                }
                State::Processing(mut fut) => {
                    match fut.poll()? {
                        Async:: Ready(Some((item, next_state))) => {
                            self.state = State::Ready(next_state);
                            return Ok(Async::Ready(Some(item)));
                        }
                        Async:: Ready(None) => {
                            return Ok(Async::Ready(None))
                        }
                        Async::Pending => {
                            self.state = State::Processing(fut);
                            return Ok(Async::Pending);
                        }
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
enum State<T, F> where F: Future {
    /// Placeholder state when doing work, or when the returned Future generated an error
    Empty,

    /// Ready to generate new future; current internal state is the `T`
    Ready(T),

    /// Working on a future generated previously
    Processing(F),
}
<|endoftext|>


<commit_before>//! Implements the API server.
use std::sync::{Arc, RwLock};
use std::thread;
use std::io::Read;
use std::os::unix::io::{FromRawFd, RawFd};

use libc;
use hyper::server::{Server, Request, Response};
use hyper::header::ContentLength;
use hyper::method::Method;
use hyper::net::HttpListener;
use hyper::uri::RequestUri;
use serde::Deserialize;
use serde_json;

use super::super::config::Config;
use super::super::memdb::stash::{MemDbStash, SyncStatus};
use super::super::Result;
use super::super::utils::{HumanDuration, run_isolated, get_systemd_fd};
use super::handlers;
use super::types::{ApiResponse, ApiError};

/// Result from a healthcheck.
#[derive(Serialize, Clone)]
pub struct HealthCheckResponse {
    pub is_offline: bool,
    pub is_healthy: bool,
    pub sync_lag: u32,
}

/// Shared access to the state of the server.
pub struct ServerContext {
    pub config: Config,
    pub stash: MemDbStash,
    enable_sync: bool,
    cached_memdb_status: RwLock<Option<SyncStatus>>,
}

/// The API server itself.
pub struct ApiServer {
    ctx: Arc<ServerContext>,
}

/// Controls how the server binds to sockets.
pub enum BindOptions<'a> {
    /// Bind according to the config file.
    UseConfig,
    /// Bind to a file descriptor.
    BindToFd(RawFd),
    /// Bind to a specific address (`host:port`).
    BindToAddr(&'a str),
}

impl ServerContext {
    pub fn check_health(&self) -> Result<()> {
        let sync_status = self.stash.get_sync_status()?;
        *self.cached_memdb_status.write().unwrap() = Some(sync_status);
        Ok(())
    }

    pub fn get_healthcheck_result(&self) -> Result<HealthCheckResponse> {
        if self.enable_sync {
            let cache_value = self.cached_memdb_status.read().unwrap();
            if let Some(ref state) = *cache_value {
                Ok(HealthCheckResponse {
                    is_offline: state.is_offline(),
                    is_healthy: state.is_healthy(),
                    sync_lag: state.lag()
                })
            } else {
                Ok(HealthCheckResponse {
                    is_offline: true,
                    is_healthy: false,
                    sync_lag: 0,
                })
            }
        } else {
            Ok(HealthCheckResponse {
                is_offline: true,
                is_healthy: true,
                sync_lag: 0,
            })
        }
    }
}

impl ApiServer {
    /// Create a new server.
    pub fn new(config: &Config, enable_sync: bool) -> Result<ApiServer> {
        Ok(ApiServer {
            ctx: Arc::new(ServerContext {
                config: config.clone(),
                stash: MemDbStash::new(config)?,
                enable_sync: enable_sync,
                cached_memdb_status: RwLock::new(None),
            }),
        })
    }

    /// Spawns a background thread that runs the sync process.
    pub fn spawn_sync_thread(&self) -> Result<()> {
        let interval = self.ctx.config.get_server_sync_interval()?;
        let std_interval = interval.to_std().unwrap();
        info!("Checking for symbols from S3 in background every {}",
              HumanDuration(interval));
        info!("Source Bucket: {}", self.ctx.config.get_aws_bucket_url()?);
        info!("Local SDKs: {}", self.ctx.stash.sdk_count()?);

        let ctx = self.ctx.clone();
        thread::spawn(move || {
            loop {
                let ctx = ctx.clone();
                run_isolated(move || ctx.stash.sync(Default::default()));
                thread::sleep(std_interval);
            }
        });

        Ok(())
    }

    /// Spawns a background check that checks the health of the system.
    pub fn spawn_healthcheck_thread(&self) -> Result<()> {
        let interval = self.ctx.config.get_server_healthcheck_interval()?;
        let std_interval = interval.to_std().unwrap();
        info!("Running healthcheck every {}", HumanDuration(interval));

        // run initial healthcheck right away.
        let ctx = self.ctx.clone();
        ctx.check_health()?;

        thread::spawn(move || {
            loop {
                let ctx = ctx.clone();
                run_isolated(move || ctx.check_health());
                thread::sleep(std_interval);
            }
        });

        Ok(())
    }

    /// Runs the server in a loop.
    pub fn run(&self, threads: usize, opts: BindOptions) -> Result<()> {
        let debug_addr;

        if self.ctx.enable_sync {
            self.spawn_sync_thread()?;
            self.spawn_healthcheck_thread()?;
        } else {
            info!("Background sync is disabled. Health check forced to healthy.");
        }

        let listener = match opts {
            BindOptions::BindToAddr(addr) => {
                debug_addr = format!("http://{}/", addr);
                HttpListener::new(addr)?
            }
            BindOptions::BindToFd(fd) => {
                debug_addr = format!("file descriptor {}", fd);
                // unsafe is okay here because we dup the fd
                unsafe { HttpListener::from_raw_fd(libc::dup(fd)) }
            }
            BindOptions::UseConfig => {
                if let Some(fd) = get_systemd_fd()? {
                    debug_addr = format!("systemd supplied fd");
                    // unsafe is sortof okay here because get_systemd_fd will
                    // not return the fd a second time and we also do not pass
                    // that information to potential children
                    unsafe { HttpListener::from_raw_fd(fd) }
                } else {
                    let addr = self.ctx.config.get_server_socket_addr()?;
                    let (host, port) = addr;
                    debug_addr = format!("http://{}:{}/", host, port);
                    HttpListener::new((host.as_str(), port))?
                }
            }
        };
        info!("Listening on {}", debug_addr);
        info!("Spawning {} listener threads", threads);

        let ctx = self.ctx.clone();
        Server::new(listener)
            .handle_threads(move |req: Request, resp: Response|
        {
            let is_head = req.method == Method::Head;
            let handler = match req.uri {
                RequestUri::AbsolutePath(ref path) => {
                    match path.as_str() {
                        "/health" => handlers::healthcheck_handler,
                        "/lookup" => handlers::lookup_symbol_handler,
                        "/sdks" => handlers::list_sdks_handler,
                        "/version" => handlers::version_handler,
                        _ => not_found,
                    }
                }
                _ => bad_request,
            };
            match handler(&*ctx.clone(), req) {
                Ok(result) => result,
                Err(err) => ApiResponse::from_error(err).unwrap(),
            }.write_to_response(is_head, resp).unwrap();
        }, threads)?;
        Ok(())
    }
}

/// Helper for the handlers to safely load request data.
pub fn load_request_data<D: Deserialize>(req: &mut Request) -> Result<D> {
    if let Some(&ContentLength(length)) = req.headers.get() {
        if length > 1024 * 1024 * 2 {
            return Err(ApiError::PayloadTooLarge.into());
        }
    } else {
        return Err(ApiError::BadRequest.into());
    }

    let mut body: Vec<u8> = vec![];
    req.read_to_end(&mut body)?;

    Ok(match serde_json::from_slice(&body[..]) {
        Ok(data) => data,
        Err(err) => { return Err(ApiError::BadJson(Box::new(err)).into()); }
    })
}

fn bad_request(_: &ServerContext, _: Request) -> Result<ApiResponse>
{
    Err(ApiError::BadRequest.into())
}

fn not_found(_: &ServerContext, _: Request) -> Result<ApiResponse>
{
    Err(ApiError::NotFound.into())
}
<commit_msg>feat: Bump max size to 10MB (#10)<commit_after>//! Implements the API server.
use std::sync::{Arc, RwLock};
use std::thread;
use std::io::Read;
use std::os::unix::io::{FromRawFd, RawFd};

use libc;
use hyper::server::{Server, Request, Response};
use hyper::header::ContentLength;
use hyper::method::Method;
use hyper::net::HttpListener;
use hyper::uri::RequestUri;
use serde::Deserialize;
use serde_json;

use super::super::config::Config;
use super::super::memdb::stash::{MemDbStash, SyncStatus};
use super::super::Result;
use super::super::utils::{HumanDuration, run_isolated, get_systemd_fd};
use super::handlers;
use super::types::{ApiResponse, ApiError};

/// Result from a healthcheck.
#[derive(Serialize, Clone)]
pub struct HealthCheckResponse {
    pub is_offline: bool,
    pub is_healthy: bool,
    pub sync_lag: u32,
}

/// Shared access to the state of the server.
pub struct ServerContext {
    pub config: Config,
    pub stash: MemDbStash,
    enable_sync: bool,
    cached_memdb_status: RwLock<Option<SyncStatus>>,
}

/// The API server itself.
pub struct ApiServer {
    ctx: Arc<ServerContext>,
}

/// Controls how the server binds to sockets.
pub enum BindOptions<'a> {
    /// Bind according to the config file.
    UseConfig,
    /// Bind to a file descriptor.
    BindToFd(RawFd),
    /// Bind to a specific address (`host:port`).
    BindToAddr(&'a str),
}

impl ServerContext {
    pub fn check_health(&self) -> Result<()> {
        let sync_status = self.stash.get_sync_status()?;
        *self.cached_memdb_status.write().unwrap() = Some(sync_status);
        Ok(())
    }

    pub fn get_healthcheck_result(&self) -> Result<HealthCheckResponse> {
        if self.enable_sync {
            let cache_value = self.cached_memdb_status.read().unwrap();
            if let Some(ref state) = *cache_value {
                Ok(HealthCheckResponse {
                    is_offline: state.is_offline(),
                    is_healthy: state.is_healthy(),
                    sync_lag: state.lag()
                })
            } else {
                Ok(HealthCheckResponse {
                    is_offline: true,
                    is_healthy: false,
                    sync_lag: 0,
                })
            }
        } else {
            Ok(HealthCheckResponse {
                is_offline: true,
                is_healthy: true,
                sync_lag: 0,
            })
        }
    }
}

impl ApiServer {
    /// Create a new server.
    pub fn new(config: &Config, enable_sync: bool) -> Result<ApiServer> {
        Ok(ApiServer {
            ctx: Arc::new(ServerContext {
                config: config.clone(),
                stash: MemDbStash::new(config)?,
                enable_sync: enable_sync,
                cached_memdb_status: RwLock::new(None),
            }),
        })
    }

    /// Spawns a background thread that runs the sync process.
    pub fn spawn_sync_thread(&self) -> Result<()> {
        let interval = self.ctx.config.get_server_sync_interval()?;
        let std_interval = interval.to_std().unwrap();
        info!("Checking for symbols from S3 in background every {}",
              HumanDuration(interval));
        info!("Source Bucket: {}", self.ctx.config.get_aws_bucket_url()?);
        info!("Local SDKs: {}", self.ctx.stash.sdk_count()?);

        let ctx = self.ctx.clone();
        thread::spawn(move || {
            loop {
                let ctx = ctx.clone();
                run_isolated(move || ctx.stash.sync(Default::default()));
                thread::sleep(std_interval);
            }
        });

        Ok(())
    }

    /// Spawns a background check that checks the health of the system.
    pub fn spawn_healthcheck_thread(&self) -> Result<()> {
        let interval = self.ctx.config.get_server_healthcheck_interval()?;
        let std_interval = interval.to_std().unwrap();
        info!("Running healthcheck every {}", HumanDuration(interval));

        // run initial healthcheck right away.
        let ctx = self.ctx.clone();
        ctx.check_health()?;

        thread::spawn(move || {
            loop {
                let ctx = ctx.clone();
                run_isolated(move || ctx.check_health());
                thread::sleep(std_interval);
            }
        });

        Ok(())
    }

    /// Runs the server in a loop.
    pub fn run(&self, threads: usize, opts: BindOptions) -> Result<()> {
        let debug_addr;

        if self.ctx.enable_sync {
            self.spawn_sync_thread()?;
            self.spawn_healthcheck_thread()?;
        } else {
            info!("Background sync is disabled. Health check forced to healthy.");
        }

        let listener = match opts {
            BindOptions::BindToAddr(addr) => {
                debug_addr = format!("http://{}/", addr);
                HttpListener::new(addr)?
            }
            BindOptions::BindToFd(fd) => {
                debug_addr = format!("file descriptor {}", fd);
                // unsafe is okay here because we dup the fd
                unsafe { HttpListener::from_raw_fd(libc::dup(fd)) }
            }
            BindOptions::UseConfig => {
                if let Some(fd) = get_systemd_fd()? {
                    debug_addr = format!("systemd supplied fd");
                    // unsafe is sortof okay here because get_systemd_fd will
                    // not return the fd a second time and we also do not pass
                    // that information to potential children
                    unsafe { HttpListener::from_raw_fd(fd) }
                } else {
                    let addr = self.ctx.config.get_server_socket_addr()?;
                    let (host, port) = addr;
                    debug_addr = format!("http://{}:{}/", host, port);
                    HttpListener::new((host.as_str(), port))?
                }
            }
        };
        info!("Listening on {}", debug_addr);
        info!("Spawning {} listener threads", threads);

        let ctx = self.ctx.clone();
        Server::new(listener)
            .handle_threads(move |req: Request, resp: Response|
        {
            let is_head = req.method == Method::Head;
            let handler = match req.uri {
                RequestUri::AbsolutePath(ref path) => {
                    match path.as_str() {
                        "/health" => handlers::healthcheck_handler,
                        "/lookup" => handlers::lookup_symbol_handler,
                        "/sdks" => handlers::list_sdks_handler,
                        "/version" => handlers::version_handler,
                        _ => not_found,
                    }
                }
                _ => bad_request,
            };
            match handler(&*ctx.clone(), req) {
                Ok(result) => result,
                Err(err) => ApiResponse::from_error(err).unwrap(),
            }.write_to_response(is_head, resp).unwrap();
        }, threads)?;
        Ok(())
    }
}

/// Helper for the handlers to safely load request data.
pub fn load_request_data<D: Deserialize>(req: &mut Request) -> Result<D> {
    if let Some(&ContentLength(length)) = req.headers.get() {
        if length > 1024 * 1024 * 10 {
            return Err(ApiError::PayloadTooLarge.into());
        }
    } else {
        return Err(ApiError::BadRequest.into());
    }

    let mut body: Vec<u8> = vec![];
    req.read_to_end(&mut body)?;

    Ok(match serde_json::from_slice(&body[..]) {
        Ok(data) => data,
        Err(err) => { return Err(ApiError::BadJson(Box::new(err)).into()); }
    })
}

fn bad_request(_: &ServerContext, _: Request) -> Result<ApiResponse>
{
    Err(ApiError::BadRequest.into())
}

fn not_found(_: &ServerContext, _: Request) -> Result<ApiResponse>
{
    Err(ApiError::NotFound.into())
}
<|endoftext|>


<commit_before>use super::{BeaconChain, ClientDB, SlotClock};
use types::{beacon_state::CommitteesError, PublicKey};

#[derive(Debug, PartialEq)]
pub enum Error {
    SlotClockError,
    CommitteesError(CommitteesError),
}

impl<T, U> BeaconChain<T, U>
where
    T: ClientDB,
    U: SlotClock,
{
    pub fn validator_index(&self, pubkey: &PublicKey) -> Option<usize> {
        for (i, validator) in self
            .head()
            .beacon_state
            .validator_registry
            .iter()
            .enumerate()
        {
            if validator.pubkey == *pubkey {
                return Some(i);
            }
        }
        None
    }

    pub fn proposer_slots(&self, validator_index: usize) -> Option<u64> {
        let slot = self.present_slot()?;
        let state = self.state(slot).ok()?;

        if let Some(validator) = state.validator_registry.get(validator_index) {
            Some(validator.proposer_slots)
        } else {
            None
        }
    }

    pub fn present_slot(&self) -> Option<u64> {
        match self.slot_clock.present_slot() {
            Ok(some_slot) => some_slot,
            _ => None,
        }
    }

    pub fn block_proposer(&self, slot: u64) -> Result<usize, CommitteesError> {
        // TODO: fix unwrap
        let present_slot = self.present_slot().unwrap();
        // TODO: fix unwrap
        let state = self.state(present_slot).unwrap();
        let index = state.get_beacon_proposer_index(slot, &self.spec)?;

        Ok(index)
    }

    pub fn justified_slot(&self) -> u64 {
        self.justified_head
            .read()
            .expect("Justified head poisoned")
            .beacon_block
            .slot
    }

    pub fn validator_attestion_slot_and_shard(&self, validator_index: usize) -> Option<(u64, u64)> {
        let present_slot = self.present_slot()?;
        let state = self.state(present_slot).ok()?;

        let (slot, shard, _committee) = state
            .attestation_slot_and_shard_for_validator(validator_index, &self.spec)
            .ok()?;
        Some((slot, shard))
    }
}

impl From<CommitteesError> for Error {
    fn from(e: CommitteesError) -> Error {
        Error::CommitteesError(e)
    }
}
<commit_msg>Refactor justified_slot fn<commit_after>use super::{BeaconChain, ClientDB, SlotClock};
use types::{beacon_state::CommitteesError, PublicKey};

#[derive(Debug, PartialEq)]
pub enum Error {
    SlotClockError,
    CommitteesError(CommitteesError),
}

impl<T, U> BeaconChain<T, U>
where
    T: ClientDB,
    U: SlotClock,
{
    pub fn validator_index(&self, pubkey: &PublicKey) -> Option<usize> {
        for (i, validator) in self
            .head()
            .beacon_state
            .validator_registry
            .iter()
            .enumerate()
        {
            if validator.pubkey == *pubkey {
                return Some(i);
            }
        }
        None
    }

    pub fn proposer_slots(&self, validator_index: usize) -> Option<u64> {
        let slot = self.present_slot()?;
        let state = self.state(slot).ok()?;

        if let Some(validator) = state.validator_registry.get(validator_index) {
            Some(validator.proposer_slots)
        } else {
            None
        }
    }

    pub fn present_slot(&self) -> Option<u64> {
        match self.slot_clock.present_slot() {
            Ok(some_slot) => some_slot,
            _ => None,
        }
    }

    pub fn block_proposer(&self, slot: u64) -> Result<usize, CommitteesError> {
        // TODO: fix unwrap
        let present_slot = self.present_slot().unwrap();
        // TODO: fix unwrap
        let state = self.state(present_slot).unwrap();
        let index = state.get_beacon_proposer_index(slot, &self.spec)?;

        Ok(index)
    }

    pub fn justified_slot(&self) -> u64 {
        // TODO: fix unwrap
        let present_slot = self.present_slot().unwrap();
        // TODO: fix unwrap
        let state = self.state(present_slot).unwrap();
        state.justified_slot
        /*
        self.justified_head
            .read()
            .expect("Justified head poisoned")
            .beacon_block
            .slot
            */
    }

    pub fn validator_attestion_slot_and_shard(&self, validator_index: usize) -> Option<(u64, u64)> {
        let present_slot = self.present_slot()?;
        let state = self.state(present_slot).ok()?;

        let (slot, shard, _committee) = state
            .attestation_slot_and_shard_for_validator(validator_index, &self.spec)
            .ok()?;
        Some((slot, shard))
    }
}

impl From<CommitteesError> for Error {
    fn from(e: CommitteesError) -> Error {
        Error::CommitteesError(e)
    }
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[link(name = "rust-crypto",
       package_id = "rust-crypto",
       vers = "0.1",
       uuid = "e8b901bb-dcef-4d06-8e78-1ff2872822dc",
       url = "https://github.com/DaGenix/rust-crypto/tree/master/src/rust-crypto")];
#[license = "MIT/ASL2"];

#[feature(asm)];
#[feature(macro_rules)];

extern mod extra;

mod cryptoutil;
pub mod digest;
pub mod hmac;
pub mod mac;
pub mod md5;
pub mod pbkdf2;
pub mod scrypt;
pub mod sha1;
pub mod sha2;
<commit_msg>Add pkgid attribute<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[link(name = "rust-crypto",
       package_id = "rust-crypto",
       vers = "0.1",
       uuid = "e8b901bb-dcef-4d06-8e78-1ff2872822dc",
       url = "https://github.com/DaGenix/rust-crypto/tree/master/src/rust-crypto")];
#[license = "MIT/ASL2"];
#[pkgid = "github.com/DaGenix/rust-crypto#0.1"];

#[feature(asm)];
#[feature(macro_rules)];

extern mod extra;

mod cryptoutil;
pub mod digest;
pub mod hmac;
pub mod mac;
pub mod md5;
pub mod pbkdf2;
pub mod scrypt;
pub mod sha1;
pub mod sha2;
<|endoftext|>