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

#include "stdafx.h"

#include <limits>
#include <string>
#include <stdlib.h>

#include "common/common.h"
#include "util/ascii_util.h"

#include "decimal.h"
#include "floatimpl.h"
#include "integer.h"

#include "zorbaserialization/serialize_zorba_types.h"
#include "zorbaserialization/serialize_template_types.h"

#ifdef ZORBA_WITH_BIG_INTEGER
# define TEMPLATE_DECL(T) /* nothing */
# define INTEGER_IMPL(I)  IntegerImpl
#else
# define TEMPLATE_DECL(T) template<typename T> /* spacer */
# define INTEGER_IMPL(I)  IntegerImpl<I> /* spacer */
#endif /* ZORBA_WITH_BIG_INTEGER */
#define INTEGER_IMPL_LL  INTEGER_IMPL(long long)
#define INTEGER_IMPL_ULL INTEGER_IMPL(unsigned long long)

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

namespace zorba {

zstring const& nan_str() {
  static zstring const value( "NaN" );
  return value;
}

zstring const& pos_inf_str() {
  static zstring const value( "INF" );
  return value;
}

zstring const& neg_inf_str() {
  static zstring const value( "-INF" );
  return value;
}

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

static void count_significant_digits( char digit, int *significant_digits,
                                      int *trailing_zeros ) {
  if ( digit == '0' )
    ++*trailing_zeros;
  else {
    if ( (*significant_digits)++ )
      *significant_digits += *trailing_zeros;
    *trailing_zeros = 0;
  }
}

template<typename FloatType>
void FloatImpl<FloatType>::parse( char const *s ) {
  if ( !*s )
    throw std::invalid_argument( "empty string" );

  int significant_digits = 0;

  s = ascii::trim_start_whitespace( s );

  if ( !parse_etc( s ) ) {
    char const *const first_non_ws = s;
    int trailing_zeros = 0;
    //
    // We need got_digit to know that we're potentially parsing a floating
    // point value comprised of at least one digit -- which means the value
    // can't be one of the special values of INF, -INF, or NaN.
    //
    // We need to know this to prevent aton() from parsing the special values
    // since it (via strtod() that it uses) allows them regardless of case
    // whereas XQuery insists on a specific case.
    //
    bool got_digit = false;

    if ( *s == '+' || *s == '-' )
      ++s;
    if ( ascii::is_digit( *s ) ) {
      got_digit = true;
      do {
        count_significant_digits( *s, &significant_digits, &trailing_zeros );
      } while ( ascii::is_digit( *++s ) );
    }
    if ( *s == '.' && ascii::is_digit( *++s ) ) {
      got_digit = true;
      do {
        count_significant_digits( *s, &significant_digits, &trailing_zeros );
      } while ( ascii::is_digit( *++s ) );
    }
    if ( *s == 'e' || *s == 'E' ) {
      ++s;
      if ( *s == '+' || *s == '-' )
        ++s;
      if ( ascii::is_digit( *s ) ) {
        got_digit = true;
        while ( ascii::is_digit( *++s ) ) ;
      }
    }
    if ( !got_digit )
      throw std::invalid_argument(
        BUILD_STRING( '"', first_non_ws, "\": invalid floating-point literal" )
      );
    value_ = ztd::aton<value_type>( first_non_ws );
  }

  precision_ = significant_digits < max_precision() ?
    significant_digits : max_precision();
}

template<typename FloatType>
bool FloatImpl<FloatType>::parse_etc( char const *s ) {
  if ( strncmp( s, "INF", 3 ) == 0 ) {
    value_ = FloatImpl<FloatType>::pos_inf().value_;
    s += 3;
  } else if ( strncmp( s, "-INF", 4 ) == 0 ) {
    value_ = FloatImpl<FloatType>::neg_inf().value_;
    s += 4;
  } else if ( strncmp( s, "NaN", 3 ) == 0 ) {
    value_ = FloatImpl<FloatType>::nan().value_;
    s += 3;
  } else
    return false;

  return !*ascii::trim_start_whitespace( s );
}

////////// constructors ///////////////////////////////////////////////////////

template<typename FloatType>
FloatImpl<FloatType>::FloatImpl( Decimal const &d ) {
  zstring const temp( d.toString() );
  parse( temp.c_str() );
}

template<typename FloatType>
TEMPLATE_DECL(IntType)
FloatImpl<FloatType>::FloatImpl( INTEGER_IMPL(IntType) const &i ) {
  zstring const temp( i.toString() );
  parse( temp.c_str() );
}

#ifndef ZORBA_WITH_BIG_INTEGER
template FloatImpl<float>::FloatImpl( INTEGER_IMPL_LL const& );
template FloatImpl<float>::FloatImpl( INTEGER_IMPL_ULL const& );

template FloatImpl<double>::FloatImpl( INTEGER_IMPL_LL const& );
template FloatImpl<double>::FloatImpl( INTEGER_IMPL_ULL const& );
#endif /* ZORBA_WITH_BIG_INTEGER */

////////// assignment operators ///////////////////////////////////////////////

template<typename FloatType>
FloatImpl<FloatType>& FloatImpl<FloatType>::operator=( Decimal const &d ) {
  zstring const temp( d.toString() );
  parse( temp.c_str() );
  return *this;
}

template<typename FloatType>
TEMPLATE_DECL(IntType)
FloatImpl<FloatType>&
FloatImpl<FloatType>::operator=( INTEGER_IMPL(IntType) const &i ) {
  zstring const temp( i.toString() );
  parse( temp.c_str() );
  return *this;
}

#ifndef ZORBA_WITH_BIG_INTEGER
template
FloatImpl<float>& FloatImpl<float>::operator=( INTEGER_IMPL_LL const& );

template
FloatImpl<float>& FloatImpl<float>::operator=( INTEGER_IMPL_ULL const& );

template
FloatImpl<double>& FloatImpl<double>::operator=( INTEGER_IMPL_LL const& );

template
FloatImpl<double>& FloatImpl<double>::operator=( INTEGER_IMPL_ULL const& );
#endif /* ZORBA_WITH_BIG_INTEGER */

////////// math functions /////////////////////////////////////////////////////

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::acos() const {
  if ( *this < neg_one() || *this > one() )
    return nan();
  return FloatImpl<FloatType>(
    isNegZero() ? -std::acos( value_ ): std::acos( value_ )
  );
}

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::asin() const {
  if ( *this < neg_one() || *this > one() )
    return nan();
  return FloatImpl<FloatType>( std::asin( value_ ) );
}

template<typename FloatType>
void FloatImpl<FloatType>::frexp( FloatImpl<FloatType> &out_mantissa,
                                  Integer &out_exponent ) const {
  int expint;
  out_mantissa = FloatImpl( ::frexp( value_, &expint ) );
  out_exponent = Integer( expint );
}

template<>
void FloatImpl<double>::modf( FloatImpl<double> &out_fraction,
                              FloatImpl<double> &out_integer ) const {
  double int_part;
  out_fraction = std::modf( value_, &int_part );
  out_integer = int_part;
}

template<>
void FloatImpl<float>::modf( FloatImpl<float> &out_fraction,
                             FloatImpl<float> &out_integer ) const {
  float int_part;
  out_fraction = ::modff( value_, &int_part );
  out_integer = int_part;
}

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::round() const {
  return round( Integer::zero() );
}

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::round( Integer const &precision ) const {
  FloatImpl result;
  if ( isFinite() && !isZero() ) {
    MAPM m(
      Decimal::round2(
        Decimal::value_type( value_ ),
        Decimal::value_type( precision.itod() )
      )
    );
    if ( value_ < 0 && m.sign() == 0 )
      result = neg_zero();
    else {
      char buf[200];
      m.toString( buf, ZORBA_FLOAT_POINT_PRECISION );
      result.parse( buf );
    }
  } else
    result.value_ = value_;

  return result;
}

template<typename FloatType> FloatImpl<FloatType>
FloatImpl<FloatType>::roundHalfToEven( Integer const &precision) const {
  FloatImpl result;
  if ( isFinite() && !isZero() ) {
    MAPM m(
      Decimal::roundHalfToEven2(
        Decimal::value_type( value_ ),
        Decimal::value_type( precision.itod() )
      )
    );
    if ( value_ < 0 && m.sign() == 0 )
      result = neg_zero();
    else {
      char buf[200];
      m.toString( buf, ZORBA_FLOAT_POINT_PRECISION );
      result.parse( buf );
    }
  } else
    result.value_ = value_;
  return result;
}

////////// miscellaneous //////////////////////////////////////////////////////

template<>
bool FloatImpl<double>::isNegZero() const {
  if ( !value_ ) {
    char const *const bytes = reinterpret_cast<char const*>( &value_ );
    // test for little endian and big endian
    return bytes[0] || bytes[7];        // TODO: depends on sizeof(double)
  }
  return false;
}

template<>
bool FloatImpl<float>::isNegZero() const {
  if ( !value_ ) {
    char const *const bytes = reinterpret_cast<char const*>( &value_ );
    // test for little endian and big endian
    return bytes[0] || bytes[3];        // TODO: depends on sizeof(float)
  }
  return false;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::nan() {
  static FloatImpl<FloatType> const value( std::sqrt( -1.0 ) );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::neg_inf() {
  static FloatImpl<FloatType> const value(
    -std::numeric_limits<FloatType>::infinity()
  );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::neg_one() {
  static FloatImpl<FloatType> const value( -1 );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::neg_zero() {
  static FloatImpl<FloatType> const value( -0.0 );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::one() {
  static FloatImpl<FloatType> const value( 1 );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::pos_inf() {
  static FloatImpl<FloatType> const value(
    std::numeric_limits<FloatType>::infinity()
  );
  return value;
}

template<typename FloatType>
zstring FloatImpl<FloatType>::toIntegerString() const {
  if ( isNaN() )
    return nan_str();
  if  (isPosInf() )
    return pos_inf_str();
  if ( isNegInf() )
    return neg_inf_str();
  if ( isPosZero() )
    return "0";
  if ( isNegZero() )
    return "-0";

  // TODO: make xs_int
  char buf[174];
  sprintf( buf, "%d", (int)value_ );
  return buf;
}

template<typename FloatType>
zstring FloatImpl<FloatType>::toString( bool no_scientific_format ) const {
  if ( isNaN() )
    return nan_str();
  if ( isPosInf() )
    return pos_inf_str();
  if ( isNegInf() )
    return neg_inf_str();
  if ( isPosZero() )
    return "0";
  if ( isNegZero() )
    return "-0";

  FloatType const absVal = fabs( value_ );
  FloatType const lower = 0.000001f, upper = 1000000.0f;

  if (no_scientific_format || (absVal < upper && absVal >= lower) || absVal == 0)
  {
#if 1
    // This is the "spec" implementation, i.e., it is an exact application of
    // the spec in  http://www.w3.org/TR/xpath-functions/#casting
    MAPM decimal_mapm( value_ );
    decimal_mapm = decimal_mapm.round( precision_ );
    return Decimal::toString(decimal_mapm, max_precision());
#else
    std::stringstream stream;
    stream.precision(7);
    stream.setf(std::ios::fixed);
    stream << value_;

    zstring result(stream.str());

    // remove non-significant trailing 0's
    long i = result.size() - 1;
    while (str[i] == '0')
      --i;

    if (i >= 0) {
      long j = i;
      while (str[j] != '.')
        --j;

      if (j >= 0)
        if (j == i)
          result.resize(i);
        else
          result.resize(i+1);
    }

    return result;
#endif
  } else {
    char format[15];
    sprintf( format, "%%#1.%dE", static_cast<int>( precision_ ) );

    char buf[174];
    sprintf( buf, format, static_cast<double>( value_ ) );

    char *e = strchr( buf, 'E' );
    char *zeros = e ? e - 1 : buf + strlen( buf ) - 1;

    while ( *zeros == '0' )
      --zeros;

    if ( e ) {
      if ( *zeros == '.' )
        ++zeros;

      zeros[1] = 'E';
      ++e;

      if ( *e == '+' )
        ++e;
      else if ( *e == '-' ) {
        ++zeros;
        zeros[1] = '-';
        ++e;
      }

      while ( *e == '0' )
        ++e;

      memmove( (void*)(zeros + 2), e, strlen( e ) + 1 );
    } else {
      if ( *zeros == '.' )
        --zeros;
      zeros[1] = '\0';
    }

    Decimal::reduce( buf );
    return buf;
  }
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::zero() {
  static FloatImpl<FloatType> const value( 0 );
  return value;
}

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

template class FloatImpl<double>;
template class FloatImpl<float>;

#ifdef WIN32
// exported for testing only
template class ZORBA_DLL_PUBLIC FloatImpl<double>;
template class ZORBA_DLL_PUBLIC FloatImpl<float>;
#endif /* WIN32 */

} // namespace zorba
/* vim:set et sw=2 ts=2: */
<commit_msg>Recognize +INF as a valid float and double.<commit_after>/*
 * Copyright 2006-2008 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "stdafx.h"

#include <limits>
#include <string>
#include <stdlib.h>

#include "common/common.h"
#include "util/ascii_util.h"

#include "decimal.h"
#include "floatimpl.h"
#include "integer.h"

#include "zorbaserialization/serialize_zorba_types.h"
#include "zorbaserialization/serialize_template_types.h"

#ifdef ZORBA_WITH_BIG_INTEGER
# define TEMPLATE_DECL(T) /* nothing */
# define INTEGER_IMPL(I)  IntegerImpl
#else
# define TEMPLATE_DECL(T) template<typename T> /* spacer */
# define INTEGER_IMPL(I)  IntegerImpl<I> /* spacer */
#endif /* ZORBA_WITH_BIG_INTEGER */
#define INTEGER_IMPL_LL  INTEGER_IMPL(long long)
#define INTEGER_IMPL_ULL INTEGER_IMPL(unsigned long long)

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

namespace zorba {

zstring const& nan_str() {
  static zstring const value( "NaN" );
  return value;
}

zstring const& pos_inf_str() {
  static zstring const value( "INF" );
  return value;
}

zstring const& neg_inf_str() {
  static zstring const value( "-INF" );
  return value;
}

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

static void count_significant_digits( char digit, int *significant_digits,
                                      int *trailing_zeros ) {
  if ( digit == '0' )
    ++*trailing_zeros;
  else {
    if ( (*significant_digits)++ )
      *significant_digits += *trailing_zeros;
    *trailing_zeros = 0;
  }
}

template<typename FloatType>
void FloatImpl<FloatType>::parse( char const *s ) {
  if ( !*s )
    throw std::invalid_argument( "empty string" );

  int significant_digits = 0;

  s = ascii::trim_start_whitespace( s );

  if ( !parse_etc( s ) ) {
    char const *const first_non_ws = s;
    int trailing_zeros = 0;
    //
    // We need got_digit to know that we're potentially parsing a floating
    // point value comprised of at least one digit -- which means the value
    // can't be one of the special values of INF, -INF, or NaN.
    //
    // We need to know this to prevent aton() from parsing the special values
    // since it (via strtod() that it uses) allows them regardless of case
    // whereas XQuery insists on a specific case.
    //
    bool got_digit = false;

    if ( *s == '+' || *s == '-' )
      ++s;
    if ( ascii::is_digit( *s ) ) {
      got_digit = true;
      do {
        count_significant_digits( *s, &significant_digits, &trailing_zeros );
      } while ( ascii::is_digit( *++s ) );
    }
    if ( *s == '.' && ascii::is_digit( *++s ) ) {
      got_digit = true;
      do {
        count_significant_digits( *s, &significant_digits, &trailing_zeros );
      } while ( ascii::is_digit( *++s ) );
    }
    if ( *s == 'e' || *s == 'E' ) {
      ++s;
      if ( *s == '+' || *s == '-' )
        ++s;
      if ( ascii::is_digit( *s ) ) {
        got_digit = true;
        while ( ascii::is_digit( *++s ) ) ;
      }
    }
    if ( !got_digit )
      throw std::invalid_argument(
        BUILD_STRING( '"', first_non_ws, "\": invalid floating-point literal" )
      );
    value_ = ztd::aton<value_type>( first_non_ws );
  }

  precision_ = significant_digits < max_precision() ?
    significant_digits : max_precision();
}

template<typename FloatType>
bool FloatImpl<FloatType>::parse_etc( char const *s ) {
  if ( strncmp( s, "INF", 3 ) == 0 ) {
    value_ = FloatImpl<FloatType>::pos_inf().value_;
    s += 3;
  } else if ( strncmp( s, "-INF", 4 ) == 0 ) {
    value_ = FloatImpl<FloatType>::neg_inf().value_;
    s += 4;
  } else if ( strncmp( s, "NaN", 3 ) == 0 ) {
    value_ = FloatImpl<FloatType>::nan().value_;
    s += 3;
  } else if ( strncmp( s, "+INF", 4 ) == 0 ) {
    value_ = FloatImpl<FloatType>::pos_inf().value_;
    s += 4;
  } else
    return false;

  return !*ascii::trim_start_whitespace( s );
}

////////// constructors ///////////////////////////////////////////////////////

template<typename FloatType>
FloatImpl<FloatType>::FloatImpl( Decimal const &d ) {
  zstring const temp( d.toString() );
  parse( temp.c_str() );
}

template<typename FloatType>
TEMPLATE_DECL(IntType)
FloatImpl<FloatType>::FloatImpl( INTEGER_IMPL(IntType) const &i ) {
  zstring const temp( i.toString() );
  parse( temp.c_str() );
}

#ifndef ZORBA_WITH_BIG_INTEGER
template FloatImpl<float>::FloatImpl( INTEGER_IMPL_LL const& );
template FloatImpl<float>::FloatImpl( INTEGER_IMPL_ULL const& );

template FloatImpl<double>::FloatImpl( INTEGER_IMPL_LL const& );
template FloatImpl<double>::FloatImpl( INTEGER_IMPL_ULL const& );
#endif /* ZORBA_WITH_BIG_INTEGER */

////////// assignment operators ///////////////////////////////////////////////

template<typename FloatType>
FloatImpl<FloatType>& FloatImpl<FloatType>::operator=( Decimal const &d ) {
  zstring const temp( d.toString() );
  parse( temp.c_str() );
  return *this;
}

template<typename FloatType>
TEMPLATE_DECL(IntType)
FloatImpl<FloatType>&
FloatImpl<FloatType>::operator=( INTEGER_IMPL(IntType) const &i ) {
  zstring const temp( i.toString() );
  parse( temp.c_str() );
  return *this;
}

#ifndef ZORBA_WITH_BIG_INTEGER
template
FloatImpl<float>& FloatImpl<float>::operator=( INTEGER_IMPL_LL const& );

template
FloatImpl<float>& FloatImpl<float>::operator=( INTEGER_IMPL_ULL const& );

template
FloatImpl<double>& FloatImpl<double>::operator=( INTEGER_IMPL_LL const& );

template
FloatImpl<double>& FloatImpl<double>::operator=( INTEGER_IMPL_ULL const& );
#endif /* ZORBA_WITH_BIG_INTEGER */

////////// math functions /////////////////////////////////////////////////////

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::acos() const {
  if ( *this < neg_one() || *this > one() )
    return nan();
  return FloatImpl<FloatType>(
    isNegZero() ? -std::acos( value_ ): std::acos( value_ )
  );
}

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::asin() const {
  if ( *this < neg_one() || *this > one() )
    return nan();
  return FloatImpl<FloatType>( std::asin( value_ ) );
}

template<typename FloatType>
void FloatImpl<FloatType>::frexp( FloatImpl<FloatType> &out_mantissa,
                                  Integer &out_exponent ) const {
  int expint;
  out_mantissa = FloatImpl( ::frexp( value_, &expint ) );
  out_exponent = Integer( expint );
}

template<>
void FloatImpl<double>::modf( FloatImpl<double> &out_fraction,
                              FloatImpl<double> &out_integer ) const {
  double int_part;
  out_fraction = std::modf( value_, &int_part );
  out_integer = int_part;
}

template<>
void FloatImpl<float>::modf( FloatImpl<float> &out_fraction,
                             FloatImpl<float> &out_integer ) const {
  float int_part;
  out_fraction = ::modff( value_, &int_part );
  out_integer = int_part;
}

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::round() const {
  return round( Integer::zero() );
}

template<typename FloatType>
FloatImpl<FloatType> FloatImpl<FloatType>::round( Integer const &precision ) const {
  FloatImpl result;
  if ( isFinite() && !isZero() ) {
    MAPM m(
      Decimal::round2(
        Decimal::value_type( value_ ),
        Decimal::value_type( precision.itod() )
      )
    );
    if ( value_ < 0 && m.sign() == 0 )
      result = neg_zero();
    else {
      char buf[200];
      m.toString( buf, ZORBA_FLOAT_POINT_PRECISION );
      result.parse( buf );
    }
  } else
    result.value_ = value_;

  return result;
}

template<typename FloatType> FloatImpl<FloatType>
FloatImpl<FloatType>::roundHalfToEven( Integer const &precision) const {
  FloatImpl result;
  if ( isFinite() && !isZero() ) {
    MAPM m(
      Decimal::roundHalfToEven2(
        Decimal::value_type( value_ ),
        Decimal::value_type( precision.itod() )
      )
    );
    if ( value_ < 0 && m.sign() == 0 )
      result = neg_zero();
    else {
      char buf[200];
      m.toString( buf, ZORBA_FLOAT_POINT_PRECISION );
      result.parse( buf );
    }
  } else
    result.value_ = value_;
  return result;
}

////////// miscellaneous //////////////////////////////////////////////////////

template<>
bool FloatImpl<double>::isNegZero() const {
  if ( !value_ ) {
    char const *const bytes = reinterpret_cast<char const*>( &value_ );
    // test for little endian and big endian
    return bytes[0] || bytes[7];        // TODO: depends on sizeof(double)
  }
  return false;
}

template<>
bool FloatImpl<float>::isNegZero() const {
  if ( !value_ ) {
    char const *const bytes = reinterpret_cast<char const*>( &value_ );
    // test for little endian and big endian
    return bytes[0] || bytes[3];        // TODO: depends on sizeof(float)
  }
  return false;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::nan() {
  static FloatImpl<FloatType> const value( std::sqrt( -1.0 ) );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::neg_inf() {
  static FloatImpl<FloatType> const value(
    -std::numeric_limits<FloatType>::infinity()
  );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::neg_one() {
  static FloatImpl<FloatType> const value( -1 );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::neg_zero() {
  static FloatImpl<FloatType> const value( -0.0 );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::one() {
  static FloatImpl<FloatType> const value( 1 );
  return value;
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::pos_inf() {
  static FloatImpl<FloatType> const value(
    std::numeric_limits<FloatType>::infinity()
  );
  return value;
}

template<typename FloatType>
zstring FloatImpl<FloatType>::toIntegerString() const {
  if ( isNaN() )
    return nan_str();
  if  (isPosInf() )
    return pos_inf_str();
  if ( isNegInf() )
    return neg_inf_str();
  if ( isPosZero() )
    return "0";
  if ( isNegZero() )
    return "-0";

  // TODO: make xs_int
  char buf[174];
  sprintf( buf, "%d", (int)value_ );
  return buf;
}

template<typename FloatType>
zstring FloatImpl<FloatType>::toString( bool no_scientific_format ) const {
  if ( isNaN() )
    return nan_str();
  if ( isPosInf() )
    return pos_inf_str();
  if ( isNegInf() )
    return neg_inf_str();
  if ( isPosZero() )
    return "0";
  if ( isNegZero() )
    return "-0";

  FloatType const absVal = fabs( value_ );
  FloatType const lower = 0.000001f, upper = 1000000.0f;

  if (no_scientific_format || (absVal < upper && absVal >= lower) || absVal == 0)
  {
#if 1
    // This is the "spec" implementation, i.e., it is an exact application of
    // the spec in  http://www.w3.org/TR/xpath-functions/#casting
    MAPM decimal_mapm( value_ );
    decimal_mapm = decimal_mapm.round( precision_ );
    return Decimal::toString(decimal_mapm, max_precision());
#else
    std::stringstream stream;
    stream.precision(7);
    stream.setf(std::ios::fixed);
    stream << value_;

    zstring result(stream.str());

    // remove non-significant trailing 0's
    long i = result.size() - 1;
    while (str[i] == '0')
      --i;

    if (i >= 0) {
      long j = i;
      while (str[j] != '.')
        --j;

      if (j >= 0)
        if (j == i)
          result.resize(i);
        else
          result.resize(i+1);
    }

    return result;
#endif
  } else {
    char format[15];
    sprintf( format, "%%#1.%dE", static_cast<int>( precision_ ) );

    char buf[174];
    sprintf( buf, format, static_cast<double>( value_ ) );

    char *e = strchr( buf, 'E' );
    char *zeros = e ? e - 1 : buf + strlen( buf ) - 1;

    while ( *zeros == '0' )
      --zeros;

    if ( e ) {
      if ( *zeros == '.' )
        ++zeros;

      zeros[1] = 'E';
      ++e;

      if ( *e == '+' )
        ++e;
      else if ( *e == '-' ) {
        ++zeros;
        zeros[1] = '-';
        ++e;
      }

      while ( *e == '0' )
        ++e;

      memmove( (void*)(zeros + 2), e, strlen( e ) + 1 );
    } else {
      if ( *zeros == '.' )
        --zeros;
      zeros[1] = '\0';
    }

    Decimal::reduce( buf );
    return buf;
  }
}

template<typename FloatType>
FloatImpl<FloatType> const& FloatImpl<FloatType>::zero() {
  static FloatImpl<FloatType> const value( 0 );
  return value;
}

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

template class FloatImpl<double>;
template class FloatImpl<float>;

#ifdef WIN32
// exported for testing only
template class ZORBA_DLL_PUBLIC FloatImpl<double>;
template class ZORBA_DLL_PUBLIC FloatImpl<float>;
#endif /* WIN32 */

} // namespace zorba
/* vim:set et sw=2 ts=2: */
<|endoftext|>


<commit_before>#include "html.h"
#include "el_li.h"
#include "document.h"

litehtml::el_li::el_li(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc)
{
}

int litehtml::el_li::render(int x, int y, int max_width, bool second_pass)
{
	if (m_list_style_type >= list_style_type_armenian && !m_index_initialized)
	{
		if (auto p = parent())
		{
			tchar_t val[2] = { 1, 0 };
			for (int i = 0, n = (int)p->get_children_count(); i < n; ++i)
			{
				auto child = p->get_child(i);
				if (child.get() == this)
				{
					set_attr(_t("list_index"), val);
					break;
				}
				else if (!t_strcmp(child->get_tagName(), _t("li")))
					++val[0];
			}
		}

		m_index_initialized = true;
	}

	return html_tag::render(x, y, max_width, second_pass);
}
<commit_msg>Handle start attribute in ordered lists<commit_after>#include "html.h"
#include "el_li.h"
#include "document.h"

litehtml::el_li::el_li(const std::shared_ptr<litehtml::document>& doc) : litehtml::html_tag(doc)
{
}

int litehtml::el_li::render(int x, int y, int max_width, bool second_pass)
{
	if (m_list_style_type >= list_style_type_armenian && !m_index_initialized)
	{
		if (auto p = parent())
		{
			const auto hasStart = p->get_attr(_t("start"));
			const int start = hasStart ? t_atoi(hasStart) : 1;
			tchar_t val[2] = { (tchar_t)start, 0 };
			for (int i = 0, n = (int)p->get_children_count(); i < n; ++i)
			{
				auto child = p->get_child(i);
				if (child.get() == this)
				{
					set_attr(_t("list_index"), val);
					break;
				}
				else if (!t_strcmp(child->get_tagName(), _t("li")))
					++val[0];
			}
		}

		m_index_initialized = true;
	}

	return html_tag::render(x, y, max_width, second_pass);
}
<|endoftext|>


<commit_before>/**
 * Unit Tests for Piezas
**/

#include <gtest/gtest.h>
#include "Piezas.h"

class PiezasTest : public ::testing::Test
{
	protected:
		PiezasTest(){} //constructor runs before each test
		virtual ~PiezasTest(){} //destructor cleans up after tests
		virtual void SetUp(){} //sets up before each test (after constructor)
		virtual void TearDown(){} //clean up after each test, (before destructor)
};

TEST(PiezasTest, sanityCheck)
{
	ASSERT_TRUE(true);
}<commit_msg>added a bunch of tests<commit_after>/**
 * Unit Tests for Piezas
 **/

#include <gtest/gtest.h>
#include "Piezas.h"

class PiezasTest : public ::testing::Test
{
  protected:
    PiezasTest(){} //constructor runs before each test
    virtual ~PiezasTest(){} //destructor cleans up after tests
    virtual void SetUp(){} //sets up before each test (after constructor)
    virtual void TearDown(){} //clean up after each test, (before destructor)
};

TEST(PiezasTest, sanityCheck)
{
  ASSERT_TRUE(true);
}

TEST(PiezasTest, constructorCheck)
{
  Piezas p;
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      ASSERT_EQ(p.pieceAt(i, j), Blank);
    }
  }
}

TEST(PiezasTest, peiceAtTest)
{
  Piezas p;
  ASSERT_EQ(p.pieceAt(-1, -1), Invalid);
  ASSERT_EQ(p.pieceAt(3, 4), Invalid);
  ASSERT_EQ(p.pieceAt(3, 0), Invalid);
  ASSERT_EQ(p.pieceAt(0, 4), Invalid);
}

TEST(PiezasTest, fullBoard_pieceAtTest)
{
  Piezas p;
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      p.dropPiece(j);
      ASSERT_NE(pieceAt(i, j), Blank);
      ASSERT_NE(pieceAt(i, j), Invalid);
      if (j % 2)
      {
        ASSERT_EQ(pieceAt(i, j), O);
      }
      else
      {
        ASSERT_EQ(pieceAt(i, j), X);
      }
    }
  }
}

TEST(PiezasTest, tieTest)
{
  Piezas p;
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      p.dropPiece(j);
      ASSERT_EQ(p.gameState(), Invalid);
    }
  }
  ASSERT_EQ(p.gameState(), Blank);
  ASSERT_EQ(p.gameState(), Invalid);
  p.reset();
  ASSERT_EQ(p.gameState(), Invalid);
  for (int j = 0; j < BOARD_COLS; j++)
  {
    p.dropPiece(j);
    ASSERT_EQ(p.gameState(), Invalid);
  }
  for (int j = 3; j >= 0; j--)
  {
    p.dropPiece(j);
    ASSERT_EQ(p.gameState(), Invalid);
  }
  for (int j = 0; j < BOARD_COLS; j++)
  {
    p.dropPiece(j);
    ASSERT_EQ(p.gameState(), Invalid);
  }
  ASSERT_EQ(p.gameState(), Blank);
}
<|endoftext|>


<commit_before>//===---------- SILGlobalOpt.cpp - Optimize global initializers -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "globalopt"
#include "swift/Basic/Demangle.h"
#include "swift/SIL/CFG.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILPasses/Passes.h"
#include "swift/SILPasses/Transforms.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/Support/Debug.h"
using namespace swift;

namespace {
/// Optimize the placement of global initializers.
///
/// TODO:
///
/// - Use CallGraphAnalysis to move initializers to the module's public entry
///   points.
///
/// - Convert trivial initializers to static initialization. This requires
///   serializing globals.
///
/// - For global "lets", generate addressors that return by value. If we also
///  converted to a static initializer, then remove the load from the addressor.
///
/// - When the addressor is local to the module, be sure it is inlined to allow
///   constant propagation in case of statically initialized "lets".
class SILGlobalOpt : public SILModuleTransform
{
  // Map each global initializer to a list of call sites.
  typedef SmallVector<ApplyInst *, 4> GlobalInitCalls;
  llvm::MapVector<SILFunction*, GlobalInitCalls> GlobalInitCallMap;

  // Mark any block that this pass has determined to be inside a loop.
  llvm::DenseSet<SILBasicBlock*> LoopBlocks;
  // Mark any functions for which loops have been analyzed.
  llvm::DenseSet<SILFunction*> LoopCheckedFunctions;

public:
  void run() override;

  StringRef getName() override { return "SIL Global Optimization"; }

protected:
  void collectGlobalInitCall(ApplyInst *AI);
  bool isInLoop(SILBasicBlock *CurBB);
  void placeInitializers(SILFunction *InitF, ArrayRef<ApplyInst*> Calls);
};
} // namespace

/// If this is a call to a global initializer, map it.
void SILGlobalOpt::collectGlobalInitCall(ApplyInst *AI) {
  FunctionRefInst *FR = dyn_cast<FunctionRefInst>(AI->getCallee());
  if (!FR)
    return;

  SILFunction *F = FR->getReferencedFunction();
  if (!F->isGlobalInit())
    return;

  GlobalInitCallMap[F].push_back(AI);
}

/// return true if this block is inside a loop.
bool SILGlobalOpt::isInLoop(SILBasicBlock *CurBB) {
  SILFunction *F = CurBB->getParent();
  // Catch the common case in which we've already hoisted the initializer.
  if (CurBB == &F->front())
    return false;

  if (LoopCheckedFunctions.insert(F).second) {
    for (auto I = scc_begin(F); !I.isAtEnd(); ++I) {
      if (I.hasLoop())
        for (SILBasicBlock *BB : *I)
          LoopBlocks.insert(BB);
    }
  }
  return LoopBlocks.count(CurBB);
}

/// Optimize placement of initializer calls given a list of calls to the
/// same initializer. All original initialization points must be dominated by
/// the final initialization calls.
///
/// For now, just hoist all initializers to their function entry.
void SILGlobalOpt::placeInitializers(SILFunction *InitF,
                                     ArrayRef<ApplyInst*> Calls) {
  DEBUG(llvm::dbgs() << "GlobalOpt: calls to "
        << Demangle::demangleSymbolAsString(InitF->getName())
        << " : " << Calls.size() << "\n");
  // Map each initializer-containing function to its final initializer call.
  llvm::DenseMap<SILFunction*, ApplyInst*> ParentFuncs;
  for (auto *AI : Calls) {
    assert(AI->getNumArguments() == 0 && "ill-formed global init call");

    auto PFI = ParentFuncs.find(AI->getFunction());
    if (PFI != ParentFuncs.end()) {
      // This call is redundant. Replace it.
      assert(cast<FunctionRefInst>(AI->getCallee())->getReferencedFunction()
             == InitF &&
             "ill-formed global init call");
      AI->replaceAllUsesWith(PFI->second);
      AI->eraseFromParent();
      continue;
    }
    // Check if this call is inside a loop. If not, don't move it.
    if (!isInLoop(AI->getParent())) {
      DEBUG(llvm::dbgs() << "  skipping (not in a loop): " << *AI
            << "  in " << AI->getFunction()->getName() << "\n");
      continue;
    }
    DEBUG(llvm::dbgs() << "  hoisting: " << *AI
          << "  in " << AI->getFunction()->getName() << "\n");

    // Move this call to the parent function's entry.
    FunctionRefInst *FuncRef = cast<FunctionRefInst>(AI->getOperand(0));
    assert(FuncRef->getReferencedFunction() == InitF && "wrong init call");
    SILFunction *ParentF = AI->getFunction();
    AI->moveBefore(ParentF->front().begin());
    FuncRef->moveBefore(AI);
    ParentFuncs[ParentF] = AI;
  }
}

void SILGlobalOpt::run() {
  for (auto &F : *getModule())
    for (auto &BB : F)
      for (auto &I : BB)
        if (ApplyInst *AI = dyn_cast<ApplyInst>(&I))
          collectGlobalInitCall(AI);

  for (auto &InitCalls : GlobalInitCallMap)
    placeInitializers(InitCalls.first, InitCalls.second);

  GlobalInitCallMap.clear();
}

SILTransform *swift::createGlobalOpt() {
  return new SILGlobalOpt();
}
<commit_msg>Convert the GlobalOpt pass into a proper SILModuleTransform.<commit_after>//===---------- SILGlobalOpt.cpp - Optimize global initializers -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "globalopt"
#include "swift/Basic/Demangle.h"
#include "swift/SIL/CFG.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILPasses/Passes.h"
#include "swift/SILPasses/Transforms.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/Support/Debug.h"
using namespace swift;

namespace {
/// Optimize the placement of global initializers.
///
/// TODO:
///
/// - Use CallGraphAnalysis to move initializers to the module's public entry
///   points.
///
/// - Convert trivial initializers to static initialization. This requires
///   serializing globals.
///
/// - For global "lets", generate addressors that return by value. If we also
///  converted to a static initializer, then remove the load from the addressor.
///
/// - When the addressor is local to the module, be sure it is inlined to allow
///   constant propagation in case of statically initialized "lets".
class SILGlobalOpt {
  SILModule *Module;

  // Map each global initializer to a list of call sites.
  typedef SmallVector<ApplyInst *, 4> GlobalInitCalls;
  llvm::MapVector<SILFunction*, GlobalInitCalls> GlobalInitCallMap;

  // Mark any block that this pass has determined to be inside a loop.
  llvm::DenseSet<SILBasicBlock*> LoopBlocks;
  // Mark any functions for which loops have been analyzed.
  llvm::DenseSet<SILFunction*> LoopCheckedFunctions;

public:
  SILGlobalOpt(SILModule *M): Module(M) {}

  void run();

protected:
  void collectGlobalInitCall(ApplyInst *AI);
  bool isInLoop(SILBasicBlock *CurBB);
  void placeInitializers(SILFunction *InitF, ArrayRef<ApplyInst*> Calls);
};
} // namespace

/// If this is a call to a global initializer, map it.
void SILGlobalOpt::collectGlobalInitCall(ApplyInst *AI) {
  FunctionRefInst *FR = dyn_cast<FunctionRefInst>(AI->getCallee());
  if (!FR)
    return;

  SILFunction *F = FR->getReferencedFunction();
  if (!F->isGlobalInit())
    return;

  GlobalInitCallMap[F].push_back(AI);
}

/// return true if this block is inside a loop.
bool SILGlobalOpt::isInLoop(SILBasicBlock *CurBB) {
  SILFunction *F = CurBB->getParent();
  // Catch the common case in which we've already hoisted the initializer.
  if (CurBB == &F->front())
    return false;

  if (LoopCheckedFunctions.insert(F).second) {
    for (auto I = scc_begin(F); !I.isAtEnd(); ++I) {
      if (I.hasLoop())
        for (SILBasicBlock *BB : *I)
          LoopBlocks.insert(BB);
    }
  }
  return LoopBlocks.count(CurBB);
}

/// Optimize placement of initializer calls given a list of calls to the
/// same initializer. All original initialization points must be dominated by
/// the final initialization calls.
///
/// For now, just hoist all initializers to their function entry.
void SILGlobalOpt::placeInitializers(SILFunction *InitF,
                                     ArrayRef<ApplyInst*> Calls) {
  DEBUG(llvm::dbgs() << "GlobalOpt: calls to "
        << Demangle::demangleSymbolAsString(InitF->getName())
        << " : " << Calls.size() << "\n");
  // Map each initializer-containing function to its final initializer call.
  llvm::DenseMap<SILFunction*, ApplyInst*> ParentFuncs;
  for (auto *AI : Calls) {
    assert(AI->getNumArguments() == 0 && "ill-formed global init call");

    auto PFI = ParentFuncs.find(AI->getFunction());
    if (PFI != ParentFuncs.end()) {
      // This call is redundant. Replace it.
      assert(cast<FunctionRefInst>(AI->getCallee())->getReferencedFunction()
             == InitF &&
             "ill-formed global init call");
      AI->replaceAllUsesWith(PFI->second);
      AI->eraseFromParent();
      continue;
    }
    // Check if this call is inside a loop. If not, don't move it.
    if (!isInLoop(AI->getParent())) {
      DEBUG(llvm::dbgs() << "  skipping (not in a loop): " << *AI
            << "  in " << AI->getFunction()->getName() << "\n");
      continue;
    }
    DEBUG(llvm::dbgs() << "  hoisting: " << *AI
          << "  in " << AI->getFunction()->getName() << "\n");

    // Move this call to the parent function's entry.
    FunctionRefInst *FuncRef = cast<FunctionRefInst>(AI->getOperand(0));
    assert(FuncRef->getReferencedFunction() == InitF && "wrong init call");
    SILFunction *ParentF = AI->getFunction();
    AI->moveBefore(ParentF->front().begin());
    FuncRef->moveBefore(AI);
    ParentFuncs[ParentF] = AI;
  }
}

void SILGlobalOpt::run() {
  for (auto &F : *Module)
    for (auto &BB : F)
      for (auto &I : BB)
        if (ApplyInst *AI = dyn_cast<ApplyInst>(&I))
          collectGlobalInitCall(AI);

  for (auto &InitCalls : GlobalInitCallMap)
    placeInitializers(InitCalls.first, InitCalls.second);
}

namespace {
class SILGlobalOptPass : public SILModuleTransform
{
  void run() override {
    SILGlobalOpt(getModule()).run();
  }

  StringRef getName() override { return "SIL Global Optimization"; }
};
} // anonymous

SILTransform *swift::createGlobalOpt() {
  return new SILGlobalOptPass();
}
<|endoftext|>


<commit_before>/*!
	\file CacheSimulator.cpp

	\author Sudnya Padalikar <sudnya.padalikar@gatech.edu>
	\date Sunday November 29, 2009
	\brief implementation of a CacheSimulator class to be the child of
		TraceGenerator
*/

#ifndef TRACE_CACHESIMULATOR_CPP_INCLUDED
#define TRACE_CACHESIMULATOR_CPP_INCLUDED

#include <ocelot/trace/interface/CacheSimulator.h>
#include <hydrazine/interface/debug.h>
#include <cmath>

#ifdef REPORT_BASE
#undef REPORT_BASE
#endif

#define REPORT_BASE 0

namespace trace
{

	CacheSimulator::CacheWay::CacheWay(ir::PTXU64 t, bool d) : dirty(d), tag(t)
	{

	}

	CacheSimulator::CacheEntry::CacheEntry(unsigned int a) : _associativity(a)
	{

	}

	bool CacheSimulator::CacheEntry::write(ir::PTXU64 tag, bool& writeback)
	{
		for(WayList::iterator way = _ways.begin();
			way != _ways.end(); ++way)
		{
			if(way->tag == tag)
			{
				_ways.erase(way);
				_ways.push_back(CacheWay(tag, true));
				writeback = false;
				return true;
			}
		}

		// miss
		if(_ways.size() == _associativity)
		{
			writeback = _ways.front().dirty;
			_ways.erase(_ways.begin());
		}

		_ways.push_back(CacheWay(tag, true));

		return false;
	}

	bool CacheSimulator::CacheEntry::read(ir::PTXU64 tag, bool& writeback)
	{
		for(WayList::iterator way = _ways.begin();
			way != _ways.end(); ++way)
		{
			if(way->tag == tag)
			{
				CacheWay newEntry(tag, way->dirty);
				_ways.erase(way);
				_ways.push_back(newEntry);
				writeback = false;
				return true;
			}
		}

		// miss
		if(_ways.size() == _associativity)
		{
			writeback = _ways.front().dirty;
			_ways.erase(_ways.begin());
		}

		_ways.push_back(CacheWay(tag, false));

		return false;
	}

	CacheSimulator::CacheSimulator()
	{
		writebackTime = 50;
		cacheSize     = 8192;
		lineSize      = 64;
		hitTime       = 1;
		missTime      = 200;
		associativity = 1;
	}

	CacheSimulator::~CacheSimulator()
	{

	}

	/*!
		called when a traced kernel is launched to retrieve some parameters
		from the kernel
	*/
	void CacheSimulator::initialize(const executive::ExecutableKernel& kernel)
	{
		_time         = 0;
		_missCount    = 0;
		_hitCount     = 0;
		_missLatency  = 0;
		_hitLatency   = 0;
		_memoryAccess = 0;

		unsigned int sets = cacheSize / (lineSize * associativity);

		_cache.resize(sets, CacheEntry(associativity));
		//report( "Initializing trace generator for kernel " << kernel->name );
	}

	void CacheSimulator::lookupEntry(int setNumber, int tag, bool accessType)
	{
		bool writeback = false;
		if(accessType)
		{
			if(!_cache[setNumber].write(tag, writeback))
			{
				++_missCount;

				_missLatency += missTime;
				_time        += missTime;

				if(writeback)
				{
					_time += writebackTime;
				}
			}
			else
			{
				++_hitCount;
				_hitLatency += hitTime;
				_time       += hitTime;
			}
		}
		else
		{
			if(!_cache[setNumber].read(tag, writeback))
			{
				++_missCount;

				_missLatency += missTime;
				_time        += missTime;

				if(writeback)
				{
					_time += writebackTime;
				}
			}
			else
			{
				++_hitCount;

				_hitLatency += hitTime;
				_time       += hitTime;
			}
		}
	}

	ir::PTXU64 CacheSimulator::getTag(ir::PTXU64 addressAccessed)
	{
		int shiftAmount = std::log2( cacheSize / (lineSize * associativity) )
			+ std::log2((lineSize * associativity));
		addressAccessed >>= shiftAmount;
		return addressAccessed;
	}

	int CacheSimulator::findSet(ir::PTXU64 addressAccessed)
	{
		addressAccessed >>= (int)std::log2((lineSize * associativity));

		ir::PTXU64 mask = (cacheSize / (lineSize * associativity)) - 1;
		return addressAccessed & mask;
	}

	int CacheSimulator::getOffset(ir::PTXU64 addressAccessed)
	{
		ir::PTXU64 mask = (lineSize * associativity) - 1;
		return addressAccessed & mask;
	}

	bool CacheSimulator::cachelineSplit(ir::PTXU64 addressAccessed,
		ir::PTXU32 bytesAccessed)
	{
		unsigned int offset;
		offset = getOffset(addressAccessed);
		if(offset+bytesAccessed > (lineSize * associativity))
		{
			return true;
		}

		return false;
	}

	void CacheSimulator::read(ir::PTXU64 addressAccessed,
		ir::PTXU32 bytesAccessed)
	{
		long long unsigned int tag;
		int setNumber;
		assertM(!cachelineSplit(addressAccessed, bytesAccessed),
			" Access accross split cache lines is not supported at this time.");
		//get tags, which set to look etc
		tag = getTag(addressAccessed);
		setNumber = findSet(addressAccessed);

		lookupEntry(setNumber, tag, false);
	}

	void CacheSimulator::write(ir::PTXU64 addressAccessed,
		ir::PTXU32 bytesAccessed)
	{
		long long unsigned int tag;
		int setNumber;
		assertM(!cachelineSplit(addressAccessed, bytesAccessed),
			" Access accross split cache lines is not supported at this time.");

		//get tags, which set to look etc
		tag = getTag(addressAccessed);
		setNumber = findSet(addressAccessed);

		lookupEntry(setNumber, tag, true);
	}

	/*!
		called whenever an event takes place
	*/
	void CacheSimulator::event(const TraceEvent & event)
	{
		if(instructionMemory)
		{
			_memoryAccess+=4;

			read(event.PC*4, 4);
		}
		else
		{
			if(!event.memory_addresses.empty())
			{
				_memoryAccess+=event.memory_addresses.size();

				if(event.instruction->opcode == ir::PTXInstruction::St) //write
				{
					for(TraceEvent::U64Vector::const_iterator
						i = event.memory_addresses.begin();
						i != event.memory_addresses.end(); ++i)
					{
						write(*i, event.memory_size);
					}
				}
				else
				{
					for(TraceEvent::U64Vector::const_iterator
						i = event.memory_addresses.begin();
						i != event.memory_addresses.end(); ++i)
					{
						read(*i, event.memory_size);
					}
				}
			}
		}
		//report( "Default Trace: " << event.toString() );
	}

	void CacheSimulator::finish()
	{
		_cache.clear();
		std::cout << "Miss Rate: " << (_missCount+0.0)/(_missCount + _hitCount) << "\n";
		std::cout << "Miss Latency: " << _missLatency<< "\n";
		std::cout << "Hit Latency: " << _hitLatency<< "\n";
		std::cout << "Total time for memory accesses: " << _time<< "\n";

		//a) Miss Rate (total_misses/total_accesses)
     //  b) Miss latency (total number of cycles servicing misses)
      // c) Hit latency (total number of cycles servicing hits)
	}

}

#endif

<commit_msg>BUG: Don't use std::log2 because windows doesn't support it.<commit_after>/*!
	\file CacheSimulator.cpp

	\author Sudnya Padalikar <sudnya.padalikar@gatech.edu>
	\date Sunday November 29, 2009
	\brief implementation of a CacheSimulator class to be the child of
		TraceGenerator
*/

#ifndef TRACE_CACHESIMULATOR_CPP_INCLUDED
#define TRACE_CACHESIMULATOR_CPP_INCLUDED

#include <ocelot/trace/interface/CacheSimulator.h>
#include <hydrazine/interface/debug.h>

#ifdef REPORT_BASE
#undef REPORT_BASE
#endif

#define REPORT_BASE 0

namespace trace
{

	static unsigned int log2(unsigned int v)
	{
		unsigned int value = 0;

		while(v != 0)
		{
			v >>= 1;
			++value;
		}

		return value;
	}

	CacheSimulator::CacheWay::CacheWay(ir::PTXU64 t, bool d) : dirty(d), tag(t)
	{

	}

	CacheSimulator::CacheEntry::CacheEntry(unsigned int a) : _associativity(a)
	{

	}

	bool CacheSimulator::CacheEntry::write(ir::PTXU64 tag, bool& writeback)
	{
		for(WayList::iterator way = _ways.begin();
			way != _ways.end(); ++way)
		{
			if(way->tag == tag)
			{
				_ways.erase(way);
				_ways.push_back(CacheWay(tag, true));
				writeback = false;
				return true;
			}
		}

		// miss
		if(_ways.size() == _associativity)
		{
			writeback = _ways.front().dirty;
			_ways.erase(_ways.begin());
		}

		_ways.push_back(CacheWay(tag, true));

		return false;
	}

	bool CacheSimulator::CacheEntry::read(ir::PTXU64 tag, bool& writeback)
	{
		for(WayList::iterator way = _ways.begin();
			way != _ways.end(); ++way)
		{
			if(way->tag == tag)
			{
				CacheWay newEntry(tag, way->dirty);
				_ways.erase(way);
				_ways.push_back(newEntry);
				writeback = false;
				return true;
			}
		}

		// miss
		if(_ways.size() == _associativity)
		{
			writeback = _ways.front().dirty;
			_ways.erase(_ways.begin());
		}

		_ways.push_back(CacheWay(tag, false));

		return false;
	}

	CacheSimulator::CacheSimulator()
	{
		writebackTime = 50;
		cacheSize     = 8192;
		lineSize      = 64;
		hitTime       = 1;
		missTime      = 200;
		associativity = 1;
	}

	CacheSimulator::~CacheSimulator()
	{

	}

	/*!
		called when a traced kernel is launched to retrieve some parameters
		from the kernel
	*/
	void CacheSimulator::initialize(const executive::ExecutableKernel& kernel)
	{
		_time         = 0;
		_missCount    = 0;
		_hitCount     = 0;
		_missLatency  = 0;
		_hitLatency   = 0;
		_memoryAccess = 0;

		unsigned int sets = cacheSize / (lineSize * associativity);

		_cache.resize(sets, CacheEntry(associativity));
		//report( "Initializing trace generator for kernel " << kernel->name );
	}

	void CacheSimulator::lookupEntry(int setNumber, int tag, bool accessType)
	{
		bool writeback = false;
		if(accessType)
		{
			if(!_cache[setNumber].write(tag, writeback))
			{
				++_missCount;

				_missLatency += missTime;
				_time        += missTime;

				if(writeback)
				{
					_time += writebackTime;
				}
			}
			else
			{
				++_hitCount;
				_hitLatency += hitTime;
				_time       += hitTime;
			}
		}
		else
		{
			if(!_cache[setNumber].read(tag, writeback))
			{
				++_missCount;

				_missLatency += missTime;
				_time        += missTime;

				if(writeback)
				{
					_time += writebackTime;
				}
			}
			else
			{
				++_hitCount;

				_hitLatency += hitTime;
				_time       += hitTime;
			}
		}
	}

	ir::PTXU64 CacheSimulator::getTag(ir::PTXU64 addressAccessed)
	{
		int shiftAmount = log2( cacheSize / (lineSize * associativity) )
			+ log2((lineSize * associativity));
		addressAccessed >>= shiftAmount;
		return addressAccessed;
	}

	int CacheSimulator::findSet(ir::PTXU64 addressAccessed)
	{
		addressAccessed >>= (int)log2((lineSize * associativity));

		ir::PTXU64 mask = (cacheSize / (lineSize * associativity)) - 1;
		return addressAccessed & mask;
	}

	int CacheSimulator::getOffset(ir::PTXU64 addressAccessed)
	{
		ir::PTXU64 mask = (lineSize * associativity) - 1;
		return addressAccessed & mask;
	}

	bool CacheSimulator::cachelineSplit(ir::PTXU64 addressAccessed,
		ir::PTXU32 bytesAccessed)
	{
		unsigned int offset;
		offset = getOffset(addressAccessed);
		if(offset+bytesAccessed > (lineSize * associativity))
		{
			return true;
		}

		return false;
	}

	void CacheSimulator::read(ir::PTXU64 addressAccessed,
		ir::PTXU32 bytesAccessed)
	{
		long long unsigned int tag;
		int setNumber;
		assertM(!cachelineSplit(addressAccessed, bytesAccessed),
			" Access accross split cache lines is not supported at this time.");
		//get tags, which set to look etc
		tag = getTag(addressAccessed);
		setNumber = findSet(addressAccessed);

		lookupEntry(setNumber, tag, false);
	}

	void CacheSimulator::write(ir::PTXU64 addressAccessed,
		ir::PTXU32 bytesAccessed)
	{
		long long unsigned int tag;
		int setNumber;
		assertM(!cachelineSplit(addressAccessed, bytesAccessed),
			" Access accross split cache lines is not supported at this time.");

		//get tags, which set to look etc
		tag = getTag(addressAccessed);
		setNumber = findSet(addressAccessed);

		lookupEntry(setNumber, tag, true);
	}

	/*!
		called whenever an event takes place
	*/
	void CacheSimulator::event(const TraceEvent & event)
	{
		if(instructionMemory)
		{
			_memoryAccess+=4;

			read(event.PC*4, 4);
		}
		else
		{
			if(!event.memory_addresses.empty())
			{
				_memoryAccess+=event.memory_addresses.size();

				if(event.instruction->opcode == ir::PTXInstruction::St) //write
				{
					for(TraceEvent::U64Vector::const_iterator
						i = event.memory_addresses.begin();
						i != event.memory_addresses.end(); ++i)
					{
						write(*i, event.memory_size);
					}
				}
				else
				{
					for(TraceEvent::U64Vector::const_iterator
						i = event.memory_addresses.begin();
						i != event.memory_addresses.end(); ++i)
					{
						read(*i, event.memory_size);
					}
				}
			}
		}
		//report( "Default Trace: " << event.toString() );
	}

	void CacheSimulator::finish()
	{
		_cache.clear();
		std::cout << "Miss Rate: " << (_missCount+0.0)/(_missCount + _hitCount) << "\n";
		std::cout << "Miss Latency: " << _missLatency<< "\n";
		std::cout << "Hit Latency: " << _hitLatency<< "\n";
		std::cout << "Total time for memory accesses: " << _time<< "\n";

		//a) Miss Rate (total_misses/total_accesses)
     //  b) Miss latency (total number of cycles servicing misses)
      // c) Hit latency (total number of cycles servicing hits)
	}

}

#endif

<|endoftext|>


<commit_before>#include "catch.hpp"
#include "matrix.h"

TEST_CASE( "Test matrix access out of range", "[matrix][access]" ) {
  SECTION("Try accessing an empty matrix"){
    matrix<int> m = {};
    REQUIRE_THROWS_AS( m.at(0,0), std::out_of_range );
  }
  SECTION("Try access out of range column"){
    matrix<int> m(2,3);
    REQUIRE_THROWS_AS( m.at(1,3), std::out_of_range );
  }
  SECTION("Try access out of range row"){
    matrix<int> m(2,3);
    REQUIRE_THROWS_AS( m.at(2,2), std::out_of_range );
  }
}
<commit_msg>Add testcases for access.<commit_after>#include "catch.hpp"
#include "matrix.h"

TEST_CASE( "Test matrix access out of range", "[matrix][access]" ) {
  SECTION("Try accessing an empty matrix"){
    matrix<int> m = {};
    REQUIRE_THROWS_AS( m.at(0,0), std::out_of_range );
  }
  SECTION("Try access out of range column"){
    matrix<int> m(2,3);
    REQUIRE_THROWS_AS( m.at(1,3), std::out_of_range );
  }
  SECTION("Try access out of range row"){
    matrix<int> m(2,3);
    REQUIRE_THROWS_AS( m.at(2,2), std::out_of_range );
  }
}

TEST_CASE( "Test changing elements", "[matrix][access]" ) {
  SECTION("Try changing a value in an empty matrix"){
    matrix<int> m = {};
    REQUIRE_THROWS_AS( m.at(0,0) = 1, std::out_of_range );
  }
  SECTION("Try changing a value in an out of range column"){
    matrix<int> m(2,3);
    REQUIRE_THROWS_AS( m.at(1,3) = 1, std::out_of_range );
  }
  SECTION("Try changing a value in an out of range row"){
    matrix<int> m(2,3);
    REQUIRE_THROWS_AS( m.at(2,2) = 1, std::out_of_range );
  }
}
<|endoftext|>


<commit_before>
#pragma once

#include <qdb/client.h>

#include "utilities.hpp"

namespace quasardb
{

class Error : public node::ObjectWrap
{
public:
    explicit Error(qdb_error_t e) : _error(e)
    {
    }

    virtual ~Error(void)
    {
    }

private:
    static void AddErrorOrigin(v8::Local<v8::Object> exports, const char * name, qdb_error_origin_t origin)
    {
        v8::Isolate * isolate = exports->GetIsolate();
        exports->ForceSet(v8::String::NewFromUtf8(isolate, name), v8::Int32::New(isolate, origin),
                          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    }

    static void AddErrorSeverity(v8::Local<v8::Object> exports, const char * name, qdb_error_severity_t severity)
    {
        v8::Isolate * isolate = exports->GetIsolate();
        exports->ForceSet(v8::String::NewFromUtf8(isolate, name), v8::Int32::New(isolate, severity),
                          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    }

    static void AddErrorCode(v8::Local<v8::Object> exports, const char * name, qdb_error_t err)
    {
        static const int code_mask = 0xFFFF;

        v8::Isolate * isolate = exports->GetIsolate();
        exports->ForceSet(v8::String::NewFromUtf8(isolate, name), v8::Int32::New(isolate, err & code_mask),
                          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    }

public:
    static void Init(v8::Local<v8::Object> exports)
    {
        v8::Isolate * isolate = exports->GetIsolate();

        // Prepare constructor template
        v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(isolate, New);
        tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Error"));
        tpl->InstanceTemplate()->SetInternalFieldCount(1);

        v8::HandleScope handle_scope(isolate);
        v8::Local<v8::Signature> s = v8::Signature::New(isolate, tpl);

        auto proto = tpl->PrototypeTemplate();

        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "informational"),
                                   v8::FunctionTemplate::New(isolate, Error::informational, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "origin"),
                                   v8::FunctionTemplate::New(isolate, Error::origin, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "severity"),
                                   v8::FunctionTemplate::New(isolate, Error::severity, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "message"),
                                   v8::FunctionTemplate::New(isolate, Error::message, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "code"),
                                   v8::FunctionTemplate::New(isolate, Error::code, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);

        AddErrorOrigin(exports, "E_ORIGIN_SYSTEM_REMOTE", qdb_e_origin_system_remote);
        AddErrorOrigin(exports, "E_ORIGIN_SYSTEM_LOCAL", qdb_e_origin_system_local);
        AddErrorOrigin(exports, "E_ORIGIN_CONNECTION", qdb_e_origin_connection);
        AddErrorOrigin(exports, "E_ORIGIN_INPUT", qdb_e_origin_input);
        AddErrorOrigin(exports, "E_ORIGIN_OPERATION", qdb_e_origin_operation);
        AddErrorOrigin(exports, "E_ORIGIN_PROTOCOL", qdb_e_origin_protocol);

        AddErrorSeverity(exports, "E_SEVERITY_UNRECOVERABLE", qdb_e_severity_unrecoverable);
        AddErrorSeverity(exports, "E_SEVERITY_ERROR", qdb_e_severity_error);
        AddErrorSeverity(exports, "E_SEVERITY_WARNING", qdb_e_severity_warning);
        AddErrorSeverity(exports, "E_SEVERITY_INFO", qdb_e_severity_info);

        AddErrorCode(exports, "E_UNINITIALIZED", qdb_e_uninitialized);
        AddErrorCode(exports, "E_ALIAS_NOT_FOUND", qdb_e_alias_not_found);
        AddErrorCode(exports, "E_ALIAS_ALREADY_EXISTS", qdb_e_alias_already_exists);
        AddErrorCode(exports, "E_OUT_OF_BOUNDS", qdb_e_out_of_bounds);
        AddErrorCode(exports, "E_SKIPPED", qdb_e_skipped);
        AddErrorCode(exports, "E_INCOMPATIBLE_TYPE", qdb_e_incompatible_type);
        AddErrorCode(exports, "E_CONTAINER_EMPTY", qdb_e_container_empty);
        AddErrorCode(exports, "E_CONTAINER_FULL", qdb_e_container_full);
        AddErrorCode(exports, "E_ELEMENT_NOT_FOUND", qdb_e_element_not_found);
        AddErrorCode(exports, "E_ELEMENT_ALREADY_EXISTS", qdb_e_element_already_exists);
        AddErrorCode(exports, "E_OVERFLOW", qdb_e_overflow);
        AddErrorCode(exports, "E_UNDERFLOW", qdb_e_underflow);
        AddErrorCode(exports, "E_TAG_ALREADY_SET", qdb_e_tag_already_set);
        AddErrorCode(exports, "E_TAG_NOT_SET", qdb_e_tag_not_set);
        AddErrorCode(exports, "E_TIMEOUT", qdb_e_timeout);
        AddErrorCode(exports, "E_CONNECTION_REFUSED", qdb_e_connection_refused);
        AddErrorCode(exports, "E_CONNECTION_RESET", qdb_e_connection_reset);
        AddErrorCode(exports, "E_UNSTABLE_CLUSTER", qdb_e_unstable_cluster);
        AddErrorCode(exports, "E_OUTDATED_TOPOLOGY", qdb_e_outdated_topology);
        AddErrorCode(exports, "E_WRONG_PEER", qdb_e_wrong_peer);
        AddErrorCode(exports, "E_TRY_AGAIN", qdb_e_try_again);
        AddErrorCode(exports, "E_CONFLICT", qdb_e_conflict);
        AddErrorCode(exports, "E_NOT_CONNECTED", qdb_e_not_connected);
        AddErrorCode(exports, "E_RESOURCE_LOCKED", qdb_e_resource_locked);
        AddErrorCode(exports, "E_SYSTEM_REMOTE", qdb_e_system_remote);
        AddErrorCode(exports, "E_SYSTEM_LOCAL", qdb_e_system_local);
        AddErrorCode(exports, "E_INTERNAL_REMOTE", qdb_e_internal_remote);
        AddErrorCode(exports, "E_INTERNAL_LOCAL", qdb_e_internal_local);
        AddErrorCode(exports, "E_NO_MEMORY_REMOTE", qdb_e_no_memory_remote);
        AddErrorCode(exports, "E_NO_MEMORY_LOCAL", qdb_e_no_memory_local);
        AddErrorCode(exports, "E_INVALID_PROTOCOL", qdb_e_invalid_protocol);
        AddErrorCode(exports, "E_HOST_NOT_FOUND", qdb_e_host_not_found);
        AddErrorCode(exports, "E_BUFFER_TOO_SMALL", qdb_e_buffer_too_small);
        AddErrorCode(exports, "E_NOT_IMPLEMENTED", qdb_e_not_implemented);
        AddErrorCode(exports, "E_INVALID_VERSION", qdb_e_invalid_version);
        AddErrorCode(exports, "E_INVALID_ARGUMENT", qdb_e_invalid_argument);
        AddErrorCode(exports, "E_INVALID_HANDLE", qdb_e_invalid_handle);
        AddErrorCode(exports, "E_RESERVED_ALIAS", qdb_e_reserved_alias);
        AddErrorCode(exports, "E_UNMATCHED_CONTENT", qdb_e_unmatched_content);
        AddErrorCode(exports, "E_INVALID_ITERATOR", qdb_e_invalid_iterator);
        AddErrorCode(exports, "E_ENTRY_TOO_LARGE", qdb_e_entry_too_large);
        AddErrorCode(exports, "E_TRANSACTION_PARTIAL_FAILURE", qdb_e_transaction_partial_failure);
        AddErrorCode(exports, "E_OPERATION_DISABLED", qdb_e_operation_disabled);
        AddErrorCode(exports, "E_OPERATION_NOT_PERMITTED", qdb_e_operation_not_permitted);
        AddErrorCode(exports, "E_ITERATOR_END", qdb_e_iterator_end);
        AddErrorCode(exports, "E_INVALID_REPLY", qdb_e_invalid_reply);
        AddErrorCode(exports, "E_OK_CREATED", qdb_e_ok_created);
        AddErrorCode(exports, "E_NO_SPACE_LEFT", qdb_e_no_space_left);
        AddErrorCode(exports, "E_QUOTA_EXCEEDED", qdb_e_quota_exceeded);

        constructor.Reset(isolate, tpl->GetFunction());
        exports->Set(v8::String::NewFromUtf8(isolate, "Error"), tpl->GetFunction());
    }

private:
    static void New(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        if (args.IsConstructCall())
        {
            MethodMan call(args);

            if (args.Length() != 1)
            {
                call.throwException("Expected exactly one argument");
                return;
            }

            ArgsEater argsEater(call);

            auto code = argsEater.eatNumber();
            if (!code.second)
            {
                call.throwException("Expected an error code as first argument");
                return;
            }

            const qdb_error_t err = static_cast<qdb_error_t>(static_cast<unsigned long>(code.first));

            Error * e = new Error(err);

            e->Wrap(args.This());
            args.GetReturnValue().Set(args.This());
        }
        else
        {
            NewInstance(args);
        }
    }

public:
    static void NewInstance(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        v8::Isolate * isolate = args.GetIsolate();
        // Invoked as plain function `MyObject(...)`, turn into construct call.
        static const int argc = 1;
        v8::Local<v8::Value> argv[argc] = {args[0]};
        v8::Local<v8::Function> cons = v8::Local<v8::Function>::New(isolate, constructor);
        v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);
        args.GetReturnValue().Set(instance);
    }

public:
    static v8::Local<v8::Object> MakeError(v8::Isolate * isolate, qdb_error_t err)
    {
        static const int argc = 1;
        v8::Local<v8::Value> argv[argc] = {v8::Number::New(isolate, err)};
        v8::Local<v8::Function> cons = v8::Local<v8::Function>::New(isolate, constructor);
        return cons->NewInstance(1, argv);
    }

private:
    template <typename F>
    static void accessor(const v8::FunctionCallbackInfo<v8::Value> & args, F f)
    {
        MethodMan call(args);

        if (args.Length() != 0)
        {
            call.throwException("Wrong number of arguments");
            return;
        }

        Error * e = call.nativeHolder<Error>();
        assert(e);

        f(args, e);
    }

public:
    static void informational(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::Boolean::New(isolate, QDB_SUCCESS(e->_error)));
        });
    }

    static void origin(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::Integer::New(isolate, QDB_ERROR_ORIGIN(e->_error)));
        });
    }

    static void severity(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::Integer::New(isolate, QDB_ERROR_SEVERITY(e->_error)));
        });
    }

    static void message(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            // the string returned by qdb_error is static it is therefore safe and efficient to do this
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, qdb_error(e->_error)));
        });
    }

    static void code(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            // the string returned by qdb_error is static it is therefore safe and efficient to do this
            v8::Isolate * isolate = args.GetIsolate();

            // read low 16-bit for the code
            args.GetReturnValue().Set(v8::Integer::NewFromUnsigned(isolate, e->_error & 0xffff));
        });
    }

private:
    const qdb_error_t _error;

    static v8::Persistent<v8::Function> constructor;
};
}
<commit_msg>Case 1508 - Replace qdb_e_wrong_peer with qdb_e_unstable_cluster Case 1509 - Replace qdb_e_outdated topology with qdb_e_unstable_cluster<commit_after>
#pragma once

#include <qdb/client.h>

#include "utilities.hpp"

namespace quasardb
{

class Error : public node::ObjectWrap
{
public:
    explicit Error(qdb_error_t e) : _error(e)
    {
    }

    virtual ~Error(void)
    {
    }

private:
    static void AddErrorOrigin(v8::Local<v8::Object> exports, const char * name, qdb_error_origin_t origin)
    {
        v8::Isolate * isolate = exports->GetIsolate();
        exports->ForceSet(v8::String::NewFromUtf8(isolate, name), v8::Int32::New(isolate, origin),
                          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    }

    static void AddErrorSeverity(v8::Local<v8::Object> exports, const char * name, qdb_error_severity_t severity)
    {
        v8::Isolate * isolate = exports->GetIsolate();
        exports->ForceSet(v8::String::NewFromUtf8(isolate, name), v8::Int32::New(isolate, severity),
                          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    }

    static void AddErrorCode(v8::Local<v8::Object> exports, const char * name, qdb_error_t err)
    {
        static const int code_mask = 0xFFFF;

        v8::Isolate * isolate = exports->GetIsolate();
        exports->ForceSet(v8::String::NewFromUtf8(isolate, name), v8::Int32::New(isolate, err & code_mask),
                          static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    }

public:
    static void Init(v8::Local<v8::Object> exports)
    {
        v8::Isolate * isolate = exports->GetIsolate();

        // Prepare constructor template
        v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(isolate, New);
        tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Error"));
        tpl->InstanceTemplate()->SetInternalFieldCount(1);

        v8::HandleScope handle_scope(isolate);
        v8::Local<v8::Signature> s = v8::Signature::New(isolate, tpl);

        auto proto = tpl->PrototypeTemplate();

        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "informational"),
                                   v8::FunctionTemplate::New(isolate, Error::informational, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "origin"),
                                   v8::FunctionTemplate::New(isolate, Error::origin, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "severity"),
                                   v8::FunctionTemplate::New(isolate, Error::severity, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "message"),
                                   v8::FunctionTemplate::New(isolate, Error::message, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);
        proto->SetAccessorProperty(v8::String::NewFromUtf8(isolate, "code"),
                                   v8::FunctionTemplate::New(isolate, Error::code, v8::Local<v8::Value>(), s),
                                   v8::Local<v8::FunctionTemplate>(), v8::ReadOnly);

        AddErrorOrigin(exports, "E_ORIGIN_SYSTEM_REMOTE", qdb_e_origin_system_remote);
        AddErrorOrigin(exports, "E_ORIGIN_SYSTEM_LOCAL", qdb_e_origin_system_local);
        AddErrorOrigin(exports, "E_ORIGIN_CONNECTION", qdb_e_origin_connection);
        AddErrorOrigin(exports, "E_ORIGIN_INPUT", qdb_e_origin_input);
        AddErrorOrigin(exports, "E_ORIGIN_OPERATION", qdb_e_origin_operation);
        AddErrorOrigin(exports, "E_ORIGIN_PROTOCOL", qdb_e_origin_protocol);

        AddErrorSeverity(exports, "E_SEVERITY_UNRECOVERABLE", qdb_e_severity_unrecoverable);
        AddErrorSeverity(exports, "E_SEVERITY_ERROR", qdb_e_severity_error);
        AddErrorSeverity(exports, "E_SEVERITY_WARNING", qdb_e_severity_warning);
        AddErrorSeverity(exports, "E_SEVERITY_INFO", qdb_e_severity_info);

        AddErrorCode(exports, "E_UNINITIALIZED", qdb_e_uninitialized);
        AddErrorCode(exports, "E_ALIAS_NOT_FOUND", qdb_e_alias_not_found);
        AddErrorCode(exports, "E_ALIAS_ALREADY_EXISTS", qdb_e_alias_already_exists);
        AddErrorCode(exports, "E_OUT_OF_BOUNDS", qdb_e_out_of_bounds);
        AddErrorCode(exports, "E_SKIPPED", qdb_e_skipped);
        AddErrorCode(exports, "E_INCOMPATIBLE_TYPE", qdb_e_incompatible_type);
        AddErrorCode(exports, "E_CONTAINER_EMPTY", qdb_e_container_empty);
        AddErrorCode(exports, "E_CONTAINER_FULL", qdb_e_container_full);
        AddErrorCode(exports, "E_ELEMENT_NOT_FOUND", qdb_e_element_not_found);
        AddErrorCode(exports, "E_ELEMENT_ALREADY_EXISTS", qdb_e_element_already_exists);
        AddErrorCode(exports, "E_OVERFLOW", qdb_e_overflow);
        AddErrorCode(exports, "E_UNDERFLOW", qdb_e_underflow);
        AddErrorCode(exports, "E_TAG_ALREADY_SET", qdb_e_tag_already_set);
        AddErrorCode(exports, "E_TAG_NOT_SET", qdb_e_tag_not_set);
        AddErrorCode(exports, "E_TIMEOUT", qdb_e_timeout);
        AddErrorCode(exports, "E_CONNECTION_REFUSED", qdb_e_connection_refused);
        AddErrorCode(exports, "E_CONNECTION_RESET", qdb_e_connection_reset);
        AddErrorCode(exports, "E_UNSTABLE_CLUSTER", qdb_e_unstable_cluster);
        AddErrorCode(exports, "E_TRY_AGAIN", qdb_e_try_again);
        AddErrorCode(exports, "E_CONFLICT", qdb_e_conflict);
        AddErrorCode(exports, "E_NOT_CONNECTED", qdb_e_not_connected);
        AddErrorCode(exports, "E_RESOURCE_LOCKED", qdb_e_resource_locked);
        AddErrorCode(exports, "E_SYSTEM_REMOTE", qdb_e_system_remote);
        AddErrorCode(exports, "E_SYSTEM_LOCAL", qdb_e_system_local);
        AddErrorCode(exports, "E_INTERNAL_REMOTE", qdb_e_internal_remote);
        AddErrorCode(exports, "E_INTERNAL_LOCAL", qdb_e_internal_local);
        AddErrorCode(exports, "E_NO_MEMORY_REMOTE", qdb_e_no_memory_remote);
        AddErrorCode(exports, "E_NO_MEMORY_LOCAL", qdb_e_no_memory_local);
        AddErrorCode(exports, "E_INVALID_PROTOCOL", qdb_e_invalid_protocol);
        AddErrorCode(exports, "E_HOST_NOT_FOUND", qdb_e_host_not_found);
        AddErrorCode(exports, "E_BUFFER_TOO_SMALL", qdb_e_buffer_too_small);
        AddErrorCode(exports, "E_NOT_IMPLEMENTED", qdb_e_not_implemented);
        AddErrorCode(exports, "E_INVALID_VERSION", qdb_e_invalid_version);
        AddErrorCode(exports, "E_INVALID_ARGUMENT", qdb_e_invalid_argument);
        AddErrorCode(exports, "E_INVALID_HANDLE", qdb_e_invalid_handle);
        AddErrorCode(exports, "E_RESERVED_ALIAS", qdb_e_reserved_alias);
        AddErrorCode(exports, "E_UNMATCHED_CONTENT", qdb_e_unmatched_content);
        AddErrorCode(exports, "E_INVALID_ITERATOR", qdb_e_invalid_iterator);
        AddErrorCode(exports, "E_ENTRY_TOO_LARGE", qdb_e_entry_too_large);
        AddErrorCode(exports, "E_TRANSACTION_PARTIAL_FAILURE", qdb_e_transaction_partial_failure);
        AddErrorCode(exports, "E_OPERATION_DISABLED", qdb_e_operation_disabled);
        AddErrorCode(exports, "E_OPERATION_NOT_PERMITTED", qdb_e_operation_not_permitted);
        AddErrorCode(exports, "E_ITERATOR_END", qdb_e_iterator_end);
        AddErrorCode(exports, "E_INVALID_REPLY", qdb_e_invalid_reply);
        AddErrorCode(exports, "E_OK_CREATED", qdb_e_ok_created);
        AddErrorCode(exports, "E_NO_SPACE_LEFT", qdb_e_no_space_left);
        AddErrorCode(exports, "E_QUOTA_EXCEEDED", qdb_e_quota_exceeded);

        constructor.Reset(isolate, tpl->GetFunction());
        exports->Set(v8::String::NewFromUtf8(isolate, "Error"), tpl->GetFunction());
    }

private:
    static void New(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        if (args.IsConstructCall())
        {
            MethodMan call(args);

            if (args.Length() != 1)
            {
                call.throwException("Expected exactly one argument");
                return;
            }

            ArgsEater argsEater(call);

            auto code = argsEater.eatNumber();
            if (!code.second)
            {
                call.throwException("Expected an error code as first argument");
                return;
            }

            const qdb_error_t err = static_cast<qdb_error_t>(static_cast<unsigned long>(code.first));

            Error * e = new Error(err);

            e->Wrap(args.This());
            args.GetReturnValue().Set(args.This());
        }
        else
        {
            NewInstance(args);
        }
    }

public:
    static void NewInstance(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        v8::Isolate * isolate = args.GetIsolate();
        // Invoked as plain function `MyObject(...)`, turn into construct call.
        static const int argc = 1;
        v8::Local<v8::Value> argv[argc] = {args[0]};
        v8::Local<v8::Function> cons = v8::Local<v8::Function>::New(isolate, constructor);
        v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);
        args.GetReturnValue().Set(instance);
    }

public:
    static v8::Local<v8::Object> MakeError(v8::Isolate * isolate, qdb_error_t err)
    {
        static const int argc = 1;
        v8::Local<v8::Value> argv[argc] = {v8::Number::New(isolate, err)};
        v8::Local<v8::Function> cons = v8::Local<v8::Function>::New(isolate, constructor);
        return cons->NewInstance(1, argv);
    }

private:
    template <typename F>
    static void accessor(const v8::FunctionCallbackInfo<v8::Value> & args, F f)
    {
        MethodMan call(args);

        if (args.Length() != 0)
        {
            call.throwException("Wrong number of arguments");
            return;
        }

        Error * e = call.nativeHolder<Error>();
        assert(e);

        f(args, e);
    }

public:
    static void informational(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::Boolean::New(isolate, QDB_SUCCESS(e->_error)));
        });
    }

    static void origin(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::Integer::New(isolate, QDB_ERROR_ORIGIN(e->_error)));
        });
    }

    static void severity(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::Integer::New(isolate, QDB_ERROR_SEVERITY(e->_error)));
        });
    }

    static void message(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            // the string returned by qdb_error is static it is therefore safe and efficient to do this
            v8::Isolate * isolate = args.GetIsolate();
            args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, qdb_error(e->_error)));
        });
    }

    static void code(const v8::FunctionCallbackInfo<v8::Value> & args)
    {
        Error::accessor(args, [](const v8::FunctionCallbackInfo<v8::Value> & args, Error * e) {
            // the string returned by qdb_error is static it is therefore safe and efficient to do this
            v8::Isolate * isolate = args.GetIsolate();

            // read low 16-bit for the code
            args.GetReturnValue().Set(v8::Integer::NewFromUnsigned(isolate, e->_error & 0xffff));
        });
    }

private:
    const qdb_error_t _error;

    static v8::Persistent<v8::Function> constructor;
};
}
<|endoftext|>


<commit_before>#include "framework/unit.h"

#include <cassert>
#include <cstdlib>
#include <regex>

char const* unit::color::reset        = "\033[0m";
char const* unit::color::black        = "\033[30m";
char const* unit::color::red          = "\033[31m";
char const* unit::color::green        = "\033[32m";
char const* unit::color::yellow       = "\033[33m";
char const* unit::color::blue         = "\033[34m";
char const* unit::color::magenta      = "\033[35m";
char const* unit::color::cyan         = "\033[36m";
char const* unit::color::white        = "\033[37m";
char const* unit::color::bold_black   = "\033[1m\033[30m";
char const* unit::color::bold_red     = "\033[1m\033[31m";
char const* unit::color::bold_green   = "\033[1m\033[32m";
char const* unit::color::bold_yellow  = "\033[1m\033[33m";
char const* unit::color::bold_blue    = "\033[1m\033[34m";
char const* unit::color::bold_magenta = "\033[1m\033[35m";
char const* unit::color::bold_cyan    = "\033[1m\033[36m";
char const* unit::color::bold_white   = "\033[1m\033[37m";
char const* unit::detail::suite       = nullptr;

namespace unit {

test::test(std::string name)
  : name_{std::move(name)}
{
}

void test::__pass(std::string msg)
{
  trace_.emplace_back(true, std::move(msg));
}

void test::__fail(std::string msg)
{
  trace_.emplace_back(false, std::move(msg));
}

std::vector<std::pair<bool, std::string>> const& test::__trace() const
{
  return trace_;
}

std::string const& test::__name() const
{
  return name_;
}

void engine::add(char const* name, std::unique_ptr<test> t)
{
  auto& suite = instance().suites_[std::string{name ? name : ""}];
  for (auto& x : suite)
    if (x->__name() == t->__name())
    {
      std::cout << "duplicate test name: " << t->__name() << '\n';
      std::abort();
    }

  suite.emplace_back(std::move(t));
}

namespace {

class logger
{
public:
  enum class level : int
  {
    quiet   = 0,
    error   = 1,
    info    = 2,
    verbose = 3,
    massive = 4
  };

  class message
  {
  public:
    message(logger& l, level lvl)
      : logger_{l},
        level_{lvl}
    {
    }

    template <typename T>
    message& operator<<(T const& x)
    {
      logger_.log(level_, x);
      return *this;
    }

  private:
    logger& logger_;
    level level_;
  };

  logger(int lvl_cons, int lvl_file, std::string const& logfile)
    : level_console_{static_cast<level>(lvl_cons)},
      level_file_{static_cast<level>(lvl_file)},
      console_{std::cerr}
  {
    if (! logfile.empty())
      file_.open(logfile);
  }

  template <typename T>
  void log(level lvl, T const& x)
  {
    if (lvl <= level_console_)
      console_ << x;

    if (lvl <= level_file_)
      file_ << x;
  }

  message error()
  {
    return message{*this, level::error};
  }

  message info()
  {
    return message{*this, level::info};
  }

  message verbose()
  {
    return message{*this, level::verbose};
  }

  message massive()
  {
    return message{*this, level::massive};
  }

private:
  level level_console_;
  level level_file_;
  std::ostream& console_;
  std::ofstream file_;
};

std::string render(std::chrono::microseconds t)
{
  return t.count() > 1000000
    ? (std::to_string(t.count() / 1000000) + '.'
      + std::to_string((t.count() % 1000000) / 10000) + " s")
    : t.count() > 1000
      ? (std::to_string(t.count() / 1000) + " ms")
      : (std::to_string(t.count()) + " us");
}

} // namespace <anonymous>

int engine::run(configuration const& cfg)
{
  if (cfg.check("help"))
  {
    cfg.usage(std::cerr);
    return 0;
  }

  if (cfg.check("no-colors"))
  {
    color::reset        = "";
    color::black        = "";
    color::red          = "";
    color::green        = "";
    color::yellow       = "";
    color::blue         = "";
    color::magenta      = "";
    color::cyan         = "";
    color::white        = "";
    color::bold_black   = "";
    color::bold_red     = "";
    color::bold_green   = "";
    color::bold_yellow  = "";
    color::bold_blue    = "";
    color::bold_magenta = "";
    color::bold_cyan    = "";
    color::bold_white   = "";
  }

  auto log_file = cfg.as<std::string>("log-file");
  logger log{*cfg.as<int>("console-verbosity"),
             *cfg.as<int>("file-verbosity"),
             log_file ? *log_file : ""};

  auto bar = '+' + std::string(70, '-') + '+';

  size_t failed_requires = 0;
  size_t total_tests = 0;
  size_t total_good = 0;
  size_t total_bad = 0;
  std::chrono::microseconds runtime;

  auto suite_rx = std::regex{*cfg.as<std::string>("suites")};
  auto test_rx = std::regex{*cfg.as<std::string>("tests")};
  for (auto& p : instance().suites_)
  {
    if (! std::regex_search(p.first, suite_rx))
      continue;

    auto suite_name = p.first.empty() ? "<unnamed>" : p.first;
    auto pad = std::string((bar.size() - suite_name.size()) / 2, ' ');

    bool displayed_header = false;

    for (auto& t : p.second)
    {
      if (! std::regex_search(t->__name(), test_rx))
        continue;

      if (! displayed_header)
      {
        log.verbose()
          << color::yellow << bar << '\n' << pad << suite_name << '\n' << bar
          << color::reset << "\n\n";

        displayed_header = true;
      }

      log.verbose()
          << color::yellow << "- " << color::reset << t->__name() << '\n';

      auto failed_require = false;
      auto start = std::chrono::system_clock::now();
      try
      {
        t->__run();
      }
      catch (require_error const& e)
      {
        failed_require = true;
      }
      auto stop = std::chrono::system_clock::now();
      auto elapsed = stop - start;
      runtime += elapsed;

      size_t good = 0;
      size_t bad = 0;
      for (auto& trace : t->__trace())
        if (trace.first)
        {
          ++good;
          log.massive() << "  " << trace.second << '\n';
        }
        else
        {
          ++bad;
          log.error() << "  " << trace.second << '\n';
        }

      if (failed_require)
      {
        ++failed_requires;
        log.error() << color::red << "     REQUIRED" << color::reset << '\n';
      }

      total_good += good;
      total_bad += bad;

      log.verbose()
          << color::yellow << "  -> " << color::cyan << good + bad
          << color::reset << " check" << (good + bad > 1 ? "s " : " ")
          << "took " << color::cyan << render(elapsed) << color::reset;

      if (bad > 0)
        log.verbose()
          << " (" << color::green << good << color::reset << '/'
          << color::red << bad << color::reset << ")" << '\n';
      else
        log.verbose() << '\n';
    }

    total_tests += p.second.size();

    if (displayed_header)
      log.verbose() << '\n';
  }

  auto suites = instance().suites_.size();
  auto percent_good =
    unsigned(100000 * total_good / double(total_good + total_bad)) / 1000.0;

  auto title = std::string{"summary"};
  auto pad = std::string((bar.size() - title.size()) / 2, ' ');
  auto indent = std::string(27, ' ');

  log.info()
    << color::cyan << bar << '\n' << pad << title << '\n' << bar
    << color::reset << "\n\n"
    << indent << "suites:  " << color::yellow << suites << color::reset << '\n'
    << indent << "tests:   " << color::yellow << total_tests << color::reset
    << '\n'
    << indent << "checks:  " << color::yellow << total_good + total_bad
    << color::reset;

  if (total_bad > 0)
    log.info()
    << " (" << color::green << total_good << color::reset << '/'
    << color::red << total_bad << color::reset << ")";

  log.info()
    << '\n' << indent << "time:    " << color::yellow << render(runtime)
    << '\n' << color::reset << indent << "success: "
    << (percent_good == 100.0 ? color::green : color::yellow)
    << percent_good << "%" << color::reset << "\n\n"
    << color::cyan << bar << color::reset << '\n';

  return 0;
}

engine& engine::instance()
{
  static engine e;
  return e;
}

} // namespace unit
<commit_msg>Use monotonic clock for measuring time intervals.<commit_after>#include "framework/unit.h"

#include <cassert>
#include <cstdlib>
#include <regex>

char const* unit::color::reset        = "\033[0m";
char const* unit::color::black        = "\033[30m";
char const* unit::color::red          = "\033[31m";
char const* unit::color::green        = "\033[32m";
char const* unit::color::yellow       = "\033[33m";
char const* unit::color::blue         = "\033[34m";
char const* unit::color::magenta      = "\033[35m";
char const* unit::color::cyan         = "\033[36m";
char const* unit::color::white        = "\033[37m";
char const* unit::color::bold_black   = "\033[1m\033[30m";
char const* unit::color::bold_red     = "\033[1m\033[31m";
char const* unit::color::bold_green   = "\033[1m\033[32m";
char const* unit::color::bold_yellow  = "\033[1m\033[33m";
char const* unit::color::bold_blue    = "\033[1m\033[34m";
char const* unit::color::bold_magenta = "\033[1m\033[35m";
char const* unit::color::bold_cyan    = "\033[1m\033[36m";
char const* unit::color::bold_white   = "\033[1m\033[37m";
char const* unit::detail::suite       = nullptr;

namespace unit {

test::test(std::string name)
  : name_{std::move(name)}
{
}

void test::__pass(std::string msg)
{
  trace_.emplace_back(true, std::move(msg));
}

void test::__fail(std::string msg)
{
  trace_.emplace_back(false, std::move(msg));
}

std::vector<std::pair<bool, std::string>> const& test::__trace() const
{
  return trace_;
}

std::string const& test::__name() const
{
  return name_;
}

void engine::add(char const* name, std::unique_ptr<test> t)
{
  auto& suite = instance().suites_[std::string{name ? name : ""}];
  for (auto& x : suite)
    if (x->__name() == t->__name())
    {
      std::cout << "duplicate test name: " << t->__name() << '\n';
      std::abort();
    }

  suite.emplace_back(std::move(t));
}

namespace {

class logger
{
public:
  enum class level : int
  {
    quiet   = 0,
    error   = 1,
    info    = 2,
    verbose = 3,
    massive = 4
  };

  class message
  {
  public:
    message(logger& l, level lvl)
      : logger_{l},
        level_{lvl}
    {
    }

    template <typename T>
    message& operator<<(T const& x)
    {
      logger_.log(level_, x);
      return *this;
    }

  private:
    logger& logger_;
    level level_;
  };

  logger(int lvl_cons, int lvl_file, std::string const& logfile)
    : level_console_{static_cast<level>(lvl_cons)},
      level_file_{static_cast<level>(lvl_file)},
      console_{std::cerr}
  {
    if (! logfile.empty())
      file_.open(logfile);
  }

  template <typename T>
  void log(level lvl, T const& x)
  {
    if (lvl <= level_console_)
      console_ << x;

    if (lvl <= level_file_)
      file_ << x;
  }

  message error()
  {
    return message{*this, level::error};
  }

  message info()
  {
    return message{*this, level::info};
  }

  message verbose()
  {
    return message{*this, level::verbose};
  }

  message massive()
  {
    return message{*this, level::massive};
  }

private:
  level level_console_;
  level level_file_;
  std::ostream& console_;
  std::ofstream file_;
};

std::string render(std::chrono::microseconds t)
{
  return t.count() > 1000000
    ? (std::to_string(t.count() / 1000000) + '.'
      + std::to_string((t.count() % 1000000) / 10000) + " s")
    : t.count() > 1000
      ? (std::to_string(t.count() / 1000) + " ms")
      : (std::to_string(t.count()) + " us");
}

} // namespace <anonymous>

int engine::run(configuration const& cfg)
{
  if (cfg.check("help"))
  {
    cfg.usage(std::cerr);
    return 0;
  }

  if (cfg.check("no-colors"))
  {
    color::reset        = "";
    color::black        = "";
    color::red          = "";
    color::green        = "";
    color::yellow       = "";
    color::blue         = "";
    color::magenta      = "";
    color::cyan         = "";
    color::white        = "";
    color::bold_black   = "";
    color::bold_red     = "";
    color::bold_green   = "";
    color::bold_yellow  = "";
    color::bold_blue    = "";
    color::bold_magenta = "";
    color::bold_cyan    = "";
    color::bold_white   = "";
  }

  auto log_file = cfg.as<std::string>("log-file");
  logger log{*cfg.as<int>("console-verbosity"),
             *cfg.as<int>("file-verbosity"),
             log_file ? *log_file : ""};

  auto bar = '+' + std::string(70, '-') + '+';

  size_t failed_requires = 0;
  size_t total_tests = 0;
  size_t total_good = 0;
  size_t total_bad = 0;
  std::chrono::microseconds runtime;

  auto suite_rx = std::regex{*cfg.as<std::string>("suites")};
  auto test_rx = std::regex{*cfg.as<std::string>("tests")};
  for (auto& p : instance().suites_)
  {
    if (! std::regex_search(p.first, suite_rx))
      continue;

    auto suite_name = p.first.empty() ? "<unnamed>" : p.first;
    auto pad = std::string((bar.size() - suite_name.size()) / 2, ' ');

    bool displayed_header = false;

    for (auto& t : p.second)
    {
      if (! std::regex_search(t->__name(), test_rx))
        continue;

      if (! displayed_header)
      {
        log.verbose()
          << color::yellow << bar << '\n' << pad << suite_name << '\n' << bar
          << color::reset << "\n\n";

        displayed_header = true;
      }

      log.verbose()
          << color::yellow << "- " << color::reset << t->__name() << '\n';

      auto failed_require = false;
      auto start = std::chrono::steady_clock::now();
      try
      {
        t->__run();
      }
      catch (require_error const& e)
      {
        failed_require = true;
      }
      auto stop = std::chrono::steady_clock::now();
      auto elapsed =
        std::chrono::duration_cast<std::chrono::microseconds>(stop - start);

      runtime += elapsed;

      size_t good = 0;
      size_t bad = 0;
      for (auto& trace : t->__trace())
        if (trace.first)
        {
          ++good;
          log.massive() << "  " << trace.second << '\n';
        }
        else
        {
          ++bad;
          log.error() << "  " << trace.second << '\n';
        }

      if (failed_require)
      {
        ++failed_requires;
        log.error() << color::red << "     REQUIRED" << color::reset << '\n';
      }

      total_good += good;
      total_bad += bad;

      log.verbose()
          << color::yellow << "  -> " << color::cyan << good + bad
          << color::reset << " check" << (good + bad > 1 ? "s " : " ")
          << "took " << color::cyan << render(elapsed) << color::reset;

      if (bad > 0)
        log.verbose()
          << " (" << color::green << good << color::reset << '/'
          << color::red << bad << color::reset << ")" << '\n';
      else
        log.verbose() << '\n';
    }

    total_tests += p.second.size();

    if (displayed_header)
      log.verbose() << '\n';
  }

  auto suites = instance().suites_.size();
  auto percent_good =
    unsigned(100000 * total_good / double(total_good + total_bad)) / 1000.0;

  auto title = std::string{"summary"};
  auto pad = std::string((bar.size() - title.size()) / 2, ' ');
  auto indent = std::string(27, ' ');

  log.info()
    << color::cyan << bar << '\n' << pad << title << '\n' << bar
    << color::reset << "\n\n"
    << indent << "suites:  " << color::yellow << suites << color::reset << '\n'
    << indent << "tests:   " << color::yellow << total_tests << color::reset
    << '\n'
    << indent << "checks:  " << color::yellow << total_good + total_bad
    << color::reset;

  if (total_bad > 0)
    log.info()
    << " (" << color::green << total_good << color::reset << '/'
    << color::red << total_bad << color::reset << ")";

  log.info()
    << '\n' << indent << "time:    " << color::yellow << render(runtime)
    << '\n' << color::reset << indent << "success: "
    << (percent_good == 100.0 ? color::green : color::yellow)
    << percent_good << "%" << color::reset << "\n\n"
    << color::cyan << bar << color::reset << '\n';

  return 0;
}

engine& engine::instance()
{
  static engine e;
  return e;
}

} // namespace unit
<|endoftext|>