<commit_before>// Copyright (c) 2016-2019 Fabian Schuiki

//! Representation of constant values and their operations
//!
//! This module implements a representation for values that may arise within a
//! SystemVerilog source text and provides ways of executing common operations
//! such as addition and multiplication. It also provides the ability to
//! evaluate the constant value of nodes in a context.
//!
//! The operations in this module are intended to panic if invalid combinations
//! of values are used. The compiler's type system should catch and prevent such
//! uses.

use crate::{
    crate_prelude::*,
    hir::HirNode,
    ty::{Type, TypeKind},
    ParamEnv, ParamEnvBinding,
};
use num::{BigInt, BigRational, One, ToPrimitive, Zero};

/// A verilog value.
pub type Value<'t> = &'t ValueData<'t>;

/// The data associated with a value.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ValueData<'t> {
    /// The type of the value.
    pub ty: Type<'t>,
    /// The actual value.
    pub kind: ValueKind,
}

/// The different forms a value can assume.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValueKind {
    /// The `void` value.
    Void,
    /// An arbitrary precision integer.
    Int(BigInt),
    /// An arbitrary precision time interval.
    Time(BigRational),
}

impl<'t> ValueData<'t> {
    /// Check if this value evaluates to true.
    pub fn is_true(&self) -> bool {
        !self.is_false()
    }

    /// Check if this value evaluates to false.
    pub fn is_false(&self) -> bool {
        match self.kind {
            ValueKind::Void => true,
            ValueKind::Int(ref v) => v.is_zero(),
            ValueKind::Time(ref v) => v.is_zero(),
        }
    }
}

/// Create a new integer value.
///
/// Panics if `ty` is not an integer type. Truncates the value to `ty`.
pub fn make_int(ty: Type, mut value: BigInt) -> ValueData {
    match *ty {
        TypeKind::Int(width, _) => {
            value = value % (BigInt::from(1) << width);
        }
        TypeKind::Bit(_) => {
            value = value % 2;
        }
        _ => panic!("create int value `{}` with non-int type {:?}", value, ty),
    }
    ValueData {
        ty: ty,
        kind: ValueKind::Int(value),
    }
}

/// Create a new time value.
pub fn make_time(value: BigRational) -> ValueData<'static> {
    ValueData {
        ty: &ty::TIME_TYPE,
        kind: ValueKind::Time(value),
    }
}

/// Determine the constant value of a node.
pub(crate) fn constant_value_of<'gcx>(
    cx: &impl Context<'gcx>,
    node_id: NodeId,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let hir = cx.hir_of(node_id)?;
    match hir {
        HirNode::Expr(expr) => const_expr(cx, expr, env),
        HirNode::ValueParam(param) => {
            let env_data = cx.param_env_data(env);
            match env_data.find_value(node_id) {
                Some(ParamEnvBinding::Indirect(assigned_id)) => {
                    return cx.constant_value_of(assigned_id.0, assigned_id.1)
                }
                Some(ParamEnvBinding::Direct(v)) => return Ok(v),
                _ => (),
            }
            if let Some(default) = param.default {
                return cx.constant_value_of(default, env);
            }
            let mut d = DiagBuilder2::error(format!(
                "{} not assigned and has no default",
                param.desc_full(),
            ));
            let contexts = cx.param_env_contexts(env);
            for &context in &contexts {
                d = d.span(cx.span(context));
            }
            if contexts.is_empty() {
                d = d.span(param.human_span());
            }
            cx.emit(d);
            Err(())
        }
        HirNode::GenvarDecl(decl) => {
            let env_data = cx.param_env_data(env);
            match env_data.find_value(node_id) {
                Some(ParamEnvBinding::Indirect(assigned_id)) => {
                    return cx.constant_value_of(assigned_id.0, assigned_id.1)
                }
                Some(ParamEnvBinding::Direct(v)) => return Ok(v),
                _ => (),
            }
            if let Some(init) = decl.init {
                return cx.constant_value_of(init, env);
            }
            cx.emit(
                DiagBuilder2::error(format!("{} not initialized", decl.desc_full()))
                    .span(decl.human_span()),
            );
            Err(())
        }
        _ => cx.unimp_msg("constant value computation of", &hir),
    }
}

/// Determine the constant value of an expression.
fn const_expr<'gcx>(
    cx: &impl Context<'gcx>,
    expr: &hir::Expr,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let ty = cx.type_of(expr.id, env)?;
    #[allow(unreachable_patterns)]
    match expr.kind {
        hir::ExprKind::IntConst(_, ref k) => Ok(cx.intern_value(make_int(ty, k.clone()))),
        hir::ExprKind::UnsizedConst('0') => Ok(cx.intern_value(make_int(ty, num::zero()))),
        hir::ExprKind::UnsizedConst('1') => Ok(cx.intern_value(make_int(ty, num::one()))),
        hir::ExprKind::TimeConst(ref k) => Ok(cx.intern_value(make_time(k.clone()))),
        hir::ExprKind::Ident(_) => cx.constant_value_of(cx.resolve_node(expr.id, env)?, env),
        hir::ExprKind::Unary(op, arg) => {
            let arg_val = cx.constant_value_of(arg, env)?;
            debug!("exec {:?}({:?})", op, arg_val);
            match arg_val.kind {
                ValueKind::Int(ref arg) => Ok(cx.intern_value(make_int(
                    ty,
                    const_unary_op_on_int(cx, expr.span, ty, op, arg)?,
                ))),
                _ => {
                    cx.emit(
                        DiagBuilder2::error(format!(
                            "{} cannot be applied to the given arguments",
                            op.desc_full(),
                        ))
                        .span(expr.span()),
                    );
                    Err(())
                }
            }
        }
        hir::ExprKind::Binary(op, lhs, rhs) => {
            let lhs_val = cx.constant_value_of(lhs, env)?;
            let rhs_val = cx.constant_value_of(rhs, env)?;
            debug!("exec {:?}({:?}, {:?})", op, lhs_val, rhs_val);
            match (&lhs_val.kind, &rhs_val.kind) {
                (&ValueKind::Int(ref lhs), &ValueKind::Int(ref rhs)) => Ok(cx.intern_value(
                    make_int(ty, const_binary_op_on_int(cx, expr.span, ty, op, lhs, rhs)?),
                )),
                _ => {
                    cx.emit(
                        DiagBuilder2::error(format!(
                            "{} cannot be applied to the given arguments",
                            op.desc_full(),
                        ))
                        .span(expr.span()),
                    );
                    Err(())
                }
            }
        }
        hir::ExprKind::Builtin(hir::BuiltinCall::Clog2(arg)) => {
            let arg_val = cx.constant_value_of(arg, env)?;
            let arg_int = match arg_val.kind {
                ValueKind::Int(ref arg) => arg,
                _ => unreachable!(),
            };
            Ok(cx.intern_value(make_int(arg_val.ty, arg_int.bits().into())))
        }
        hir::ExprKind::Ternary(cond, true_expr, false_expr) => {
            let cond_val = cx.constant_value_of(cond, env)?;
            match cond_val.is_true() {
                true => cx.constant_value_of(true_expr, env),
                false => cx.constant_value_of(false_expr, env),
            }
        }
        _ => cx.unimp_msg("constant value computation of", expr),
    }
}

fn const_unary_op_on_int<'gcx>(
    cx: &impl Context<'gcx>,
    span: Span,
    ty: Type<'gcx>,
    op: hir::UnaryOp,
    arg: &BigInt,
) -> Result<BigInt> {
    Ok(match op {
        hir::UnaryOp::BitNot => (BigInt::one() << ty.width()) - 1 - arg,
        hir::UnaryOp::LogicNot => (arg.is_zero() as usize).into(),
        _ => {
            cx.emit(
                DiagBuilder2::error(format!(
                    "{} cannot be applied to integer `{}`",
                    op.desc_full(),
                    arg,
                ))
                .span(span),
            );
            return Err(());
        }
    })
}

fn const_binary_op_on_int<'gcx>(
    cx: &impl Context<'gcx>,
    span: Span,
    _ty: Type<'gcx>,
    op: hir::BinaryOp,
    lhs: &BigInt,
    rhs: &BigInt,
) -> Result<BigInt> {
    Ok(match op {
        hir::BinaryOp::Add => lhs + rhs,
        hir::BinaryOp::Sub => lhs - rhs,
        hir::BinaryOp::Mul => lhs * rhs,
        hir::BinaryOp::Div => lhs / rhs,
        hir::BinaryOp::Mod => lhs % rhs,
        hir::BinaryOp::Eq => ((lhs == rhs) as usize).into(),
        hir::BinaryOp::Neq => ((lhs != rhs) as usize).into(),
        hir::BinaryOp::Lt => ((lhs < rhs) as usize).into(),
        hir::BinaryOp::Leq => ((lhs <= rhs) as usize).into(),
        hir::BinaryOp::Gt => ((lhs > rhs) as usize).into(),
        hir::BinaryOp::Geq => ((lhs >= rhs) as usize).into(),
        hir::BinaryOp::LogicShL | hir::BinaryOp::ArithShL => match rhs.to_isize() {
            Some(sh) if sh < 0 => lhs >> -sh as usize,
            Some(sh) => lhs << sh as usize,
            None => num::zero(),
        },
        hir::BinaryOp::LogicShR | hir::BinaryOp::ArithShR => match rhs.to_isize() {
            Some(sh) if sh < 0 => lhs << -sh as usize,
            Some(sh) => lhs >> sh as usize,
            None => num::zero(),
        },
        _ => {
            cx.emit(
                DiagBuilder2::error(format!(
                    "{} cannot be applied to integers `{}` and `{}`",
                    op.desc_full(),
                    lhs,
                    rhs
                ))
                .span(span),
            );
            return Err(());
        }
    })
}

/// Determine the default value of a type.
pub(crate) fn type_default_value<'gcx>(cx: &impl Context<'gcx>, ty: Type<'gcx>) -> Value<'gcx> {
    match *ty {
        TypeKind::Void => cx.intern_value(ValueData {
            ty: &ty::VOID_TYPE,
            kind: ValueKind::Void,
        }),
        TypeKind::Time => cx.intern_value(make_time(Zero::zero())),
        TypeKind::Bit(..) | TypeKind::Int(..) => cx.intern_value(make_int(ty, Zero::zero())),
        TypeKind::Named(_, _, ty) => type_default_value(cx, ty),
        TypeKind::Struct(_) => {
            unimplemented!("create a struct value and fill in defaults for each field")
        }
        TypeKind::PackedArray(..) => {
            unimplemented!("create a packed array value and fill in defaults for each element")
        }
    }
}
<commit_msg>errors: improve constant value errors<commit_after>// Copyright (c) 2016-2019 Fabian Schuiki

//! Representation of constant values and their operations
//!
//! This module implements a representation for values that may arise within a
//! SystemVerilog source text and provides ways of executing common operations
//! such as addition and multiplication. It also provides the ability to
//! evaluate the constant value of nodes in a context.
//!
//! The operations in this module are intended to panic if invalid combinations
//! of values are used. The compiler's type system should catch and prevent such
//! uses.

use crate::{
    crate_prelude::*,
    hir::HirNode,
    ty::{Type, TypeKind},
    ParamEnv, ParamEnvBinding,
};
use num::{BigInt, BigRational, One, ToPrimitive, Zero};

/// A verilog value.
pub type Value<'t> = &'t ValueData<'t>;

/// The data associated with a value.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ValueData<'t> {
    /// The type of the value.
    pub ty: Type<'t>,
    /// The actual value.
    pub kind: ValueKind,
}

/// The different forms a value can assume.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValueKind {
    /// The `void` value.
    Void,
    /// An arbitrary precision integer.
    Int(BigInt),
    /// An arbitrary precision time interval.
    Time(BigRational),
}

impl<'t> ValueData<'t> {
    /// Check if this value evaluates to true.
    pub fn is_true(&self) -> bool {
        !self.is_false()
    }

    /// Check if this value evaluates to false.
    pub fn is_false(&self) -> bool {
        match self.kind {
            ValueKind::Void => true,
            ValueKind::Int(ref v) => v.is_zero(),
            ValueKind::Time(ref v) => v.is_zero(),
        }
    }
}

/// Create a new integer value.
///
/// Panics if `ty` is not an integer type. Truncates the value to `ty`.
pub fn make_int(ty: Type, mut value: BigInt) -> ValueData {
    match *ty {
        TypeKind::Int(width, _) => {
            value = value % (BigInt::from(1) << width);
        }
        TypeKind::Bit(_) => {
            value = value % 2;
        }
        _ => panic!("create int value `{}` with non-int type {:?}", value, ty),
    }
    ValueData {
        ty: ty,
        kind: ValueKind::Int(value),
    }
}

/// Create a new time value.
pub fn make_time(value: BigRational) -> ValueData<'static> {
    ValueData {
        ty: &ty::TIME_TYPE,
        kind: ValueKind::Time(value),
    }
}

/// Determine the constant value of a node.
pub(crate) fn constant_value_of<'gcx>(
    cx: &impl Context<'gcx>,
    node_id: NodeId,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let hir = cx.hir_of(node_id)?;
    match hir {
        HirNode::Expr(expr) => const_expr(cx, expr, env),
        HirNode::ValueParam(param) => {
            let env_data = cx.param_env_data(env);
            match env_data.find_value(node_id) {
                Some(ParamEnvBinding::Indirect(assigned_id)) => {
                    return cx.constant_value_of(assigned_id.0, assigned_id.1)
                }
                Some(ParamEnvBinding::Direct(v)) => return Ok(v),
                _ => (),
            }
            if let Some(default) = param.default {
                return cx.constant_value_of(default, env);
            }
            let mut d = DiagBuilder2::error(format!(
                "{} not assigned and has no default",
                param.desc_full(),
            ));
            let contexts = cx.param_env_contexts(env);
            for &context in &contexts {
                d = d.span(cx.span(context));
            }
            if contexts.is_empty() {
                d = d.span(param.human_span());
            }
            cx.emit(d);
            Err(())
        }
        HirNode::GenvarDecl(decl) => {
            let env_data = cx.param_env_data(env);
            match env_data.find_value(node_id) {
                Some(ParamEnvBinding::Indirect(assigned_id)) => {
                    return cx.constant_value_of(assigned_id.0, assigned_id.1)
                }
                Some(ParamEnvBinding::Direct(v)) => return Ok(v),
                _ => (),
            }
            if let Some(init) = decl.init {
                return cx.constant_value_of(init, env);
            }
            cx.emit(
                DiagBuilder2::error(format!("{} not initialized", decl.desc_full()))
                    .span(decl.human_span()),
            );
            Err(())
        }
        HirNode::VarDecl(_) => {
            cx.emit(
                DiagBuilder2::error(format!("{} has no constant value", hir.desc_full()))
                    .span(hir.human_span()),
            );
            Err(())
        }
        _ => cx.unimp_msg("constant value computation of", &hir),
    }
}

/// Determine the constant value of an expression.
fn const_expr<'gcx>(
    cx: &impl Context<'gcx>,
    expr: &hir::Expr,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let ty = cx.type_of(expr.id, env)?;
    #[allow(unreachable_patterns)]
    match expr.kind {
        hir::ExprKind::IntConst(_, ref k) => Ok(cx.intern_value(make_int(ty, k.clone()))),
        hir::ExprKind::UnsizedConst('0') => Ok(cx.intern_value(make_int(ty, num::zero()))),
        hir::ExprKind::UnsizedConst('1') => Ok(cx.intern_value(make_int(ty, num::one()))),
        hir::ExprKind::TimeConst(ref k) => Ok(cx.intern_value(make_time(k.clone()))),
        hir::ExprKind::Ident(_) => {
            let binding = cx.resolve_node(expr.id, env)?;
            match cx.constant_value_of(binding, env) {
                Ok(k) => Ok(k),
                Err(_) => {
                    let hir = cx.hir_of(binding)?;
                    cx.emit(
                        DiagBuilder2::note(format!(
                            "constant value of {} needed here",
                            hir.desc_full()
                        ))
                        .span(cx.span(expr.id)),
                    );
                    Err(())
                }
            }
        }
        hir::ExprKind::Unary(op, arg) => {
            let arg_val = cx.constant_value_of(arg, env)?;
            debug!("exec {:?}({:?})", op, arg_val);
            match arg_val.kind {
                ValueKind::Int(ref arg) => Ok(cx.intern_value(make_int(
                    ty,
                    const_unary_op_on_int(cx, expr.span, ty, op, arg)?,
                ))),
                _ => {
                    cx.emit(
                        DiagBuilder2::error(format!(
                            "{} cannot be applied to the given arguments",
                            op.desc_full(),
                        ))
                        .span(expr.span()),
                    );
                    Err(())
                }
            }
        }
        hir::ExprKind::Binary(op, lhs, rhs) => {
            let lhs_val = cx.constant_value_of(lhs, env)?;
            let rhs_val = cx.constant_value_of(rhs, env)?;
            debug!("exec {:?}({:?}, {:?})", op, lhs_val, rhs_val);
            match (&lhs_val.kind, &rhs_val.kind) {
                (&ValueKind::Int(ref lhs), &ValueKind::Int(ref rhs)) => Ok(cx.intern_value(
                    make_int(ty, const_binary_op_on_int(cx, expr.span, ty, op, lhs, rhs)?),
                )),
                _ => {
                    cx.emit(
                        DiagBuilder2::error(format!(
                            "{} cannot be applied to the given arguments",
                            op.desc_full(),
                        ))
                        .span(expr.span()),
                    );
                    Err(())
                }
            }
        }
        hir::ExprKind::Builtin(hir::BuiltinCall::Clog2(arg)) => {
            let arg_val = cx.constant_value_of(arg, env)?;
            let arg_int = match arg_val.kind {
                ValueKind::Int(ref arg) => arg,
                _ => unreachable!(),
            };
            Ok(cx.intern_value(make_int(arg_val.ty, arg_int.bits().into())))
        }
        hir::ExprKind::Ternary(cond, true_expr, false_expr) => {
            let cond_val = cx.constant_value_of(cond, env)?;
            match cond_val.is_true() {
                true => cx.constant_value_of(true_expr, env),
                false => cx.constant_value_of(false_expr, env),
            }
        }
        _ => cx.unimp_msg("constant value computation of", expr),
    }
}

fn const_unary_op_on_int<'gcx>(
    cx: &impl Context<'gcx>,
    span: Span,
    ty: Type<'gcx>,
    op: hir::UnaryOp,
    arg: &BigInt,
) -> Result<BigInt> {
    Ok(match op {
        hir::UnaryOp::BitNot => (BigInt::one() << ty.width()) - 1 - arg,
        hir::UnaryOp::LogicNot => (arg.is_zero() as usize).into(),
        _ => {
            cx.emit(
                DiagBuilder2::error(format!(
                    "{} cannot be applied to integer `{}`",
                    op.desc_full(),
                    arg,
                ))
                .span(span),
            );
            return Err(());
        }
    })
}

fn const_binary_op_on_int<'gcx>(
    cx: &impl Context<'gcx>,
    span: Span,
    _ty: Type<'gcx>,
    op: hir::BinaryOp,
    lhs: &BigInt,
    rhs: &BigInt,
) -> Result<BigInt> {
    Ok(match op {
        hir::BinaryOp::Add => lhs + rhs,
        hir::BinaryOp::Sub => lhs - rhs,
        hir::BinaryOp::Mul => lhs * rhs,
        hir::BinaryOp::Div => lhs / rhs,
        hir::BinaryOp::Mod => lhs % rhs,
        hir::BinaryOp::Eq => ((lhs == rhs) as usize).into(),
        hir::BinaryOp::Neq => ((lhs != rhs) as usize).into(),
        hir::BinaryOp::Lt => ((lhs < rhs) as usize).into(),
        hir::BinaryOp::Leq => ((lhs <= rhs) as usize).into(),
        hir::BinaryOp::Gt => ((lhs > rhs) as usize).into(),
        hir::BinaryOp::Geq => ((lhs >= rhs) as usize).into(),
        hir::BinaryOp::LogicShL | hir::BinaryOp::ArithShL => match rhs.to_isize() {
            Some(sh) if sh < 0 => lhs >> -sh as usize,
            Some(sh) => lhs << sh as usize,
            None => num::zero(),
        },
        hir::BinaryOp::LogicShR | hir::BinaryOp::ArithShR => match rhs.to_isize() {
            Some(sh) if sh < 0 => lhs << -sh as usize,
            Some(sh) => lhs >> sh as usize,
            None => num::zero(),
        },
        _ => {
            cx.emit(
                DiagBuilder2::error(format!(
                    "{} cannot be applied to integers `{}` and `{}`",
                    op.desc_full(),
                    lhs,
                    rhs
                ))
                .span(span),
            );
            return Err(());
        }
    })
}

/// Determine the default value of a type.
pub(crate) fn type_default_value<'gcx>(cx: &impl Context<'gcx>, ty: Type<'gcx>) -> Value<'gcx> {
    match *ty {
        TypeKind::Void => cx.intern_value(ValueData {
            ty: &ty::VOID_TYPE,
            kind: ValueKind::Void,
        }),
        TypeKind::Time => cx.intern_value(make_time(Zero::zero())),
        TypeKind::Bit(..) | TypeKind::Int(..) => cx.intern_value(make_int(ty, Zero::zero())),
        TypeKind::Named(_, _, ty) => type_default_value(cx, ty),
        TypeKind::Struct(_) => {
            unimplemented!("create a struct value and fill in defaults for each field")
        }
        TypeKind::PackedArray(..) => {
            unimplemented!("create a packed array value and fill in defaults for each element")
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2019 Fabian Schuiki

//! Representation of constant values and their operations
//!
//! This module implements a representation for values that may arise within a
//! SystemVerilog source text and provides ways of executing common operations
//! such as addition and multiplication. It also provides the ability to
//! evaluate the constant value of nodes in a context.
//!
//! The operations in this module are intended to panic if invalid combinations
//! of values are used. The compiler's type system should catch and prevent such
//! uses.

use crate::{
    crate_prelude::*,
    hir::HirNode,
    ty::{Type, TypeKind},
    ParamEnv,
};
use num::{BigInt, Zero};

/// A verilog value.
pub type Value<'t> = &'t ValueData<'t>;

/// The data associated with a value.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ValueData<'t> {
    /// The type of the value.
    pub ty: Type<'t>,
    /// The actual value.
    pub kind: ValueKind,
}

/// The different forms a value can assume.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValueKind {
    /// The `void` value.
    Void,
    /// An arbitrary precision integer.
    Int(BigInt),
}

/// Create a new integer value.
///
/// Panics if `ty` is not an integer type. Truncates the value to `ty`.
pub fn make_int(ty: Type, mut value: BigInt) -> ValueData {
    match *ty {
        TypeKind::Int(width, _) => {
            value = value % (BigInt::from(1) << width);
        }
        TypeKind::Bit(_) => {
            value = value % 2;
        }
        _ => panic!("create int value `{}` with non-int type {:?}", value, ty),
    }
    ValueData {
        ty: ty,
        kind: ValueKind::Int(value),
    }
}

/// Determine the constant value of a node.
pub(crate) fn constant_value_of<'gcx>(
    cx: &impl Context<'gcx>,
    node_id: NodeId,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let hir = cx.hir_of(node_id)?;
    match hir {
        HirNode::Expr(expr) => const_expr(cx, expr, env),
        HirNode::ValueParam(param) => {
            let env_data = cx.param_env_data(env);
            if let Some(assigned_id) = env_data.find_value(node_id) {
                return cx.constant_value_of(assigned_id.0, assigned_id.1);
            }
            if let Some(default) = param.default {
                return cx.constant_value_of(default, env);
            }
            let mut d = DiagBuilder2::error(format!(
                "{} not assigned and has no default",
                param.desc_full(),
            ));
            let contexts = cx.param_env_contexts(env);
            for &context in &contexts {
                d = d.span(cx.span(context));
            }
            if contexts.is_empty() {
                d = d.span(param.human_span());
            }
            cx.emit(d);
            Err(())
        }
        _ => cx.unimp_msg("constant value computation of", &hir),
    }
}

/// Determine the constant value of an expression.
fn const_expr<'gcx>(
    cx: &impl Context<'gcx>,
    expr: &hir::Expr,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let ty = cx.type_of(expr.id, env)?;
    #[allow(unreachable_patterns)]
    match expr.kind {
        hir::ExprKind::IntConst(ref k) => Ok(cx.intern_value(make_int(ty, k.clone()))),
        hir::ExprKind::Ident(_) => cx.constant_value_of(cx.resolve_node(expr.id, env)?, env),
        _ => cx.unimp_msg("constant value computation of", expr),
    }
}

/// Determine the default value of a type.
pub(crate) fn type_default_value<'gcx>(cx: &impl Context<'gcx>, ty: Type<'gcx>) -> Value<'gcx> {
    match *ty {
        TypeKind::Void => cx.intern_value(ValueData {
            ty,
            kind: ValueKind::Void,
        }),
        TypeKind::Bit(..) | TypeKind::Int(..) => cx.intern_value(make_int(ty, Zero::zero())),
        TypeKind::Named(_, _, ty) => type_default_value(cx, ty),
    }
}
<commit_msg>svlog: add time constants<commit_after>// Copyright (c) 2016-2019 Fabian Schuiki

//! Representation of constant values and their operations
//!
//! This module implements a representation for values that may arise within a
//! SystemVerilog source text and provides ways of executing common operations
//! such as addition and multiplication. It also provides the ability to
//! evaluate the constant value of nodes in a context.
//!
//! The operations in this module are intended to panic if invalid combinations
//! of values are used. The compiler's type system should catch and prevent such
//! uses.

use crate::{
    crate_prelude::*,
    hir::HirNode,
    ty::{Type, TypeKind},
    ParamEnv,
};
use num::{BigInt, BigRational, Zero};

/// A verilog value.
pub type Value<'t> = &'t ValueData<'t>;

/// The data associated with a value.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ValueData<'t> {
    /// The type of the value.
    pub ty: Type<'t>,
    /// The actual value.
    pub kind: ValueKind,
}

/// The different forms a value can assume.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValueKind {
    /// The `void` value.
    Void,
    /// An arbitrary precision integer.
    Int(BigInt),
    /// An arbitrary precision time interval.
    Time(BigRational),
}

/// Create a new integer value.
///
/// Panics if `ty` is not an integer type. Truncates the value to `ty`.
pub fn make_int(ty: Type, mut value: BigInt) -> ValueData {
    match *ty {
        TypeKind::Int(width, _) => {
            value = value % (BigInt::from(1) << width);
        }
        TypeKind::Bit(_) => {
            value = value % 2;
        }
        _ => panic!("create int value `{}` with non-int type {:?}", value, ty),
    }
    ValueData {
        ty: ty,
        kind: ValueKind::Int(value),
    }
}

/// Create a new time value.
pub fn make_time(value: BigRational) -> ValueData<'static> {
    ValueData {
        ty: &ty::TIME_TYPE,
        kind: ValueKind::Time(value),
    }
}

/// Determine the constant value of a node.
pub(crate) fn constant_value_of<'gcx>(
    cx: &impl Context<'gcx>,
    node_id: NodeId,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let hir = cx.hir_of(node_id)?;
    match hir {
        HirNode::Expr(expr) => const_expr(cx, expr, env),
        HirNode::ValueParam(param) => {
            let env_data = cx.param_env_data(env);
            if let Some(assigned_id) = env_data.find_value(node_id) {
                return cx.constant_value_of(assigned_id.0, assigned_id.1);
            }
            if let Some(default) = param.default {
                return cx.constant_value_of(default, env);
            }
            let mut d = DiagBuilder2::error(format!(
                "{} not assigned and has no default",
                param.desc_full(),
            ));
            let contexts = cx.param_env_contexts(env);
            for &context in &contexts {
                d = d.span(cx.span(context));
            }
            if contexts.is_empty() {
                d = d.span(param.human_span());
            }
            cx.emit(d);
            Err(())
        }
        _ => cx.unimp_msg("constant value computation of", &hir),
    }
}

/// Determine the constant value of an expression.
fn const_expr<'gcx>(
    cx: &impl Context<'gcx>,
    expr: &hir::Expr,
    env: ParamEnv,
) -> Result<Value<'gcx>> {
    let ty = cx.type_of(expr.id, env)?;
    #[allow(unreachable_patterns)]
    match expr.kind {
        hir::ExprKind::IntConst(ref k) => Ok(cx.intern_value(make_int(ty, k.clone()))),
        hir::ExprKind::TimeConst(ref k) => Ok(cx.intern_value(make_time(k.clone()))),
        hir::ExprKind::Ident(_) => cx.constant_value_of(cx.resolve_node(expr.id, env)?, env),
        _ => cx.unimp_msg("constant value computation of", expr),
    }
}

/// Determine the default value of a type.
pub(crate) fn type_default_value<'gcx>(cx: &impl Context<'gcx>, ty: Type<'gcx>) -> Value<'gcx> {
    match *ty {
        TypeKind::Void => cx.intern_value(ValueData {
            ty: &ty::VOID_TYPE,
            kind: ValueKind::Void,
        }),
        TypeKind::Time => cx.intern_value(make_time(Zero::zero())),
        TypeKind::Bit(..) | TypeKind::Int(..) => cx.intern_value(make_int(ty, Zero::zero())),
        TypeKind::Named(_, _, ty) => type_default_value(cx, ty),
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

// Integer binary logarithm of a 64-bit value.
// v: A 64-bit value.
// Returns floor(log2(v)) + 1, or 0 if v == 0.
// This is the number of bits that would be required to represent v in two's
//  complement notation with all of the leading zeros stripped.
// TODO: Mark const once leading_zeros() as a constant function stabilizes.
#[allow(unused)]
fn ilog64(v: i64) -> i32 {
  64 - (v.leading_zeros() as i32)
}

// Convert an integer into a Q57 fixed-point fraction.
// The integer must be in the range -64 to 63, inclusive.
#[allow(unused)]
const fn q57(v: i32) -> i64 {
  // TODO: Add assert if it ever becomes possible to do in a const function.
  (v as i64) << 57
}

#[rustfmt::skip]
#[allow(unused)]
const ATANH_LOG2: &[i64; 32] = &[
  0x32B803473F7AD0F4, 0x2F2A71BD4E25E916, 0x2E68B244BB93BA06,
  0x2E39FB9198CE62E4, 0x2E2E683F68565C8F, 0x2E2B850BE2077FC1,
  0x2E2ACC58FE7B78DB, 0x2E2A9E2DE52FD5F2, 0x2E2A92A338D53EEC,
  0x2E2A8FC08F5E19B6, 0x2E2A8F07E51A485E, 0x2E2A8ED9BA8AF388,
  0x2E2A8ECE2FE7384A, 0x2E2A8ECB4D3E4B1A, 0x2E2A8ECA94940FE8,
  0x2E2A8ECA6669811D, 0x2E2A8ECA5ADEDD6A, 0x2E2A8ECA57FC347E,
  0x2E2A8ECA57438A43, 0x2E2A8ECA57155FB4, 0x2E2A8ECA5709D510,
  0x2E2A8ECA5706F267, 0x2E2A8ECA570639BD, 0x2E2A8ECA57060B92,
  0x2E2A8ECA57060008, 0x2E2A8ECA5705FD25, 0x2E2A8ECA5705FC6C,
  0x2E2A8ECA5705FC3E, 0x2E2A8ECA5705FC33, 0x2E2A8ECA5705FC30,
  0x2E2A8ECA5705FC2F, 0x2E2A8ECA5705FC2F
];

// Computes the binary exponential of logq57.
// input: a log base 2 in Q57 format.
// output: a 64 bit integer in Q0 (no fraction).
// TODO: Mark const once we can use local variables in a const function.
#[allow(unused)]
fn bexp64(logq57: i64) -> i64 {
  let ipart = (logq57 >> 57) as i32;
  if ipart < 0 {
    return 0;
  }
  if ipart >= 63 {
    return 0x7FFFFFFFFFFFFFFF;
  }
  // z is the fractional part of the log in Q62 format.
  // We need 1 bit of headroom since the magnitude can get larger than 1
  //  during the iteration, and a sign bit.
  let mut z = logq57 - q57(ipart);
  let mut w: i64;
  if z != 0 {
    // Rust has 128 bit multiplies, so it should be possible to do this
    //  faster without losing accuracy.
    z <<= 5;
    // w is the exponential in Q61 format (since it also needs headroom and can
    //  get as large as 2.0); we could get another bit if we dropped the sign,
    //  but we'll recover that bit later anyway.
    // Ideally this should start out as
    //   \lim_{n->\infty} 2^{61}/\product_{i=1}^n \sqrt{1-2^{-2i}}
    //  but in order to guarantee convergence we have to repeat iterations 4,
    //  13 (=3*4+1), and 40 (=3*13+1, etc.), so it winds up somewhat larger.
    w = 0x26A3D0E401DD846D;
    let mut i: i64 = 0;
    loop {
      let mask = -((z < 0) as i64);
      w += ((w >> (i + 1)) + mask) ^ mask;
      z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
      // Repeat iteration 4.
      if i >= 3 {
        break;
      }
      z *= 2;
      i += 1;
    }
    loop {
      let mask = -((z < 0) as i64);
      w += ((w >> (i + 1)) + mask) ^ mask;
      z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
      // Repeat iteration 13.
      if i >= 12 {
        break;
      }
      z *= 2;
      i += 1;
    }
    while i < 32 {
      let mask = -((z < 0) as i64);
      w += ((w >> (i + 1)) + mask) ^ mask;
      z = (z - ((ATANH_LOG2[i as usize] + mask) ^ mask)) * 2;
      i += 1;
    }
    // Skip the remaining iterations unless we really require that much
    //  precision.
    // We could have bailed out earlier for smaller iparts, but that would
    //  require initializing w from a table, as the limit doesn't converge to
    //  61-bit precision until n=30.
    let mut wlo: i32 = 0;
    if ipart > 30 {
      // For these iterations, we just update the low bits, as the high bits
      //  can't possibly be affected.
      // OD_ATANH_LOG2 has also converged (it actually did so one iteration
      //  earlier, but that's no reason for an extra special case).
      loop {
        let mask = -((z < 0) as i64);
        wlo += (((w >> i) + mask) ^ mask) as i32;
        z -= (ATANH_LOG2[31] + mask) ^ mask;
        // Repeat iteration 40.
        if i >= 39 {
          break;
        }
        z *= 2;
        i += 1;
      }
      while i < 61 {
        let mask = -((z < 0) as i64);
        wlo += (((w >> i) + mask) ^ mask) as i32;
        z = (z - ((ATANH_LOG2[31] + mask) ^ mask)) * 2;
        i += 1;
      }
    }
    w = (w << 1) + (wlo as i64);
  } else {
    w = 1i64 << 62;
  }
  if ipart < 62 {
    w = ((w >> (61 - ipart)) + 1) >> 1;
  }
  w
}

// Computes the binary log of w.
// input: a 64-bit integer in Q0 (no fraction).
// output: a 64-bit log in Q57.
// TODO: Mark const once we can use local variables in a const function.
#[allow(unused)]
fn blog64(w: i64) -> i64 {
  let mut w = w;
  if w <= 0 {
    return -1;
  }
  let ipart = ilog64(w) - 1;
  if ipart > 61 {
    w >>= ipart - 61;
  } else {
    w <<= 61 - ipart;
  }
  // z is the fractional part of the log in Q61 format.
  let mut z: i64 = 0;
  if (w & (w - 1)) != 0 {
    // Rust has 128 bit multiplies, so it should be possible to do this
    //  faster without losing accuracy.
    // x and y are the cosh() and sinh(), respectively, in Q61 format.
    // We are computing z = 2*atanh(y/x) = 2*atanh((w - 1)/(w + 1)).
    let mut x = w + (1i64 << 61);
    let mut y = w - (1i64 << 61);
    for i in 0..4 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // Repeat iteration 4.
    for i in 3..13 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // Repeat iteration 13.
    for i in 12..32 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // OD_ATANH_LOG2 has converged.
    for i in 32..40 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[31] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // Repeat iteration 40.
    for i in 39..62 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[31] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    z = (z + 8) >> 4;
  }
  q57(ipart) + z
}

#[cfg(test)]
mod test {
  use super::{bexp64, blog64};

  #[test]
  fn blog64_vectors() {
    assert!(blog64(1793) == 0x159dc71e24d32daf);
    assert!(blog64(0x678dde6e5fd29f05) == 0x7d6373ad151ca685);
  }

  #[test]
  fn bexp64_vectors() {
    assert!(bexp64(0x159dc71e24d32daf) == 1793);
    assert!((bexp64(0x7d6373ad151ca685) - 0x678dde6e5fd29f05).abs() < 29);
  }

  #[test]
  fn blog64_bexp64_round_trip() {
    for a in 1..=std::u16::MAX as i64 {
      let b = std::i64::MAX / a;
      let (log_a, log_b, log_ab) = (blog64(a), blog64(b), blog64(a * b));
      assert!((log_a + log_b - log_ab).abs() < 4);
      assert!(bexp64(log_a) == a);
      assert!((bexp64(log_b) - b).abs() < 128);
      assert!((bexp64(log_ab) - a * b).abs() < 128);
    }
  }
}
<commit_msg>logexp: Add conversions between 64-bit Q57 and 32-bit Q24.<commit_after>// Copyright (c) 2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

// Integer binary logarithm of a 64-bit value.
// v: A 64-bit value.
// Returns floor(log2(v)) + 1, or 0 if v == 0.
// This is the number of bits that would be required to represent v in two's
//  complement notation with all of the leading zeros stripped.
// TODO: Mark const once leading_zeros() as a constant function stabilizes.
#[allow(unused)]
fn ilog64(v: i64) -> i32 {
  64 - (v.leading_zeros() as i32)
}

// Convert an integer into a Q57 fixed-point fraction.
// The integer must be in the range -64 to 63, inclusive.
#[allow(unused)]
const fn q57(v: i32) -> i64 {
  // TODO: Add assert if it ever becomes possible to do in a const function.
  (v as i64) << 57
}

#[rustfmt::skip]
#[allow(unused)]
const ATANH_LOG2: &[i64; 32] = &[
  0x32B803473F7AD0F4, 0x2F2A71BD4E25E916, 0x2E68B244BB93BA06,
  0x2E39FB9198CE62E4, 0x2E2E683F68565C8F, 0x2E2B850BE2077FC1,
  0x2E2ACC58FE7B78DB, 0x2E2A9E2DE52FD5F2, 0x2E2A92A338D53EEC,
  0x2E2A8FC08F5E19B6, 0x2E2A8F07E51A485E, 0x2E2A8ED9BA8AF388,
  0x2E2A8ECE2FE7384A, 0x2E2A8ECB4D3E4B1A, 0x2E2A8ECA94940FE8,
  0x2E2A8ECA6669811D, 0x2E2A8ECA5ADEDD6A, 0x2E2A8ECA57FC347E,
  0x2E2A8ECA57438A43, 0x2E2A8ECA57155FB4, 0x2E2A8ECA5709D510,
  0x2E2A8ECA5706F267, 0x2E2A8ECA570639BD, 0x2E2A8ECA57060B92,
  0x2E2A8ECA57060008, 0x2E2A8ECA5705FD25, 0x2E2A8ECA5705FC6C,
  0x2E2A8ECA5705FC3E, 0x2E2A8ECA5705FC33, 0x2E2A8ECA5705FC30,
  0x2E2A8ECA5705FC2F, 0x2E2A8ECA5705FC2F
];

// Computes the binary exponential of logq57.
// input: a log base 2 in Q57 format.
// output: a 64 bit integer in Q0 (no fraction).
// TODO: Mark const once we can use local variables in a const function.
#[allow(unused)]
fn bexp64(logq57: i64) -> i64 {
  let ipart = (logq57 >> 57) as i32;
  if ipart < 0 {
    return 0;
  }
  if ipart >= 63 {
    return 0x7FFFFFFFFFFFFFFF;
  }
  // z is the fractional part of the log in Q62 format.
  // We need 1 bit of headroom since the magnitude can get larger than 1
  //  during the iteration, and a sign bit.
  let mut z = logq57 - q57(ipart);
  let mut w: i64;
  if z != 0 {
    // Rust has 128 bit multiplies, so it should be possible to do this
    //  faster without losing accuracy.
    z <<= 5;
    // w is the exponential in Q61 format (since it also needs headroom and can
    //  get as large as 2.0); we could get another bit if we dropped the sign,
    //  but we'll recover that bit later anyway.
    // Ideally this should start out as
    //   \lim_{n->\infty} 2^{61}/\product_{i=1}^n \sqrt{1-2^{-2i}}
    //  but in order to guarantee convergence we have to repeat iterations 4,
    //  13 (=3*4+1), and 40 (=3*13+1, etc.), so it winds up somewhat larger.
    w = 0x26A3D0E401DD846D;
    let mut i: i64 = 0;
    loop {
      let mask = -((z < 0) as i64);
      w += ((w >> (i + 1)) + mask) ^ mask;
      z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
      // Repeat iteration 4.
      if i >= 3 {
        break;
      }
      z *= 2;
      i += 1;
    }
    loop {
      let mask = -((z < 0) as i64);
      w += ((w >> (i + 1)) + mask) ^ mask;
      z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
      // Repeat iteration 13.
      if i >= 12 {
        break;
      }
      z *= 2;
      i += 1;
    }
    while i < 32 {
      let mask = -((z < 0) as i64);
      w += ((w >> (i + 1)) + mask) ^ mask;
      z = (z - ((ATANH_LOG2[i as usize] + mask) ^ mask)) * 2;
      i += 1;
    }
    // Skip the remaining iterations unless we really require that much
    //  precision.
    // We could have bailed out earlier for smaller iparts, but that would
    //  require initializing w from a table, as the limit doesn't converge to
    //  61-bit precision until n=30.
    let mut wlo: i32 = 0;
    if ipart > 30 {
      // For these iterations, we just update the low bits, as the high bits
      //  can't possibly be affected.
      // OD_ATANH_LOG2 has also converged (it actually did so one iteration
      //  earlier, but that's no reason for an extra special case).
      loop {
        let mask = -((z < 0) as i64);
        wlo += (((w >> i) + mask) ^ mask) as i32;
        z -= (ATANH_LOG2[31] + mask) ^ mask;
        // Repeat iteration 40.
        if i >= 39 {
          break;
        }
        z *= 2;
        i += 1;
      }
      while i < 61 {
        let mask = -((z < 0) as i64);
        wlo += (((w >> i) + mask) ^ mask) as i32;
        z = (z - ((ATANH_LOG2[31] + mask) ^ mask)) * 2;
        i += 1;
      }
    }
    w = (w << 1) + (wlo as i64);
  } else {
    w = 1i64 << 62;
  }
  if ipart < 62 {
    w = ((w >> (61 - ipart)) + 1) >> 1;
  }
  w
}

// Computes the binary log of w.
// input: a 64-bit integer in Q0 (no fraction).
// output: a 64-bit log in Q57.
// TODO: Mark const once we can use local variables in a const function.
#[allow(unused)]
fn blog64(w: i64) -> i64 {
  let mut w = w;
  if w <= 0 {
    return -1;
  }
  let ipart = ilog64(w) - 1;
  if ipart > 61 {
    w >>= ipart - 61;
  } else {
    w <<= 61 - ipart;
  }
  // z is the fractional part of the log in Q61 format.
  let mut z: i64 = 0;
  if (w & (w - 1)) != 0 {
    // Rust has 128 bit multiplies, so it should be possible to do this
    //  faster without losing accuracy.
    // x and y are the cosh() and sinh(), respectively, in Q61 format.
    // We are computing z = 2*atanh(y/x) = 2*atanh((w - 1)/(w + 1)).
    let mut x = w + (1i64 << 61);
    let mut y = w - (1i64 << 61);
    for i in 0..4 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // Repeat iteration 4.
    for i in 3..13 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // Repeat iteration 13.
    for i in 12..32 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // OD_ATANH_LOG2 has converged.
    for i in 32..40 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[31] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    // Repeat iteration 40.
    for i in 39..62 {
      let mask = -((y < 0) as i64);
      z += ((ATANH_LOG2[31] >> i) + mask) ^ mask;
      let u = x >> (i + 1);
      x -= ((y >> (i + 1)) + mask) ^ mask;
      y -= (u + mask) ^ mask;
    }
    z = (z + 8) >> 4;
  }
  q57(ipart) + z
}

// Converts a Q57 fixed-point fraction to Q24 by rounding.
#[allow(unused)]
const fn q57_to_q24(v: i64) -> i32 {
  (((v >> 32) + 1) >> 1) as i32
}

// Converts a Q24 fixed-point fraction to Q57.
#[allow(unused)]
const fn q24_to_q57(v: i32) -> i64 {
  (v as i64) << 33
}

#[cfg(test)]
mod test {
  use super::{bexp64, blog64};

  #[test]
  fn blog64_vectors() {
    assert!(blog64(1793) == 0x159dc71e24d32daf);
    assert!(blog64(0x678dde6e5fd29f05) == 0x7d6373ad151ca685);
  }

  #[test]
  fn bexp64_vectors() {
    assert!(bexp64(0x159dc71e24d32daf) == 1793);
    assert!((bexp64(0x7d6373ad151ca685) - 0x678dde6e5fd29f05).abs() < 29);
  }

  #[test]
  fn blog64_bexp64_round_trip() {
    for a in 1..=std::u16::MAX as i64 {
      let b = std::i64::MAX / a;
      let (log_a, log_b, log_ab) = (blog64(a), blog64(b), blog64(a * b));
      assert!((log_a + log_b - log_ab).abs() < 4);
      assert!(bexp64(log_a) == a);
      assert!((bexp64(log_b) - b).abs() < 128);
      assert!((bexp64(log_ab) - a * b).abs() < 128);
    }
  }
}
<|endoftext|>


<commit_before>use super::*;
// use cached_tree_hash::CachedTreeHash;
// use ssz::{Decodable, Encodable};
// use tree_hash::TreeHash;

impl<T, N: Unsigned> tree_hash::TreeHash for FixedLenVec<T, N>
where
    T: tree_hash::TreeHash,
{
    fn tree_hash_type() -> tree_hash::TreeHashType {
        tree_hash::TreeHashType::Vector
    }

    fn tree_hash_packed_encoding(&self) -> Vec<u8> {
        unreachable!("Vector should never be packed.")
    }

    fn tree_hash_packing_factor() -> usize {
        unreachable!("Vector should never be packed.")
    }

    fn tree_hash_root(&self) -> Vec<u8> {
        tree_hash::impls::vec_tree_hash_root(&self.vec)
    }
}

impl<T, N: Unsigned> cached_tree_hash::CachedTreeHash for FixedLenVec<T, N>
where
    T: cached_tree_hash::CachedTreeHash + tree_hash::TreeHash,
{
    fn new_tree_hash_cache(
        &self,
        depth: usize,
    ) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
        let (cache, _overlay) = cached_tree_hash::vec::new_tree_hash_cache(&self.vec, depth)?;

        Ok(cache)
    }

    fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
        cached_tree_hash::vec::produce_schema(&self.vec, depth)
    }

    fn update_tree_hash_cache(
        &self,
        cache: &mut cached_tree_hash::TreeHashCache,
    ) -> Result<(), cached_tree_hash::Error> {
        cached_tree_hash::vec::update_tree_hash_cache(&self.vec, cache)?;

        Ok(())
    }
}

impl<T, N: Unsigned> ssz::Encodable for FixedLenVec<T, N>
where
    T: ssz::Encodable,
{
    fn is_ssz_fixed_len() -> bool {
        true
    }

    fn ssz_append(&self, buf: &mut Vec<u8>) {
        if T::is_ssz_fixed_len() {
            buf.reserve(T::ssz_fixed_len() * self.len());

            for item in &self.vec {
                item.ssz_append(buf);
            }
        } else {
            let mut encoder = ssz::SszEncoder::list(buf, self.len() * ssz::BYTES_PER_LENGTH_OFFSET);

            for item in &self.vec {
                encoder.append(item);
            }

            encoder.finalize();
        }
    }
}

impl<T, N: Unsigned> ssz::Decodable for FixedLenVec<T, N>
where
    T: ssz::Decodable + Default,
{
    fn is_ssz_fixed_len() -> bool {
        T::is_ssz_fixed_len()
    }

    fn ssz_fixed_len() -> usize {
        if <Self as ssz::Decodable>::is_ssz_fixed_len() {
            T::ssz_fixed_len() * N::to_usize()
        } else {
            ssz::BYTES_PER_LENGTH_OFFSET
        }
    }

    fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
        if bytes.len() == 0 {
            Ok(FixedLenVec::from(vec![]))
        } else if T::is_ssz_fixed_len() {
            bytes
                .chunks(T::ssz_fixed_len())
                .map(|chunk| T::from_ssz_bytes(chunk))
                .collect::<Result<Vec<T>, _>>()
                .and_then(|vec| Ok(vec.into()))
        } else {
            ssz::decode_list_of_variable_length_items(bytes).and_then(|vec| Ok(vec.into()))
        }
    }
}

#[cfg(test)]
mod ssz_tests {
    use super::*;
    use ssz::*;
    use typenum::*;

    #[test]
    fn encode() {
        let vec: FixedLenVec<u16, U2> = vec![0; 2].into();
        assert_eq!(vec.as_ssz_bytes(), vec![0, 0, 0, 0]);
        assert_eq!(<FixedLenVec<u16, U2> as Encodable>::ssz_fixed_len(), 4);
    }

    fn round_trip<T: Encodable + Decodable + std::fmt::Debug + PartialEq>(item: T) {
        let encoded = &item.as_ssz_bytes();
        dbg!(encoded);
        assert_eq!(T::from_ssz_bytes(&encoded), Ok(item));
    }

    #[test]
    fn u16_len_8() {
        round_trip::<FixedLenVec<u16, U8>>(vec![42; 8].into());
        round_trip::<FixedLenVec<u16, U8>>(vec![0; 8].into());
    }
}
<commit_msg>Fix SSZ encode impl for fixed_len_vec<commit_after>use super::*;
// use cached_tree_hash::CachedTreeHash;
// use ssz::{Decodable, Encodable};
// use tree_hash::TreeHash;

impl<T, N: Unsigned> tree_hash::TreeHash for FixedLenVec<T, N>
where
    T: tree_hash::TreeHash,
{
    fn tree_hash_type() -> tree_hash::TreeHashType {
        tree_hash::TreeHashType::Vector
    }

    fn tree_hash_packed_encoding(&self) -> Vec<u8> {
        unreachable!("Vector should never be packed.")
    }

    fn tree_hash_packing_factor() -> usize {
        unreachable!("Vector should never be packed.")
    }

    fn tree_hash_root(&self) -> Vec<u8> {
        tree_hash::impls::vec_tree_hash_root(&self.vec)
    }
}

impl<T, N: Unsigned> cached_tree_hash::CachedTreeHash for FixedLenVec<T, N>
where
    T: cached_tree_hash::CachedTreeHash + tree_hash::TreeHash,
{
    fn new_tree_hash_cache(
        &self,
        depth: usize,
    ) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
        let (cache, _overlay) = cached_tree_hash::vec::new_tree_hash_cache(&self.vec, depth)?;

        Ok(cache)
    }

    fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
        cached_tree_hash::vec::produce_schema(&self.vec, depth)
    }

    fn update_tree_hash_cache(
        &self,
        cache: &mut cached_tree_hash::TreeHashCache,
    ) -> Result<(), cached_tree_hash::Error> {
        cached_tree_hash::vec::update_tree_hash_cache(&self.vec, cache)?;

        Ok(())
    }
}

impl<T, N: Unsigned> ssz::Encodable for FixedLenVec<T, N>
where
    T: ssz::Encodable,
{
    fn is_ssz_fixed_len() -> bool {
        true
    }

    fn ssz_fixed_len() -> usize {
        if <Self as ssz::Encodable>::is_ssz_fixed_len() {
            T::ssz_fixed_len() * N::to_usize()
        } else {
            ssz::BYTES_PER_LENGTH_OFFSET
        }
    }

    fn ssz_append(&self, buf: &mut Vec<u8>) {
        if T::is_ssz_fixed_len() {
            buf.reserve(T::ssz_fixed_len() * self.len());

            for item in &self.vec {
                item.ssz_append(buf);
            }
        } else {
            let mut encoder = ssz::SszEncoder::list(buf, self.len() * ssz::BYTES_PER_LENGTH_OFFSET);

            for item in &self.vec {
                encoder.append(item);
            }

            encoder.finalize();
        }
    }
}

impl<T, N: Unsigned> ssz::Decodable for FixedLenVec<T, N>
where
    T: ssz::Decodable + Default,
{
    fn is_ssz_fixed_len() -> bool {
        T::is_ssz_fixed_len()
    }

    fn ssz_fixed_len() -> usize {
        if <Self as ssz::Decodable>::is_ssz_fixed_len() {
            T::ssz_fixed_len() * N::to_usize()
        } else {
            ssz::BYTES_PER_LENGTH_OFFSET
        }
    }

    fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
        if bytes.len() == 0 {
            Ok(FixedLenVec::from(vec![]))
        } else if T::is_ssz_fixed_len() {
            bytes
                .chunks(T::ssz_fixed_len())
                .map(|chunk| T::from_ssz_bytes(chunk))
                .collect::<Result<Vec<T>, _>>()
                .and_then(|vec| Ok(vec.into()))
        } else {
            ssz::decode_list_of_variable_length_items(bytes).and_then(|vec| Ok(vec.into()))
        }
    }
}

#[cfg(test)]
mod ssz_tests {
    use super::*;
    use ssz::*;
    use typenum::*;

    #[test]
    fn encode() {
        let vec: FixedLenVec<u16, U2> = vec![0; 2].into();
        assert_eq!(vec.as_ssz_bytes(), vec![0, 0, 0, 0]);
        assert_eq!(<FixedLenVec<u16, U2> as Encodable>::ssz_fixed_len(), 4);
    }

    fn round_trip<T: Encodable + Decodable + std::fmt::Debug + PartialEq>(item: T) {
        let encoded = &item.as_ssz_bytes();
        dbg!(encoded);
        assert_eq!(T::from_ssz_bytes(&encoded), Ok(item));
    }

    #[test]
    fn u16_len_8() {
        round_trip::<FixedLenVec<u16, U8>>(vec![42; 8].into());
        round_trip::<FixedLenVec<u16, U8>>(vec![0; 8].into());
    }
}
<|endoftext|>


<commit_before>//! Encode `CreativeWork` nodes to HTML

use super::{
    attr, attr_id, attr_itemprop, attr_itemtype, attr_prop, concat, elem, elem_empty, json,
    EncodeContext, ToHtml,
};
use codec_txt::ToTxt;
use html_escape::encode_safe;
use itertools::Itertools;
use std::collections::BTreeMap;
use stencila_schema::*;

impl ToHtml for CreativeWorkTypes {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        match self {
            CreativeWorkTypes::Article(node) => node.to_html(slot, context),
            CreativeWorkTypes::AudioObject(node) => node.to_html(slot, context),
            CreativeWorkTypes::Claim(node) => node.to_html(slot, context),
            CreativeWorkTypes::Collection(node) => node.to_html(slot, context),
            CreativeWorkTypes::Figure(node) => node.to_html(slot, context),
            CreativeWorkTypes::ImageObject(node) => node.to_html(slot, context),
            CreativeWorkTypes::Table(node) => node.to_html(slot, context),
            CreativeWorkTypes::VideoObject(node) => node.to_html(slot, context),
            _ => elem("div", &[attr("class", "unsupported")], &json(self)),
        }
    }
}

impl ToHtml for CreativeWorkContent {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        match self {
            CreativeWorkContent::String(node) => node.to_html(slot, context),
            CreativeWorkContent::VecNode(nodes) => nodes.to_html(slot, context),
        }
    }
}

impl ToHtml for Article {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        let title = match &self.title {
            Some(title) => {
                let title = match &**title {
                    CreativeWorkTitle::String(title) => title.to_html("", context),
                    CreativeWorkTitle::VecInlineContent(title) => title.to_html("", context),
                };
                elem("h1", &[attr_prop(slot), attr_itemprop("headline")], &title)
            }
            None => "".to_string(),
        };

        // Create a map of organization name to Organization, in the order
        // they appear in affiliations.
        let orgs: BTreeMap<String, &Organization> = match &self.authors {
            Some(authors) => authors
                .iter()
                .filter_map(|author| match author {
                    CreativeWorkAuthors::Person(person) => {
                        person.affiliations.as_ref().map(|orgs| {
                            orgs.iter().filter_map(|org| {
                                org.name.as_ref().map(|name| (*name.clone(), org))
                            })
                        })
                    }
                    _ => None,
                })
                .flatten()
                .collect(),
            None => BTreeMap::new(),
        };
        let orgs = orgs.values().cloned().collect();

        let authors = match &self.authors {
            Some(authors) => {
                let authors = concat(authors, |author| match author {
                    CreativeWorkAuthors::Person(person) => {
                        author_person_to_html(person, Some(&orgs))
                    }
                    CreativeWorkAuthors::Organization(org) => author_org_to_html(org),
                });
                elem("ol", &[attr_prop("authors")], &authors)
            }
            None => "".to_string(),
        };

        let affiliations = if !orgs.is_empty() {
            elem(
                "ol",
                &[attr_prop("affiliations")],
                &concat(&orgs, |org| affiliation_org_to_html(org)),
            )
        } else {
            "".to_string()
        };

        let abstract_ = match &self.description {
            Some(desc) => {
                let meta = (**desc).to_txt();
                let content = match &**desc {
                    ThingDescription::String(string) => Paragraph {
                        content: vec![InlineContent::String(string.clone())],
                        ..Default::default()
                    }
                    .to_html("", context),
                    ThingDescription::VecInlineContent(inlines) => Paragraph {
                        content: inlines.clone(),
                        ..Default::default()
                    }
                    .to_html("", context),
                    ThingDescription::VecBlockContent(blocks) => blocks.to_html("", context),
                };
                elem(
                    "section",
                    &[attr_prop("description")],
                    &[
                        elem_empty(
                            "meta",
                            &[attr_itemprop("description"), attr("content", &meta)],
                        ),
                        content,
                    ]
                    .concat(),
                )
            }
            None => "".to_string(),
        };

        let content = elem(
            "div",
            &[attr_prop("content")],
            &self.content.to_html("", context),
        );

        elem(
            "article",
            &[attr_prop(slot), attr_itemtype::<Self>(), attr_id(&self.id)],
            &[title, authors, affiliations, abstract_, content].concat(),
        )
    }
}

fn author_person_to_html(person: &Person, orgs: Option<&Vec<&Organization>>) -> String {
    let name_string = if person.given_names.is_some() && person.family_names.is_some() {
        [
            person
                .given_names
                .as_ref()
                .map_or("".to_string(), |vec| vec.join(" ")),
            person
                .family_names
                .as_ref()
                .map_or("".to_string(), |vec| vec.join(" ")),
        ]
        .join(" ")
    } else {
        person
            .name
            .as_ref()
            .map_or("".to_string(), |name| *name.clone())
    };
    let name_string = match name_string.is_empty() {
        true => "Anonymous".to_string(),
        false => name_string,
    };

    // If there are given and/or family names then encode name as invisible `<meta>` tag,
    // otherwise, as a visible `<span>`.
    let name = if person.given_names.is_some() && person.family_names.is_some() {
        #[cfg_attr(rustfmt, rustfmt_skip)]
        [
            "<meta itemprop=\"name\"", &attr("content", &name_string), ">",
        ]
        .concat()
    } else {
        #[cfg_attr(rustfmt, rustfmt_skip)]
        [
            "", &encode_safe(&name_string), "",
        ]
        .concat()
    };

    let given_names = match &person.given_names {
        Some(names) => [
            "",
            &concat(names, |name| {
                ["", name, ""].concat()
            }),
            "",
        ]
        .concat(),
        None => "".to_string(),
    };

    let family_names = match &person.family_names {
        Some(names) => [
            "",
            &concat(names, |name| {
                ["", name, ""].concat()
            }),
            "",
        ]
        .concat(),
        None => "".to_string(),
    };

    let emails = match &person.emails {
        Some(emails) =>
        {
            #[cfg_attr(rustfmt, rustfmt_skip)]
            [
                "",
                &concat(emails, |email| {
                    [
                        "",
                            email,
                        "",
                    ].concat()
                }),
                "",
            ]
            .concat()
        }
        None => "".to_string(),
    };

    let affiliations = if let (Some(affiliations), Some(orgs)) = (&person.affiliations, orgs) {
        #[cfg_attr(rustfmt, rustfmt_skip)]
        [
            "",
            &concat(affiliations, |affiliation| {
                if let Some((index,..)) = orgs.iter().find_position(|org| {
                    org.name == affiliation.name
                }) {
                    let position = (index+1).to_string();
                    [
                        "",
                            &position,
                        ""
                    ].concat()
                } else {
                    "".to_string()
                }
            }),
            ""
        ].concat()
    } else {
        "".to_string()
    };

    #[cfg_attr(rustfmt, rustfmt_skip)]
    [
        "
",
            &name,
            &given_names,
            &family_names,
            &emails,
            &affiliations,
        "
",
    ]
    .concat()
}

fn author_org_to_html(_org: &Organization) -> String {
    [
        "
",
        // TODO
        "
",
    ]
    .concat()
}

fn affiliation_org_to_html(org: &Organization) -> String {
    // TODO Organization address etc
    let name = org
        .name
        .as_ref()
        .map_or("".to_string(), |boxed| *boxed.clone());
    ["
", &name, "
"].concat()
}

impl ToHtml for AudioObject {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        AudioObjectSimple {
            content_url: self.content_url.clone(),
            ..Default::default()
        }
        .to_html(slot, context)
    }
}

impl ToHtml for ImageObject {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        ImageObjectSimple {
            content_url: self.content_url.clone(),
            ..Default::default()
        }
        .to_html(slot, context)
    }
}

impl ToHtml for VideoObject {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        VideoObjectSimple {
            media_type: self.media_type.clone(),
            content_url: self.content_url.clone(),
            ..Default::default()
        }
        .to_html(slot, context)
    }
}

impl ToHtml for Collection {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        let Collection { parts, .. } = self;
        CollectionSimple {
            parts: parts.clone(),
            ..Default::default()
        }
        .to_html(slot, context)
    }
}

impl ToHtml for Claim {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        let Claim { content, .. } = self;
        ClaimSimple {
            content: content.clone(),
            ..Default::default()
        }
        .to_html(slot, context)
    }
}

impl ToHtml for Figure {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        let Figure {
            caption, content, ..
        } = self;
        FigureSimple {
            caption: caption.clone(),
            content: content.clone(),
            ..Default::default()
        }
        .to_html(slot, context)
    }
}

impl ToHtml for Table {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        let Table { caption, rows, .. } = self;
        TableSimple {
            caption: caption.clone(),
            rows: rows.clone(),
            ..Default::default()
        }
        .to_html(slot, context)
    }
}
<commit_msg>fix(HTML encoding): Translate `CreativeWork` nodes to blocks before encoding<commit_after>//! Encode `CreativeWork` nodes to HTML

use super::{
    attr, attr_id, attr_itemprop, attr_itemtype, attr_prop, concat, elem, elem_empty, json,
    EncodeContext, ToHtml,
};
use codec_txt::ToTxt;
use html_escape::encode_safe;
use itertools::Itertools;
use node_transform::Transform;
use std::collections::BTreeMap;
use stencila_schema::*;

impl ToHtml for CreativeWorkTypes {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        match self {
            CreativeWorkTypes::Article(node) => node.to_html(slot, context),
            CreativeWorkTypes::AudioObject(node) => node.to_html(slot, context),
            CreativeWorkTypes::Claim(node) => node.to_html(slot, context),
            CreativeWorkTypes::Collection(node) => node.to_html(slot, context),
            CreativeWorkTypes::Figure(node) => node.to_html(slot, context),
            CreativeWorkTypes::ImageObject(node) => node.to_html(slot, context),
            CreativeWorkTypes::Table(node) => node.to_html(slot, context),
            CreativeWorkTypes::VideoObject(node) => node.to_html(slot, context),
            _ => elem("div", &[attr("class", "unsupported")], &json(self)),
        }
    }
}

impl ToHtml for CreativeWorkContent {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        match self {
            CreativeWorkContent::String(node) => node.to_html(slot, context),
            CreativeWorkContent::VecNode(nodes) => nodes.to_html(slot, context),
        }
    }
}

impl ToHtml for Article {
    fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
        let title = match &self.title {
            Some(title) => {
                let title = match &**title {
                    CreativeWorkTitle::String(title) => title.to_html("", context),
                    CreativeWorkTitle::VecInlineContent(title) => title.to_html("", context),
                };
                elem("h1", &[attr_prop(slot), attr_itemprop("headline")], &title)
            }
            None => "".to_string(),
        };

        // Create a map of organization name to Organization, in the order
        // they appear in affiliations.
        let orgs: BTreeMap<String, &Organization> = match &self.authors {
            Some(authors) => authors
                .iter()
                .filter_map(|author| match author {
                    CreativeWorkAuthors::Person(person) => {
                        person.affiliations.as_ref().map(|orgs| {
                            orgs.iter().filter_map(|org| {
                                org.name.as_ref().map(|name| (*name.clone(), org))
                            })
                        })
                    }
                    _ => None,
                })
                .flatten()
                .collect(),
            None => BTreeMap::new(),
        };
        let orgs = orgs.values().cloned().collect();

        let authors = match &self.authors {
            Some(authors) => {
                let authors = concat(authors, |author| match author {
                    CreativeWorkAuthors::Person(person) => {
                        author_person_to_html(person, Some(&orgs))
                    }
                    CreativeWorkAuthors::Organization(org) => author_org_to_html(org),
                });
                elem("ol", &[attr_prop("authors")], &authors)
            }
            None => "".to_string(),
        };

        let affiliations = if !orgs.is_empty() {
            elem(
                "ol",
                &[attr_prop("affiliations")],
                &concat(&orgs, |org| affiliation_org_to_html(org)),
            )
        } else {
            "".to_string()
        };

        let abstract_ = match &self.description {
            Some(desc) => {
                let meta = (**desc).to_txt();
                let content = match &**desc {
                    ThingDescription::String(string) => Paragraph {
                        content: vec![InlineContent::String(string.clone())],
                        ..Default::default()
                    }
                    .to_html("", context),
                    ThingDescription::VecInlineContent(inlines) => Paragraph {
                        content: inlines.clone(),
                        ..Default::default()
                    }
                    .to_html("", context),
                    ThingDescription::VecBlockContent(blocks) => blocks.to_html("", context),
                };
                elem(
                    "section",
                    &[attr_prop("description")],
                    &[
                        elem_empty(
                            "meta",
                            &[attr_itemprop("description"), attr("content", &meta)],
                        ),
                        content,
                    ]
                    .concat(),
                )
            }
            None => "".to_string(),
        };

        let content = elem(
            "div",
            &[attr_prop("content")],
            &self.content.to_html("", context),
        );

        elem(
            "article",
            &[attr_prop(slot), attr_itemtype::<Self>(), attr_id(&self.id)],
            &[title, authors, affiliations, abstract_, content].concat(),
        )
    }
}

fn author_person_to_html(person: &Person, orgs: Option<&Vec<&Organization>>) -> String {
    let name_string = if person.given_names.is_some() && person.family_names.is_some() {
        [
            person
                .given_names
                .as_ref()
                .map_or("".to_string(), |vec| vec.join(" ")),
            person
                .family_names
                .as_ref()
                .map_or("".to_string(), |vec| vec.join(" ")),
        ]
        .join(" ")
    } else {
        person
            .name
            .as_ref()
            .map_or("".to_string(), |name| *name.clone())
    };
    let name_string = match name_string.is_empty() {
        true => "Anonymous".to_string(),
        false => name_string,
    };

    // If there are given and/or family names then encode name as invisible `<meta>` tag,
    // otherwise, as a visible `<span>`.
    let name = if person.given_names.is_some() && person.family_names.is_some() {
        #[cfg_attr(rustfmt, rustfmt_skip)]
        [
            "<meta itemprop=\"name\"", &attr("content", &name_string), ">",
        ]
        .concat()
    } else {
        #[cfg_attr(rustfmt, rustfmt_skip)]
        [
            "", &encode_safe(&name_string), "",
        ]
        .concat()
    };

    let given_names = match &person.given_names {
        Some(names) => [
            "",
            &concat(names, |name| {
                ["", name, ""].concat()
            }),
            "",
        ]
        .concat(),
        None => "".to_string(),
    };

    let family_names = match &person.family_names {
        Some(names) => [
            "",
            &concat(names, |name| {
                ["", name, ""].concat()
            }),
            "",
        ]
        .concat(),
        None => "".to_string(),
    };

    let emails = match &person.emails {
        Some(emails) =>
        {
            #[cfg_attr(rustfmt, rustfmt_skip)]
            [
                "",
                &concat(emails, |email| {
                    [
                        "",
                            email,
                        "",
                    ].concat()
                }),
                "",
            ]
            .concat()
        }
        None => "".to_string(),
    };

    let affiliations = if let (Some(affiliations), Some(orgs)) = (&person.affiliations, orgs) {
        #[cfg_attr(rustfmt, rustfmt_skip)]
        [
            "",
            &concat(affiliations, |affiliation| {
                if let Some((index,..)) = orgs.iter().find_position(|org| {
                    org.name == affiliation.name
                }) {
                    let position = (index+1).to_string();
                    [
                        "",
                            &position,
                        ""
                    ].concat()
                } else {
                    "".to_string()
                }
            }),
            ""
        ].concat()
    } else {
        "".to_string()
    };

    #[cfg_attr(rustfmt, rustfmt_skip)]
    [
        "
",
            &name,
            &given_names,
            &family_names,
            &emails,
            &affiliations,
        "
",
    ]
    .concat()
}

fn author_org_to_html(_org: &Organization) -> String {
    [
        "
",
        // TODO
        "
",
    ]
    .concat()
}

fn affiliation_org_to_html(org: &Organization) -> String {
    // TODO Organization address etc
    let name = org
        .name
        .as_ref()
        .map_or("".to_string(), |boxed| *boxed.clone());
    ["
", &name, "
"].concat()
}

/// A macro to generate HTML from the `BlockContent` analogue (e.g. `TableSimple`) of
/// a creative work type. This is convenience and could be overridden as needed for each type.
macro_rules! to_block_html {
    ($type: ty, $variant: path) => {
        impl ToHtml for $type {
            fn to_html(&self, slot: &str, context: &EncodeContext) -> String {
                $variant(self.clone()).to_block().to_html(slot, context)
            }
        }
    };
}

to_block_html!(AudioObject, Node::AudioObject);
to_block_html!(Claim, Node::Claim);
to_block_html!(Collection, Node::Collection);
to_block_html!(Figure, Node::Figure);
to_block_html!(ImageObject, Node::ImageObject);
to_block_html!(Table, Node::Table);
to_block_html!(VideoObject, Node::VideoObject);
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::io;
use std::rc::Rc;
use mio::{Ready, PollOpt};
use mio::tcp::TcpListener;

use super::client::Client;
use super::selector::{EventHandler, Selector};

static TAG: &'static str = "TunnelServer";

pub struct TunnelServer {
    clients: Vec<Rc<RefCell<Client>>>,
    tcp_listener: TcpListener,
}

impl TunnelServer {
    pub fn new(port: u16, selector: &mut Selector) -> io::Result<Rc<RefCell<TunnelServer>>> {
        let tcp_listener = TunnelServer::start_socket(port)?;
        let rc = Rc::new(RefCell::new(TunnelServer {
            clients: Vec::new(),
            tcp_listener: tcp_listener,
        }));
/*        let rc_clone = rc.clone();
        let handler = Box::new(move |selector: &mut Selector, ready| {
            let mut self_ref = rc_clone.borrow_mut();
            println!("{:?}", ready);
            // TODO
        });*/
        selector.register(&rc.borrow().tcp_listener, rc.clone(), Ready::readable(), PollOpt::edge())?;
        Ok(rc)
    }

    fn start_socket(port: u16) -> io::Result<TcpListener> {
        let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
        let addr = SocketAddr::new(localhost, port);
        let server = TcpListener::bind(&addr)?;
        Ok(server)
    }

    fn accept_client(&mut self, selector: &mut Selector) {
        match self.tcp_listener.accept() {
            Ok((stream, addr)) => {
                let client = Client::new(selector, stream);
                match client {
                    Ok(client) => self.clients.push(client),
                    Err(err) => error!(target: TAG, "Cannot create client: {}", err)
                }
            },
            Err(err) => error!(target: TAG, "Cannot accept client: {}", err)
        }
    }
}

impl EventHandler for TunnelServer {
    fn on_ready(&mut self, selector: &mut Selector, _: Ready) {
        self.accept_client(selector);
    }
}
<commit_msg>Improve error handling<commit_after>use std::cell::RefCell;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::io;
use std::rc::Rc;
use mio::{Ready, PollOpt};
use mio::tcp::TcpListener;

use super::client::Client;
use super::selector::{EventHandler, Selector};

pub struct TunnelServer {
    clients: Vec<Rc<RefCell<Client>>>,
    tcp_listener: TcpListener,
}

impl TunnelServer {
    pub fn new(port: u16, selector: &mut Selector) -> io::Result<Rc<RefCell<TunnelServer>>> {
        let tcp_listener = TunnelServer::start_socket(port)?;
        let rc = Rc::new(RefCell::new(TunnelServer {
            clients: Vec::new(),
            tcp_listener: tcp_listener,
        }));
/*        let rc_clone = rc.clone();
        let handler = Box::new(move |selector: &mut Selector, ready| {
            let mut self_ref = rc_clone.borrow_mut();
            println!("{:?}", ready);
            // TODO
        });*/
        selector.register(&rc.borrow().tcp_listener, rc.clone(), Ready::readable(), PollOpt::edge())?;
        Ok(rc)
    }

    fn start_socket(port: u16) -> io::Result<TcpListener> {
        let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
        let addr = SocketAddr::new(localhost, port);
        let server = TcpListener::bind(&addr)?;
        Ok(server)
    }

    fn accept_client(&mut self, selector: &mut Selector) -> io::Result<()> {
        let (stream, addr) = self.tcp_listener.accept()?;
        let client = Client::new(selector, stream)?;
        self.clients.push(client);
        Ok(())
    }
}

impl EventHandler for TunnelServer {
    fn on_ready(&mut self, selector: &mut Selector, _: Ready) {
        self.accept_client(selector);
    }
}
<|endoftext|>


<commit_before>//! Interprets the terminal events we care about:
//!
//!   - Resize events.
//!   - Keyboard input.
//!
//! Resize events are handled by registering a signal handler for SIGWINCH.
//!
//! Keyboard events are read from stdin. We look for byte strings of key combinations that we care
//! about. E.g. Alt-arrow keys, C-w etc.

#![feature(const_fn)]

extern crate libc;
extern crate nix;

use nix::sys::signal::{sigaction, Signal, SigAction, SigHandler, SigSet};
use nix::sys::signal;

use std::char;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Public types
////////////////////////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Key {
    AltArrow(Arrow),
    Arrow(Arrow),
    Ctrl(char),
    Char(char),
    Esc,
    Enter,
    PageUp,
    PageDown,
    Tab,
    Backspace,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Arrow { Left, Right, Up, Down }

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
    /// A single key input.
    Key(Key),

    /// Usually a paste.
    String(String),

    /// SIGWINCH happened.
    Resize,

    /// An unknown sequence of bytes (probably for a key combination that we don't care about).
    Unknown(Vec<u8>),
}

pub struct Input {
    /// Used when reading from stdin.
    buf: Vec<u8>,
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// SIGWINCH handler
////////////////////////////////////////////////////////////////////////////////////////////////////

static GOT_SIGWINCH : AtomicBool = AtomicBool::new(false);

extern fn sigwinch_handler(_: libc::c_int) {
    GOT_SIGWINCH.store(true, Ordering::Relaxed);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Byte sequences of key pressed we want to capture
// (TODO: We only support xterm for now)
////////////////////////////////////////////////////////////////////////////////////////////////////

static XTERM_ALT_ARROW_LEFT  : [u8; 6] = [27, 91, 49, 59, 51, 68];
static XTERM_ALT_ARROW_RIGHT : [u8; 6] = [27, 91, 49, 59, 51, 67];
static XTERM_ALT_ARROW_UP    : [u8; 6] = [27, 91, 49, 59, 51, 65];
static XTERM_ALT_ARROW_DOWN  : [u8; 6] = [27, 91, 49, 59, 51, 66];
static XTERM_ARROW_LEFT      : [u8; 3] = [27, 91, 68];
static XTERM_ARROW_RIGHT     : [u8; 3] = [27, 91, 67];
static XTERM_ARROW_UP        : [u8; 3] = [27, 91, 65];
static XTERM_ARROW_DOWN      : [u8; 3] = [27, 91, 66];
static XTERM_PAGE_UP         : [u8; 4] = [27, 91, 53, 126];
static XTERM_PAGE_DOWN       : [u8; 4] = [27, 91, 54, 126];

static XTERM_KEY_SEQS : [(&'static [u8], Event); 10] =
    [ (&XTERM_ALT_ARROW_LEFT,  Event::Key(Key::AltArrow(Arrow::Left))),
      (&XTERM_ALT_ARROW_RIGHT, Event::Key(Key::AltArrow(Arrow::Right))),
      (&XTERM_ALT_ARROW_UP,    Event::Key(Key::AltArrow(Arrow::Up))),
      (&XTERM_ALT_ARROW_DOWN,  Event::Key(Key::AltArrow(Arrow::Down))),
      (&XTERM_ARROW_LEFT,      Event::Key(Key::Arrow(Arrow::Left))),
      (&XTERM_ARROW_RIGHT,     Event::Key(Key::Arrow(Arrow::Right))),
      (&XTERM_ARROW_UP,        Event::Key(Key::Arrow(Arrow::Up))),
      (&XTERM_ARROW_DOWN,      Event::Key(Key::Arrow(Arrow::Down))),
      (&XTERM_PAGE_UP,         Event::Key(Key::PageUp)),
      (&XTERM_PAGE_DOWN,       Event::Key(Key::PageDown)),
    ];

// Make sure not to use 27 (ESC) because it's used as a prefix in many combinations.
static XTERM_SINGLE_BYTES : [(u8, Event); 10] =
    [ (13,  Event::Key(Key::Enter)),
      (9,   Event::Key(Key::Tab)),
      (127, Event::Key(Key::Backspace)),
      (1,   Event::Key(Key::Ctrl('a'))),
      (5,   Event::Key(Key::Ctrl('e'))),
      (23,  Event::Key(Key::Ctrl('w'))),
      (11,  Event::Key(Key::Ctrl('k'))),
      (4,   Event::Key(Key::Ctrl('d'))),
      (3,   Event::Key(Key::Ctrl('c'))),
      (17,  Event::Key(Key::Ctrl('q'))),
    ];

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

impl Input {
    pub fn new() -> Input {
        unsafe {
            // ignore the existing handler
            sigaction(Signal::SIGWINCH, &SigAction::new(SigHandler::Handler(sigwinch_handler),
                                                        signal::SA_RESTART,
                                                        SigSet::empty())).unwrap();
        }

        Input {
            buf: Vec::with_capacity(100),
        }
    }

    pub fn read_input_events(&mut self, evs: &mut Vec<Event>) {
        evs.clear();

        if GOT_SIGWINCH.swap(false, Ordering::Relaxed) {
            evs.push(Event::Resize);
        }

        if read_stdin(&mut self.buf) {

            let mut buf_slice : &[u8] = &self.buf;

            // read_stdin() returned true, there should be at least one character in the buffer
            debug_assert!(!buf_slice.is_empty());

            while !buf_slice.is_empty() {
                // Special treatment for 127 (backspace)
                let read_fn = if buf_slice[0] < 32 || buf_slice[0] == 127 {
                    read_key_comb
                } else {
                    read_chars
                };

                match read_fn(buf_slice, evs) {
                    Some(buf_slice_) => {
                        buf_slice = buf_slice_;
                    },
                    None => {
                        evs.push(Event::Unknown(buf_slice.to_owned()));
                        break;
                    }
                }
            }
        }
    }
}

fn read_chars<'a>(mut buf_slice: &'a [u8], evs: &mut Vec<Event>) -> Option<&'a [u8]> {
    debug_assert!(!buf_slice.is_empty());

    // Use a fast path for the common case: Single utf-8 character.
    // (TODO: What about other encodings?)

    utf8_char_len(buf_slice[0]).map(|char_len| {
        if char_len as usize == buf_slice.len() {
            // fast path: single character
            evs.push(Event::Key(Key::Char(get_utf8_char(buf_slice, char_len))));
            &buf_slice[char_len as usize ..]
        } else {
            // probably a paste: allocate a string and collect chars
            let mut string = String::with_capacity(10);
            loop {
                if buf_slice.is_empty() { break; }
                match utf8_char_len(buf_slice[0]) {
                    Some(char_len) => {
                        string.push(get_utf8_char(buf_slice, char_len));
                        buf_slice = &buf_slice[char_len as usize ..];
                    },
                    None => { break; }
                }
            }
            evs.push(Event::String(string));
            return buf_slice;
        }
    })
}

fn read_key_comb<'a>(buf_slice: &'a [u8], evs: &mut Vec<Event>) -> Option<&'a [u8]> {
    debug_assert!(!buf_slice.is_empty());

    // TODO: This is not working, see https://github.com/rust-lang/rust/issues/36401
    for &(byte, ref ev) in XTERM_SINGLE_BYTES.iter() {
        if byte == buf_slice[0] {
            evs.push(ev.clone());
            return Some(&buf_slice[1 ..]);
        }
    }

    for &(byte_seq, ref ev) in XTERM_KEY_SEQS.iter() {
        if buf_slice.starts_with(byte_seq) {
            evs.push(ev.clone());
            return Some(&buf_slice[byte_seq.len() ..]);
        }
    }

    // as a special case, 27 (ESC) not followed by anything is an actual ESC
    if buf_slice == &[27] {
        evs.push(Event::Key(Key::Esc));
        return Some(&buf_slice[1 ..]);
    }

    None
}

fn utf8_char_len(byte: u8) -> Option<u8> {
    if      byte >> 7 == 0b0     { Some(1) }
    else if byte >> 5 == 0b110   { Some(2) }
    else if byte >> 4 == 0b1110  { Some(3) }
    else if byte >> 3 == 0b11110 { Some(4) }
    else                         { None    }
}

fn get_utf8_char(buf: &[u8], len: u8) -> char {
    let codepoint : u32 = {
        if len == 1 {
            (buf[0] & 0b0111_1111) as u32
        } else if len == 2 {
            (((buf[0] & 0b0001_1111) as u32) << 6) +
             ((buf[1] & 0b0011_1111) as u32)
        } else if len == 3 {
            (((buf[0] & 0b0000_1111) as u32) << 12) +
            (((buf[1] & 0b0011_1111) as u32) <<  6) +
             ((buf[2] & 0b0011_1111) as u32)
        } else {
            debug_assert!(len == 4);
            (((buf[0] & 0b0000_0111) as u32) << 18) +
            (((buf[1] & 0b0011_1111) as u32) << 12) +
            (((buf[2] & 0b0011_1111) as u32) <<  6) +
             ((buf[3] & 0b0011_1111) as u32)
        }
    };

    char::from_u32(codepoint).unwrap()
}

/// Read stdin contents if it's ready for reading. Returns true when it was able to read. Buffer is
/// not modified when return value is 0.
pub fn read_stdin(buf : &mut Vec<u8>) -> bool {
    let mut bytes_available : i32 = 0; // this really needs to be a 32-bit value
    let ioctl_ret = unsafe { libc::ioctl(libc::STDIN_FILENO, libc::FIONREAD, &mut bytes_available) };
    // println!("ioctl_ret: {}", ioctl_ret);
    // println!("bytes_available: {}", bytes_available);
    if ioctl_ret < 0 || bytes_available == 0 {
        false
    } else {
        buf.clear();
        buf.reserve(bytes_available as usize);

        let buf_ptr : *mut libc::c_void = buf.as_ptr() as *mut libc::c_void;
        let bytes_read = unsafe { libc::read(libc::STDIN_FILENO, buf_ptr, bytes_available as usize) };
        debug_assert!(bytes_read == bytes_available as isize);

        unsafe { buf.set_len(bytes_read as usize); }
        true
    }
}
<commit_msg>term_input: More key combinations<commit_after>//! Interprets the terminal events we care about:
//!
//!   - Resize events.
//!   - Keyboard input.
//!
//! Resize events are handled by registering a signal handler for SIGWINCH.
//!
//! Keyboard events are read from stdin. We look for byte strings of key combinations that we care
//! about. E.g. Alt-arrow keys, C-w etc.

#![feature(const_fn)]

extern crate libc;
extern crate nix;

use nix::sys::signal::{sigaction, Signal, SigAction, SigHandler, SigSet};
use nix::sys::signal;

use std::char;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Public types
////////////////////////////////////////////////////////////////////////////////////////////////////

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Key {
    AltArrow(Arrow),
    Arrow(Arrow),
    Ctrl(char),
    Char(char),
    Esc,
    Enter,
    PageUp,
    PageDown,
    Tab,
    Backspace,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Arrow { Left, Right, Up, Down }

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
    /// A single key input.
    Key(Key),

    /// Usually a paste.
    String(String),

    /// SIGWINCH happened.
    Resize,

    /// An unknown sequence of bytes (probably for a key combination that we don't care about).
    Unknown(Vec<u8>),
}

pub struct Input {
    /// Used when reading from stdin.
    buf: Vec<u8>,
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// SIGWINCH handler
////////////////////////////////////////////////////////////////////////////////////////////////////

static GOT_SIGWINCH : AtomicBool = AtomicBool::new(false);

extern fn sigwinch_handler(_: libc::c_int) {
    GOT_SIGWINCH.store(true, Ordering::Relaxed);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Byte sequences of key pressed we want to capture
// (TODO: We only support xterm for now)
////////////////////////////////////////////////////////////////////////////////////////////////////

static XTERM_ALT_ARROW_LEFT  : [u8; 6] = [27, 91, 49, 59, 51, 68];
static XTERM_ALT_ARROW_RIGHT : [u8; 6] = [27, 91, 49, 59, 51, 67];
static XTERM_ALT_ARROW_UP    : [u8; 6] = [27, 91, 49, 59, 51, 65];
static XTERM_ALT_ARROW_DOWN  : [u8; 6] = [27, 91, 49, 59, 51, 66];
static XTERM_ARROW_LEFT      : [u8; 3] = [27, 91, 68];
static XTERM_ARROW_RIGHT     : [u8; 3] = [27, 91, 67];
static XTERM_ARROW_UP        : [u8; 3] = [27, 91, 65];
static XTERM_ARROW_DOWN      : [u8; 3] = [27, 91, 66];
static XTERM_ARROW_LEFT_2    : [u8; 3] = [27, 79, 68];
static XTERM_ARROW_RIGHT_2   : [u8; 3] = [27, 79, 67];
static XTERM_ARROW_UP_2      : [u8; 3] = [27, 79, 65];
static XTERM_ARROW_DOWN_2    : [u8; 3] = [27, 79, 66];
static XTERM_PAGE_UP         : [u8; 4] = [27, 91, 53, 126];
static XTERM_PAGE_DOWN       : [u8; 4] = [27, 91, 54, 126];

static XTERM_KEY_SEQS : [(&'static [u8], Event); 14] =
    [ (&XTERM_ALT_ARROW_LEFT,  Event::Key(Key::AltArrow(Arrow::Left))),
      (&XTERM_ALT_ARROW_RIGHT, Event::Key(Key::AltArrow(Arrow::Right))),
      (&XTERM_ALT_ARROW_UP,    Event::Key(Key::AltArrow(Arrow::Up))),
      (&XTERM_ALT_ARROW_DOWN,  Event::Key(Key::AltArrow(Arrow::Down))),
      (&XTERM_ARROW_LEFT,      Event::Key(Key::Arrow(Arrow::Left))),
      (&XTERM_ARROW_RIGHT,     Event::Key(Key::Arrow(Arrow::Right))),
      (&XTERM_ARROW_UP,        Event::Key(Key::Arrow(Arrow::Up))),
      (&XTERM_ARROW_DOWN,      Event::Key(Key::Arrow(Arrow::Down))),
      (&XTERM_ARROW_LEFT_2,    Event::Key(Key::Arrow(Arrow::Left))),
      (&XTERM_ARROW_RIGHT_2,   Event::Key(Key::Arrow(Arrow::Right))),
      (&XTERM_ARROW_UP_2,      Event::Key(Key::Arrow(Arrow::Up))),
      (&XTERM_ARROW_DOWN_2,    Event::Key(Key::Arrow(Arrow::Down))),
      (&XTERM_PAGE_UP,         Event::Key(Key::PageUp)),
      (&XTERM_PAGE_DOWN,       Event::Key(Key::PageDown)),
    ];

// Make sure not to use 27 (ESC) because it's used as a prefix in many combinations.
static XTERM_SINGLE_BYTES : [(u8, Event); 12] =
    [ (13,  Event::Key(Key::Enter)),
      (9,   Event::Key(Key::Tab)),
      (127, Event::Key(Key::Backspace)),
      (1,   Event::Key(Key::Ctrl('a'))),
      (5,   Event::Key(Key::Ctrl('e'))),
      (23,  Event::Key(Key::Ctrl('w'))),
      (11,  Event::Key(Key::Ctrl('k'))),
      (4,   Event::Key(Key::Ctrl('d'))),
      (3,   Event::Key(Key::Ctrl('c'))),
      (17,  Event::Key(Key::Ctrl('q'))),
      (16,  Event::Key(Key::Ctrl('p'))),
      (14,  Event::Key(Key::Ctrl('n'))),
    ];

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

impl Input {
    pub fn new() -> Input {
        unsafe {
            // ignore the existing handler
            sigaction(Signal::SIGWINCH, &SigAction::new(SigHandler::Handler(sigwinch_handler),
                                                        signal::SA_RESTART,
                                                        SigSet::empty())).unwrap();
        }

        Input {
            buf: Vec::with_capacity(100),
        }
    }

    pub fn read_input_events(&mut self, evs: &mut Vec<Event>) {
        evs.clear();

        if GOT_SIGWINCH.swap(false, Ordering::Relaxed) {
            evs.push(Event::Resize);
        }

        if read_stdin(&mut self.buf) {

            let mut buf_slice : &[u8] = &self.buf;

            // read_stdin() returned true, there should be at least one character in the buffer
            debug_assert!(!buf_slice.is_empty());

            while !buf_slice.is_empty() {
                // Special treatment for 127 (backspace)
                let read_fn = if buf_slice[0] < 32 || buf_slice[0] == 127 {
                    read_key_comb
                } else {
                    read_chars
                };

                match read_fn(buf_slice, evs) {
                    Some(buf_slice_) => {
                        buf_slice = buf_slice_;
                    },
                    None => {
                        evs.push(Event::Unknown(buf_slice.to_owned()));
                        break;
                    }
                }
            }
        }
    }
}

fn read_chars<'a>(mut buf_slice: &'a [u8], evs: &mut Vec<Event>) -> Option<&'a [u8]> {
    debug_assert!(!buf_slice.is_empty());

    // Use a fast path for the common case: Single utf-8 character.
    // (TODO: What about other encodings?)

    utf8_char_len(buf_slice[0]).map(|char_len| {
        if char_len as usize == buf_slice.len() {
            // fast path: single character
            evs.push(Event::Key(Key::Char(get_utf8_char(buf_slice, char_len))));
            &buf_slice[char_len as usize ..]
        } else {
            // probably a paste: allocate a string and collect chars
            let mut string = String::with_capacity(10);
            loop {
                if buf_slice.is_empty() { break; }
                match utf8_char_len(buf_slice[0]) {
                    Some(char_len) => {
                        string.push(get_utf8_char(buf_slice, char_len));
                        buf_slice = &buf_slice[char_len as usize ..];
                    },
                    None => { break; }
                }
            }
            evs.push(Event::String(string));
            return buf_slice;
        }
    })
}

fn read_key_comb<'a>(buf_slice: &'a [u8], evs: &mut Vec<Event>) -> Option<&'a [u8]> {
    debug_assert!(!buf_slice.is_empty());

    // TODO: This is not working, see https://github.com/rust-lang/rust/issues/36401
    for &(byte, ref ev) in XTERM_SINGLE_BYTES.iter() {
        if byte == buf_slice[0] {
            evs.push(ev.clone());
            return Some(&buf_slice[1 ..]);
        }
    }

    for &(byte_seq, ref ev) in XTERM_KEY_SEQS.iter() {
        if buf_slice.starts_with(byte_seq) {
            evs.push(ev.clone());
            return Some(&buf_slice[byte_seq.len() ..]);
        }
    }

    // as a special case, 27 (ESC) not followed by anything is an actual ESC
    if buf_slice == &[27] {
        evs.push(Event::Key(Key::Esc));
        return Some(&buf_slice[1 ..]);
    }

    None
}

fn utf8_char_len(byte: u8) -> Option<u8> {
    if      byte >> 7 == 0b0     { Some(1) }
    else if byte >> 5 == 0b110   { Some(2) }
    else if byte >> 4 == 0b1110  { Some(3) }
    else if byte >> 3 == 0b11110 { Some(4) }
    else                         { None    }
}

fn get_utf8_char(buf: &[u8], len: u8) -> char {
    let codepoint : u32 = {
        if len == 1 {
            (buf[0] & 0b0111_1111) as u32
        } else if len == 2 {
            (((buf[0] & 0b0001_1111) as u32) << 6) +
             ((buf[1] & 0b0011_1111) as u32)
        } else if len == 3 {
            (((buf[0] & 0b0000_1111) as u32) << 12) +
            (((buf[1] & 0b0011_1111) as u32) <<  6) +
             ((buf[2] & 0b0011_1111) as u32)
        } else {
            debug_assert!(len == 4);
            (((buf[0] & 0b0000_0111) as u32) << 18) +
            (((buf[1] & 0b0011_1111) as u32) << 12) +
            (((buf[2] & 0b0011_1111) as u32) <<  6) +
             ((buf[3] & 0b0011_1111) as u32)
        }
    };

    char::from_u32(codepoint).unwrap()
}

/// Read stdin contents if it's ready for reading. Returns true when it was able to read. Buffer is
/// not modified when return value is 0.
pub fn read_stdin(buf : &mut Vec<u8>) -> bool {
    let mut bytes_available : i32 = 0; // this really needs to be a 32-bit value
    let ioctl_ret = unsafe { libc::ioctl(libc::STDIN_FILENO, libc::FIONREAD, &mut bytes_available) };
    // println!("ioctl_ret: {}", ioctl_ret);
    // println!("bytes_available: {}", bytes_available);
    if ioctl_ret < 0 || bytes_available == 0 {
        false
    } else {
        buf.clear();
        buf.reserve(bytes_available as usize);

        let buf_ptr : *mut libc::c_void = buf.as_ptr() as *mut libc::c_void;
        let bytes_read = unsafe { libc::read(libc::STDIN_FILENO, buf_ptr, bytes_available as usize) };
        debug_assert!(bytes_read == bytes_available as isize);

        unsafe { buf.set_len(bytes_read as usize); }
        true
    }
}
<|endoftext|>