<commit_before>//
// Matirx.hpp
// ~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_MATRIX_HPP
#define EIGENJS_MATRIX_HPP

#include <node.h>
#include <v8.h>

#include <nan.h>

#include <eigen3/Eigen/Dense>

#include "base.hpp"
#include "Complex.hpp"

#define EIGENJS_MATRIX_BINARY_OPERATOR( NAME, OP )                             \
static NAN_METHOD( NAME ) {                                                    \
  const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );         \
  NanScope();                                                                  \
                                                                               \
  if ( args.Length() == 1 && base_type::HasInstance( args[0] ) ) {             \
    const Matrix* rhs_obj =                                                    \
        node::ObjectWrap::Unwrap<Matrix>( args[0]->ToObject() );               \
                                                                               \
    if ( is_nonconformate_arguments( obj, rhs_obj ) ) {                        \
      NanReturnUndefined();                                                    \
    }                                                                          \
                                                                               \
    const typename matrix_type::Index& rows = obj->matrix_.rows();             \
    const typename matrix_type::Index& cols = obj->matrix_.cols();             \
                                                                               \
    v8::Local<v8::Function> ctr = NanNew( base_type::constructor );            \
    v8::Local<v8::Value> argv[] = {                                            \
        NanNew<v8::Integer>( rows )                                            \
      , NanNew<v8::Integer>( cols )                                            \
    };                                                                         \
                                                                               \
    v8::Local<v8::Object> new_matrix = ctr->NewInstance(                       \
        sizeof( argv ) / sizeof( v8::Local<v8::Value> )                        \
      , argv                                                                   \
    );                                                                         \
                                                                               \
    Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>( new_matrix );          \
    new_obj->matrix_ = obj->matrix_ OP rhs_obj->matrix_;                       \
                                                                               \
    NanReturnValue( new_matrix );                                              \
  }                                                                            \
                                                                               \
  NanReturnUndefined();                                                        \
}                                                                              \
/**/

#define EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE( NAME, OP )                 \
static NAN_METHOD( NAME ) {                                                    \
  Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );               \
  NanScope();                                                                  \
                                                                               \
  if ( args.Length() == 1 && base_type::HasInstance( args[0] ) ) {             \
    const Matrix* rhs_obj =                                                    \
        node::ObjectWrap::Unwrap<Matrix>( args[0]->ToObject() );               \
                                                                               \
    if ( is_nonconformate_arguments( obj, rhs_obj ) ) {                        \
      NanReturnUndefined();                                                    \
    }                                                                          \
                                                                               \
    obj->matrix_ OP##= rhs_obj->matrix_;                                       \
                                                                               \
    NanReturnValue( args.This() );                                             \
  }                                                                            \
                                                                               \
  NanReturnUndefined();                                                        \
}                                                                              \
/**/

namespace EigenJS {

template <typename ValueType, const char* ClassName>
class Matrix : public node::ObjectWrap, base<Matrix, ValueType, ClassName> {
 public:
  typedef base<::EigenJS::Matrix, ValueType, ClassName> base_type;
  typedef typename base_type::element_type element_type;
  typedef typename base_type::complex_type complex_type;
  typedef typename base_type::matrix_type matrix_type;

 public:
  static void Init(v8::Handle<v8::Object> exports) {
    NanScope();

    v8::Local<v8::FunctionTemplate> tpl = NanNew<v8::FunctionTemplate>(New);
    tpl->SetClassName(NanNew(ClassName));
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    NODE_SET_PROTOTYPE_METHOD(tpl, "rows", rows);
    NODE_SET_PROTOTYPE_METHOD(tpl, "cols", cols);
    NODE_SET_PROTOTYPE_METHOD(tpl, "set", set);
    NODE_SET_PROTOTYPE_METHOD(tpl, "get", get);

    NODE_SET_PROTOTYPE_METHOD(tpl, "add", add);
    NODE_SET_PROTOTYPE_METHOD(tpl, "adda", adda);
    NODE_SET_PROTOTYPE_METHOD(tpl, "sub", sub);
    NODE_SET_PROTOTYPE_METHOD(tpl, "suba", suba);

    NODE_SET_PROTOTYPE_METHOD(tpl, "mul", mul);
    NODE_SET_PROTOTYPE_METHOD(tpl, "mula", mula);

    NODE_SET_PROTOTYPE_METHOD(tpl, "div", div);
    NODE_SET_PROTOTYPE_METHOD(tpl, "diva", diva);

    NODE_SET_PROTOTYPE_METHOD(tpl, "toString", toString);

    NanAssignPersistent(base_type::constructor, tpl->GetFunction());
    exports->Set(NanNew(ClassName), tpl->GetFunction());

    NanAssignPersistent(base_type::function_template, tpl);
  }

  static NAN_METHOD(rows) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    NanReturnValue(NanNew<v8::Integer>(obj->matrix_.rows()));
  }

  static NAN_METHOD(cols) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    NanReturnValue(NanNew<v8::Integer>(obj->matrix_.cols()));
  }

  static NAN_METHOD(set) {
    Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 1 && args[0]->IsArray()) {
      v8::Local<v8::Array> array = args[0].As<v8::Array>();
      uint32_t len = array->Length();
      const typename matrix_type::Index& rows = obj->matrix_.rows();
      const typename matrix_type::Index& cols = obj->matrix_.cols();
      const typename matrix_type::Index& elems = rows * cols;

      if (len != elems) {
        len < rows * cols
          ? NanThrowError("Too few coefficients passed to Matrix")
          : NanThrowError("Too many coefficients passed to Matrix");
        NanReturnUndefined();
      }

      for (uint32_t i = 0; i < len; ++i) {
        v8::Local<v8::Value> elem = array->Get(i);
        obj->matrix_(i / cols, i % cols) = elem->NumberValue();
      }
    } else if (
        args.Length() == 3 &&
        args[0]->IsNumber() &&
        args[1]->IsNumber() &&
        args[2]->IsNumber()) {
      const typename matrix_type::Index& row = args[0]->Uint32Value();
      const typename matrix_type::Index& col = args[1]->Uint32Value();
      const element_type& value = args[2]->NumberValue();

      if (is_out_of_range(obj->matrix_, row, col))
        NanReturnUndefined();

      obj->matrix_(row, col) = value;
    }

    NanReturnValue(args.This());
  }

  static NAN_METHOD(get) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 2 &&
        args[0]->IsNumber() &&
        args[1]->IsNumber()) {
      typename matrix_type::Index row = args[0]->Uint32Value();
      typename matrix_type::Index col = args[1]->Uint32Value();

      if (is_out_of_range(obj->matrix_, row, col))
        NanReturnUndefined();

      element_type value = obj->matrix_(row, col);
      NanReturnValue(NanNew(value));
    }

    NanReturnUndefined();
  }

  EIGENJS_MATRIX_BINARY_OPERATOR(add, +)
  EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE(adda, +)

  EIGENJS_MATRIX_BINARY_OPERATOR(sub, -)
  EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE(suba, -)

  static NAN_METHOD(mul) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );
    const typename matrix_type::Index& rows = obj->matrix_.rows();
    const typename matrix_type::Index& cols = obj->matrix_.cols();
    NanScope();

    if (args.Length() == 1 && base_type::HasInstance(args[0])) {
      const Matrix* rhs_obj =
          node::ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());

      if (is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {
          NanNew<v8::Integer>(rows)
        , NanNew<v8::Integer>(cols)
      };

      v8::Local<v8::Object> new_matrix = ctr->NewInstance(
          sizeof(argv) / sizeof(v8::Local<v8::Value>)
        , argv
      );

      Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>(new_matrix);
      new_obj->matrix_ = obj->matrix_ * rhs_obj->matrix_;

      NanReturnValue(new_matrix);
    } else if (args.Length() == 1 && args[0]->IsNumber()) {
      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {
          NanNew<v8::Integer>(rows)
        , NanNew<v8::Integer>(cols)
      };

      v8::Local<v8::Object> new_matrix = ctr->NewInstance(
          sizeof(argv) / sizeof(v8::Local<v8::Value>)
        , argv
      );

      Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>(new_matrix);
      new_obj->matrix_ = obj->matrix_ * args[0]->NumberValue();

      NanReturnValue(new_matrix);
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(mula) {
    Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 1 && base_type::HasInstance(args[0])) {
      const Matrix* rhs_obj =
          node::ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());

      if (is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      obj->matrix_ *= rhs_obj->matrix_;

      NanReturnValue(args.This());
    } else if (args.Length() == 1 && args[0]->IsNumber()) {
      obj->matrix_ *= args[0]->NumberValue();

      NanReturnValue(args.This());
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(div) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );
    const typename matrix_type::Index& rows = obj->matrix_.rows();
    const typename matrix_type::Index& cols = obj->matrix_.cols();
    NanScope();

    if (args.Length() == 1 && args[0]->IsNumber()) {
      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {
          NanNew<v8::Integer>(rows)
        , NanNew<v8::Integer>(cols)
      };

      v8::Local<v8::Object> new_matrix = ctr->NewInstance(
          sizeof(argv) / sizeof(v8::Local<v8::Value>)
        , argv
      );

      Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>(new_matrix);
      new_obj->matrix_ = obj->matrix_ / args[0]->NumberValue();

      NanReturnValue(new_matrix);
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(diva) {
    Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 1 && args[0]->IsNumber()) {
      obj->matrix_ /= args[0]->NumberValue();

      NanReturnValue(args.This());
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(toString) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    std::ostringstream result;
    result << obj->matrix_;

    NanReturnValue(NanNew(result.str().c_str()));
  }

 private:
  Matrix(
      const typename matrix_type::Index& rows
    , const typename matrix_type::Index& cols)
    : matrix_(matrix_type::Zero(rows, cols))
  {}
  ~Matrix() {}

  static NAN_METHOD(New) {
    NanScope();

    if (args.Length() < 2) {
      NanThrowError("Tried creating matrix without rows and columns arguments");
      NanReturnUndefined();
    }

    if (args.IsConstructCall()) {
      typename matrix_type::Index rows = args[0]->Uint32Value();
      typename matrix_type::Index columns = args[1]->Uint32Value();
      Matrix* obj = new Matrix(rows, columns);
      obj->Wrap(args.This());
      NanReturnValue(args.This());
    } else {
      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {args[0], args[1]};
      NanReturnValue(
        ctr->NewInstance(
            sizeof(argv) / sizeof(v8::Local<v8::Value>)
          , argv
        )
      );
    }
  }

 private:
  static inline bool is_out_of_range(
      const matrix_type& matrix
    , const typename matrix_type::Index& row
    , const typename matrix_type::Index& col) {
    return row < 0 || row >= matrix.rows() || col < 0 || col >= matrix.cols()
      ? NanThrowError("Row or column numbers are out of range"), true
      : false;
  }

  static inline bool is_nonconformate_arguments(
      const Matrix* const& op1
    , const Matrix* const& op2) {
    return op1->matrix_.rows() != op2->matrix_.rows() ||
           op1->matrix_.cols() != op2->matrix_.cols()
      ? NanThrowError("Nonconformant arguments"), true
      : false;
  }

  static inline bool is_invalid_matrix_product(
      const Matrix* const& op1
    , const Matrix* const& op2) {
    return op1->matrix_.cols() != op2->matrix_.rows()
      ? NanThrowError("Invalid matrix product"), true
      : false;
  }

 private:
  matrix_type matrix_;
};

}  // namespace EigenJS

#undef EIGENJS_MATRIX_BINARY_OPERATOR
#undef EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE

#endif  // EIGENJS_MATRIX_HPP
<commit_msg>src: added instance method 'Matrix.equals()'<commit_after>//
// Matirx.hpp
// ~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_MATRIX_HPP
#define EIGENJS_MATRIX_HPP

#include <node.h>
#include <v8.h>

#include <nan.h>

#include <eigen3/Eigen/Dense>

#include "base.hpp"
#include "Complex.hpp"

#define EIGENJS_MATRIX_BINARY_OPERATOR( NAME, OP )                             \
static NAN_METHOD( NAME ) {                                                    \
  const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );         \
  NanScope();                                                                  \
                                                                               \
  if ( args.Length() == 1 && base_type::HasInstance( args[0] ) ) {             \
    const Matrix* rhs_obj =                                                    \
        node::ObjectWrap::Unwrap<Matrix>( args[0]->ToObject() );               \
                                                                               \
    if ( is_nonconformate_arguments( obj, rhs_obj ) ) {                        \
      NanReturnUndefined();                                                    \
    }                                                                          \
                                                                               \
    const typename matrix_type::Index& rows = obj->matrix_.rows();             \
    const typename matrix_type::Index& cols = obj->matrix_.cols();             \
                                                                               \
    v8::Local<v8::Function> ctr = NanNew( base_type::constructor );            \
    v8::Local<v8::Value> argv[] = {                                            \
        NanNew<v8::Integer>( rows )                                            \
      , NanNew<v8::Integer>( cols )                                            \
    };                                                                         \
                                                                               \
    v8::Local<v8::Object> new_matrix = ctr->NewInstance(                       \
        sizeof( argv ) / sizeof( v8::Local<v8::Value> )                        \
      , argv                                                                   \
    );                                                                         \
                                                                               \
    Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>( new_matrix );          \
    new_obj->matrix_ = obj->matrix_ OP rhs_obj->matrix_;                       \
                                                                               \
    NanReturnValue( new_matrix );                                              \
  }                                                                            \
                                                                               \
  NanReturnUndefined();                                                        \
}                                                                              \
/**/

#define EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE( NAME, OP )                 \
static NAN_METHOD( NAME ) {                                                    \
  Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );               \
  NanScope();                                                                  \
                                                                               \
  if ( args.Length() == 1 && base_type::HasInstance( args[0] ) ) {             \
    const Matrix* rhs_obj =                                                    \
        node::ObjectWrap::Unwrap<Matrix>( args[0]->ToObject() );               \
                                                                               \
    if ( is_nonconformate_arguments( obj, rhs_obj ) ) {                        \
      NanReturnUndefined();                                                    \
    }                                                                          \
                                                                               \
    obj->matrix_ OP##= rhs_obj->matrix_;                                       \
                                                                               \
    NanReturnValue( args.This() );                                             \
  }                                                                            \
                                                                               \
  NanReturnUndefined();                                                        \
}                                                                              \
/**/

namespace EigenJS {

template <typename ValueType, const char* ClassName>
class Matrix : public node::ObjectWrap, base<Matrix, ValueType, ClassName> {
 public:
  typedef base<::EigenJS::Matrix, ValueType, ClassName> base_type;
  typedef typename base_type::element_type element_type;
  typedef typename base_type::complex_type complex_type;
  typedef typename base_type::matrix_type matrix_type;

 public:
  static void Init(v8::Handle<v8::Object> exports) {
    NanScope();

    v8::Local<v8::FunctionTemplate> tpl = NanNew<v8::FunctionTemplate>(New);
    tpl->SetClassName(NanNew(ClassName));
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    NODE_SET_PROTOTYPE_METHOD(tpl, "rows", rows);
    NODE_SET_PROTOTYPE_METHOD(tpl, "cols", cols);
    NODE_SET_PROTOTYPE_METHOD(tpl, "set", set);
    NODE_SET_PROTOTYPE_METHOD(tpl, "get", get);

    NODE_SET_PROTOTYPE_METHOD(tpl, "add", add);
    NODE_SET_PROTOTYPE_METHOD(tpl, "adda", adda);
    NODE_SET_PROTOTYPE_METHOD(tpl, "sub", sub);
    NODE_SET_PROTOTYPE_METHOD(tpl, "suba", suba);

    NODE_SET_PROTOTYPE_METHOD(tpl, "mul", mul);
    NODE_SET_PROTOTYPE_METHOD(tpl, "mula", mula);

    NODE_SET_PROTOTYPE_METHOD(tpl, "div", div);
    NODE_SET_PROTOTYPE_METHOD(tpl, "diva", diva);

    NODE_SET_PROTOTYPE_METHOD(tpl, "equals", equals);
    NODE_SET_PROTOTYPE_METHOD(tpl, "toString", toString);

    NanAssignPersistent(base_type::constructor, tpl->GetFunction());
    exports->Set(NanNew(ClassName), tpl->GetFunction());

    NanAssignPersistent(base_type::function_template, tpl);
  }

  static NAN_METHOD(rows) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    NanReturnValue(NanNew<v8::Integer>(obj->matrix_.rows()));
  }

  static NAN_METHOD(cols) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    NanReturnValue(NanNew<v8::Integer>(obj->matrix_.cols()));
  }

  static NAN_METHOD(set) {
    Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 1 && args[0]->IsArray()) {
      v8::Local<v8::Array> array = args[0].As<v8::Array>();
      uint32_t len = array->Length();
      const typename matrix_type::Index& rows = obj->matrix_.rows();
      const typename matrix_type::Index& cols = obj->matrix_.cols();
      const typename matrix_type::Index& elems = rows * cols;

      if (len != elems) {
        len < rows * cols
          ? NanThrowError("Too few coefficients passed to Matrix")
          : NanThrowError("Too many coefficients passed to Matrix");
        NanReturnUndefined();
      }

      for (uint32_t i = 0; i < len; ++i) {
        v8::Local<v8::Value> elem = array->Get(i);
        obj->matrix_(i / cols, i % cols) = elem->NumberValue();
      }
    } else if (
        args.Length() == 3 &&
        args[0]->IsNumber() &&
        args[1]->IsNumber() &&
        args[2]->IsNumber()) {
      const typename matrix_type::Index& row = args[0]->Uint32Value();
      const typename matrix_type::Index& col = args[1]->Uint32Value();
      const element_type& value = args[2]->NumberValue();

      if (is_out_of_range(obj->matrix_, row, col))
        NanReturnUndefined();

      obj->matrix_(row, col) = value;
    }

    NanReturnValue(args.This());
  }

  static NAN_METHOD(get) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 2 &&
        args[0]->IsNumber() &&
        args[1]->IsNumber()) {
      typename matrix_type::Index row = args[0]->Uint32Value();
      typename matrix_type::Index col = args[1]->Uint32Value();

      if (is_out_of_range(obj->matrix_, row, col))
        NanReturnUndefined();

      element_type value = obj->matrix_(row, col);
      NanReturnValue(NanNew(value));
    }

    NanReturnUndefined();
  }

  EIGENJS_MATRIX_BINARY_OPERATOR(add, +)
  EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE(adda, +)

  EIGENJS_MATRIX_BINARY_OPERATOR(sub, -)
  EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE(suba, -)

  static NAN_METHOD(mul) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );
    const typename matrix_type::Index& rows = obj->matrix_.rows();
    const typename matrix_type::Index& cols = obj->matrix_.cols();
    NanScope();

    if (args.Length() == 1 && base_type::HasInstance(args[0])) {
      const Matrix* rhs_obj =
          node::ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());

      if (is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {
          NanNew<v8::Integer>(rows)
        , NanNew<v8::Integer>(cols)
      };

      v8::Local<v8::Object> new_matrix = ctr->NewInstance(
          sizeof(argv) / sizeof(v8::Local<v8::Value>)
        , argv
      );

      Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>(new_matrix);
      new_obj->matrix_ = obj->matrix_ * rhs_obj->matrix_;

      NanReturnValue(new_matrix);
    } else if (args.Length() == 1 && args[0]->IsNumber()) {
      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {
          NanNew<v8::Integer>(rows)
        , NanNew<v8::Integer>(cols)
      };

      v8::Local<v8::Object> new_matrix = ctr->NewInstance(
          sizeof(argv) / sizeof(v8::Local<v8::Value>)
        , argv
      );

      Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>(new_matrix);
      new_obj->matrix_ = obj->matrix_ * args[0]->NumberValue();

      NanReturnValue(new_matrix);
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(mula) {
    Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 1 && base_type::HasInstance(args[0])) {
      const Matrix* rhs_obj =
          node::ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());

      if (is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      obj->matrix_ *= rhs_obj->matrix_;

      NanReturnValue(args.This());
    } else if (args.Length() == 1 && args[0]->IsNumber()) {
      obj->matrix_ *= args[0]->NumberValue();

      NanReturnValue(args.This());
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(div) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>( args.This() );
    const typename matrix_type::Index& rows = obj->matrix_.rows();
    const typename matrix_type::Index& cols = obj->matrix_.cols();
    NanScope();

    if (args.Length() == 1 && args[0]->IsNumber()) {
      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {
          NanNew<v8::Integer>(rows)
        , NanNew<v8::Integer>(cols)
      };

      v8::Local<v8::Object> new_matrix = ctr->NewInstance(
          sizeof(argv) / sizeof(v8::Local<v8::Value>)
        , argv
      );

      Matrix* new_obj = node::ObjectWrap::Unwrap<Matrix>(new_matrix);
      new_obj->matrix_ = obj->matrix_ / args[0]->NumberValue();

      NanReturnValue(new_matrix);
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(diva) {
    Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    if (args.Length() == 1 && args[0]->IsNumber()) {
      obj->matrix_ /= args[0]->NumberValue();

      NanReturnValue(args.This());
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(equals) {
    NanScope();

    if (args.Length() == 1 && base_type::is_matrix(args[0])) {
      const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
      const Matrix* rhs_obj =
          node::ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());
      NanReturnValue(NanNew<v8::Boolean>(obj->matrix_ == rhs_obj->matrix_));
    }

    NanReturnUndefined();
  }

  static NAN_METHOD(toString) {
    const Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
    NanScope();

    std::ostringstream result;
    result << obj->matrix_;

    NanReturnValue(NanNew(result.str().c_str()));
  }

 private:
  Matrix(
      const typename matrix_type::Index& rows
    , const typename matrix_type::Index& cols)
    : matrix_(matrix_type::Zero(rows, cols))
  {}
  ~Matrix() {}

  static NAN_METHOD(New) {
    NanScope();

    if (args.Length() < 2) {
      NanThrowError("Tried creating matrix without rows and columns arguments");
      NanReturnUndefined();
    }

    if (args.IsConstructCall()) {
      typename matrix_type::Index rows = args[0]->Uint32Value();
      typename matrix_type::Index columns = args[1]->Uint32Value();
      Matrix* obj = new Matrix(rows, columns);
      obj->Wrap(args.This());
      NanReturnValue(args.This());
    } else {
      v8::Local<v8::Function> ctr = NanNew(base_type::constructor);
      v8::Local<v8::Value> argv[] = {args[0], args[1]};
      NanReturnValue(
        ctr->NewInstance(
            sizeof(argv) / sizeof(v8::Local<v8::Value>)
          , argv
        )
      );
    }
  }

 private:
  static inline bool is_out_of_range(
      const matrix_type& matrix
    , const typename matrix_type::Index& row
    , const typename matrix_type::Index& col) {
    return row < 0 || row >= matrix.rows() || col < 0 || col >= matrix.cols()
      ? NanThrowError("Row or column numbers are out of range"), true
      : false;
  }

  static inline bool is_nonconformate_arguments(
      const Matrix* const& op1
    , const Matrix* const& op2) {
    return op1->matrix_.rows() != op2->matrix_.rows() ||
           op1->matrix_.cols() != op2->matrix_.cols()
      ? NanThrowError("Nonconformant arguments"), true
      : false;
  }

  static inline bool is_invalid_matrix_product(
      const Matrix* const& op1
    , const Matrix* const& op2) {
    return op1->matrix_.cols() != op2->matrix_.rows()
      ? NanThrowError("Invalid matrix product"), true
      : false;
  }

 private:
  matrix_type matrix_;
};

}  // namespace EigenJS

#undef EIGENJS_MATRIX_BINARY_OPERATOR
#undef EIGENJS_MATRIX_BINARY_OPERATOR_COMMUTATIVE

#endif  // EIGENJS_MATRIX_HPP
<|endoftext|>


<commit_before>

//#if !defined(_MSC_VER) || defined(_M_AMD64)
//#define USE_PAGE_ALIGNED_ALLOCATION
//#endif

#ifdef USE_PAGE_ALIGNED_ALLOCATION

#ifdef _WIN32
#include <Windows.h>
#else
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
#include <cstring>
#endif
#endif

#include <nstd/Memory.h>
#include <nstd/Debug.h>

#ifdef USE_PAGE_ALIGNED_ALLOCATION

class _Memory
{
public:
    struct PageHeader
    {
      uint64_t checkValue;
      size_t size;
    };

    struct PageFooter
    {
      uint64_t checkValue;
    };

    struct FreePage : public PageHeader
    {
      FreePage* next;
    };

    static size_t pageSize;
    static FreePage* firstFreePage;
    static size_t freePageCount;
#ifdef _WIN32
    static CRITICAL_SECTION criticalSection; // TODO: is it possible to use atomic operations instead?
#else
    static pthread_mutex_t mutex;
#endif

    static const uint64_t headerCheckValue = 0x1235543212355432LL;
    static const uint64_t headerCheckValueUsed = 0x1235543212355433LL;
    static const uint64_t footerCheckValue = 0x1235543212355432LL;

    static const size_t maxFreePageCount = 64;

private:
    static _Memory memory;

    _Memory()
    {
#ifdef _WIN32
#ifndef __CYGWIN__
      InitializeCriticalSection(&criticalSection);
      SYSTEM_INFO si;
      GetSystemInfo(&si);
      pageSize = si.dwPageSize;
#endif
#else
      pageSize = sysconf(_SC_PAGESIZE);
#endif
    }
    ~_Memory()
    {
#ifdef _WIN32
      DeleteCriticalSection(&criticalSection);
#endif
    }
};

#ifdef _MSC_VER
#pragma warning(disable: 4073)
#pragma init_seg(lib)
_Memory _Memory::memory;
#else
_Memory _Memory::memory __attribute__ ((init_priority (101)));
#endif
size_t _Memory::pageSize;
_Memory::FreePage* _Memory::firstFreePage = 0;
size_t _Memory::freePageCount = 0;
#ifdef _WIN32
CRITICAL_SECTION _Memory::criticalSection;
#else
pthread_mutex_t _Memory::mutex = PTHREAD_MUTEX_INITIALIZER;
#endif

void_t* Memory::alloc(size_t size)
{
  size_t allocatedSize;
  return alloc(size, allocatedSize);
}

void_t* Memory::alloc(size_t size, size_t& rsize)
{
#ifdef __CYGWIN__
  if(_Memory::pageSize == 0)
  {
      InitializeCriticalSection(&_Memory::criticalSection);
      SYSTEM_INFO si;
      GetSystemInfo(&si);
      _Memory::pageSize = si.dwPageSize;
  }
#else
  ASSERT(_Memory::pageSize > 0);
#endif
  size_t minAllocSize = size + (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  if(minAllocSize <= _Memory::pageSize)
  {
#ifdef _WIN32
    EnterCriticalSection(&_Memory::criticalSection);
#else
    pthread_mutex_lock(&_Memory::mutex);
#endif
    if(_Memory::firstFreePage)
    {
      _Memory::PageHeader* header = _Memory::firstFreePage;
      _Memory::firstFreePage = _Memory::firstFreePage->next;
      --_Memory::freePageCount;
#ifdef _WIN32
      LeaveCriticalSection(&_Memory::criticalSection);
#else
      pthread_mutex_unlock(&_Memory::mutex);
#endif
      rsize = _Memory::pageSize - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
      header->checkValue = _Memory::headerCheckValue;
      return (uint8_t*)header + sizeof(_Memory::PageHeader);
    }
    else
    {
#ifdef _WIN32
      LeaveCriticalSection(&_Memory::criticalSection);
#else
      pthread_mutex_unlock(&_Memory::mutex);
#endif
    }
  }
  size_t pageCount = (minAllocSize + _Memory::pageSize - 1) / _Memory::pageSize;
  size_t allocSize = pageCount * _Memory::pageSize;

  _Memory::PageHeader* header;
#ifdef _WIN32
  header = (_Memory::PageHeader*)VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
  header = (_Memory::PageHeader*)memalign(_Memory::pageSize, allocSize);
#endif
  if(!header) // out of memory?
  {
    Debug::printf(_T("Memory::alloc: error: Could not allocate %llu pages.\n"), (uint64_t)pageCount); TRAP();
    do // wait and try again...
    {
#ifdef _WIN32
      Sleep(5000);
      header = (_Memory::PageHeader*)VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
      sleep(5);
      header = (_Memory::PageHeader*)memalign(_Memory::pageSize, allocSize);
#endif
    } while(!header);
  }
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + allocSize - sizeof(_Memory::PageFooter));
  header->size = allocSize;
  header->checkValue = _Memory::headerCheckValue;
  footer->checkValue = _Memory::footerCheckValue;
  rsize = allocSize - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  return (void_t*)((uint8_t*)header + sizeof(_Memory::PageHeader));
}

size_t Memory::size(void_t* buffer)
{
  if(!buffer) return 0;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::size: error: The passed buffer was freed.\n")); TRAP();
      return 0;
    }
    Debug::print(_T("Memory::size: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return 0;
  }
  return header->size - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
}
/*
size_t Memory::pageSize()
{
  return _Memory::pageSize;
}
*/
void_t Memory::free(void_t* buffer)
{
  if(!buffer) return;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::free: error: The passed buffer was already freed.\n")); TRAP();
      return;
    }
    Debug::print(_T("Memory::free: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return;
  }
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + header->size - sizeof(_Memory::PageFooter));
  if(footer->checkValue != _Memory::footerCheckValue)
  {
    Debug::print(_T("Memory::free: error: The passed buffer is corrupted.\n")); TRAP(); // buffer overrun?
    footer->checkValue = _Memory::footerCheckValue;
  }

  if(header->size > _Memory::pageSize || _Memory::freePageCount >= _Memory::maxFreePageCount)
  {
#ifdef _WIN32
    VERIFY(VirtualFree(header, 0, MEM_RELEASE));
#else
    ::free(header);
#endif
    return;
  }

  header->checkValue = _Memory::headerCheckValueUsed;
  ((_Memory::FreePage*)header)->next = 0;

#ifdef _WIN32
  EnterCriticalSection(&_Memory::criticalSection);
#else
  pthread_mutex_lock(&_Memory::mutex);
#endif
  ((_Memory::FreePage*)header)->next = _Memory::firstFreePage;
  _Memory::firstFreePage = (_Memory::FreePage*)header;
  ++_Memory::freePageCount;
#ifdef _WIN32
  LeaveCriticalSection(&_Memory::criticalSection);
#else
  pthread_mutex_unlock(&_Memory::mutex);
#endif
}

void_t* operator new(size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t* operator new [](size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t operator delete(void_t* buffer)
{
  Memory::free(buffer);
}

void_t operator delete[](void_t* buffer)
{
  Memory::free(buffer);
}

#else // !USE_PAGE_ALIGNED_ALLOCATION

#ifdef _WIN32
#include <Windows.h>
#else
#include <cstdlib>
#include <malloc.h>
#include <unistd.h>
#include <cstring>
#endif

class _Memory
{
public:
    struct PageHeader
    {
      uint64_t checkValue;
      size_t size;
    };

    struct PageFooter
    {
      uint64_t checkValue;
    };

    struct FreePage : public PageHeader
    {
      FreePage* next;
    };

    static const uint64_t headerCheckValue = 0x1235543212355432LL;
    static const uint64_t headerCheckValueUsed = 0x1235543212355433LL;
    static const uint64_t footerCheckValue = 0x1235543212355432LL;

#ifdef _WIN32
    static HANDLE processHeap;
#endif
private:
    static _Memory memory;

    _Memory()
    {
#ifdef _WIN32
      processHeap = GetProcessHeap();
#endif
    }
};

#ifdef _MSC_VER
#pragma warning(disable: 4073)
#pragma init_seg(lib)
_Memory _Memory::memory;
#else
_Memory _Memory::memory __attribute__ ((init_priority (101)));
#endif
#ifdef _WIN32
HANDLE _Memory::processHeap = 0;
#endif

void_t* Memory::alloc(size_t minSize, size_t& rsize)
{
#ifdef _WIN32
  ASSERT(_Memory::processHeap);
#endif
  size_t minAllocSize = minSize + (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  _Memory::PageHeader* header;
#ifdef _WIN32
  header = (_Memory::PageHeader*)HeapAlloc(_Memory::processHeap, 0, minAllocSize);
#else
  header = (_Memory::PageHeader*)malloc(minAllocSize);
#endif
  if(!header) // out of memory?
  {
    Debug::printf(_T("Memory::alloc: error: Could not allocate %llu bytes.\n"), (uint64_t)minAllocSize); TRAP();
    do // wait and try again...
    {
#ifdef _WIN32
      Sleep(5000);
      header = (_Memory::PageHeader*)HeapAlloc(_Memory::processHeap, 0, minAllocSize);
#else
      sleep(5);
      header = (_Memory::PageHeader*)malloc(minAllocSize);
#endif
    } while(!header);
  }
  size_t allocSize;
#ifdef _WIN32
  allocSize = HeapSize(_Memory::processHeap, 0, header);
#else
  allocSize = malloc_usable_size(header);
#endif
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + allocSize - sizeof(_Memory::PageFooter));
  header->size = allocSize;
  header->checkValue = _Memory::headerCheckValue;
  footer->checkValue = _Memory::footerCheckValue;
  rsize = allocSize - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  return (void_t*)((uint8_t*)header + sizeof(_Memory::PageHeader));
}

void_t* Memory::alloc(size_t size)
{
  size_t allocatedSize;
  return alloc(size, allocatedSize);
}

size_t Memory::size(void_t* buffer)
{
  if(!buffer) return 0;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::size: error: The passed buffer was freed.\n")); TRAP();
      return 0;
    }
    Debug::print(_T("Memory::size: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return 0;
  }
  return header->size - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
}

void_t Memory::free(void_t* buffer)
{
  if(!buffer) return;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::free: error: The passed buffer was already freed.\n")); TRAP();
      return;
    }
    Debug::print(_T("Memory::free: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return;
  }
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + header->size - sizeof(_Memory::PageFooter));
  if(footer->checkValue != _Memory::footerCheckValue)
  {
    Debug::print(_T("Memory::free: error: The passed buffer is corrupted.\n")); TRAP(); // buffer overrun?
    footer->checkValue = _Memory::footerCheckValue;
  }
#ifdef _WIN32
  VERIFY(HeapFree(_Memory::processHeap, 0, header));
#else
  free(header);
#endif
}

void_t* operator new(size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t* operator new [](size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t operator delete(void_t* buffer)
{
  Memory::free(buffer);
}

void_t operator delete[](void_t* buffer)
{
  Memory::free(buffer);
}

#endif // !USE_PAGE_ALIGNED_ALLOCATION

void_t Memory::copy(void_t* dest, const void_t* src, size_t length)
{
#ifdef _WIN32
  CopyMemory(dest, src, length);
#else
  memcpy(dest, src, length);
#endif
}

void_t Memory::move(void_t* dest, const void_t* src, size_t length)
{
#ifdef _WIN32
  MoveMemory(dest, src, length);
#else
  memmove(dest, src, length);
#endif
}

void_t Memory::fill(void_t* buffer, byte_t value, size_t size)
{
#ifdef _WIN32
  FillMemory(buffer, size, value);
#else
  memset(buffer, value, size);
#endif
}

void_t Memory::zero(void_t* buffer, size_t size)
{
#ifdef _WIN32
  ZeroMemory(buffer, size);
#else
  memset(buffer, 0, size);
#endif
}

int_t Memory::compare(const void_t* ptr1, const void_t* ptr2, size_t count)
{
  return memcmp(ptr1, ptr2, count);
}
<commit_msg>Memory: Fix free not working on Linux<commit_after>

//#if !defined(_MSC_VER) || defined(_M_AMD64)
//#define USE_PAGE_ALIGNED_ALLOCATION
//#endif

#ifdef USE_PAGE_ALIGNED_ALLOCATION

#ifdef _WIN32
#include <Windows.h>
#else
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
#include <cstring>
#endif
#endif

#include <nstd/Memory.h>
#include <nstd/Debug.h>

#ifdef USE_PAGE_ALIGNED_ALLOCATION

class _Memory
{
public:
    struct PageHeader
    {
      uint64_t checkValue;
      size_t size;
    };

    struct PageFooter
    {
      uint64_t checkValue;
    };

    struct FreePage : public PageHeader
    {
      FreePage* next;
    };

    static size_t pageSize;
    static FreePage* firstFreePage;
    static size_t freePageCount;
#ifdef _WIN32
    static CRITICAL_SECTION criticalSection; // TODO: is it possible to use atomic operations instead?
#else
    static pthread_mutex_t mutex;
#endif

    static const uint64_t headerCheckValue = 0x1235543212355432LL;
    static const uint64_t headerCheckValueUsed = 0x1235543212355433LL;
    static const uint64_t footerCheckValue = 0x1235543212355432LL;

    static const size_t maxFreePageCount = 64;

private:
    static _Memory memory;

    _Memory()
    {
#ifdef _WIN32
#ifndef __CYGWIN__
      InitializeCriticalSection(&criticalSection);
      SYSTEM_INFO si;
      GetSystemInfo(&si);
      pageSize = si.dwPageSize;
#endif
#else
      pageSize = sysconf(_SC_PAGESIZE);
#endif
    }
    ~_Memory()
    {
#ifdef _WIN32
      DeleteCriticalSection(&criticalSection);
#endif
    }
};

#ifdef _MSC_VER
#pragma warning(disable: 4073)
#pragma init_seg(lib)
_Memory _Memory::memory;
#else
_Memory _Memory::memory __attribute__ ((init_priority (101)));
#endif
size_t _Memory::pageSize;
_Memory::FreePage* _Memory::firstFreePage = 0;
size_t _Memory::freePageCount = 0;
#ifdef _WIN32
CRITICAL_SECTION _Memory::criticalSection;
#else
pthread_mutex_t _Memory::mutex = PTHREAD_MUTEX_INITIALIZER;
#endif

void_t* Memory::alloc(size_t size)
{
  size_t allocatedSize;
  return alloc(size, allocatedSize);
}

void_t* Memory::alloc(size_t size, size_t& rsize)
{
#ifdef __CYGWIN__
  if(_Memory::pageSize == 0)
  {
      InitializeCriticalSection(&_Memory::criticalSection);
      SYSTEM_INFO si;
      GetSystemInfo(&si);
      _Memory::pageSize = si.dwPageSize;
  }
#else
  ASSERT(_Memory::pageSize > 0);
#endif
  size_t minAllocSize = size + (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  if(minAllocSize <= _Memory::pageSize)
  {
#ifdef _WIN32
    EnterCriticalSection(&_Memory::criticalSection);
#else
    pthread_mutex_lock(&_Memory::mutex);
#endif
    if(_Memory::firstFreePage)
    {
      _Memory::PageHeader* header = _Memory::firstFreePage;
      _Memory::firstFreePage = _Memory::firstFreePage->next;
      --_Memory::freePageCount;
#ifdef _WIN32
      LeaveCriticalSection(&_Memory::criticalSection);
#else
      pthread_mutex_unlock(&_Memory::mutex);
#endif
      rsize = _Memory::pageSize - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
      header->checkValue = _Memory::headerCheckValue;
      return (uint8_t*)header + sizeof(_Memory::PageHeader);
    }
    else
    {
#ifdef _WIN32
      LeaveCriticalSection(&_Memory::criticalSection);
#else
      pthread_mutex_unlock(&_Memory::mutex);
#endif
    }
  }
  size_t pageCount = (minAllocSize + _Memory::pageSize - 1) / _Memory::pageSize;
  size_t allocSize = pageCount * _Memory::pageSize;

  _Memory::PageHeader* header;
#ifdef _WIN32
  header = (_Memory::PageHeader*)VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
  header = (_Memory::PageHeader*)memalign(_Memory::pageSize, allocSize);
#endif
  if(!header) // out of memory?
  {
    Debug::printf(_T("Memory::alloc: error: Could not allocate %llu pages.\n"), (uint64_t)pageCount); TRAP();
    do // wait and try again...
    {
#ifdef _WIN32
      Sleep(5000);
      header = (_Memory::PageHeader*)VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
      sleep(5);
      header = (_Memory::PageHeader*)memalign(_Memory::pageSize, allocSize);
#endif
    } while(!header);
  }
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + allocSize - sizeof(_Memory::PageFooter));
  header->size = allocSize;
  header->checkValue = _Memory::headerCheckValue;
  footer->checkValue = _Memory::footerCheckValue;
  rsize = allocSize - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  return (void_t*)((uint8_t*)header + sizeof(_Memory::PageHeader));
}

size_t Memory::size(void_t* buffer)
{
  if(!buffer) return 0;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::size: error: The passed buffer was freed.\n")); TRAP();
      return 0;
    }
    Debug::print(_T("Memory::size: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return 0;
  }
  return header->size - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
}
/*
size_t Memory::pageSize()
{
  return _Memory::pageSize;
}
*/
void_t Memory::free(void_t* buffer)
{
  if(!buffer) return;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::free: error: The passed buffer was already freed.\n")); TRAP();
      return;
    }
    Debug::print(_T("Memory::free: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return;
  }
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + header->size - sizeof(_Memory::PageFooter));
  if(footer->checkValue != _Memory::footerCheckValue)
  {
    Debug::print(_T("Memory::free: error: The passed buffer is corrupted.\n")); TRAP(); // buffer overrun?
    footer->checkValue = _Memory::footerCheckValue;
  }

  if(header->size > _Memory::pageSize || _Memory::freePageCount >= _Memory::maxFreePageCount)
  {
#ifdef _WIN32
    VERIFY(VirtualFree(header, 0, MEM_RELEASE));
#else
    ::free(header);
#endif
    return;
  }

  header->checkValue = _Memory::headerCheckValueUsed;
  ((_Memory::FreePage*)header)->next = 0;

#ifdef _WIN32
  EnterCriticalSection(&_Memory::criticalSection);
#else
  pthread_mutex_lock(&_Memory::mutex);
#endif
  ((_Memory::FreePage*)header)->next = _Memory::firstFreePage;
  _Memory::firstFreePage = (_Memory::FreePage*)header;
  ++_Memory::freePageCount;
#ifdef _WIN32
  LeaveCriticalSection(&_Memory::criticalSection);
#else
  pthread_mutex_unlock(&_Memory::mutex);
#endif
}

void_t* operator new(size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t* operator new [](size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t operator delete(void_t* buffer)
{
  Memory::free(buffer);
}

void_t operator delete[](void_t* buffer)
{
  Memory::free(buffer);
}

#else // !USE_PAGE_ALIGNED_ALLOCATION

#ifdef _WIN32
#include <Windows.h>
#else
#include <cstdlib>
#include <malloc.h>
#include <unistd.h>
#include <cstring>
#endif

class _Memory
{
public:
    struct PageHeader
    {
      uint64_t checkValue;
      size_t size;
    };

    struct PageFooter
    {
      uint64_t checkValue;
    };

    struct FreePage : public PageHeader
    {
      FreePage* next;
    };

    static const uint64_t headerCheckValue = 0x1235543212355432LL;
    static const uint64_t headerCheckValueUsed = 0x1235543212355433LL;
    static const uint64_t footerCheckValue = 0x1235543212355432LL;

#ifdef _WIN32
    static HANDLE processHeap;
#endif
private:
    static _Memory memory;

    _Memory()
    {
#ifdef _WIN32
      processHeap = GetProcessHeap();
#endif
    }
};

#ifdef _MSC_VER
#pragma warning(disable: 4073)
#pragma init_seg(lib)
_Memory _Memory::memory;
#else
_Memory _Memory::memory __attribute__ ((init_priority (101)));
#endif
#ifdef _WIN32
HANDLE _Memory::processHeap = 0;
#endif

void_t* Memory::alloc(size_t minSize, size_t& rsize)
{
#ifdef _WIN32
  ASSERT(_Memory::processHeap);
#endif
  size_t minAllocSize = minSize + (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  _Memory::PageHeader* header;
#ifdef _WIN32
  header = (_Memory::PageHeader*)HeapAlloc(_Memory::processHeap, 0, minAllocSize);
#else
  header = (_Memory::PageHeader*)malloc(minAllocSize);
#endif
  if(!header) // out of memory?
  {
    Debug::printf(_T("Memory::alloc: error: Could not allocate %llu bytes.\n"), (uint64_t)minAllocSize); TRAP();
    do // wait and try again...
    {
#ifdef _WIN32
      Sleep(5000);
      header = (_Memory::PageHeader*)HeapAlloc(_Memory::processHeap, 0, minAllocSize);
#else
      sleep(5);
      header = (_Memory::PageHeader*)malloc(minAllocSize);
#endif
    } while(!header);
  }
  size_t allocSize;
#ifdef _WIN32
  allocSize = HeapSize(_Memory::processHeap, 0, header);
#else
  allocSize = malloc_usable_size(header);
#endif
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + allocSize - sizeof(_Memory::PageFooter));
  header->size = allocSize;
  header->checkValue = _Memory::headerCheckValue;
  footer->checkValue = _Memory::footerCheckValue;
  rsize = allocSize - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
  return (void_t*)((uint8_t*)header + sizeof(_Memory::PageHeader));
}

void_t* Memory::alloc(size_t size)
{
  size_t allocatedSize;
  return alloc(size, allocatedSize);
}

size_t Memory::size(void_t* buffer)
{
  if(!buffer) return 0;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::size: error: The passed buffer was freed.\n")); TRAP();
      return 0;
    }
    Debug::print(_T("Memory::size: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return 0;
  }
  return header->size - (sizeof(_Memory::PageHeader) + sizeof(_Memory::PageFooter));
}

void_t Memory::free(void_t* buffer)
{
  if(!buffer) return;
  _Memory::PageHeader* header = (_Memory::PageHeader*)((uint8_t*)buffer - sizeof(_Memory::PageHeader));
  if(header->checkValue != _Memory::headerCheckValue)
  {
    if(header->checkValue == _Memory::headerCheckValueUsed)
    {
      Debug::print(_T("Memory::free: error: The passed buffer was already freed.\n")); TRAP();
      return;
    }
    Debug::print(_T("Memory::free: error: The passed buffer is invalid or corrupted.\n")); TRAP();
    return;
  }
  _Memory::PageFooter* footer = (_Memory::PageFooter*)((uint8_t*)header + header->size - sizeof(_Memory::PageFooter));
  if(footer->checkValue != _Memory::footerCheckValue)
  {
    Debug::print(_T("Memory::free: error: The passed buffer is corrupted.\n")); TRAP(); // buffer overrun?
    footer->checkValue = _Memory::footerCheckValue;
  }
#ifdef _WIN32
  VERIFY(HeapFree(_Memory::processHeap, 0, header));
#else
  ::free(header);
#endif
}

void_t* operator new(size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t* operator new [](size_t size)
{
  size_t allocatedSize;
  return Memory::alloc(size, allocatedSize);
}

void_t operator delete(void_t* buffer)
{
  Memory::free(buffer);
}

void_t operator delete[](void_t* buffer)
{
  Memory::free(buffer);
}

#endif // !USE_PAGE_ALIGNED_ALLOCATION

void_t Memory::copy(void_t* dest, const void_t* src, size_t length)
{
#ifdef _WIN32
  CopyMemory(dest, src, length);
#else
  memcpy(dest, src, length);
#endif
}

void_t Memory::move(void_t* dest, const void_t* src, size_t length)
{
#ifdef _WIN32
  MoveMemory(dest, src, length);
#else
  memmove(dest, src, length);
#endif
}

void_t Memory::fill(void_t* buffer, byte_t value, size_t size)
{
#ifdef _WIN32
  FillMemory(buffer, size, value);
#else
  memset(buffer, value, size);
#endif
}

void_t Memory::zero(void_t* buffer, size_t size)
{
#ifdef _WIN32
  ZeroMemory(buffer, size);
#else
  memset(buffer, 0, size);
#endif
}

int_t Memory::compare(const void_t* ptr1, const void_t* ptr2, size_t count)
{
  return memcmp(ptr1, ptr2, count);
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "browser/api/atom_api_protocol.h"

#include "base/stl_util.h"
#include "browser/atom_browser_context.h"
#include "browser/net/adapter_request_job.h"
#include "browser/net/atom_url_request_context_getter.h"
#include "browser/net/atom_url_request_job_factory.h"
#include "common/v8/native_type_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "net/url_request/url_request_context.h"

#include "common/v8/node_common.h"

namespace atom {

namespace api {

typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;

namespace {

// The protocol module object.
ScopedPersistent<v8::Object> g_protocol_object;

// Registered protocol handlers.
typedef std::map<std::string, RefCountedV8Function> HandlersMap;
static HandlersMap g_handlers;

static const char* kEarlyUseProtocolError = "This method can only be used"
    "after the application has finished launching.";

// Emit an event for the protocol module.
void EmitEventInUI(const std::string& event, const std::string& parameter) {
  v8::Locker locker(node_isolate);
  v8::HandleScope handle_scope(node_isolate);

  v8::Handle<v8::Value> argv[] = {
      ToV8Value(event),
      ToV8Value(parameter),
  };
  node::MakeCallback(g_protocol_object.NewHandle(node_isolate),
                     "emit", 2, argv);
}

// Convert the URLRequest object to V8 object.
v8::Handle<v8::Object> ConvertURLRequestToV8Object(
    const net::URLRequest* request) {
  v8::Local<v8::Object> obj = v8::Object::New();
  obj->Set(ToV8Value("method"), ToV8Value(request->method()));
  obj->Set(ToV8Value("url"), ToV8Value(request->url().spec()));
  obj->Set(ToV8Value("referrer"), ToV8Value(request->referrer()));
  return obj;
}

// Get the job factory.
AtomURLRequestJobFactory* GetRequestJobFactory() {
  return AtomBrowserContext::Get()->url_request_context_getter()->job_factory();
}

class CustomProtocolRequestJob : public AdapterRequestJob {
 public:
  CustomProtocolRequestJob(ProtocolHandler* protocol_handler,
                           net::URLRequest* request,
                           net::NetworkDelegate* network_delegate)
      : AdapterRequestJob(protocol_handler, request, network_delegate) {
  }

  // AdapterRequestJob:
  virtual void GetJobTypeInUI() OVERRIDE {
    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

    v8::Locker locker(node_isolate);
    v8::HandleScope handle_scope(node_isolate);

    // Call the JS handler.
    v8::Handle<v8::Value> argv[] = {
      ConvertURLRequestToV8Object(request()),
    };
    RefCountedV8Function callback = g_handlers[request()->url().scheme()];
    v8::Handle<v8::Value> result = callback->NewHandle(node_isolate)->Call(
        v8::Context::GetCurrent()->Global(), 1, argv);

    // Determine the type of the job we are going to create.
    if (result->IsString()) {
      std::string data = FromV8Value(result);
      content::BrowserThread::PostTask(
          content::BrowserThread::IO,
          FROM_HERE,
          base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
                     GetWeakPtr(),
                     "text/plain",
                     "UTF-8",
                     data));
      return;
    } else if (result->IsObject()) {
      v8::Handle<v8::Object> obj = result->ToObject();
      std::string name = FromV8Value(obj->GetConstructorName());
      if (name == "RequestStringJob") {
        std::string mime_type = FromV8Value(obj->Get(
            v8::String::New("mimeType")));
        std::string charset = FromV8Value(obj->Get(v8::String::New("charset")));
        std::string data = FromV8Value(obj->Get(v8::String::New("data")));

        content::BrowserThread::PostTask(
            content::BrowserThread::IO,
            FROM_HERE,
            base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
                       GetWeakPtr(),
                       mime_type,
                       charset,
                       data));
        return;
      } else if (name == "RequestFileJob") {
        base::FilePath path = FromV8Value(obj->Get(v8::String::New("path")));

        content::BrowserThread::PostTask(
            content::BrowserThread::IO,
            FROM_HERE,
            base::Bind(&AdapterRequestJob::CreateFileJobAndStart,
                       GetWeakPtr(),
                       path));
        return;
      }
    }

    // Try the default protocol handler if we have.
    if (default_protocol_handler()) {
      content::BrowserThread::PostTask(
          content::BrowserThread::IO,
          FROM_HERE,
          base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart,
                     GetWeakPtr()));
      return;
    }

    // Fallback to the not implemented error.
    content::BrowserThread::PostTask(
        content::BrowserThread::IO,
        FROM_HERE,
        base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
                   GetWeakPtr(),
                   net::ERR_NOT_IMPLEMENTED));
  }
};

// Always return the same CustomProtocolRequestJob for all requests, because
// the content API needs the ProtocolHandler to return a job immediately, and
// getting the real job from the JS requires asynchronous calls, so we have
// to create an adapter job first.
// Users can also pass an extra ProtocolHandler as the fallback one when
// registered handler doesn't want to deal with the request.
class CustomProtocolHandler : public ProtocolHandler {
 public:
  explicit CustomProtocolHandler(ProtocolHandler* protocol_handler = NULL)
      : protocol_handler_(protocol_handler) {
  }

  virtual net::URLRequestJob* MaybeCreateJob(
      net::URLRequest* request,
      net::NetworkDelegate* network_delegate) const OVERRIDE {
    return new CustomProtocolRequestJob(protocol_handler_.get(),
                                        request,
                                        network_delegate);
  }

  ProtocolHandler* ReleaseDefaultProtocolHandler() {
    return protocol_handler_.release();
  }

  ProtocolHandler* original_handler() { return protocol_handler_.get(); }

 private:
  scoped_ptr<ProtocolHandler> protocol_handler_;

  DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
};

}  // namespace

// static
void Protocol::RegisterProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  RefCountedV8Function callback;
  if (!FromV8Arguments(args, &scheme, &callback))
    return node::ThrowTypeError("Bad argument");

  if (g_handlers.find(scheme) != g_handlers.end() ||
      GetRequestJobFactory()->IsHandledProtocol(scheme))
    return node::ThrowError("The scheme is already registered");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Store the handler in a map.
  g_handlers[scheme] = callback;

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&RegisterProtocolInIO, scheme));
}

// static
void Protocol::UnregisterProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  if (!FromV8Arguments(args, &scheme))
    return node::ThrowTypeError("Bad argument");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Erase the handler from map.
  HandlersMap::iterator it(g_handlers.find(scheme));
  if (it == g_handlers.end())
    return node::ThrowError("The scheme has not been registered");
  g_handlers.erase(it);

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&UnregisterProtocolInIO, scheme));
}

// static
void Protocol::IsHandledProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  if (!FromV8Arguments(args, &scheme))
    return node::ThrowTypeError("Bad argument");

  args.GetReturnValue().Set(GetRequestJobFactory()->IsHandledProtocol(scheme));
}

// static
void Protocol::InterceptProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  RefCountedV8Function callback;
  if (!FromV8Arguments(args, &scheme, &callback))
    return node::ThrowTypeError("Bad argument");

  if (!GetRequestJobFactory()->HasProtocolHandler(scheme))
    return node::ThrowError("Cannot intercept procotol");

  if (ContainsKey(g_handlers, scheme))
    return node::ThrowError("Cannot intercept custom procotols");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Store the handler in a map.
  g_handlers[scheme] = callback;

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&InterceptProtocolInIO, scheme));
}

// static
void Protocol::UninterceptProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  if (!FromV8Arguments(args, &scheme))
    return node::ThrowTypeError("Bad argument");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Erase the handler from map.
  HandlersMap::iterator it(g_handlers.find(scheme));
  if (it == g_handlers.end())
    return node::ThrowError("The scheme has not been registered");
  g_handlers.erase(it);

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&UninterceptProtocolInIO,
                                              scheme));
}

// static
void Protocol::RegisterProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
  job_factory->SetProtocolHandler(scheme, new CustomProtocolHandler);

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "registered",
                                              scheme));
}

// static
void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
  job_factory->SetProtocolHandler(scheme, NULL);

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "unregistered",
                                              scheme));
}

// static
void Protocol::InterceptProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
  ProtocolHandler* original_handler = job_factory->GetProtocolHandler(scheme);
  if (original_handler == NULL) {
    content::BrowserThread::PostTask(
        content::BrowserThread::UI,
        FROM_HERE,
        base::Bind(&EmitEventInUI,
                   "error",
                   "There is no protocol handler to intercpet"));
    return;
  }

  job_factory->ReplaceProtocol(scheme,
                               new CustomProtocolHandler(original_handler));

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "intercepted",
                                              scheme));
}

// static
void Protocol::UninterceptProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());

  // Check if the protocol handler is intercepted.
  CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
      job_factory->GetProtocolHandler(scheme));
  if (handler->original_handler() == NULL) {
    content::BrowserThread::PostTask(
        content::BrowserThread::UI,
        FROM_HERE,
        base::Bind(&EmitEventInUI,
                   "error",
                   "The protocol is not intercpeted"));
    return;
  }

  // Reset the protocol handler to the orignal one and delete current
  // protocol handler.
  ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler();
  delete job_factory->ReplaceProtocol(scheme, original_handler);

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "unintercepted",
                                              scheme));
}

// static
void Protocol::Initialize(v8::Handle<v8::Object> target) {
  // Remember the protocol object, used for emitting event later.
  g_protocol_object.reset(target);

  NODE_SET_METHOD(target, "registerProtocol", RegisterProtocol);
  NODE_SET_METHOD(target, "unregisterProtocol", UnregisterProtocol);
  NODE_SET_METHOD(target, "isHandledProtocol", IsHandledProtocol);
  NODE_SET_METHOD(target, "interceptProtocol", InterceptProtocol);
  NODE_SET_METHOD(target, "uninterceptProtocol", UninterceptProtocol);
}

}  // namespace api

}  // namespace atom

NODE_MODULE(atom_browser_protocol, atom::api::Protocol::Initialize)
<commit_msg>linux: Fix crash when using protocol module early.<commit_after>// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "browser/api/atom_api_protocol.h"

#include "base/stl_util.h"
#include "browser/atom_browser_context.h"
#include "browser/net/adapter_request_job.h"
#include "browser/net/atom_url_request_context_getter.h"
#include "browser/net/atom_url_request_job_factory.h"
#include "common/v8/native_type_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "net/url_request/url_request_context.h"

#include "common/v8/node_common.h"

namespace atom {

namespace api {

typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;

namespace {

// The protocol module object.
ScopedPersistent<v8::Object> g_protocol_object;

// Registered protocol handlers.
typedef std::map<std::string, RefCountedV8Function> HandlersMap;
static HandlersMap g_handlers;

static const char* kEarlyUseProtocolError = "This method can only be used"
    "after the application has finished launching.";

// Emit an event for the protocol module.
void EmitEventInUI(const std::string& event, const std::string& parameter) {
  v8::Locker locker(node_isolate);
  v8::HandleScope handle_scope(node_isolate);

  v8::Handle<v8::Value> argv[] = {
      ToV8Value(event),
      ToV8Value(parameter),
  };
  node::MakeCallback(g_protocol_object.NewHandle(node_isolate),
                     "emit", 2, argv);
}

// Convert the URLRequest object to V8 object.
v8::Handle<v8::Object> ConvertURLRequestToV8Object(
    const net::URLRequest* request) {
  v8::Local<v8::Object> obj = v8::Object::New();
  obj->Set(ToV8Value("method"), ToV8Value(request->method()));
  obj->Set(ToV8Value("url"), ToV8Value(request->url().spec()));
  obj->Set(ToV8Value("referrer"), ToV8Value(request->referrer()));
  return obj;
}

// Get the job factory.
AtomURLRequestJobFactory* GetRequestJobFactory() {
  return AtomBrowserContext::Get()->url_request_context_getter()->job_factory();
}

class CustomProtocolRequestJob : public AdapterRequestJob {
 public:
  CustomProtocolRequestJob(ProtocolHandler* protocol_handler,
                           net::URLRequest* request,
                           net::NetworkDelegate* network_delegate)
      : AdapterRequestJob(protocol_handler, request, network_delegate) {
  }

  // AdapterRequestJob:
  virtual void GetJobTypeInUI() OVERRIDE {
    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

    v8::Locker locker(node_isolate);
    v8::HandleScope handle_scope(node_isolate);

    // Call the JS handler.
    v8::Handle<v8::Value> argv[] = {
      ConvertURLRequestToV8Object(request()),
    };
    RefCountedV8Function callback = g_handlers[request()->url().scheme()];
    v8::Handle<v8::Value> result = callback->NewHandle(node_isolate)->Call(
        v8::Context::GetCurrent()->Global(), 1, argv);

    // Determine the type of the job we are going to create.
    if (result->IsString()) {
      std::string data = FromV8Value(result);
      content::BrowserThread::PostTask(
          content::BrowserThread::IO,
          FROM_HERE,
          base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
                     GetWeakPtr(),
                     "text/plain",
                     "UTF-8",
                     data));
      return;
    } else if (result->IsObject()) {
      v8::Handle<v8::Object> obj = result->ToObject();
      std::string name = FromV8Value(obj->GetConstructorName());
      if (name == "RequestStringJob") {
        std::string mime_type = FromV8Value(obj->Get(
            v8::String::New("mimeType")));
        std::string charset = FromV8Value(obj->Get(v8::String::New("charset")));
        std::string data = FromV8Value(obj->Get(v8::String::New("data")));

        content::BrowserThread::PostTask(
            content::BrowserThread::IO,
            FROM_HERE,
            base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
                       GetWeakPtr(),
                       mime_type,
                       charset,
                       data));
        return;
      } else if (name == "RequestFileJob") {
        base::FilePath path = FromV8Value(obj->Get(v8::String::New("path")));

        content::BrowserThread::PostTask(
            content::BrowserThread::IO,
            FROM_HERE,
            base::Bind(&AdapterRequestJob::CreateFileJobAndStart,
                       GetWeakPtr(),
                       path));
        return;
      }
    }

    // Try the default protocol handler if we have.
    if (default_protocol_handler()) {
      content::BrowserThread::PostTask(
          content::BrowserThread::IO,
          FROM_HERE,
          base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart,
                     GetWeakPtr()));
      return;
    }

    // Fallback to the not implemented error.
    content::BrowserThread::PostTask(
        content::BrowserThread::IO,
        FROM_HERE,
        base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
                   GetWeakPtr(),
                   net::ERR_NOT_IMPLEMENTED));
  }
};

// Always return the same CustomProtocolRequestJob for all requests, because
// the content API needs the ProtocolHandler to return a job immediately, and
// getting the real job from the JS requires asynchronous calls, so we have
// to create an adapter job first.
// Users can also pass an extra ProtocolHandler as the fallback one when
// registered handler doesn't want to deal with the request.
class CustomProtocolHandler : public ProtocolHandler {
 public:
  explicit CustomProtocolHandler(ProtocolHandler* protocol_handler = NULL)
      : protocol_handler_(protocol_handler) {
  }

  virtual net::URLRequestJob* MaybeCreateJob(
      net::URLRequest* request,
      net::NetworkDelegate* network_delegate) const OVERRIDE {
    return new CustomProtocolRequestJob(protocol_handler_.get(),
                                        request,
                                        network_delegate);
  }

  ProtocolHandler* ReleaseDefaultProtocolHandler() {
    return protocol_handler_.release();
  }

  ProtocolHandler* original_handler() { return protocol_handler_.get(); }

 private:
  scoped_ptr<ProtocolHandler> protocol_handler_;

  DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
};

}  // namespace

// static
void Protocol::RegisterProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  RefCountedV8Function callback;
  if (!FromV8Arguments(args, &scheme, &callback))
    return node::ThrowTypeError("Bad argument");

  if (g_handlers.find(scheme) != g_handlers.end() ||
      GetRequestJobFactory()->IsHandledProtocol(scheme))
    return node::ThrowError("The scheme is already registered");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Store the handler in a map.
  g_handlers[scheme] = callback;

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&RegisterProtocolInIO, scheme));
}

// static
void Protocol::UnregisterProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  if (!FromV8Arguments(args, &scheme))
    return node::ThrowTypeError("Bad argument");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Erase the handler from map.
  HandlersMap::iterator it(g_handlers.find(scheme));
  if (it == g_handlers.end())
    return node::ThrowError("The scheme has not been registered");
  g_handlers.erase(it);

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&UnregisterProtocolInIO, scheme));
}

// static
void Protocol::IsHandledProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  if (!FromV8Arguments(args, &scheme))
    return node::ThrowTypeError("Bad argument");

  args.GetReturnValue().Set(GetRequestJobFactory()->IsHandledProtocol(scheme));
}

// static
void Protocol::InterceptProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  RefCountedV8Function callback;
  if (!FromV8Arguments(args, &scheme, &callback))
    return node::ThrowTypeError("Bad argument");

  if (!GetRequestJobFactory()->HasProtocolHandler(scheme))
    return node::ThrowError("Cannot intercept procotol");

  if (ContainsKey(g_handlers, scheme))
    return node::ThrowError("Cannot intercept custom procotols");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Store the handler in a map.
  g_handlers[scheme] = callback;

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&InterceptProtocolInIO, scheme));
}

// static
void Protocol::UninterceptProtocol(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::string scheme;
  if (!FromV8Arguments(args, &scheme))
    return node::ThrowTypeError("Bad argument");

  if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
    return node::ThrowError(kEarlyUseProtocolError);

  // Erase the handler from map.
  HandlersMap::iterator it(g_handlers.find(scheme));
  if (it == g_handlers.end())
    return node::ThrowError("The scheme has not been registered");
  g_handlers.erase(it);

  content::BrowserThread::PostTask(content::BrowserThread::IO,
                                   FROM_HERE,
                                   base::Bind(&UninterceptProtocolInIO,
                                              scheme));
}

// static
void Protocol::RegisterProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
  job_factory->SetProtocolHandler(scheme, new CustomProtocolHandler);

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "registered",
                                              scheme));
}

// static
void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
  job_factory->SetProtocolHandler(scheme, NULL);

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "unregistered",
                                              scheme));
}

// static
void Protocol::InterceptProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
  ProtocolHandler* original_handler = job_factory->GetProtocolHandler(scheme);
  if (original_handler == NULL) {
    content::BrowserThread::PostTask(
        content::BrowserThread::UI,
        FROM_HERE,
        base::Bind(&EmitEventInUI,
                   "error",
                   "There is no protocol handler to intercpet"));
    return;
  }

  job_factory->ReplaceProtocol(scheme,
                               new CustomProtocolHandler(original_handler));

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "intercepted",
                                              scheme));
}

// static
void Protocol::UninterceptProtocolInIO(const std::string& scheme) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
  AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());

  // Check if the protocol handler is intercepted.
  CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
      job_factory->GetProtocolHandler(scheme));
  if (handler->original_handler() == NULL) {
    content::BrowserThread::PostTask(
        content::BrowserThread::UI,
        FROM_HERE,
        base::Bind(&EmitEventInUI,
                   "error",
                   "The protocol is not intercpeted"));
    return;
  }

  // Reset the protocol handler to the orignal one and delete current
  // protocol handler.
  ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler();
  delete job_factory->ReplaceProtocol(scheme, original_handler);

  content::BrowserThread::PostTask(content::BrowserThread::UI,
                                   FROM_HERE,
                                   base::Bind(&EmitEventInUI,
                                              "unintercepted",
                                              scheme));
}

// static
void Protocol::Initialize(v8::Handle<v8::Object> target) {
  // Remember the protocol object, used for emitting event later.
  g_protocol_object.reset(target);

#if defined(OS_LINUX)
  // Make sure the job factory has been created.
  AtomBrowserContext::Get()->url_request_context_getter()->
      GetURLRequestContext();
#endif

  NODE_SET_METHOD(target, "registerProtocol", RegisterProtocol);
  NODE_SET_METHOD(target, "unregisterProtocol", UnregisterProtocol);
  NODE_SET_METHOD(target, "isHandledProtocol", IsHandledProtocol);
  NODE_SET_METHOD(target, "interceptProtocol", InterceptProtocol);
  NODE_SET_METHOD(target, "uninterceptProtocol", UninterceptProtocol);
}

}  // namespace api

}  // namespace atom

NODE_MODULE(atom_browser_protocol, atom::api::Protocol::Initialize)
<|endoftext|>


<commit_before>/*
 * Copyright 2018 The Cartographer Authors
 *
 * 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 "cartographer/cloud/internal/handlers/add_sensor_data_batch_handler.h"

#include "absl/memory/memory.h"
#include "async_grpc/rpc_handler.h"
#include "cartographer/cloud/internal/map_builder_context_interface.h"
#include "cartographer/cloud/proto/map_builder_service.pb.h"
#include "cartographer/mapping/local_slam_result_data.h"
#include "cartographer/metrics/counter.h"
#include "cartographer/sensor/internal/dispatchable.h"
#include "cartographer/sensor/timed_point_cloud_data.h"
#include "google/protobuf/empty.pb.h"

namespace cartographer {
namespace cloud {
namespace handlers {

metrics::Family<metrics::Counter>*
    AddSensorDataBatchHandler::counter_metrics_family_ =
        metrics::Family<metrics::Counter>::Null();

void AddSensorDataBatchHandler::OnRequest(
    const proto::AddSensorDataBatchRequest& request) {
  for (const proto::SensorData& sensor_data : request.sensor_data()) {
    if (!GetContext<MapBuilderContextInterface>()->CheckClientIdForTrajectory(
            sensor_data.sensor_metadata().client_id(),
            sensor_data.sensor_metadata().trajectory_id())) {
      LOG(ERROR) << "Unknown trajectory with ID "
                 << sensor_data.sensor_metadata().trajectory_id()
                 << " and client_id "
                 << sensor_data.sensor_metadata().client_id();
      Finish(::grpc::Status(::grpc::NOT_FOUND, "Unknown trajectory"));
      return;
    }
    ClientMetrics* const metrics =
        GetOrCreateClientMetrics(sensor_data.sensor_metadata().client_id(),
                                 sensor_data.sensor_metadata().trajectory_id());
    switch (sensor_data.sensor_data_case()) {
      case proto::SensorData::kOdometryData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.odometry_data())));
        metrics->odometry_sensor_counter->Increment();
        break;
      case proto::SensorData::kImuData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(sensor_data.sensor_metadata().trajectory_id(),
                                sensor::MakeDispatchable(
                                    sensor_data.sensor_metadata().sensor_id(),
                                    sensor::FromProto(sensor_data.imu_data())));
        metrics->imu_sensor_counter->Increment();
        break;
      case proto::SensorData::kTimedPointCloudData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.timed_point_cloud_data())));
        metrics->timed_point_cloud_counter->Increment();
        break;
      case proto::SensorData::kFixedFramePoseData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.fixed_frame_pose_data())));
        metrics->fixed_frame_pose_counter->Increment();
        break;
      case proto::SensorData::kLandmarkData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.landmark_data())));
        metrics->landmark_counter->Increment();
        break;
      case proto::SensorData::kLocalSlamResultData:
        GetContext<MapBuilderContextInterface>()->EnqueueLocalSlamResultData(
            sensor_data.sensor_metadata().trajectory_id(),
            sensor_data.sensor_metadata().sensor_id(),
            sensor_data.local_slam_result_data());
        metrics->local_slam_result_counter->Increment();
        break;
      default:
        LOG(FATAL) << "Unknown sensor data type: "
                   << sensor_data.sensor_data_case();
    }
  }
  Send(absl::make_unique<google::protobuf::Empty>());
}

void AddSensorDataBatchHandler::RegisterMetrics(
    metrics::FamilyFactory* family_factory) {
  counter_metrics_family_ = family_factory->NewCounterFamily(
      "cartographer_sensor_data_total", "Sensor data received");
}

AddSensorDataBatchHandler::ClientMetrics*
AddSensorDataBatchHandler::GetOrCreateClientMetrics(
    const std::string& client_id, int trajectory_id) {
  const std::string map_key = client_id + std::to_string(trajectory_id);
  auto client_metric_map_itr = client_metric_map_.find(map_key);
  if (client_metric_map_itr != client_metric_map_.end()) {
    return client_metric_map_itr->second.get();
  }

  LOG(INFO) << "Create metrics handler for client: " << client_id;
  auto new_metrics = absl::make_unique<ClientMetrics>();
  new_metrics->odometry_sensor_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "odometry"}});
  new_metrics->imu_sensor_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "imu"}});
  new_metrics->fixed_frame_pose_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "fixed_frame_pose"}});
  new_metrics->landmark_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "landmark"}});
  new_metrics->local_slam_result_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "local_slam_result"}});
  new_metrics->timed_point_cloud_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "timed_point_cloud"}});

  // Obtain pointer before ownership is transferred.
  auto* new_metrics_ptr = new_metrics.get();
  client_metric_map_[map_key] = std::move(new_metrics);
  return new_metrics_ptr;
}

}  // namespace handlers
}  // namespace cloud
}  // namespace cartographer
<commit_msg>Remove noisy loggin from AddSensorDataBatchHandler (#1366)<commit_after>/*
 * Copyright 2018 The Cartographer Authors
 *
 * 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 "cartographer/cloud/internal/handlers/add_sensor_data_batch_handler.h"

#include "absl/memory/memory.h"
#include "async_grpc/rpc_handler.h"
#include "cartographer/cloud/internal/map_builder_context_interface.h"
#include "cartographer/cloud/proto/map_builder_service.pb.h"
#include "cartographer/mapping/local_slam_result_data.h"
#include "cartographer/metrics/counter.h"
#include "cartographer/sensor/internal/dispatchable.h"
#include "cartographer/sensor/timed_point_cloud_data.h"
#include "google/protobuf/empty.pb.h"

namespace cartographer {
namespace cloud {
namespace handlers {

metrics::Family<metrics::Counter>*
    AddSensorDataBatchHandler::counter_metrics_family_ =
        metrics::Family<metrics::Counter>::Null();

void AddSensorDataBatchHandler::OnRequest(
    const proto::AddSensorDataBatchRequest& request) {
  for (const proto::SensorData& sensor_data : request.sensor_data()) {
    if (!GetContext<MapBuilderContextInterface>()->CheckClientIdForTrajectory(
            sensor_data.sensor_metadata().client_id(),
            sensor_data.sensor_metadata().trajectory_id())) {
      LOG(ERROR) << "Unknown trajectory with ID "
                 << sensor_data.sensor_metadata().trajectory_id()
                 << " and client_id "
                 << sensor_data.sensor_metadata().client_id();
      Finish(::grpc::Status(::grpc::NOT_FOUND, "Unknown trajectory"));
      return;
    }
    ClientMetrics* const metrics =
        GetOrCreateClientMetrics(sensor_data.sensor_metadata().client_id(),
                                 sensor_data.sensor_metadata().trajectory_id());
    switch (sensor_data.sensor_data_case()) {
      case proto::SensorData::kOdometryData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.odometry_data())));
        metrics->odometry_sensor_counter->Increment();
        break;
      case proto::SensorData::kImuData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(sensor_data.sensor_metadata().trajectory_id(),
                                sensor::MakeDispatchable(
                                    sensor_data.sensor_metadata().sensor_id(),
                                    sensor::FromProto(sensor_data.imu_data())));
        metrics->imu_sensor_counter->Increment();
        break;
      case proto::SensorData::kTimedPointCloudData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.timed_point_cloud_data())));
        metrics->timed_point_cloud_counter->Increment();
        break;
      case proto::SensorData::kFixedFramePoseData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.fixed_frame_pose_data())));
        metrics->fixed_frame_pose_counter->Increment();
        break;
      case proto::SensorData::kLandmarkData:
        GetUnsynchronizedContext<MapBuilderContextInterface>()
            ->EnqueueSensorData(
                sensor_data.sensor_metadata().trajectory_id(),
                sensor::MakeDispatchable(
                    sensor_data.sensor_metadata().sensor_id(),
                    sensor::FromProto(sensor_data.landmark_data())));
        metrics->landmark_counter->Increment();
        break;
      case proto::SensorData::kLocalSlamResultData:
        GetContext<MapBuilderContextInterface>()->EnqueueLocalSlamResultData(
            sensor_data.sensor_metadata().trajectory_id(),
            sensor_data.sensor_metadata().sensor_id(),
            sensor_data.local_slam_result_data());
        metrics->local_slam_result_counter->Increment();
        break;
      default:
        LOG(FATAL) << "Unknown sensor data type: "
                   << sensor_data.sensor_data_case();
    }
  }
  Send(absl::make_unique<google::protobuf::Empty>());
}

void AddSensorDataBatchHandler::RegisterMetrics(
    metrics::FamilyFactory* family_factory) {
  counter_metrics_family_ = family_factory->NewCounterFamily(
      "cartographer_sensor_data_total", "Sensor data received");
}

AddSensorDataBatchHandler::ClientMetrics*
AddSensorDataBatchHandler::GetOrCreateClientMetrics(
    const std::string& client_id, int trajectory_id) {
  const std::string map_key = client_id + std::to_string(trajectory_id);
  auto client_metric_map_itr = client_metric_map_.find(map_key);
  if (client_metric_map_itr != client_metric_map_.end()) {
    return client_metric_map_itr->second.get();
  }

  auto new_metrics = absl::make_unique<ClientMetrics>();
  new_metrics->odometry_sensor_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "odometry"}});
  new_metrics->imu_sensor_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "imu"}});
  new_metrics->fixed_frame_pose_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "fixed_frame_pose"}});
  new_metrics->landmark_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "landmark"}});
  new_metrics->local_slam_result_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "local_slam_result"}});
  new_metrics->timed_point_cloud_counter = counter_metrics_family_->Add(
      {{"client_id", client_id},
       {"trajectory_id", std::to_string(trajectory_id)},
       {"sensor", "timed_point_cloud"}});

  // Obtain pointer before ownership is transferred.
  auto* new_metrics_ptr = new_metrics.get();
  client_metric_map_[map_key] = std::move(new_metrics);
  return new_metrics_ptr;
}

}  // namespace handlers
}  // namespace cloud
}  // namespace cartographer
<|endoftext|>