<commit_before>/*
 * \copyright Copyright 2014 Xiang Zhang All Rights Reserved.
 * \license @{
 *
 * 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.
 *
 * @}
 */

#ifndef THUNDER_TENSOR_COMPLEX_INL_TRANSFORM_HPP_
#define THUNDER_TENSOR_COMPLEX_INL_TRANSFORM_HPP_

#include "thunder/tensor/complex.hpp"
#include "thunder/tensor/complex-inl.hpp"

#include <complex>

#include "thunder/exception.hpp"
#include "thunder/tensor/index_iterator.hpp"
#include "thunder/tensor/math.hpp"
#include "thunder/tensor/tensor.hpp"

namespace thunder {
namespace tensor {
namespace math {

// Transformations
template < typename D, typename A, typename T1 >
T1 extract(const T1 &x, const Tensor< Storage< ::std::complex< D >, A > > &y) {
  typedef Tensor< Storage < ::std::complex< D >, A > > T2;
  if (x.dimension() < y.dimension()) {
    throw out_of_range("Dimension exceeds limit.");
  }
  typename T1::dim_type y_dimension =
      static_cast< typename T1::dim_type >(y.dimension());
  for (typename T1::dim_type i = 0; i < y_dimension; ++i) {
    if (x.size(i) != static_cast< typename T1::size_type >(y.size(i))) {
      throw out_of_range("Size does not match.");
    }
  }
  typename T1::size_storage sz(x.dimension() - y_dimension + 1);
  for (typename T1::dim_type i = y_dimension; i < x.dimension(); ++i) {
    sz[i - y_dimension + 1] = x.size(i);
  }
  // Get the size of returning tensor
  sz[0] = 0;
  if (y.isContiguous()) {
    typename T2::size_type y_length = y.length();
    typename T2::pointer y_data = y.data();
    for (typename T2::size_type i = 0; i < y_length; ++i) {
      if (static_cast< bool >(::std::real(y_data[i])) == true) {
        ++sz[0];
      }
    }
  } else {
    for (typename T2::reference_iterator begin = y.reference_begin(),
             end = y.reference_end(); begin != end; ++begin) {
      if (static_cast< bool >(::std::real(*begin)) == true) {
        ++sz[0];
      }
    }
  }

  // Create the new tensor and do the copy
  T1 t(sz);
  if (x.isContiguous() && y.isContiguous()) {
    typename T2::size_type y_length = y.length();
    typename T2::pointer y_data = y.data();
    typename T1::difference_type x_step = x.stride(y_dimension - 1);
    typename T1::size_type x_step_size = ::std::abs(x_step);
    typename T1::difference_type x_step_increment = x_step > 0 ? 1 : -1;
    typename T1::pointer t_data = t.data();
    typename T1::pointer x_data = x.data();
    typename T1::size_type current = 0;
    for (typename T2::size_type i = 0; i < y_length; ++i) {
      if (static_cast< bool >(::std::real(y_data[i])) == true) {
        for (typename T1::size_type j = 0; j < x_step_size; ++j) {
          t_data[current++] = x_data[i * x_step + j * x_step_increment];
        }
      }
    }
  } else {
    typename T1::size_storage y_size(y.dimension());
    for (typename T1::dim_type i = 0; i < y_size.size(); ++i) {
      y_size[i] = y.size(i);
    }
    typename T1::size_type pos = 0;
    IndexIterator< typename T1::size_storage > t_begin =
        IndexIterator< typename T1::size_storage>::begin(y_size);
    for (IndexIterator< typename T2::size_storage > begin =
             IndexIterator< typename T2::size_storage >::begin(y.size()),
             end = IndexIterator< typename T2::size_storage >::end(y.size());
         begin != end; ++begin, ++t_begin) {
      if (static_cast< bool >(::std::real(y[*begin]())) == true) {
        t[pos++].copy(x[*t_begin]);
      }
    }
  }
  return t;
}

template < typename D, typename A, typename T1 >
T1 shuffle(const T1 &x, const Tensor< Storage< ::std::complex< D >, A > > &y) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T2;
  if (static_cast< typename T1::dim_type >(
          y.size(y.dimension() - 1)) != x.dimension()) {
    throw out_of_range("Shuffle dimension mismatches.");
  }
  if (y.dimension() == 1) {
    T1 t;
    typename T1::size_storage ind(x.dimension());
    for (typename T2::dim_type i = 0; i < y.size(0); ++i) {
      ind[i] = static_cast< typename T1::size_type >(::std::real(y(i)));
      if (ind[i] >= x.size(i)) {
        throw out_of_range("Shuffle index exceeds limit.");
      }
    }
    t() = x(ind);
    return t;
  }
  typename T1::size_storage sz(y.dimension() - 1);
  for (typename T1::dim_type i = 0; i < sz.size(); ++i) {
    sz[i] = y.size(i);
  }
  T1 t(sz);
  typename T1::size_storage ind(x.dimension());
  for (IndexIterator< typename T1::size_storage > begin =
           IndexIterator< typename T1::size_storage >::begin(sz),
           end = IndexIterator< typename T1::size_storage >::end(sz);
       begin != end; ++begin) {
    for (typename T1::dim_type i = 0; i < ind.size(); ++i) {
      ind[i] = static_cast< typename T1::size_type >(::std::real(y[*begin](i)));
      if (ind[i] >= x.size(i)) {
        throw out_of_range("Shuffle index exceeds limit.");
      }
    }
    t(*begin) = x(ind);
  }
  return t;
}

template < typename D, typename A, typename T1 >
T1 permute(const T1 &x, const Tensor< Storage< ::std::complex< D >, A > > &y,
           typename T1::dim_type d) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T2;
  if (y.dimension() != 1) {
    throw invalid_argument("Permutation index dimension exceeds 1.");
  }
  if (d >= x.dimension()) {
    throw out_of_range("Dimension exceeds limit.");
  }

  typename T1::size_storage sz(x.dimension());
  for (typename T1::dim_type i = 0; i < d; ++i) {
    sz[i] = x.size(i);
  }
  sz[d] = y.size(0);
  for (typename T1::dim_type i = d + 1; i < x.dimension(); ++i) {
    sz[i] = x.size(i);
  }

  T1 t(sz);
  if (x.partialContiguity(0, d) &&
      x.partialContiguity(d + 1, x.dimension() - 1)) {
    typename T1::pointer x_pointer = x.data();
    typename T1::size_type x_left_length = 1;
    for (typename T1::dim_type i = 0; i < d; ++i) {
      x_left_length *= x.size(i);
    }
    typename T1::difference_type x_left_step = d > 0 ? x.stride(d - 1) : 0;
    typename T1::size_type x_right_length = 1;
    for (typename T1::dim_type i = d + 1; i < x.dimension(); ++i) {
      x_right_length *= x.size(i);
    }
    typename T1::difference_type x_right_step = x.stride(x.dimension() - 1);
    typename T1::size_type x_length = x.size(d);
    typename T1::difference_type x_step = x.stride(d);
    typename T1::pointer t_pointer = t.data();
    typename T1::difference_type t_left_step = d > 0 ? t.stride(d - 1) : 0;
    typename T1::difference_type t_right_step = t.stride(t.dimension() - 1);
    typename T1::difference_type t_length = t.size(d);
    typename T1::difference_type t_step = t.stride(d);
    typename T2::pointer y_pointer = y.data();
    typename T2::difference_type y_step = y.stride(y.dimension() - 1);
    for (typename T1::size_type i = 0; i < t_length; ++i) {
      typename T1::size_type x_index = static_cast< typename T1::size_type >(
          ::std::real(y_pointer[i * y_step]));
      if (x_index >= x_length) {
        throw out_of_range("Index exceeds limit.");
      }
      for (typename T1::size_type j = 0; j < x_left_length; ++j) {
        for (typename T1::size_type k = 0; k < x_right_length; ++k) {
          t_pointer[j * t_left_step + i * t_step + k * t_right_step] =
              x_pointer[j * x_left_step + x_index * x_step + k * x_right_step];
        }
      }
    }
  } else {
    typename T1::size_storage iter_size = sz;
    iter_size[d] = 1;
    typename T1::size_storage x_ind(sz.size());
    typename T1::size_storage t_ind(sz.size());
    for (typename T2::size_type i = 0; i < y.length(); ++i) {
      typename T1::size_type x_index =
          static_cast< typename T1::size_type >(::std::real(y(i)));
      if (x_index >= x.size(d)) {
        throw out_of_range("Index exceeds limit.");
      }
      for (IndexIterator< typename T1::size_storage > begin =
               IndexIterator< typename T1::size_storage >::begin(iter_size),
               end = IndexIterator< typename T1::size_storage >::end(iter_size);
           begin != end; ++begin) {
        x_ind = *begin;
        t_ind = *begin;
        x_ind[d] = x_index;
        t_ind[d] = i;
        t(t_ind) = x(x_ind);
      }
    }
  }

  return t;
}

template < typename D, typename A >
typename Tensor< Storage< ::std::complex< D >, A > >::real_tensor viewReal(
    const Tensor< Storage< ::std::complex< D >, A > > &x) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T;
  typedef typename T::real_tensor R;
  return R();
}

template < typename D, typename A >
typename Tensor< Storage< ::std::complex< D >, A > >::real_tensor viewImag(
    const Tensor< Storage< ::std::complex< D >, A > > &x) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T;
  typedef typename T::real_tensor R;
  return R();
}

}  // namespace math
}  // namespace tensor
}  // namespace thunder

#endif  // THUNDER_TENSOR_COMPLEX_INL_TRANSFORM_HPP_
<commit_msg>Implemented viewing of real and imaginery parts of a complex tensor<commit_after>/*
 * \copyright Copyright 2014 Xiang Zhang All Rights Reserved.
 * \license @{
 *
 * 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.
 *
 * @}
 */

#ifndef THUNDER_TENSOR_COMPLEX_INL_TRANSFORM_HPP_
#define THUNDER_TENSOR_COMPLEX_INL_TRANSFORM_HPP_

#include "thunder/tensor/complex.hpp"
#include "thunder/tensor/complex-inl.hpp"

#include <complex>
#include <memory>

#include "thunder/exception.hpp"
#include "thunder/tensor/index_iterator.hpp"
#include "thunder/tensor/math.hpp"
#include "thunder/tensor/tensor.hpp"

namespace thunder {
namespace tensor {
namespace math {

// Transformations
template < typename D, typename A, typename T1 >
T1 extract(const T1 &x, const Tensor< Storage< ::std::complex< D >, A > > &y) {
  typedef Tensor< Storage < ::std::complex< D >, A > > T2;
  if (x.dimension() < y.dimension()) {
    throw out_of_range("Dimension exceeds limit.");
  }
  typename T1::dim_type y_dimension =
      static_cast< typename T1::dim_type >(y.dimension());
  for (typename T1::dim_type i = 0; i < y_dimension; ++i) {
    if (x.size(i) != static_cast< typename T1::size_type >(y.size(i))) {
      throw out_of_range("Size does not match.");
    }
  }
  typename T1::size_storage sz(x.dimension() - y_dimension + 1);
  for (typename T1::dim_type i = y_dimension; i < x.dimension(); ++i) {
    sz[i - y_dimension + 1] = x.size(i);
  }
  // Get the size of returning tensor
  sz[0] = 0;
  if (y.isContiguous()) {
    typename T2::size_type y_length = y.length();
    typename T2::pointer y_data = y.data();
    for (typename T2::size_type i = 0; i < y_length; ++i) {
      if (static_cast< bool >(::std::real(y_data[i])) == true) {
        ++sz[0];
      }
    }
  } else {
    for (typename T2::reference_iterator begin = y.reference_begin(),
             end = y.reference_end(); begin != end; ++begin) {
      if (static_cast< bool >(::std::real(*begin)) == true) {
        ++sz[0];
      }
    }
  }

  // Create the new tensor and do the copy
  T1 t(sz);
  if (x.isContiguous() && y.isContiguous()) {
    typename T2::size_type y_length = y.length();
    typename T2::pointer y_data = y.data();
    typename T1::difference_type x_step = x.stride(y_dimension - 1);
    typename T1::size_type x_step_size = ::std::abs(x_step);
    typename T1::difference_type x_step_increment = x_step > 0 ? 1 : -1;
    typename T1::pointer t_data = t.data();
    typename T1::pointer x_data = x.data();
    typename T1::size_type current = 0;
    for (typename T2::size_type i = 0; i < y_length; ++i) {
      if (static_cast< bool >(::std::real(y_data[i])) == true) {
        for (typename T1::size_type j = 0; j < x_step_size; ++j) {
          t_data[current++] = x_data[i * x_step + j * x_step_increment];
        }
      }
    }
  } else {
    typename T1::size_storage y_size(y.dimension());
    for (typename T1::dim_type i = 0; i < y_size.size(); ++i) {
      y_size[i] = y.size(i);
    }
    typename T1::size_type pos = 0;
    IndexIterator< typename T1::size_storage > t_begin =
        IndexIterator< typename T1::size_storage>::begin(y_size);
    for (IndexIterator< typename T2::size_storage > begin =
             IndexIterator< typename T2::size_storage >::begin(y.size()),
             end = IndexIterator< typename T2::size_storage >::end(y.size());
         begin != end; ++begin, ++t_begin) {
      if (static_cast< bool >(::std::real(y[*begin]())) == true) {
        t[pos++].copy(x[*t_begin]);
      }
    }
  }
  return t;
}

template < typename D, typename A, typename T1 >
T1 shuffle(const T1 &x, const Tensor< Storage< ::std::complex< D >, A > > &y) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T2;
  if (static_cast< typename T1::dim_type >(
          y.size(y.dimension() - 1)) != x.dimension()) {
    throw out_of_range("Shuffle dimension mismatches.");
  }
  if (y.dimension() == 1) {
    T1 t;
    typename T1::size_storage ind(x.dimension());
    for (typename T2::dim_type i = 0; i < y.size(0); ++i) {
      ind[i] = static_cast< typename T1::size_type >(::std::real(y(i)));
      if (ind[i] >= x.size(i)) {
        throw out_of_range("Shuffle index exceeds limit.");
      }
    }
    t() = x(ind);
    return t;
  }
  typename T1::size_storage sz(y.dimension() - 1);
  for (typename T1::dim_type i = 0; i < sz.size(); ++i) {
    sz[i] = y.size(i);
  }
  T1 t(sz);
  typename T1::size_storage ind(x.dimension());
  for (IndexIterator< typename T1::size_storage > begin =
           IndexIterator< typename T1::size_storage >::begin(sz),
           end = IndexIterator< typename T1::size_storage >::end(sz);
       begin != end; ++begin) {
    for (typename T1::dim_type i = 0; i < ind.size(); ++i) {
      ind[i] = static_cast< typename T1::size_type >(::std::real(y[*begin](i)));
      if (ind[i] >= x.size(i)) {
        throw out_of_range("Shuffle index exceeds limit.");
      }
    }
    t(*begin) = x(ind);
  }
  return t;
}

template < typename D, typename A, typename T1 >
T1 permute(const T1 &x, const Tensor< Storage< ::std::complex< D >, A > > &y,
           typename T1::dim_type d) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T2;
  if (y.dimension() != 1) {
    throw invalid_argument("Permutation index dimension exceeds 1.");
  }
  if (d >= x.dimension()) {
    throw out_of_range("Dimension exceeds limit.");
  }

  typename T1::size_storage sz(x.dimension());
  for (typename T1::dim_type i = 0; i < d; ++i) {
    sz[i] = x.size(i);
  }
  sz[d] = y.size(0);
  for (typename T1::dim_type i = d + 1; i < x.dimension(); ++i) {
    sz[i] = x.size(i);
  }

  T1 t(sz);
  if (x.partialContiguity(0, d) &&
      x.partialContiguity(d + 1, x.dimension() - 1)) {
    typename T1::pointer x_pointer = x.data();
    typename T1::size_type x_left_length = 1;
    for (typename T1::dim_type i = 0; i < d; ++i) {
      x_left_length *= x.size(i);
    }
    typename T1::difference_type x_left_step = d > 0 ? x.stride(d - 1) : 0;
    typename T1::size_type x_right_length = 1;
    for (typename T1::dim_type i = d + 1; i < x.dimension(); ++i) {
      x_right_length *= x.size(i);
    }
    typename T1::difference_type x_right_step = x.stride(x.dimension() - 1);
    typename T1::size_type x_length = x.size(d);
    typename T1::difference_type x_step = x.stride(d);
    typename T1::pointer t_pointer = t.data();
    typename T1::difference_type t_left_step = d > 0 ? t.stride(d - 1) : 0;
    typename T1::difference_type t_right_step = t.stride(t.dimension() - 1);
    typename T1::difference_type t_length = t.size(d);
    typename T1::difference_type t_step = t.stride(d);
    typename T2::pointer y_pointer = y.data();
    typename T2::difference_type y_step = y.stride(y.dimension() - 1);
    for (typename T1::size_type i = 0; i < t_length; ++i) {
      typename T1::size_type x_index = static_cast< typename T1::size_type >(
          ::std::real(y_pointer[i * y_step]));
      if (x_index >= x_length) {
        throw out_of_range("Index exceeds limit.");
      }
      for (typename T1::size_type j = 0; j < x_left_length; ++j) {
        for (typename T1::size_type k = 0; k < x_right_length; ++k) {
          t_pointer[j * t_left_step + i * t_step + k * t_right_step] =
              x_pointer[j * x_left_step + x_index * x_step + k * x_right_step];
        }
      }
    }
  } else {
    typename T1::size_storage iter_size = sz;
    iter_size[d] = 1;
    typename T1::size_storage x_ind(sz.size());
    typename T1::size_storage t_ind(sz.size());
    for (typename T2::size_type i = 0; i < y.length(); ++i) {
      typename T1::size_type x_index =
          static_cast< typename T1::size_type >(::std::real(y(i)));
      if (x_index >= x.size(d)) {
        throw out_of_range("Index exceeds limit.");
      }
      for (IndexIterator< typename T1::size_storage > begin =
               IndexIterator< typename T1::size_storage >::begin(iter_size),
               end = IndexIterator< typename T1::size_storage >::end(iter_size);
           begin != end; ++begin) {
        x_ind = *begin;
        t_ind = *begin;
        x_ind[d] = x_index;
        t_ind[d] = i;
        t(t_ind) = x(x_ind);
      }
    }
  }

  return t;
}

template < typename D, typename A >
typename Tensor< Storage< ::std::complex< D >, A > >::real_tensor viewReal(
    const Tensor< Storage< ::std::complex< D >, A > > &x) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T;
  typedef typename T::real_tensor R;
  typedef typename T::real_storage S;

  // Getting the size
  typename R::size_storage size(x.dimension());
  for (typename R::dim_type i = 0; size.size(); ++i) {
    size[i] = static_cast< typename R::size_type >(x.size(i));
  }
  // Getting the stride
  typename R::stride_storage stride(x.dimension());
  for (typename R::dim_type i = 0; stride.size(); ++i) {
    stride[i] = 2 * static_cast< typename R::difference_type >(x.stride(i));
  }
  // Getting the storage
  S view = x.storage()->template view< S >();
  typename R::storage_pointer storage =
      ::std::make_shared< S >(view.shared(), view.size(), view.allocator());
  // Getting the offset
  typename R::size_type offset =
      2 * static_cast< typename R::size_type >(x.offset());

  return R(size, stride, storage, offset);
}

template < typename D, typename A >
typename Tensor< Storage< ::std::complex< D >, A > >::real_tensor viewImag(
    const Tensor< Storage< ::std::complex< D >, A > > &x) {
  typedef Tensor< Storage< ::std::complex< D >, A > > T;
  typedef typename T::real_tensor R;
  typedef typename T::real_storage S;

  // Getting the size
  typename R::size_storage size(x.dimension());
  for (typename R::dim_type i = 0; size.size(); ++i) {
    size[i] = static_cast< typename R::size_type >(x.size(i));
  }
  // Getting the stride
  typename R::stride_storage stride(x.dimension());
  for (typename R::dim_type i = 0; stride.size(); ++i) {
    stride[i] = 2 * static_cast< typename R::difference_type >(x.stride(i));
  }
  // Getting the storage
  S view = x.storage()->template view< S >();
  typename R::storage_pointer storage =
      ::std::make_shared< S >(view.shared(), view.size(), view.allocator());
  // Getting the offset
  typename R::size_type offset =
      2 * static_cast< typename R::size_type >(x.offset()) + 1;

  return R(size, stride, storage, offset);
}

}  // namespace math
}  // namespace tensor
}  // namespace thunder

#endif  // THUNDER_TENSOR_COMPLEX_INL_TRANSFORM_HPP_
<|endoftext|>


<commit_before>/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

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 "paddle/fluid/inference/analysis/tensorrt_subgraph_pass.h"

#include <gflags/gflags.h>
#include <gtest/gtest.h>
#include "paddle/fluid/inference/analysis/dfg_graphviz_draw_pass.h"
#include "paddle/fluid/inference/analysis/ut_helper.h"

namespace paddle {
namespace inference {
namespace analysis {

DEFINE_string(dot_dir, "./", "");

TEST(TensorRTSubGraphPass, main) {
  std::unordered_set<std::string> teller_set(
      {"elementwise_add", "mul", "sigmoid"});
  SubGraphSplitter::NodeInsideSubgraphTeller teller = [&](const Node* node) {
    if (node->type() != Node::Type::kFunction) return false;
    const auto* func = static_cast<const Function*>(node);
    if (teller_set.count(func->func_type())) return true;
    return false;
  };

  Argument argument(FLAGS_inference_model_dir);
  int* minimum_subgraph_size = new int(0);
  int* max_batch_size = new int(3);
  int* workspace_size = new int(1 << 20);
  std::string* precision_mode = new std::string("FP32");
  argument.Set<int>("minimun_subgraph_size", minimum_subgraph_size);
  argument.Set<int>("max_batch_size", max_batch_size);
  argument.Set<int>("workspace_size", workspace_size);
  argument.Set<std::string>("precision_mode", precision_mode);

  DFG_GraphvizDrawPass::Config config{FLAGS_dot_dir, "origin"};
  DFG_GraphvizDrawPass::Config config1{FLAGS_dot_dir, "fusion"};

  DFG_GraphvizDrawPass dfg_pass(config);
  DFG_GraphvizDrawPass dfg_pass1(config1);
  FluidToDataFlowGraphPass pass0;
  TensorRTSubGraphPass trt_pass(std::move(teller));

  dfg_pass.Initialize(&argument);
  dfg_pass1.Initialize(&argument);
  pass0.Initialize(&argument);
  trt_pass.Initialize(&argument);

  argument.main_dfg.reset(new DataFlowGraph);
  pass0.Run(argument.main_dfg.get());
  dfg_pass.Run(argument.main_dfg.get());
  trt_pass.Run(argument.main_dfg.get());
  dfg_pass1.Run(argument.main_dfg.get());

  // Check the TRT op's block desc
  for (auto& node : argument.main_dfg->nodes.nodes()) {
    if (node->IsFunctionBlock()) {
      LOG(INFO) << "get function block";
    }
  }
}

}  // namespace analysis
}  // namespace inference
}  // namespace paddle
<commit_msg>fix the ut test error :)<commit_after>/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

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 "paddle/fluid/inference/analysis/tensorrt_subgraph_pass.h"

#include <gflags/gflags.h>
#include <gtest/gtest.h>
#include "paddle/fluid/inference/analysis/dfg_graphviz_draw_pass.h"
#include "paddle/fluid/inference/analysis/ut_helper.h"

namespace paddle {
namespace inference {
namespace analysis {

DEFINE_string(dot_dir, "./", "");

TEST(TensorRTSubGraphPass, main) {
  std::unordered_set<std::string> teller_set(
      {"elementwise_add", "mul", "sigmoid"});
  SubGraphSplitter::NodeInsideSubgraphTeller teller = [&](const Node* node) {
    if (node->type() != Node::Type::kFunction) return false;
    const auto* func = static_cast<const Function*>(node);
    if (teller_set.count(func->func_type())) return true;
    return false;
  };

  Argument argument(FLAGS_inference_model_dir);
  int* minimum_subgraph_size = new int(0);
  int* max_batch_size = new int(3);
  int* workspace_size = new int(1 << 20);
  std::string* precision_mode = new std::string("FP32");
  argument.Set<int>("minimum_subgraph_size", minimum_subgraph_size);
  argument.Set<int>("max_batch_size", max_batch_size);
  argument.Set<int>("workspace_size", workspace_size);
  argument.Set<std::string>("precision_mode", precision_mode);

  DFG_GraphvizDrawPass::Config config{FLAGS_dot_dir, "origin"};
  DFG_GraphvizDrawPass::Config config1{FLAGS_dot_dir, "fusion"};

  DFG_GraphvizDrawPass dfg_pass(config);
  DFG_GraphvizDrawPass dfg_pass1(config1);
  FluidToDataFlowGraphPass pass0;
  TensorRTSubGraphPass trt_pass(std::move(teller));

  dfg_pass.Initialize(&argument);
  dfg_pass1.Initialize(&argument);
  pass0.Initialize(&argument);
  trt_pass.Initialize(&argument);

  argument.main_dfg.reset(new DataFlowGraph);
  pass0.Run(argument.main_dfg.get());
  dfg_pass.Run(argument.main_dfg.get());
  trt_pass.Run(argument.main_dfg.get());
  dfg_pass1.Run(argument.main_dfg.get());

  // Check the TRT op's block desc
  for (auto& node : argument.main_dfg->nodes.nodes()) {
    if (node->IsFunctionBlock()) {
      LOG(INFO) << "get function block";
    }
  }
}

}  // namespace analysis
}  // namespace inference
}  // namespace paddle
<|endoftext|>


<commit_before>#include "TensorData.h"

#include <iostream>
#include <sstream>

#include <SmurffCpp/ConstVMatrixExprIterator.hpp>

using namespace Eigen;
using namespace smurff;

//convert array of coordinates to [nnz x nmodes] matrix
MatrixXui32 toMatrixNew(const std::vector<std::uint32_t>& columns, std::uint64_t nnz, std::uint64_t nmodes)
{
   MatrixXui32 idx(nnz, nmodes);
   for (std::uint64_t row = 0; row < nnz; row++)
   {
      for (std::uint64_t col = 0; col < nmodes; col++)
      {
         idx(row, col) = columns[col * nnz + row];
      }
   }
   return idx;
}

TensorData::TensorData(const smurff::TensorConfig& tc)
   : m_dims(tc.getDims()),
     m_nnz(tc.getNNZ()),
     m_Y(std::make_shared<std::vector<std::shared_ptr<SparseMode> > >())
{
   //combine coordinates into [nnz x nmodes] matrix
   MatrixXui32 idx = toMatrixNew(tc.getColumns(), tc.getNNZ(), tc.getNModes());

   for (std::uint64_t mode = 0; mode < tc.getNModes(); mode++)
   {
      m_Y->push_back(std::make_shared<SparseMode>(idx, tc.getValues(), mode, m_dims[mode]));
   }
}

std::shared_ptr<SparseMode> TensorData::Y(std::uint64_t mode) const
{
   return m_Y->operator[](mode);
}

void TensorData::init_pre()
{
   throw std::runtime_error("not implemented");
}

double TensorData::sum() const
{
   throw std::runtime_error("not implemented");
}

std::uint64_t TensorData::nmode() const
{
   return m_dims.size();
}

std::uint64_t TensorData::nnz() const
{
   return m_nnz;
}

std::uint64_t TensorData::nna() const
{
   return 0;
}

PVec<> TensorData::dim() const
{
   std::vector<int> pvec_dims;
   for(auto& d : m_dims)
      pvec_dims.push_back(static_cast<int>(d));
   return PVec<>(pvec_dims);
}

double TensorData::train_rmse(const SubModel& model) const
{
   //there was no implementation of train_rmse for tensors in macau
   throw std::runtime_error("not implemented");
}

//d is an index of column in U matrix
//this function selects d'th hyperplane from mode`th SparseMode
//it does j multiplications
//where each multiplication is a cwiseProduct of columns from each V matrix
void TensorData::get_pnm(const SubModel& model, uint32_t mode, int d, Eigen::VectorXd& rr, Eigen::MatrixXd& MM)
{
   const double alpha = this->noise()->getAlpha();
   std::shared_ptr<SparseMode> sview = Y(mode); //get tensor rotation for mode

   auto V0 = model.CVbegin(mode); //get first V matrix
   for (std::uint64_t j = sview->beginPlane(d); j < sview->endPlane(d); j++) //go through hyperplane in tensor rotation
   {
      VectorXd col = (*V0).col(sview->getIndices()(j, 0)); //create a copy of m'th column from V (m = 0)
      auto V = model.CVbegin(mode); //get V matrices for mode
      for (std::uint64_t m = 1; m < sview->getNCoords(); m++) //go through each coordinate of value
      {
         ++V; //inc iterator prior to access since we are starting from m = 1
         col.noalias() = col.cwiseProduct((*V).col(sview->getIndices()(j, m))); //multiply by m'th column from V
      }

      MM.triangularView<Eigen::Lower>() += alpha * col * col.transpose(); // MM = MM + (col * colT) * alpha (where col = product of columns in each V)
      rr.noalias() += col * sview->getValues()[j] * alpha; // rr = rr + (col * value) * alpha (where value = j'th value of Y)
   }
}

void TensorData::update_pnm(const SubModel& model, uint32_t mode)
{
   //do not need to cache VV here
}

double TensorData::sumsq(const SubModel& model) const
{
   double sumsq = 0.0;

   std::shared_ptr<SparseMode> sview = Y(0);

   std::vector<int> coords(this->nmode());

   #pragma omp parallel for schedule(dynamic, 4) reduction(+:sumsq)
   for(std::uint64_t n = 0; n < sview->getNPlanes(); n++) //go through each hyperplane
   {
      coords[0] = n;

      for(std::uint64_t j = sview->beginPlane(n); j < sview->endPlane(n); j++) //go through each item in the plane
      {
         for(std::uint64_t m = 0; m < sview->getNCoords(); m++) //go through each coordinate of the item
            coords[m + 1] = static_cast<int>(sview->getIndices()(j, m));

         double pred = model.predict(smurff::PVec<>(coords));
         sumsq += square(pred - sview->getValues()[j]);
      }
   }

   return sumsq;
}

double TensorData::var_total() const
{
   double se = 0.0;
   double mean_value = 0; // what should be mean value?

   std::shared_ptr<SparseMode> sview = Y(0);

   #pragma omp parallel for schedule(dynamic, 4) reduction(+:se)
   for(std::uint64_t n = 0; n < sview->getNPlanes(); n++) //go through each hyperplane
   {
      for(std::uint64_t j = sview->beginPlane(n); j < sview->endPlane(n); j++) //go through each item in the plane
      {
         se += square(sview->getValues()[j] - mean_value);
      }
   }

   double var = se / sview->getValues().size();
   if (var <= 0.0 || std::isnan(var))
   {
      // if var cannot be computed using 1.0
      var = 1.0;
   }

   return var;
}

std::ostream& TensorData::info(std::ostream& os, std::string indent)
{
   throw std::runtime_error("not implemented");
}
<commit_msg>implement sum in TensorData<commit_after>#include "TensorData.h"

#include <iostream>
#include <sstream>

#include <SmurffCpp/ConstVMatrixExprIterator.hpp>

using namespace Eigen;
using namespace smurff;

//convert array of coordinates to [nnz x nmodes] matrix
MatrixXui32 toMatrixNew(const std::vector<std::uint32_t>& columns, std::uint64_t nnz, std::uint64_t nmodes)
{
   MatrixXui32 idx(nnz, nmodes);
   for (std::uint64_t row = 0; row < nnz; row++)
   {
      for (std::uint64_t col = 0; col < nmodes; col++)
      {
         idx(row, col) = columns[col * nnz + row];
      }
   }
   return idx;
}

TensorData::TensorData(const smurff::TensorConfig& tc)
   : m_dims(tc.getDims()),
     m_nnz(tc.getNNZ()),
     m_Y(std::make_shared<std::vector<std::shared_ptr<SparseMode> > >())
{
   //combine coordinates into [nnz x nmodes] matrix
   MatrixXui32 idx = toMatrixNew(tc.getColumns(), tc.getNNZ(), tc.getNModes());

   for (std::uint64_t mode = 0; mode < tc.getNModes(); mode++)
   {
      m_Y->push_back(std::make_shared<SparseMode>(idx, tc.getValues(), mode, m_dims[mode]));
   }
}

std::shared_ptr<SparseMode> TensorData::Y(std::uint64_t mode) const
{
   return m_Y->operator[](mode);
}

void TensorData::init_pre()
{
   throw std::runtime_error("not implemented");
}

double TensorData::sum() const
{
   double esum = 0.0;
   double mean_value = 0; // what should be mean value?

   std::shared_ptr<SparseMode> sview = Y(0);

   #pragma omp parallel for schedule(dynamic, 4) reduction(+:esum)
   for(std::uint64_t n = 0; n < sview->getNPlanes(); n++) //go through each hyperplane
   {
      for(std::uint64_t j = sview->beginPlane(n); j < sview->endPlane(n); j++) //go through each item in the plane
      {
         esum += sview->getValues()[j];
      }
   }

   return esum;
}

std::uint64_t TensorData::nmode() const
{
   return m_dims.size();
}

std::uint64_t TensorData::nnz() const
{
   return m_nnz;
}

std::uint64_t TensorData::nna() const
{
   return 0;
}

PVec<> TensorData::dim() const
{
   std::vector<int> pvec_dims;
   for(auto& d : m_dims)
      pvec_dims.push_back(static_cast<int>(d));
   return PVec<>(pvec_dims);
}

double TensorData::train_rmse(const SubModel& model) const
{
   //there was no implementation of train_rmse for tensors in macau
   throw std::runtime_error("not implemented");
}

//d is an index of column in U matrix
//this function selects d'th hyperplane from mode`th SparseMode
//it does j multiplications
//where each multiplication is a cwiseProduct of columns from each V matrix
void TensorData::get_pnm(const SubModel& model, uint32_t mode, int d, Eigen::VectorXd& rr, Eigen::MatrixXd& MM)
{
   const double alpha = this->noise()->getAlpha();
   std::shared_ptr<SparseMode> sview = Y(mode); //get tensor rotation for mode

   auto V0 = model.CVbegin(mode); //get first V matrix
   for (std::uint64_t j = sview->beginPlane(d); j < sview->endPlane(d); j++) //go through hyperplane in tensor rotation
   {
      VectorXd col = (*V0).col(sview->getIndices()(j, 0)); //create a copy of m'th column from V (m = 0)
      auto V = model.CVbegin(mode); //get V matrices for mode
      for (std::uint64_t m = 1; m < sview->getNCoords(); m++) //go through each coordinate of value
      {
         ++V; //inc iterator prior to access since we are starting from m = 1
         col.noalias() = col.cwiseProduct((*V).col(sview->getIndices()(j, m))); //multiply by m'th column from V
      }

      MM.triangularView<Eigen::Lower>() += alpha * col * col.transpose(); // MM = MM + (col * colT) * alpha (where col = product of columns in each V)
      rr.noalias() += col * sview->getValues()[j] * alpha; // rr = rr + (col * value) * alpha (where value = j'th value of Y)
   }
}

void TensorData::update_pnm(const SubModel& model, uint32_t mode)
{
   //do not need to cache VV here
}

double TensorData::sumsq(const SubModel& model) const
{
   double sumsq = 0.0;

   std::shared_ptr<SparseMode> sview = Y(0);

   std::vector<int> coords(this->nmode());

   #pragma omp parallel for schedule(dynamic, 4) reduction(+:sumsq)
   for(std::uint64_t n = 0; n < sview->getNPlanes(); n++) //go through each hyperplane
   {
      coords[0] = n;

      for(std::uint64_t j = sview->beginPlane(n); j < sview->endPlane(n); j++) //go through each item in the plane
      {
         for(std::uint64_t m = 0; m < sview->getNCoords(); m++) //go through each coordinate of the item
            coords[m + 1] = static_cast<int>(sview->getIndices()(j, m));

         double pred = model.predict(smurff::PVec<>(coords));
         sumsq += square(pred - sview->getValues()[j]);
      }
   }

   return sumsq;
}

double TensorData::var_total() const
{
   double se = 0.0;
   double mean_value = 0; // what should be mean value?

   std::shared_ptr<SparseMode> sview = Y(0);

   #pragma omp parallel for schedule(dynamic, 4) reduction(+:se)
   for(std::uint64_t n = 0; n < sview->getNPlanes(); n++) //go through each hyperplane
   {
      for(std::uint64_t j = sview->beginPlane(n); j < sview->endPlane(n); j++) //go through each item in the plane
      {
         se += square(sview->getValues()[j] - mean_value);
      }
   }

   double var = se / sview->getValues().size();
   if (var <= 0.0 || std::isnan(var))
   {
      // if var cannot be computed using 1.0
      var = 1.0;
   }

   return var;
}

std::ostream& TensorData::info(std::ostream& os, std::string indent)
{
   throw std::runtime_error("not implemented");
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Any modified source or binaries are used only with the BitShares network.
 *
 * 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#pragma once

#define HARDFORK_357_TIME (fc::time_point_sec( 1444416300 ))
#define HARDFORK_359_TIME (fc::time_point_sec( 1444416300 ))
#define HARDFORK_409_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_413_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_415_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_416_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_419_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_436_TIME (fc::time_point_sec( 2000000000 ))

// #445 Refund create order fees on cancel
// 2015-12-02 17:00:00 UTC / 12:00:00 EST
#define HARDFORK_445_TIME (fc::time_point_sec( 1449075600 ))

// #453 Hardfork to retroactively correct referral percentages
// 2015-12-02 17:00:00 UTC / 12:00:00 EST
#define HARDFORK_453_TIME (fc::time_point_sec( 1449075600 ))
<commit_msg>hardfork.hpp:  Set hardfork timestamps<commit_after>/*
 * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Any modified source or binaries are used only with the BitShares network.
 *
 * 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#pragma once

#define HARDFORK_357_TIME (fc::time_point_sec( 1444416300 ))
#define HARDFORK_359_TIME (fc::time_point_sec( 1444416300 ))
#define HARDFORK_409_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_413_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_415_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_416_TIME (fc::time_point_sec( 1446652800 ))
#define HARDFORK_419_TIME (fc::time_point_sec( 1446652800 ))

// #436 Prevent margin call from being triggered unless feed < call price
#define HARDFORK_436_TIME (fc::time_point_sec( 1450288800 ))

// #445 Refund create order fees on cancel
#define HARDFORK_445_TIME (fc::time_point_sec( 1450288800 ))

// #453 Hardfork to retroactively correct referral percentages
#define HARDFORK_453_TIME (fc::time_point_sec( 1450288800 ))
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * 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/.
 */

#include <assert.h>
#include <stdio.h>
#include <string.h>

#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>

#include <LibreOfficeKit/LibreOfficeKitGtk.h>
#include <LibreOfficeKit/LibreOfficeKitInit.h>
#include "../lokdocview_quad/lokdocview_quad.h"

#include <com/sun/star/awt/Key.hpp>

#ifndef g_info
#define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
#endif

static int help()
{
    fprintf( stderr, "Usage: gtktiledviewer <absolute-path-to-libreoffice-install> <path-to-document>\n" );
    return 1;
}

static GtkWidget* pDocView;
static GtkWidget* pDocViewQuad;
static GtkWidget* pVBox;
// GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
static GtkComboBoxText* pPartSelector;
#endif

static LibreOfficeKit* pOffice;
static char* pFileName;

const float fZooms[] = { 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 5.0 };

void changeZoom( GtkWidget* pButton, gpointer /* pItem */ )
{
    const char *sName = gtk_tool_button_get_stock_id( GTK_TOOL_BUTTON(pButton) );

    float fZoom = 0;
    float fCurrentZoom = 0;

    if ( pDocView )
    {
        fCurrentZoom = lok_docview_get_zoom( LOK_DOCVIEW(pDocView) );
    }
    else if ( pDocViewQuad )
    {
        fCurrentZoom = lok_docview_quad_get_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad) );
    }

    if ( strcmp(sName, "gtk-zoom-in") == 0)
    {
        for ( unsigned int i = 0; i < sizeof( fZooms ) / sizeof( fZooms[0] ); i++ )
        {
            if ( fCurrentZoom < fZooms[i] )
            {
                fZoom = fZooms[i];
                break;
            }
        }
    }
    else if ( strcmp(sName, "gtk-zoom-100") == 0)
    {
        fZoom = 1;
    }
    else if ( strcmp(sName, "gtk-zoom-fit") == 0)
    {
        // TODO -- will need access to lokdocview internals?
    }
    else if ( strcmp(sName, "gtk-zoom-out") == 0)
    {
        for ( unsigned int i = 0; i < sizeof( fZooms ) / sizeof( fZooms[0] ); i++ )
        {
            if ( fCurrentZoom > fZooms[i] )
            {
                fZoom = fZooms[i];
            }
        }
    }

    if ( fZoom != 0 )
    {
        if ( pDocView )
        {
            lok_docview_set_zoom( LOK_DOCVIEW(pDocView), fZoom );
        }
        else if ( pDocViewQuad )
        {
            lok_docview_quad_set_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad), fZoom );
        }
    }
}

void changeQuadView( GtkWidget* /*pButton*/, gpointer /* pItem */ )
{
    if ( pDocView )
    {
        const float fCurrentZoom = lok_docview_get_zoom( LOK_DOCVIEW(pDocView) );
        gtk_widget_destroy( pDocView );
        pDocView = 0;
        pDocViewQuad = lok_docview_quad_new( pOffice );
        gtk_container_add( GTK_CONTAINER(pVBox), pDocViewQuad );
        gtk_widget_show( pDocViewQuad );

        lok_docview_quad_set_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad), fCurrentZoom );
        lok_docview_quad_open_document( LOK_DOCVIEW_QUAD(pDocViewQuad), pFileName );
    }
    else if ( pDocViewQuad )
    {
        const float fCurrentZoom = lok_docview_quad_get_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad) );
        gtk_widget_destroy( pDocViewQuad );
        pDocViewQuad = 0;
        pDocView = lok_docview_new( pOffice );
        gtk_container_add( GTK_CONTAINER(pVBox), pDocView );
        gtk_widget_show( pDocView );

        lok_docview_set_zoom( LOK_DOCVIEW(pDocView), fCurrentZoom );
        lok_docview_open_document( LOK_DOCVIEW(pDocView), pFileName );
    }
}

/// Receives a key press or release event.
static void signalKey(GtkWidget* /*pWidget*/, GdkEventKey* pEvent, gpointer /*pData*/)
{
    LOKDocView* pLOKDocView = LOK_DOCVIEW(pDocView);
    int nCode = 0;

    if (!pLOKDocView->m_bEdit)
    {
        g_info("signalKey: not in edit mode, ignore");
        return;
    }

    switch (pEvent->keyval)
    {
    case GDK_BackSpace:
        nCode = com::sun::star::awt::Key::BACKSPACE;
        break;
    case GDK_Return:
        nCode = com::sun::star::awt::Key::RETURN;
        break;
    default:
        if (pEvent->keyval >= GDK_F1 && pEvent->keyval <= GDK_F26)
            nCode = com::sun::star::awt::Key::F1 + (pEvent->keyval - GDK_F1);
        else
            nCode = gdk_keyval_to_unicode(pEvent->keyval);
    }

    if (pEvent->type == GDK_KEY_RELEASE)
        pLOKDocView->pOffice->pClass->postKeyEvent(pLOKDocView->pOffice, LOK_KEYEVENT_KEYUP, nCode);
    else
        pLOKDocView->pOffice->pClass->postKeyEvent(pLOKDocView->pOffice, LOK_KEYEVENT_KEYINPUT, nCode);
}

// GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
void populatePartSelector()
{
    gtk_list_store_clear( GTK_LIST_STORE(
                              gtk_combo_box_get_model(
                                  GTK_COMBO_BOX(pPartSelector) )) );

    if ( !pDocView )
    {
        return;
    }

    const int nMaxLength = 50;
    char sText[nMaxLength];

    int nParts = lok_docview_get_parts( LOK_DOCVIEW(pDocView) );
    for ( int i = 0; i < nParts; i++ )
    {
        char* pName = lok_docview_get_part_name( LOK_DOCVIEW(pDocView), i );
        assert( pName );
        snprintf( sText, nMaxLength, "%i (%s)", i+1, pName );
        free( pName );

        gtk_combo_box_text_append_text( pPartSelector, sText );
    }
    gtk_combo_box_set_active( GTK_COMBO_BOX(pPartSelector),
                              lok_docview_get_part( LOK_DOCVIEW(pDocView) ) );
}

void changePart( GtkWidget* pSelector, gpointer /* pItem */ )
{
    int nPart = gtk_combo_box_get_active( GTK_COMBO_BOX(pSelector) );

    // We don't really care about the quad view for now -- it's only purpose
    // is to check that the edges of tiles aren't messed up, and no real
    // reason to maintain it to be able to show other document parts etc.
    if ( pDocView )
    {
        lok_docview_set_part( LOK_DOCVIEW(pDocView), nPart );
    }
}

void populatePartModeSelector( GtkComboBoxText* pSelector )
{
    gtk_combo_box_text_append_text( pSelector, "Default" );
    gtk_combo_box_text_append_text( pSelector, "Slide" );
    gtk_combo_box_text_append_text( pSelector, "Notes" );
    gtk_combo_box_text_append_text( pSelector, "Combined (SlideNotes)" );
    gtk_combo_box_text_append_text( pSelector, "Embedded Objects" );
    gtk_combo_box_set_active( GTK_COMBO_BOX(pSelector), 0 );
}

void changePartMode( GtkWidget* pSelector, gpointer /* pItem */ )
{
    // Just convert directly back to the LibreOfficeKitPartMode enum.
    // I.e. the ordering above should match the enum member ordering.
    LibreOfficeKitPartMode ePartMode =
        LibreOfficeKitPartMode( gtk_combo_box_get_active( GTK_COMBO_BOX(pSelector) ) );

    if ( pDocView )
    {
        lok_docview_set_partmode( LOK_DOCVIEW(pDocView), ePartMode );
    }

    // The number of items could change e.g. if we change from slide
    // to embeddede obj mode -- hence we should update the part list.
    populatePartSelector();
}
#endif

int main( int argc, char* argv[] )
{
    if( argc < 3 ||
        ( argc > 1 && ( !strcmp( argv[1], "--help" ) || !strcmp( argv[1], "-h" ) ) ) )
        return help();

    if ( argv[1][0] != '/' )
    {
        fprintf(stderr, "Absolute path required to libreoffice install\n");
        return 1;
    }

    pOffice = lok_init( argv[1] );
    if ( pOffice == NULL )
        return 1;

    gtk_init( &argc, &argv );

    GtkWidget *pWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_title( GTK_WINDOW(pWindow), "LibreOfficeKit GTK Tiled Viewer" );
    gtk_window_set_default_size(GTK_WINDOW(pWindow), 800, 600);
    g_signal_connect( pWindow, "destroy", G_CALLBACK(gtk_main_quit), NULL );

    pVBox = gtk_vbox_new( FALSE, 0 );
    gtk_container_add( GTK_CONTAINER(pWindow), pVBox );

    // Toolbar
    GtkWidget* pToolbar = gtk_toolbar_new();
    gtk_toolbar_set_style( GTK_TOOLBAR(pToolbar), GTK_TOOLBAR_ICONS );

    GtkToolItem* pZoomIn = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_IN );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoomIn, 0);
    g_signal_connect( G_OBJECT(pZoomIn), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pZoom1 = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_100 );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoom1, -1);
    g_signal_connect( G_OBJECT(pZoom1), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pZoomFit = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_FIT );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoomFit, -1);
    g_signal_connect( G_OBJECT(pZoomFit), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pZoomOut = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_OUT );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoomOut, -1);
    g_signal_connect( G_OBJECT(pZoomOut), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pSeparator1 = gtk_separator_tool_item_new();
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pSeparator1, -1);

// GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
    GtkToolItem* pPartSelectorToolItem = gtk_tool_item_new();
    GtkWidget* pComboBox = gtk_combo_box_text_new();
    gtk_container_add( GTK_CONTAINER(pPartSelectorToolItem), pComboBox );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pPartSelectorToolItem, -1 );
    g_signal_connect( G_OBJECT(pComboBox), "changed", G_CALLBACK(changePart), NULL );

    pPartSelector = GTK_COMBO_BOX_TEXT(pComboBox);

    GtkToolItem* pSeparator2 = gtk_separator_tool_item_new();
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pSeparator2, -1);

    GtkToolItem* pPartModeSelectorToolItem = gtk_tool_item_new();
    GtkWidget* pPartModeComboBox = gtk_combo_box_text_new();
    gtk_container_add( GTK_CONTAINER(pPartModeSelectorToolItem), pPartModeComboBox );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pPartModeSelectorToolItem, -1 );
    g_signal_connect( G_OBJECT(pPartModeComboBox), "changed", G_CALLBACK(changePartMode), NULL );
#endif

    GtkToolItem* pSeparator3 = gtk_separator_tool_item_new();
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pSeparator3, -1);

    GtkToolItem* pEnableQuadView = gtk_toggle_tool_button_new();
    gtk_tool_button_set_label( GTK_TOOL_BUTTON(pEnableQuadView), "Use Quad View" );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pEnableQuadView, -1 );
    g_signal_connect( G_OBJECT(pEnableQuadView), "toggled", G_CALLBACK(changeQuadView), NULL );

    gtk_box_pack_start( GTK_BOX(pVBox), pToolbar, FALSE, FALSE, 0 ); // Adds to top.

    // Docview
    pDocView = lok_docview_new( pOffice );
    pDocViewQuad = 0;

    // Input handling.
    g_signal_connect(pWindow, "key-press-event", G_CALLBACK(signalKey), NULL);
    g_signal_connect(pWindow, "key-release-event", G_CALLBACK(signalKey), NULL);

    gtk_container_add( GTK_CONTAINER(pVBox), pDocView );

    gtk_widget_show_all( pWindow );

    pFileName = argv[2];
    int bOpened = lok_docview_open_document( LOK_DOCVIEW(pDocView), argv[2] );
    if (!bOpened)
        g_error("main: lok_docview_open_document() failed with '%s'", pOffice->pClass->getError(pOffice));
    assert( LOK_DOCVIEW(pDocView)->pDocument );

    // GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
    populatePartSelector();
    populatePartModeSelector( GTK_COMBO_BOX_TEXT(pPartModeComboBox) );
#endif

    gtk_main();

    pOffice->pClass->destroy( pOffice );

    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>gtktiledviewer: map css::awt::Key::ESCAPE<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * 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/.
 */

#include <assert.h>
#include <stdio.h>
#include <string.h>

#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>

#include <LibreOfficeKit/LibreOfficeKitGtk.h>
#include <LibreOfficeKit/LibreOfficeKitInit.h>
#include "../lokdocview_quad/lokdocview_quad.h"

#include <com/sun/star/awt/Key.hpp>

#ifndef g_info
#define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
#endif

static int help()
{
    fprintf( stderr, "Usage: gtktiledviewer <absolute-path-to-libreoffice-install> <path-to-document>\n" );
    return 1;
}

static GtkWidget* pDocView;
static GtkWidget* pDocViewQuad;
static GtkWidget* pVBox;
// GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
static GtkComboBoxText* pPartSelector;
#endif

static LibreOfficeKit* pOffice;
static char* pFileName;

const float fZooms[] = { 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 5.0 };

void changeZoom( GtkWidget* pButton, gpointer /* pItem */ )
{
    const char *sName = gtk_tool_button_get_stock_id( GTK_TOOL_BUTTON(pButton) );

    float fZoom = 0;
    float fCurrentZoom = 0;

    if ( pDocView )
    {
        fCurrentZoom = lok_docview_get_zoom( LOK_DOCVIEW(pDocView) );
    }
    else if ( pDocViewQuad )
    {
        fCurrentZoom = lok_docview_quad_get_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad) );
    }

    if ( strcmp(sName, "gtk-zoom-in") == 0)
    {
        for ( unsigned int i = 0; i < sizeof( fZooms ) / sizeof( fZooms[0] ); i++ )
        {
            if ( fCurrentZoom < fZooms[i] )
            {
                fZoom = fZooms[i];
                break;
            }
        }
    }
    else if ( strcmp(sName, "gtk-zoom-100") == 0)
    {
        fZoom = 1;
    }
    else if ( strcmp(sName, "gtk-zoom-fit") == 0)
    {
        // TODO -- will need access to lokdocview internals?
    }
    else if ( strcmp(sName, "gtk-zoom-out") == 0)
    {
        for ( unsigned int i = 0; i < sizeof( fZooms ) / sizeof( fZooms[0] ); i++ )
        {
            if ( fCurrentZoom > fZooms[i] )
            {
                fZoom = fZooms[i];
            }
        }
    }

    if ( fZoom != 0 )
    {
        if ( pDocView )
        {
            lok_docview_set_zoom( LOK_DOCVIEW(pDocView), fZoom );
        }
        else if ( pDocViewQuad )
        {
            lok_docview_quad_set_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad), fZoom );
        }
    }
}

void changeQuadView( GtkWidget* /*pButton*/, gpointer /* pItem */ )
{
    if ( pDocView )
    {
        const float fCurrentZoom = lok_docview_get_zoom( LOK_DOCVIEW(pDocView) );
        gtk_widget_destroy( pDocView );
        pDocView = 0;
        pDocViewQuad = lok_docview_quad_new( pOffice );
        gtk_container_add( GTK_CONTAINER(pVBox), pDocViewQuad );
        gtk_widget_show( pDocViewQuad );

        lok_docview_quad_set_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad), fCurrentZoom );
        lok_docview_quad_open_document( LOK_DOCVIEW_QUAD(pDocViewQuad), pFileName );
    }
    else if ( pDocViewQuad )
    {
        const float fCurrentZoom = lok_docview_quad_get_zoom( LOK_DOCVIEW_QUAD(pDocViewQuad) );
        gtk_widget_destroy( pDocViewQuad );
        pDocViewQuad = 0;
        pDocView = lok_docview_new( pOffice );
        gtk_container_add( GTK_CONTAINER(pVBox), pDocView );
        gtk_widget_show( pDocView );

        lok_docview_set_zoom( LOK_DOCVIEW(pDocView), fCurrentZoom );
        lok_docview_open_document( LOK_DOCVIEW(pDocView), pFileName );
    }
}

/// Receives a key press or release event.
static void signalKey(GtkWidget* /*pWidget*/, GdkEventKey* pEvent, gpointer /*pData*/)
{
    LOKDocView* pLOKDocView = LOK_DOCVIEW(pDocView);
    int nCode = 0;

    if (!pLOKDocView->m_bEdit)
    {
        g_info("signalKey: not in edit mode, ignore");
        return;
    }

    switch (pEvent->keyval)
    {
    case GDK_BackSpace:
        nCode = com::sun::star::awt::Key::BACKSPACE;
        break;
    case GDK_Return:
        nCode = com::sun::star::awt::Key::RETURN;
        break;
    case GDK_Escape:
        nCode = com::sun::star::awt::Key::ESCAPE;
        break;
    default:
        if (pEvent->keyval >= GDK_F1 && pEvent->keyval <= GDK_F26)
            nCode = com::sun::star::awt::Key::F1 + (pEvent->keyval - GDK_F1);
        else
            nCode = gdk_keyval_to_unicode(pEvent->keyval);
    }

    if (pEvent->type == GDK_KEY_RELEASE)
        pLOKDocView->pOffice->pClass->postKeyEvent(pLOKDocView->pOffice, LOK_KEYEVENT_KEYUP, nCode);
    else
        pLOKDocView->pOffice->pClass->postKeyEvent(pLOKDocView->pOffice, LOK_KEYEVENT_KEYINPUT, nCode);
}

// GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
void populatePartSelector()
{
    gtk_list_store_clear( GTK_LIST_STORE(
                              gtk_combo_box_get_model(
                                  GTK_COMBO_BOX(pPartSelector) )) );

    if ( !pDocView )
    {
        return;
    }

    const int nMaxLength = 50;
    char sText[nMaxLength];

    int nParts = lok_docview_get_parts( LOK_DOCVIEW(pDocView) );
    for ( int i = 0; i < nParts; i++ )
    {
        char* pName = lok_docview_get_part_name( LOK_DOCVIEW(pDocView), i );
        assert( pName );
        snprintf( sText, nMaxLength, "%i (%s)", i+1, pName );
        free( pName );

        gtk_combo_box_text_append_text( pPartSelector, sText );
    }
    gtk_combo_box_set_active( GTK_COMBO_BOX(pPartSelector),
                              lok_docview_get_part( LOK_DOCVIEW(pDocView) ) );
}

void changePart( GtkWidget* pSelector, gpointer /* pItem */ )
{
    int nPart = gtk_combo_box_get_active( GTK_COMBO_BOX(pSelector) );

    // We don't really care about the quad view for now -- it's only purpose
    // is to check that the edges of tiles aren't messed up, and no real
    // reason to maintain it to be able to show other document parts etc.
    if ( pDocView )
    {
        lok_docview_set_part( LOK_DOCVIEW(pDocView), nPart );
    }
}

void populatePartModeSelector( GtkComboBoxText* pSelector )
{
    gtk_combo_box_text_append_text( pSelector, "Default" );
    gtk_combo_box_text_append_text( pSelector, "Slide" );
    gtk_combo_box_text_append_text( pSelector, "Notes" );
    gtk_combo_box_text_append_text( pSelector, "Combined (SlideNotes)" );
    gtk_combo_box_text_append_text( pSelector, "Embedded Objects" );
    gtk_combo_box_set_active( GTK_COMBO_BOX(pSelector), 0 );
}

void changePartMode( GtkWidget* pSelector, gpointer /* pItem */ )
{
    // Just convert directly back to the LibreOfficeKitPartMode enum.
    // I.e. the ordering above should match the enum member ordering.
    LibreOfficeKitPartMode ePartMode =
        LibreOfficeKitPartMode( gtk_combo_box_get_active( GTK_COMBO_BOX(pSelector) ) );

    if ( pDocView )
    {
        lok_docview_set_partmode( LOK_DOCVIEW(pDocView), ePartMode );
    }

    // The number of items could change e.g. if we change from slide
    // to embeddede obj mode -- hence we should update the part list.
    populatePartSelector();
}
#endif

int main( int argc, char* argv[] )
{
    if( argc < 3 ||
        ( argc > 1 && ( !strcmp( argv[1], "--help" ) || !strcmp( argv[1], "-h" ) ) ) )
        return help();

    if ( argv[1][0] != '/' )
    {
        fprintf(stderr, "Absolute path required to libreoffice install\n");
        return 1;
    }

    pOffice = lok_init( argv[1] );
    if ( pOffice == NULL )
        return 1;

    gtk_init( &argc, &argv );

    GtkWidget *pWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_title( GTK_WINDOW(pWindow), "LibreOfficeKit GTK Tiled Viewer" );
    gtk_window_set_default_size(GTK_WINDOW(pWindow), 800, 600);
    g_signal_connect( pWindow, "destroy", G_CALLBACK(gtk_main_quit), NULL );

    pVBox = gtk_vbox_new( FALSE, 0 );
    gtk_container_add( GTK_CONTAINER(pWindow), pVBox );

    // Toolbar
    GtkWidget* pToolbar = gtk_toolbar_new();
    gtk_toolbar_set_style( GTK_TOOLBAR(pToolbar), GTK_TOOLBAR_ICONS );

    GtkToolItem* pZoomIn = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_IN );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoomIn, 0);
    g_signal_connect( G_OBJECT(pZoomIn), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pZoom1 = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_100 );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoom1, -1);
    g_signal_connect( G_OBJECT(pZoom1), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pZoomFit = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_FIT );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoomFit, -1);
    g_signal_connect( G_OBJECT(pZoomFit), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pZoomOut = gtk_tool_button_new_from_stock( GTK_STOCK_ZOOM_OUT );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pZoomOut, -1);
    g_signal_connect( G_OBJECT(pZoomOut), "clicked", G_CALLBACK(changeZoom), NULL );

    GtkToolItem* pSeparator1 = gtk_separator_tool_item_new();
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pSeparator1, -1);

// GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
    GtkToolItem* pPartSelectorToolItem = gtk_tool_item_new();
    GtkWidget* pComboBox = gtk_combo_box_text_new();
    gtk_container_add( GTK_CONTAINER(pPartSelectorToolItem), pComboBox );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pPartSelectorToolItem, -1 );
    g_signal_connect( G_OBJECT(pComboBox), "changed", G_CALLBACK(changePart), NULL );

    pPartSelector = GTK_COMBO_BOX_TEXT(pComboBox);

    GtkToolItem* pSeparator2 = gtk_separator_tool_item_new();
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pSeparator2, -1);

    GtkToolItem* pPartModeSelectorToolItem = gtk_tool_item_new();
    GtkWidget* pPartModeComboBox = gtk_combo_box_text_new();
    gtk_container_add( GTK_CONTAINER(pPartModeSelectorToolItem), pPartModeComboBox );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pPartModeSelectorToolItem, -1 );
    g_signal_connect( G_OBJECT(pPartModeComboBox), "changed", G_CALLBACK(changePartMode), NULL );
#endif

    GtkToolItem* pSeparator3 = gtk_separator_tool_item_new();
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pSeparator3, -1);

    GtkToolItem* pEnableQuadView = gtk_toggle_tool_button_new();
    gtk_tool_button_set_label( GTK_TOOL_BUTTON(pEnableQuadView), "Use Quad View" );
    gtk_toolbar_insert( GTK_TOOLBAR(pToolbar), pEnableQuadView, -1 );
    g_signal_connect( G_OBJECT(pEnableQuadView), "toggled", G_CALLBACK(changeQuadView), NULL );

    gtk_box_pack_start( GTK_BOX(pVBox), pToolbar, FALSE, FALSE, 0 ); // Adds to top.

    // Docview
    pDocView = lok_docview_new( pOffice );
    pDocViewQuad = 0;

    // Input handling.
    g_signal_connect(pWindow, "key-press-event", G_CALLBACK(signalKey), NULL);
    g_signal_connect(pWindow, "key-release-event", G_CALLBACK(signalKey), NULL);

    gtk_container_add( GTK_CONTAINER(pVBox), pDocView );

    gtk_widget_show_all( pWindow );

    pFileName = argv[2];
    int bOpened = lok_docview_open_document( LOK_DOCVIEW(pDocView), argv[2] );
    if (!bOpened)
        g_error("main: lok_docview_open_document() failed with '%s'", pOffice->pClass->getError(pOffice));
    assert( LOK_DOCVIEW(pDocView)->pDocument );

    // GtkComboBox requires gtk 2.24 or later
#if ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 24 ) || GTK_MAJOR_VERSION > 2
    populatePartSelector();
    populatePartModeSelector( GTK_COMBO_BOX_TEXT(pPartModeComboBox) );
#endif

    gtk_main();

    pOffice->pClass->destroy( pOffice );

    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008-2010 QXmpp Developers
 *
 * Authors:
 *	Ian Reinhart Geiser
 *  Jeremy Lainé
 *
 * Source:
 *	http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QBuffer>
#include <QDebug>

#include "QXmppMessage.h"

#include "ibbTransferTarget.h"

IbbTransferTarget::IbbTransferTarget(QObject *parent)
    : QXmppClient(parent)
{
    m_buffer = new QBuffer(this);

    bool check = connect(&getTransferManager(), SIGNAL(fileReceived(QXmppTransferJob*)),
                    this, SLOT(slotFileReceived(QXmppTransferJob*)));
    Q_ASSERT(check);

    getTransferManager().setSupportedMethods(QXmppTransferJob::InBandMethod);
}

IbbTransferTarget::~IbbTransferTarget()
{
}

void IbbTransferTarget::slotFileReceived(QXmppTransferJob *job)
{
    qDebug() << "Got transfer request from:" << job->jid();

    bool check = connect(job, SIGNAL(error(QXmppTransferJob::Error)), this, SLOT(slotError(QXmppTransferJob::Error)));
    Q_ASSERT(check);

    check = connect(job, SIGNAL(finished()), this, SLOT(slotFinished()));
    Q_ASSERT(check);

    check = connect(job, SIGNAL(progress(qint64,qint64)), this, SLOT(slotProgress(qint64,qint64)));
    Q_ASSERT(check);

    m_buffer->open(QIODevice::WriteOnly);
    job->accept(m_buffer);
}

void IbbTransferTarget::slotError(QXmppTransferJob::Error error)
{
    qDebug() << "Transmission failed:" << error;
}

void IbbTransferTarget::slotFinished()
{
    qDebug() << "Transfer finished:" << m_buffer->data();
}

void IbbTransferTarget::slotProgress(qint64 done, qint64 total)
{
     qDebug() << "Transfer progress:" << done << "/" << total;
}
<commit_msg>suppress unused variable warning<commit_after>/*
 * Copyright (C) 2008-2010 QXmpp Developers
 *
 * Authors:
 *	Ian Reinhart Geiser
 *  Jeremy Lainé
 *
 * Source:
 *	http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QBuffer>
#include <QDebug>

#include "QXmppMessage.h"

#include "ibbTransferTarget.h"

IbbTransferTarget::IbbTransferTarget(QObject *parent)
    : QXmppClient(parent)
{
    m_buffer = new QBuffer(this);

    bool check = connect(&getTransferManager(), SIGNAL(fileReceived(QXmppTransferJob*)),
                    this, SLOT(slotFileReceived(QXmppTransferJob*)));
    Q_ASSERT(check);
    Q_UNUSED(check);

    getTransferManager().setSupportedMethods(QXmppTransferJob::InBandMethod);
}

IbbTransferTarget::~IbbTransferTarget()
{
}

void IbbTransferTarget::slotFileReceived(QXmppTransferJob *job)
{
    qDebug() << "Got transfer request from:" << job->jid();

    bool check = connect(job, SIGNAL(error(QXmppTransferJob::Error)), this, SLOT(slotError(QXmppTransferJob::Error)));
    Q_ASSERT(check);

    check = connect(job, SIGNAL(finished()), this, SLOT(slotFinished()));
    Q_ASSERT(check);

    check = connect(job, SIGNAL(progress(qint64,qint64)), this, SLOT(slotProgress(qint64,qint64)));
    Q_ASSERT(check);

    m_buffer->open(QIODevice::WriteOnly);
    job->accept(m_buffer);
}

void IbbTransferTarget::slotError(QXmppTransferJob::Error error)
{
    qDebug() << "Transmission failed:" << error;
}

void IbbTransferTarget::slotFinished()
{
    qDebug() << "Transfer finished:" << m_buffer->data();
}

void IbbTransferTarget::slotProgress(qint64 done, qint64 total)
{
     qDebug() << "Transfer progress:" << done << "/" << total;
}
<|endoftext|>


<commit_before>#pragma once

#include "constants.hpp"
#include "grabber_client.hpp"
#include "keyboard_event_output_manager.hpp"
#include "local_datagram_server.hpp"
#include "logger.hpp"
#include "userspace_defs.h"
#include <vector>

class receiver final {
public:
  receiver(void) : exit_loop_(false) {
    enum {
      buffer_length = 8 * 1024,
    };
    buffer_.resize(buffer_length);
  }

  void start(void) {
    if (server_) {
      logger::get_logger().error("receiver is already running");
      return;
    }

    const char* path = constants::get_console_user_socket_file_path();
    unlink(path);
    server_ = std::make_unique<local_datagram_server>(path);
    chmod(path, 0600);

    grabber_client_ = std::make_unique<grabber_client>();
    grabber_client_->connect();

    exit_loop_ = false;
    thread_ = std::thread([this] { this->worker(); });

    logger::get_logger().info("receiver is started");
  }

  void stop(void) {
    unlink(constants::get_console_user_socket_file_path());

    if (!thread_.joinable()) {
      return;
    }
    if (!server_) {
      return;
    }

    exit_loop_ = true;
    thread_.join();
    server_.reset(nullptr);
    grabber_client_.reset(nullptr);

    logger::get_logger().info("receiver is stopped");
  }

  void worker(void) {
    if (!server_) {
      return;
    }

    while (!exit_loop_) {
      boost::system::error_code ec;
      std::size_t n = server_->receive(boost::asio::buffer(buffer_), boost::posix_time::seconds(1), ec);

      if (!ec && n > 0) {
        switch (buffer_[0]) {
        case KRBN_OPERATION_TYPE_STOP_KEY_REPEAT:
          keyboard_event_output_manager_.stop_key_repeat();
          break;

        case KRBN_OPERATION_TYPE_POST_MODIFIER_FLAGS:
          if (n != sizeof(krbn_operation_type_post_modifier_flags)) {
            logger::get_logger().error("invalid size for KRBN_OPERATION_TYPE_POST_MODIFIER_FLAGS");
          } else {
            auto p = reinterpret_cast<krbn_operation_type_post_modifier_flags*>(&(buffer_[0]));
            keyboard_event_output_manager_.post_modifier_flags(p->flags);
          }
          break;

        case KRBN_OPERATION_TYPE_POST_KEY:
          if (n != sizeof(krbn_operation_type_post_key)) {
            logger::get_logger().error("invalid size for KRBN_OPERATION_TYPE_POST_KEY");
          } else {
            auto p = reinterpret_cast<krbn_operation_type_post_key*>(&(buffer_[0]));
            keyboard_event_output_manager_.post_key(p->key_code, p->event_type, p->flags);
          }
          break;

        default:
          break;
        }
      }
    }
  }

private:
  std::vector<uint8_t> buffer_;
  std::unique_ptr<local_datagram_server> server_;
  std::unique_ptr<grabber_client> grabber_client_;
  std::thread thread_;
  volatile bool exit_loop_;

  keyboard_event_output_manager keyboard_event_output_manager_;
};
<commit_msg>stop key repeat at end of loop<commit_after>#pragma once

#include "constants.hpp"
#include "grabber_client.hpp"
#include "keyboard_event_output_manager.hpp"
#include "local_datagram_server.hpp"
#include "logger.hpp"
#include "userspace_defs.h"
#include <vector>

class receiver final {
public:
  receiver(void) : exit_loop_(false) {
    enum {
      buffer_length = 8 * 1024,
    };
    buffer_.resize(buffer_length);
  }

  void start(void) {
    if (server_) {
      logger::get_logger().error("receiver is already running");
      return;
    }

    const char* path = constants::get_console_user_socket_file_path();
    unlink(path);
    server_ = std::make_unique<local_datagram_server>(path);
    chmod(path, 0600);

    grabber_client_ = std::make_unique<grabber_client>();
    grabber_client_->connect();

    exit_loop_ = false;
    thread_ = std::thread([this] { this->worker(); });

    logger::get_logger().info("receiver is started");
  }

  void stop(void) {
    unlink(constants::get_console_user_socket_file_path());

    if (!thread_.joinable()) {
      return;
    }
    if (!server_) {
      return;
    }

    exit_loop_ = true;
    thread_.join();
    server_.reset(nullptr);
    grabber_client_.reset(nullptr);

    logger::get_logger().info("receiver is stopped");
  }

  void worker(void) {
    if (!server_) {
      return;
    }

    while (!exit_loop_) {
      boost::system::error_code ec;
      std::size_t n = server_->receive(boost::asio::buffer(buffer_), boost::posix_time::seconds(1), ec);

      if (!ec && n > 0) {
        switch (buffer_[0]) {
        case KRBN_OPERATION_TYPE_STOP_KEY_REPEAT:
          keyboard_event_output_manager_.stop_key_repeat();
          break;

        case KRBN_OPERATION_TYPE_POST_MODIFIER_FLAGS:
          if (n != sizeof(krbn_operation_type_post_modifier_flags)) {
            logger::get_logger().error("invalid size for KRBN_OPERATION_TYPE_POST_MODIFIER_FLAGS");
          } else {
            auto p = reinterpret_cast<krbn_operation_type_post_modifier_flags*>(&(buffer_[0]));
            keyboard_event_output_manager_.post_modifier_flags(p->flags);
          }
          break;

        case KRBN_OPERATION_TYPE_POST_KEY:
          if (n != sizeof(krbn_operation_type_post_key)) {
            logger::get_logger().error("invalid size for KRBN_OPERATION_TYPE_POST_KEY");
          } else {
            auto p = reinterpret_cast<krbn_operation_type_post_key*>(&(buffer_[0]));
            keyboard_event_output_manager_.post_key(p->key_code, p->event_type, p->flags);
          }
          break;

        default:
          break;
        }
      }
    }

    keyboard_event_output_manager_.stop_key_repeat();
  }

private:
  std::vector<uint8_t> buffer_;
  std::unique_ptr<local_datagram_server> server_;
  std::unique_ptr<grabber_client> grabber_client_;
  std::thread thread_;
  volatile bool exit_loop_;

  keyboard_event_output_manager keyboard_event_output_manager_;
};
<|endoftext|>


<commit_before>//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//

//------------------------------------------------------------------------------
//
// Example: WebSocket server, synchronous
//
//------------------------------------------------------------------------------

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/functional/hash.hpp>
//#include <boost/asio/buffers_iterator.hpp>
//#include <cstdlib>
//#include <functional>
#include <iostream>
#include <string>
#include <thread>

#include <array>
#include <unordered_map>
#include <strawpoll.hpp>

using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>

//------------------------------------------------------------------------------

namespace detail
{
  template<typename T> struct hash
  {
    size_t operator() (const T& v) const noexcept
    {
      if (v.is_v4()) return v.to_v4().to_ulong();
      if (v.is_v6())
      {
          auto const& range = v.to_v6().to_bytes();
          return boost::hash_range(range.begin(), range.end());
      }
      if (v.is_unspecified()) return 0x4751301174351161ul;
      return boost::hash_value(v.to_string());
    }
  };

  template<typename T, typename M> class member_func_ptr_closure
  {
  public:
    using obj_ptr_t = T*;
    using member_func_ptr_t = M;

    constexpr member_func_ptr_closure(
      obj_ptr_t obj_ptr,
      member_func_ptr_t member_func_ptr
    ) noexcept
      : obj_ptr_{obj_ptr}, member_func_ptr_{member_func_ptr}
    {}

    template<typename... Args> auto operator() (Args&&... args)
    {
      return ((obj_ptr_)->*(member_func_ptr_))(std::forward<Args>(args)...);
    }

  private:
    obj_ptr_t obj_ptr_;
    member_func_ptr_t member_func_ptr_;
  };

  namespace conv
  {
    boost::asio::const_buffer m_b(FlatBufferRef buffer)
    {
      return { buffer.data, buffer.size };
    }
  }
} // namespace detail

//using poll_data_t = PollData<VoteGuard<boost::asio::ip::address, detail::hash>>;
using poll_data_t = PollData<HippieVoteGuard<boost::asio::ip::address>>;

// Report a failure
void fail(boost::system::error_code ec, char const* what)
{
    std::cerr << what << ": " << ec.message() << "\n";
}

// Echoes back all received WebSocket messages
template<typename FC, typename FB> class session
{
public:
  // Take ownership of the socket
  explicit session(
    tcp::socket&& socket,
    boost::asio::ip::address address,
    FC on_close,
    FB broadcast,
    size_t session_id,
    poll_data_t& poll_data
  )
      : ws_{std::move(socket)}
      , address_{address}
      , strand_{ws_.get_io_service()}
      , read_in_process_{false}
      , write_in_process_{false}
      , on_close_{on_close}
      , broadcast_{broadcast}
      , session_id_{session_id}
      , poll_data_{poll_data}
  {
    ws_.binary(true); // we'll only write binary

    // Accept the websocket handshake
    ws_.async_accept(
        strand_.wrap([this](boost::system::error_code ec) { on_accept(ec); })
    );
  }

  session(const session&) = delete;
  session(session&&) = default;

  session& operator= (const session&) = delete;
  session& operator= (session&&) = default;

  ~session() = default;

  void add_message(FlatBufferRef br)
  {
    if (
      std::is_same_v<
        poll_data_t::vote_guard_t,
        HippieVoteGuard<boost::asio::ip::address>
      > || poll_data_.vote_guard.has_voted(address_)
    )
      message_queue_.push_back(detail::conv::m_b(br));
  }

  void flush_message_queue()
  {
    if (write_in_process_) return;

    if (message_queue_.empty()) return;

    ws_.async_write(
      std::array<boost::asio::const_buffer, 1>{{
        std::move(message_queue_.back())
      }},
      strand_.wrap(
        [this](boost::system::error_code ec, size_t bytes_transferred)
        {
          write_in_process_ = false;
          on_write(ec, bytes_transferred);
        }
      )
    );

    write_in_process_ = true;
    message_queue_.pop_back();
  }

private:
  websocket::stream<tcp::socket> ws_;
  boost::asio::ip::address address_;
  boost::asio::io_service::strand strand_;
  boost::beast::multi_buffer buffer_;
  std::vector<boost::asio::const_buffer> message_queue_;
  bool read_in_process_;
  bool write_in_process_;
  FC on_close_;
  FB broadcast_;
  size_t session_id_;
  poll_data_t& poll_data_;

  void on_accept(boost::system::error_code ec)
  {
    if (ec) return fail(ec, "accept");

    // Read a message
    do_read();
  }

  void do_read()
  {
    if (read_in_process_) return;
    // Clear the buffer
    buffer_.consume(buffer_.size());

    // Read a message into our buffer
    ws_.async_read(
      buffer_,
      strand_.wrap(
        [this](boost::system::error_code ec, size_t bytes_transferred)
        {
          read_in_process_ = false;
          on_read(ec, bytes_transferred);
        }
      )
    );
    read_in_process_ = true;
  }

  void on_read(
    boost::system::error_code ec,
    size_t bytes_transferred
  ) {
    boost::ignore_unused(bytes_transferred);

    // This indicates that the session was closed
    if (ec == websocket::error::closed)
    {
      on_close_(session_id_);
      return;
    }

    if (ec) fail(ec, "read");

    build_responses();
    flush_message_queue();
  }

  void on_write(
    boost::system::error_code ec,
    size_t// bytes_transferred
  ) {
    //boost::ignore_unused(bytes_transferred);

    if (ec) return fail(ec, "write");

    if (!message_queue_.empty())
    {
      flush_message_queue();
      return;
    }

    // Do another read
    do_read();
  }

  void build_responses()
  {
    const auto add_response = [&queue = message_queue_](FlatBufferRef br)
    {
      queue.push_back(detail::conv::m_b(br));
    };

    for (const auto buffer : buffer_.data())
    {
      const auto ok = flatbuffers::Verifier(
        boost::asio::buffer_cast<const uint8_t*>(buffer),
        boost::asio::buffer_size(buffer)
      ).VerifyBuffer<Strawpoll::Request>(nullptr);

      if (!ok) {
        add_response(poll_data_.error_responses.invalid_message.ref());
        continue;
      }

      const auto request = flatbuffers::GetRoot<Strawpoll::Request>(
        boost::asio::buffer_cast<const uint8_t*>(buffer)
      );

      switch(request->type())
      {
        case Strawpoll::RequestType_Poll:
          if (poll_data_.vote_guard.has_voted(address_))
            add_response(poll_data_.result.ref());

          add_response(poll_data_.poll_response.ref());
          break;
        case Strawpoll::RequestType_Result:
          poll_data_.register_vote(
            request->vote(),
            address_,
            add_response,
            [&broadcast = broadcast_](FlatBufferRef br) { broadcast(br); }
          );
          break;
        default:
          add_response(poll_data_.error_responses.invalid_type.ref());
      }
    }
  }
};

// Accepts incoming connections and launches the sessions
class listener
{
public:
  void on_session_close(size_t session_id)
  {
    sessions_.erase(session_id);
  }

  void broadcast(FlatBufferRef br)
  {
    for (auto& [key, session] : sessions_)
    {
      session.add_message(br);
      session.flush_message_queue();
    }
  }

  using on_session_close_t = detail::member_func_ptr_closure<
    listener,
    decltype(&listener::on_session_close)
  >;
  using broadcast_t = detail::member_func_ptr_closure<
    listener,
    decltype(&listener::broadcast)
  >;

  using session_t = session<on_session_close_t, broadcast_t>;
  using sessions_t = std::unordered_map<size_t, session_t>;

  explicit listener(
      boost::asio::io_service& ios,
      tcp::endpoint endpoint)
      : strand_{ios}
      , acceptor_{ios}
      , socket_{ios}
      , session_id_counter_{}
      , on_session_close_{this, &listener::on_session_close}
      , broadcast_{this, &listener::broadcast}
      , poll_data_{}
  {
    boost::system::error_code ec;

    // Open the acceptor
    acceptor_.open(endpoint.protocol(), ec);
    if (ec)
    {
      fail(ec, "open");
      return;
    }

    // Bind to the server address
    acceptor_.bind(endpoint, ec);
    if (ec)
    {
      fail(ec, "bind");
      return;
    }

    // Start listening for connections
    acceptor_.listen(boost::asio::socket_base::max_connections, ec);
    if (ec)
    {
      fail(ec, "listen");
      return;
    }

    if (! acceptor_.is_open()) return;
    do_accept();
  }

  listener(const listener&) = delete;
  listener(listener&&) = default;

  listener& operator= (const listener&) = delete;
  listener& operator= (listener&&) = default;

  ~listener() = default;

  void do_accept()
  {
    acceptor_.async_accept(
      socket_,
      strand_.wrap([this](boost::system::error_code ec) { on_accept(ec); })
    );
  }

  void on_accept(boost::system::error_code ec)
  {
    if (ec)
    {
      fail(ec, "accept");
    }
    else
    {
      // Create the session and run it
      const auto address = socket_.remote_endpoint().address();
      sessions_.try_emplace(
        session_id_counter_,
        std::move(socket_),
        std::move(address),
        on_session_close_,
        broadcast_,
        session_id_counter_,
        poll_data_
      );
      ++session_id_counter_;
    }

    // Accept another connection
    do_accept();
  }

private:
  boost::asio::io_service::strand strand_;
  tcp::acceptor acceptor_;
  tcp::socket socket_;
  size_t session_id_counter_;
  on_session_close_t on_session_close_;
  broadcast_t broadcast_;
  sessions_t sessions_;
  poll_data_t poll_data_;
};

//------------------------------------------------------------------------------

int main()
{
  const auto address = boost::asio::ip::address::from_string("127.0.0.1");
  const auto port = static_cast<unsigned short>(3003);

  boost::asio::io_service ios{1};
  listener lis{ios, tcp::endpoint{address, port}};
  ios.run(); // blocking
}
<commit_msg>Adapt beast example to new PollData result api<commit_after>//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//

//------------------------------------------------------------------------------
//
// Example: WebSocket server, synchronous
//
//------------------------------------------------------------------------------

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/functional/hash.hpp>
//#include <boost/asio/buffers_iterator.hpp>
//#include <cstdlib>
//#include <functional>
#include <iostream>
#include <string>
#include <thread>

#include <array>
#include <unordered_map>
#include <strawpoll.hpp>

using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>

//------------------------------------------------------------------------------

namespace detail
{
  template<typename T> struct hash
  {
    size_t operator() (const T& v) const noexcept
    {
      if (v.is_v4()) return v.to_v4().to_ulong();
      if (v.is_v6())
      {
          auto const& range = v.to_v6().to_bytes();
          return boost::hash_range(range.begin(), range.end());
      }
      if (v.is_unspecified()) return 0x4751301174351161ul;
      return boost::hash_value(v.to_string());
    }
  };

  template<typename T, typename M> class member_func_ptr_closure
  {
  public:
    using obj_ptr_t = T*;
    using member_func_ptr_t = M;

    constexpr member_func_ptr_closure(
      obj_ptr_t obj_ptr,
      member_func_ptr_t member_func_ptr
    ) noexcept
      : obj_ptr_{obj_ptr}, member_func_ptr_{member_func_ptr}
    {}

    template<typename... Args> auto operator() (Args&&... args)
    {
      return ((obj_ptr_)->*(member_func_ptr_))(std::forward<Args>(args)...);
    }

  private:
    obj_ptr_t obj_ptr_;
    member_func_ptr_t member_func_ptr_;
  };

  namespace conv
  {
    boost::asio::const_buffer m_b(FlatBufferRef buffer)
    {
      return { buffer.data, buffer.size };
    }
  }
} // namespace detail

//using poll_data_t = PollData<VoteGuard<boost::asio::ip::address, detail::hash>>;
using poll_data_t = PollData<HippieVoteGuard<boost::asio::ip::address>>;

// Report a failure
void fail(boost::system::error_code ec, char const* what)
{
    std::cerr << what << ": " << ec.message() << "\n";
}

// Echoes back all received WebSocket messages
template<typename FC, typename FB> class session
{
public:
  // Take ownership of the socket
  explicit session(
    tcp::socket&& socket,
    boost::asio::ip::address address,
    FC on_close,
    FB broadcast,
    size_t session_id,
    poll_data_t& poll_data
  )
      : ws_{std::move(socket)}
      , address_{address}
      , strand_{ws_.get_io_service()}
      , read_in_process_{false}
      , write_in_process_{false}
      , on_close_{on_close}
      , broadcast_{broadcast}
      , session_id_{session_id}
      , poll_data_{poll_data}
  {
    ws_.binary(true); // we'll only write binary

    // Accept the websocket handshake
    ws_.async_accept(
        strand_.wrap([this](boost::system::error_code ec) { on_accept(ec); })
    );
  }

  session(const session&) = delete;
  session(session&&) = default;

  session& operator= (const session&) = delete;
  session& operator= (session&&) = default;

  ~session() = default;

  void add_message(FlatBufferRef br)
  {
    if (
      std::is_same_v<
        poll_data_t::vote_guard_t,
        HippieVoteGuard<boost::asio::ip::address>
      >
      || !write_in_process_ // write should always write the most recent
      || poll_data_.vote_guard.has_voted(address_)
    )
      message_queue_.push_back(detail::conv::m_b(br));
  }

  void flush_message_queue()
  {
    if (write_in_process_) return;

    if (message_queue_.empty()) return;

    ws_.async_write(
      std::array<boost::asio::const_buffer, 1>{{
        std::move(message_queue_.back())
      }},
      strand_.wrap(
        [this](boost::system::error_code ec, size_t bytes_transferred)
        {
          write_in_process_ = false;
          on_write(ec, bytes_transferred);
        }
      )
    );

    write_in_process_ = true;
    message_queue_.pop_back();
  }

private:
  websocket::stream<tcp::socket> ws_;
  boost::asio::ip::address address_;
  boost::asio::io_service::strand strand_;
  boost::beast::multi_buffer buffer_;
  std::vector<boost::asio::const_buffer> message_queue_;
  bool read_in_process_;
  bool write_in_process_;
  FC on_close_;
  FB broadcast_;
  size_t session_id_;
  poll_data_t& poll_data_;

  void on_accept(boost::system::error_code ec)
  {
    if (ec) return fail(ec, "accept");

    // Read a message
    do_read();
  }

  void do_read()
  {
    if (read_in_process_) return;
    // Clear the buffer
    buffer_.consume(buffer_.size());

    // Read a message into our buffer
    ws_.async_read(
      buffer_,
      strand_.wrap(
        [this](boost::system::error_code ec, size_t bytes_transferred)
        {
          read_in_process_ = false;
          on_read(ec, bytes_transferred);
        }
      )
    );
    read_in_process_ = true;
  }

  void on_read(
    boost::system::error_code ec,
    size_t bytes_transferred
  ) {
    boost::ignore_unused(bytes_transferred);

    // This indicates that the session was closed
    if (ec == websocket::error::closed)
    {
      on_close_(session_id_);
      return;
    }

    if (ec) fail(ec, "read");

    build_responses();
    flush_message_queue();
  }

  void on_write(
    boost::system::error_code ec,
    size_t// bytes_transferred
  ) {
    //boost::ignore_unused(bytes_transferred);

    if (ec) return fail(ec, "write");

    if (!message_queue_.empty())
    {
      flush_message_queue();
      return;
    }

    // Do another read
    do_read();
  }

  void build_responses()
  {
    const auto add_response = [&queue = message_queue_](FlatBufferRef br)
    {
      queue.push_back(detail::conv::m_b(br));
    };

    for (const auto buffer : buffer_.data())
    {
      const auto ok = flatbuffers::Verifier(
        boost::asio::buffer_cast<const uint8_t*>(buffer),
        boost::asio::buffer_size(buffer)
      ).VerifyBuffer<Strawpoll::Request>(nullptr);

      if (!ok) {
        add_response(poll_data_.error_responses.invalid_message.ref());
        continue;
      }

      const auto request = flatbuffers::GetRoot<Strawpoll::Request>(
        boost::asio::buffer_cast<const uint8_t*>(buffer)
      );

      switch(request->type())
      {
        case Strawpoll::RequestType_Poll:
          add_response(poll_data_.poll_response.ref());

          if (poll_data_.vote_guard.has_voted(address_))
            add_response(poll_data_.result_ref());
          break;
        case Strawpoll::RequestType_Result:
          poll_data_.register_vote(
            request->vote(),
            address_,
            add_response,
            [&broadcast = broadcast_](FlatBufferRef br) { broadcast(br); }
          );
          break;
        default:
          add_response(poll_data_.error_responses.invalid_type.ref());
      }
    }
  }
};

// Accepts incoming connections and launches the sessions
class listener
{
public:
  void on_session_close(size_t session_id)
  {
    sessions_.erase(session_id);
  }

  void broadcast(FlatBufferRef br)
  {
    for (auto& [key, session] : sessions_)
    {
      session.add_message(br);
      session.flush_message_queue();
    }
  }

  using on_session_close_t = detail::member_func_ptr_closure<
    listener,
    decltype(&listener::on_session_close)
  >;
  using broadcast_t = detail::member_func_ptr_closure<
    listener,
    decltype(&listener::broadcast)
  >;

  using session_t = session<on_session_close_t, broadcast_t>;
  using sessions_t = std::unordered_map<size_t, session_t>;

  explicit listener(
      boost::asio::io_service& ios,
      tcp::endpoint endpoint)
      : strand_{ios}
      , acceptor_{ios}
      , socket_{ios}
      , session_id_counter_{}
      , on_session_close_{this, &listener::on_session_close}
      , broadcast_{this, &listener::broadcast}
      , poll_data_{}
  {
    boost::system::error_code ec;

    // Open the acceptor
    acceptor_.open(endpoint.protocol(), ec);
    if (ec)
    {
      fail(ec, "open");
      return;
    }

    // Bind to the server address
    acceptor_.bind(endpoint, ec);
    if (ec)
    {
      fail(ec, "bind");
      return;
    }

    // Start listening for connections
    acceptor_.listen(boost::asio::socket_base::max_connections, ec);
    if (ec)
    {
      fail(ec, "listen");
      return;
    }

    if (! acceptor_.is_open()) return;
    do_accept();
  }

  listener(const listener&) = delete;
  listener(listener&&) = default;

  listener& operator= (const listener&) = delete;
  listener& operator= (listener&&) = default;

  ~listener() = default;

  void do_accept()
  {
    acceptor_.async_accept(
      socket_,
      strand_.wrap([this](boost::system::error_code ec) { on_accept(ec); })
    );
  }

  void on_accept(boost::system::error_code ec)
  {
    if (ec)
    {
      fail(ec, "accept");
    }
    else
    {
      // Create the session and run it
      const auto address = socket_.remote_endpoint().address();
      sessions_.try_emplace(
        session_id_counter_,
        std::move(socket_),
        std::move(address),
        on_session_close_,
        broadcast_,
        session_id_counter_,
        poll_data_
      );
      ++session_id_counter_;
    }

    // Accept another connection
    do_accept();
  }

private:
  boost::asio::io_service::strand strand_;
  tcp::acceptor acceptor_;
  tcp::socket socket_;
  size_t session_id_counter_;
  on_session_close_t on_session_close_;
  broadcast_t broadcast_;
  sessions_t sessions_;
  poll_data_t poll_data_;
};

//------------------------------------------------------------------------------

int main()
{
  const auto address = boost::asio::ip::address::from_string("127.0.0.1");
  const auto port = static_cast<unsigned short>(3003);

  boost::asio::io_service ios{1};
  listener lis{ios, tcp::endpoint{address, port}};
  ios.run(); // blocking
}
<|endoftext|>