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

#include "test/core/end2end/end2end_tests.h"

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

#include "src/core/lib/surface/channel.h"

#include <grpc/byte_buffer.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/lib/gpr/string.h"
#include "test/core/end2end/cq_verifier.h"

static void* tag(intptr_t t) { return (void*)t; }

static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
                                            const char* test_name,
                                            grpc_channel_args* client_args,
                                            grpc_channel_args* server_args) {
  grpc_end2end_test_fixture f;
  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
  f = config.create_fixture(client_args, server_args);
  config.init_server(&f, server_args);
  config.init_client(&f, client_args);
  return f;
}

static gpr_timespec n_seconds_from_now(int n) {
  return grpc_timeout_seconds_to_deadline(n);
}

static gpr_timespec five_seconds_from_now(void) {
  return n_seconds_from_now(5);
}

static void drain_cq(grpc_completion_queue* cq) {
  grpc_event ev;
  do {
    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
}

static void shutdown_server(grpc_end2end_test_fixture* f) {
  if (!f->server) return;
  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
                                         grpc_timeout_seconds_to_deadline(5),
                                         nullptr)
                 .type == GRPC_OP_COMPLETE);
  grpc_server_destroy(f->server);
  f->server = nullptr;
}

static void shutdown_client(grpc_end2end_test_fixture* f) {
  if (!f->client) return;
  grpc_channel_destroy(f->client);
  f->client = nullptr;
}

static void end_test(grpc_end2end_test_fixture* f) {
  shutdown_server(f);
  shutdown_client(f);

  grpc_completion_queue_shutdown(f->cq);
  drain_cq(f->cq);
  grpc_completion_queue_destroy(f->cq);
  grpc_completion_queue_destroy(f->shutdown_cq);
}

static void run_one_request(grpc_end2end_test_config config,
                            grpc_end2end_test_fixture f,
                            bool request_is_success) {
  grpc_call* c;
  grpc_call* s;
  cq_verifier* cqv = cq_verifier_create(f.cq);
  grpc_op ops[6];
  grpc_op* op;
  grpc_metadata_array initial_metadata_recv;
  grpc_metadata_array trailing_metadata_recv;
  grpc_metadata_array request_metadata_recv;
  grpc_call_details call_details;
  grpc_status_code status;
  grpc_call_error error;
  grpc_slice details;
  int was_cancelled = 2;

  gpr_timespec deadline = five_seconds_from_now();
  c = grpc_channel_create_call(f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
                               grpc_slice_from_static_string("/foo"), nullptr,
                               deadline, nullptr);
  GPR_ASSERT(c);

  grpc_metadata_array_init(&initial_metadata_recv);
  grpc_metadata_array_init(&trailing_metadata_recv);
  grpc_metadata_array_init(&request_metadata_recv);
  grpc_call_details_init(&call_details);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_INITIAL_METADATA;
  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
  op->data.recv_status_on_client.status = &status;
  op->data.recv_status_on_client.status_details = &details;
  op->data.recv_status_on_client.error_string = nullptr;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  error =
      grpc_server_request_call(f.server, &s, &call_details,
                               &request_metadata_recv, f.cq, f.cq, tag(101));
  GPR_ASSERT(GRPC_CALL_OK == error);
  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
  cq_verify(cqv);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
  op->data.send_status_from_server.trailing_metadata_count = 0;
  op->data.send_status_from_server.status =
      request_is_success ? GRPC_STATUS_OK : GRPC_STATUS_UNIMPLEMENTED;
  grpc_slice status_details = grpc_slice_from_static_string("xyz");
  op->data.send_status_from_server.status_details = &status_details;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
  op->data.recv_close_on_server.cancelled = &was_cancelled;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(102),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
  cq_verify(cqv);

  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
  GPR_ASSERT(0 == call_details.flags);

  grpc_slice_unref(details);
  grpc_metadata_array_destroy(&initial_metadata_recv);
  grpc_metadata_array_destroy(&trailing_metadata_recv);
  grpc_metadata_array_destroy(&request_metadata_recv);
  grpc_call_details_destroy(&call_details);

  grpc_call_unref(c);
  grpc_call_unref(s);

  cq_verifier_destroy(cqv);
}

static void test_channelz(grpc_end2end_test_config config) {
  grpc_end2end_test_fixture f;

  f = begin_test(config, "test_channelz", nullptr, nullptr);
  grpc_core::channelz::Channel* channelz_channel =
      grpc_channel_get_channelz_node(f.client);

  char* json = channelz_channel->RenderJSON();
  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"0\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"0\""));
  gpr_free(json);

  // one successful request
  run_one_request(config, f, true);

  json = channelz_channel->RenderJSON();
  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"1\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
  gpr_free(json);

  // one failed request
  run_one_request(config, f, false);

  json = channelz_channel->RenderJSON();
  gpr_log(GPR_INFO, "%s", json);
  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"2\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"1\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
  gpr_free(json);

  end_test(&f);
  config.tear_down_data(&f);
}

static void test_channelz_with_channel_trace(grpc_end2end_test_config config) {
  grpc_end2end_test_fixture f;

  grpc_arg client_a;
  client_a.type = GRPC_ARG_INTEGER;
  client_a.key = const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE);
  client_a.value.integer = 5;
  grpc_channel_args client_args = {1, &client_a};

  f = begin_test(config, "test_channelz_with_channel_trace", &client_args,
                 nullptr);
  grpc_core::channelz::Channel* channelz_channel =
      grpc_channel_get_channelz_node(f.client);

  char* json = channelz_channel->RenderJSON();
  gpr_log(GPR_INFO, "%s", json);
  GPR_ASSERT(nullptr != strstr(json, "\"trace\""));
  GPR_ASSERT(nullptr != strstr(json, "\"description\":\"Channel created\""));
  GPR_ASSERT(nullptr != strstr(json, "\"severity\":\"CT_INFO\""));
  gpr_free(json);

  end_test(&f);
  config.tear_down_data(&f);
}
void channelz(grpc_end2end_test_config config) {
  test_channelz(config);
  test_channelz_with_channel_trace(config);
}

void channelz_pre_init(void) {}
<commit_msg>fixup! Rename channelz Channel to ChannelNode<commit_after>/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "test/core/end2end/end2end_tests.h"

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

#include "src/core/lib/surface/channel.h"

#include <grpc/byte_buffer.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/lib/gpr/string.h"
#include "test/core/end2end/cq_verifier.h"

static void* tag(intptr_t t) { return (void*)t; }

static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
                                            const char* test_name,
                                            grpc_channel_args* client_args,
                                            grpc_channel_args* server_args) {
  grpc_end2end_test_fixture f;
  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
  f = config.create_fixture(client_args, server_args);
  config.init_server(&f, server_args);
  config.init_client(&f, client_args);
  return f;
}

static gpr_timespec n_seconds_from_now(int n) {
  return grpc_timeout_seconds_to_deadline(n);
}

static gpr_timespec five_seconds_from_now(void) {
  return n_seconds_from_now(5);
}

static void drain_cq(grpc_completion_queue* cq) {
  grpc_event ev;
  do {
    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
}

static void shutdown_server(grpc_end2end_test_fixture* f) {
  if (!f->server) return;
  grpc_server_shutdown_and_notify(f->server, f->shutdown_cq, tag(1000));
  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
                                         grpc_timeout_seconds_to_deadline(5),
                                         nullptr)
                 .type == GRPC_OP_COMPLETE);
  grpc_server_destroy(f->server);
  f->server = nullptr;
}

static void shutdown_client(grpc_end2end_test_fixture* f) {
  if (!f->client) return;
  grpc_channel_destroy(f->client);
  f->client = nullptr;
}

static void end_test(grpc_end2end_test_fixture* f) {
  shutdown_server(f);
  shutdown_client(f);

  grpc_completion_queue_shutdown(f->cq);
  drain_cq(f->cq);
  grpc_completion_queue_destroy(f->cq);
  grpc_completion_queue_destroy(f->shutdown_cq);
}

static void run_one_request(grpc_end2end_test_config config,
                            grpc_end2end_test_fixture f,
                            bool request_is_success) {
  grpc_call* c;
  grpc_call* s;
  cq_verifier* cqv = cq_verifier_create(f.cq);
  grpc_op ops[6];
  grpc_op* op;
  grpc_metadata_array initial_metadata_recv;
  grpc_metadata_array trailing_metadata_recv;
  grpc_metadata_array request_metadata_recv;
  grpc_call_details call_details;
  grpc_status_code status;
  grpc_call_error error;
  grpc_slice details;
  int was_cancelled = 2;

  gpr_timespec deadline = five_seconds_from_now();
  c = grpc_channel_create_call(f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
                               grpc_slice_from_static_string("/foo"), nullptr,
                               deadline, nullptr);
  GPR_ASSERT(c);

  grpc_metadata_array_init(&initial_metadata_recv);
  grpc_metadata_array_init(&trailing_metadata_recv);
  grpc_metadata_array_init(&request_metadata_recv);
  grpc_call_details_init(&call_details);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_INITIAL_METADATA;
  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
  op->data.recv_status_on_client.status = &status;
  op->data.recv_status_on_client.status_details = &details;
  op->data.recv_status_on_client.error_string = nullptr;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  error =
      grpc_server_request_call(f.server, &s, &call_details,
                               &request_metadata_recv, f.cq, f.cq, tag(101));
  GPR_ASSERT(GRPC_CALL_OK == error);
  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
  cq_verify(cqv);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
  op->data.send_status_from_server.trailing_metadata_count = 0;
  op->data.send_status_from_server.status =
      request_is_success ? GRPC_STATUS_OK : GRPC_STATUS_UNIMPLEMENTED;
  grpc_slice status_details = grpc_slice_from_static_string("xyz");
  op->data.send_status_from_server.status_details = &status_details;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
  op->data.recv_close_on_server.cancelled = &was_cancelled;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(102),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
  cq_verify(cqv);

  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
  GPR_ASSERT(0 == call_details.flags);

  grpc_slice_unref(details);
  grpc_metadata_array_destroy(&initial_metadata_recv);
  grpc_metadata_array_destroy(&trailing_metadata_recv);
  grpc_metadata_array_destroy(&request_metadata_recv);
  grpc_call_details_destroy(&call_details);

  grpc_call_unref(c);
  grpc_call_unref(s);

  cq_verifier_destroy(cqv);
}

static void test_channelz(grpc_end2end_test_config config) {
  grpc_end2end_test_fixture f;

  f = begin_test(config, "test_channelz", nullptr, nullptr);
  grpc_core::channelz::ChannelNode* channelz_channel =
      grpc_channel_get_channelz_node(f.client);

  char* json = channelz_channel->RenderJSON();
  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"0\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"0\""));
  gpr_free(json);

  // one successful request
  run_one_request(config, f, true);

  json = channelz_channel->RenderJSON();
  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"1\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
  gpr_free(json);

  // one failed request
  run_one_request(config, f, false);

  json = channelz_channel->RenderJSON();
  gpr_log(GPR_INFO, "%s", json);
  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"2\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"1\""));
  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
  gpr_free(json);

  end_test(&f);
  config.tear_down_data(&f);
}

static void test_channelz_with_channel_trace(grpc_end2end_test_config config) {
  grpc_end2end_test_fixture f;

  grpc_arg client_a;
  client_a.type = GRPC_ARG_INTEGER;
  client_a.key = const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE);
  client_a.value.integer = 5;
  grpc_channel_args client_args = {1, &client_a};

  f = begin_test(config, "test_channelz_with_channel_trace", &client_args,
                 nullptr);
  grpc_core::channelz::ChannelNode* channelz_channel =
      grpc_channel_get_channelz_node(f.client);

  char* json = channelz_channel->RenderJSON();
  gpr_log(GPR_INFO, "%s", json);
  GPR_ASSERT(nullptr != strstr(json, "\"trace\""));
  GPR_ASSERT(nullptr != strstr(json, "\"description\":\"Channel created\""));
  GPR_ASSERT(nullptr != strstr(json, "\"severity\":\"CT_INFO\""));
  gpr_free(json);

  end_test(&f);
  config.tear_down_data(&f);
}
void channelz(grpc_end2end_test_config config) {
  test_channelz(config);
  test_channelz_with_channel_trace(config);
}

void channelz_pre_init(void) {}
<|endoftext|>


<commit_before>/*
 * numerical_comparisons.hpp
 *
 *  Created on: Aug 13, 2013
 *      Author: Péter Fankhauser
 *	 Institute: ETH Zurich, Autonomous Systems Lab
 */

#ifndef SM_NUMERICALCOMPARISON_HPP_
#define SM_NUMERICALCOMPARISON_HPP_

#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include <cmath> // std::abs
#include <stdexcept> // std::invalid_argument
#include "assert_macros.hpp"

namespace sm {

/*
 * Functions to compare floating point numbers with a relative error (epsilon).
 *
 * As the precision of floating point numbers are limited and depending on the
 * magnitude, we compare two numbers a and b by comparing their difference |a - b|
 * to the magnitude of the the bigger number max(|a|, |b|) multiplied with the
 * relative error epsilon. Therefore, a and b are approximately equal if it holds
 * |a - b| <= max(|a|, |b|) * epsilon. This can be applied analogously to greater
 * and less than comparisons.
 *
 * More information on compare floating point numbers is given in
 * - http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
 * - The art of computer programming, Volume 2 / Seminumerical Algorithms, Donald E. Knuth (page 218)
 */

namespace internal {

/*!
 * Takes max(|a|, |b|) and multiplies it with epsilon.
 * @param a the first number.
 * @param b the second number.
 * @param epsilon the precision (epsilon > 0).
 * @return the result of max(|a|, |b|) * epsilon.
 */
template<typename ValueType_>
static inline ValueType_ maxTimesEpsilon(const ValueType_ a, const ValueType_ b, const ValueType_ epsilon)
{
  SM_ASSERT_GT_DBG(std::invalid_argument, epsilon, 0.0, "This method is only valid for an epsilon greater than 0.");
  return std::max(std::abs(a), std::abs(b)) * epsilon;
}

} /* namespace internal */

/*!
 * Checks if two numbers a and b are equal within a relative error.
 * @param[in] a the first number to compare.
 * @param[in] b the second number to compare.
 * @param[in] epsilon the relative error (optional, if not declared the precision of the datatype).
 * @return true if a and b are approximately equal, false otherwise.
 */
template<typename ValueType_>
static bool approximatelyEqual(const ValueType_ a, const ValueType_ b, ValueType_ epsilon = std::numeric_limits<ValueType_>::epsilon())
{
  return std::abs(a - b) <= internal::maxTimesEpsilon(a, b, epsilon);
}

/*!
 * Checks if a is greater than b (a > b) within a relative error.
 * @param a the first number to compare.
 * @param b the second number to compare.
 * @param epsilon the relative error (optional, if not declared the precision of the datatype).
 * @return true if a definitely greater than b, false otherwise.
 */
template<typename ValueType_>
static bool definitelyGreaterThan(const ValueType_ a, const ValueType_ b, ValueType_ epsilon = std::numeric_limits<ValueType_>::epsilon())
{
  return (a - b) > internal::maxTimesEpsilon(a, b, epsilon);
}

/*!
 * Checks if a is less than b (a < b) within a relative error.
 * @param a the first number to compare.
 * @param b the second number to compare.
 * @param epsilon the relative error (optional, if not declared the precision of the datatype).
 * @return true if a definitely less than b, false otherwise.
 */
template<typename ValueType_>
static bool definitelyLessThan(const ValueType_ a, const ValueType_ b, ValueType_ epsilon = std::numeric_limits<ValueType_>::epsilon())
{
  return (b - a) > internal::maxTimesEpsilon(a, b, epsilon);
}

} /* namespace sm */
#endif /* SM_NUMERICALCOMPARISON_HPP_ */
<commit_msg>Fixed numerical comparison unit-tests<commit_after>/*
 * numerical_comparisons.hpp
 *
 *  Created on: Aug 13, 2013
 *      Author: Péter Fankhauser
 *	 Institute: ETH Zurich, Autonomous Systems Lab
 */

#ifndef SM_NUMERICALCOMPARISON_HPP_
#define SM_NUMERICALCOMPARISON_HPP_

#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include <cmath> // std::abs
#include <stdexcept> // std::invalid_argument
#include "assert_macros.hpp"

namespace sm {

/*
 * Functions to compare floating point numbers with a relative error (epsilon).
 *
 * As the precision of floating point numbers are limited and depending on the
 * magnitude, we compare two numbers a and b by comparing their difference |a - b|
 * to the magnitude of the the bigger number max(|a|, |b|) multiplied with the
 * relative error epsilon. Therefore, a and b are approximately equal if it holds
 * |a - b| <= max(|a|, |b|) * epsilon. This can be applied analogously to greater
 * and less than comparisons.
 *
 * More information on compare floating point numbers is given in
 * - http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
 * - The art of computer programming, Volume 2 / Seminumerical Algorithms, Donald E. Knuth (page 218)
 */

namespace internal {

/*!
 * Takes max(|a|, |b|) and multiplies it with epsilon.
 * @param a the first number.
 * @param b the second number.
 * @param epsilon the precision (epsilon > 0).
 * @return the result of max(|a|, |b|) * epsilon.
 */
template<typename ValueType_>
static inline ValueType_ maxTimesEpsilon(const ValueType_ a, const ValueType_ b, const ValueType_ epsilon)
{
  SM_ASSERT_GT_DBG(std::invalid_argument, epsilon, 0.0, "This method is only valid for an epsilon greater than 0.");
  return std::max(std::abs(a), std::abs(b)) * epsilon;
}

} /* namespace internal */

/*!
 * Checks if two numbers a and b are equal within a relative error.
 * @param[in] a the first number to compare.
 * @param[in] b the second number to compare.
 * @param[in] epsilon the relative error (optional, if not declared the precision of the datatype).
 * @return true if a and b are approximately equal, false otherwise.
 */
template<typename ValueType_>
static bool approximatelyEqual(const ValueType_ a, const ValueType_ b, ValueType_ epsilon = std::numeric_limits<ValueType_>::epsilon())
{
  SM_ASSERT_GT(std::invalid_argument, epsilon, 0.0, "This method is only valid for an epsilon greater than 0.");
  return std::abs(a - b) <= internal::maxTimesEpsilon(a, b, epsilon);
}

/*!
 * Checks if a is greater than b (a > b) within a relative error.
 * @param a the first number to compare.
 * @param b the second number to compare.
 * @param epsilon the relative error (optional, if not declared the precision of the datatype).
 * @return true if a definitely greater than b, false otherwise.
 */
template<typename ValueType_>
static bool definitelyGreaterThan(const ValueType_ a, const ValueType_ b, ValueType_ epsilon = std::numeric_limits<ValueType_>::epsilon())
{
  SM_ASSERT_GT(std::invalid_argument, epsilon, 0.0, "This method is only valid for an epsilon greater than 0.");
  return (a - b) > internal::maxTimesEpsilon(a, b, epsilon);
}

/*!
 * Checks if a is less than b (a < b) within a relative error.
 * @param a the first number to compare.
 * @param b the second number to compare.
 * @param epsilon the relative error (optional, if not declared the precision of the datatype).
 * @return true if a definitely less than b, false otherwise.
 */
template<typename ValueType_>
static bool definitelyLessThan(const ValueType_ a, const ValueType_ b, ValueType_ epsilon = std::numeric_limits<ValueType_>::epsilon())
{
  SM_ASSERT_GT(std::invalid_argument, epsilon, 0.0, "This method is only valid for an epsilon greater than 0.");
  return (b - a) > internal::maxTimesEpsilon(a, b, epsilon);
}

} /* namespace sm */
#endif /* SM_NUMERICALCOMPARISON_HPP_ */
<|endoftext|>


<commit_before>#include "SimulatorWindow.hpp"
#include "rendering/SimRenderView.hpp"
#include "Physics/Environment.hpp"

#include <QAbstractTableModel>

static const QString columnNames[] = {
	"Visibility",
	"Ball Sensor",
	"Charger"
};

class RobotTableModel: public QAbstractTableModel
{
public:
	RobotTableModel(Environment *env)
	: _env(env)
	{
		layoutChanged();
	}

	Robot *robotForRow(int row) const
	{
		const Environment::RobotMap *map;
		if (row < _env->yellow().size())
		{
			map = &_env->yellow();
		} else {
			row -= _env->yellow().size();
			map = &_env->blue();
		}
		Environment::RobotMap::const_iterator i = map->begin();
		i += row;
		return *i;
	}

	virtual int columnCount(const QModelIndex &parent) const
	{
		return sizeof(columnNames) / sizeof(columnNames[0]);
	}

	virtual int rowCount(const QModelIndex &parent) const
	{
		if (_env)
		{
			return _env->blue().size() + _env->yellow().size();
		} else {
			return 0;
		}
	}

	virtual Qt::ItemFlags flags(const QModelIndex &index) const
	{
		Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
		if (index.column() == 0)
		{
			flags |= Qt::ItemIsEditable;
		} else {
			flags |= Qt::ItemIsUserCheckable;
		}

		return flags;
	}

	virtual QVariant data(const QModelIndex &index, int role) const
	{
		if (_env)
		{
			Robot *robot = robotForRow(index.row());

			if (index.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
			{
				return QVariant(robot->visibility);
			} else if (index.column() == 1 && role == Qt::CheckStateRole)
			{
				return robot->ballSensorWorks ? Qt::Checked : Qt::Unchecked;
			} else if (index.column() == 2 && role == Qt::CheckStateRole)
			{
				return robot->chargerWorks ? Qt::Checked : Qt::Unchecked;
			} else if (role == Qt::BackgroundRole)
			{
				if (index.row() < _env->yellow().size())
				{
					return QVariant(QColor(255, 255, 128));
				} else {
					return QVariant(QColor(128, 128, 255));
				}
			}
		}
		return QVariant();
	}

	virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
	{
		Robot *robot = robotForRow(index.row());
		switch (index.column())
		{
			case 0:
				robot->visibility = value.toInt();
				return true;

			case 1:
				robot->ballSensorWorks = value.toBool();
				return true;

			case 2:
				robot->chargerWorks = value.toBool();
				return true;
		}

		return false;
	}

	virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
	{
		if (role == Qt::DisplayRole)
		{
			if (orientation == Qt::Horizontal)
			{
				return columnNames[section];
			} else if (_env)
			{
				return QVariant(robotForRow(section)->shell);
			}
		}
		return QVariant();
	}

private:
	Environment *_env;
};

////////////////////////////////////
// SimulatorWindow class
////////////////////////////////////
SimulatorWindow::SimulatorWindow(Environment *env, QWidget* parent):
	QMainWindow(parent), _env(env)
{
	_ui.setupUi(this);

	// renderer setup
	_render = _ui.renderViewWidget;

	// connect renderer to simulator
	connect(_env, SIGNAL(addNewRobot(bool,int)), _render, SLOT(addRobot(bool,int)));
	connect(_env, SIGNAL(removeExistingRobot(bool,int)), _render, SLOT(removeRobot(bool,int)));
	connect(_env, SIGNAL(setRobotPose(bool,int,QVector3D,qreal,QVector3D)),
			    _render, SLOT(setRobotPose(bool,int,QVector3D,qreal,QVector3D)));

	// set up table
	_model = new RobotTableModel(_env);
	_ui.robotTable->setModel(_model);
	_ui.robotTable->resizeColumnsToContents();

	// start up environment
	_env->init();
}

void SimulatorWindow::on_dropFrame_clicked()
{
	if (_env)
	{
		_env->dropFrame();
	}
}

void SimulatorWindow::on_ballVisibility_valueChanged(int value)
{
	if (_env)
	{
		_env->ballVisibility = value;
	}
}
<commit_msg>fixed problem causing config window to not show up<commit_after>#include "SimulatorWindow.hpp"
#include "rendering/SimRenderView.hpp"
#include "Physics/Environment.hpp"

#include <QAbstractTableModel>

static const QString columnNames[] = {
	"Visibility",
	"Ball Sensor",
	"Charger"
};

class RobotTableModel: public QAbstractTableModel
{
public:
	RobotTableModel(Environment *env)
	: _env(env)
	{
		layoutChanged();
	}

	Robot *robotForRow(int row) const
	{
		const Environment::RobotMap *map;
		if (row < _env->yellow().size())
		{
			map = &_env->yellow();
		} else {
			row -= _env->yellow().size();
			map = &_env->blue();
		}
		Environment::RobotMap::const_iterator i = map->begin();
		i += row;
		return *i;
	}

	virtual int columnCount(const QModelIndex &parent) const
	{
		return sizeof(columnNames) / sizeof(columnNames[0]);
	}

	virtual int rowCount(const QModelIndex &parent) const
	{
		if (_env)
		{
			return _env->blue().size() + _env->yellow().size();
		} else {
			return 0;
		}
	}

	virtual Qt::ItemFlags flags(const QModelIndex &index) const
	{
		Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
		if (index.column() == 0)
		{
			flags |= Qt::ItemIsEditable;
		} else {
			flags |= Qt::ItemIsUserCheckable;
		}

		return flags;
	}

	virtual QVariant data(const QModelIndex &index, int role) const
	{
		if (_env)
		{
			Robot *robot = robotForRow(index.row());

			if (index.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
			{
				return QVariant(robot->visibility);
			} else if (index.column() == 1 && role == Qt::CheckStateRole)
			{
				return robot->ballSensorWorks ? Qt::Checked : Qt::Unchecked;
			} else if (index.column() == 2 && role == Qt::CheckStateRole)
			{
				return robot->chargerWorks ? Qt::Checked : Qt::Unchecked;
			} else if (role == Qt::BackgroundRole)
			{
				if (index.row() < _env->yellow().size())
				{
					return QVariant(QColor(255, 255, 128));
				} else {
					return QVariant(QColor(128, 128, 255));
				}
			}
		}
		return QVariant();
	}

	virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
	{
		Robot *robot = robotForRow(index.row());
		switch (index.column())
		{
			case 0:
				robot->visibility = value.toInt();
				return true;

			case 1:
				robot->ballSensorWorks = value.toBool();
				return true;

			case 2:
				robot->chargerWorks = value.toBool();
				return true;
		}

		return false;
	}

	virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
	{
		if (role == Qt::DisplayRole)
		{
			if (orientation == Qt::Horizontal)
			{
				return columnNames[section];
			} else if (_env)
			{
				return QVariant(robotForRow(section)->shell);
			}
		}
		return QVariant();
	}

private:
	Environment *_env;
};

////////////////////////////////////
// SimulatorWindow class
////////////////////////////////////
SimulatorWindow::SimulatorWindow(Environment *env, QWidget* parent):
	QMainWindow(parent), _env(env)
{
	_ui.setupUi(this);

	// renderer setup
	_render = _ui.renderViewWidget;

	// connect renderer to simulator
	connect(_env, SIGNAL(addNewRobot(bool,int)), _render, SLOT(addRobot(bool,int)));
	connect(_env, SIGNAL(removeExistingRobot(bool,int)), _render, SLOT(removeRobot(bool,int)));
	connect(_env, SIGNAL(setRobotPose(bool,int,QVector3D,qreal,QVector3D)),
			    _render, SLOT(setRobotPose(bool,int,QVector3D,qreal,QVector3D)));

	// start up environment
	_env->init();

	// set up table
	_model = new RobotTableModel(_env);
	_ui.robotTable->setModel(_model);
	_ui.robotTable->resizeColumnsToContents();
}

void SimulatorWindow::on_dropFrame_clicked()
{
	if (_env)
	{
		_env->dropFrame();
	}
}

void SimulatorWindow::on_ballVisibility_valueChanged(int value)
{
	if (_env)
	{
		_env->ballVisibility = value;
	}
}
<|endoftext|>


<commit_before>#include "playing_screen.h"

#include "game_manager.h"
#include "../hmi/hmi_manager.h"

void PlayingScreen::init() {
	ball.init();
	ball.set_queue(true);
	pad1.init();
	pad1.reverse(true);
	pad2.init();

	if (game.data.last_winner == PLAYER1) {
		ball_position = HMI_WIDTH;
		ball_direction = -1;
	} else {
		ball_position = -HMI_WIDTH;
		ball_direction = 1;
	}

	ball_speed.init();
	ball_speed.set_duration(60000);
	ball_speed.start();

	hmi.log("C'est (re)parti !\n");
}

void PlayingScreen::animate() {
	hmi.leds.clear();
	ball_speed.animate();

	// Compute ball speed and new position
	float real_speed = HMI_DTMS / (500.0 + (1 - ball_speed) * 1000.0 - ball_local_speed) * HMI_WIDTH * 2;
	ball_position += real_speed * ball_direction;
	ball.set_shiny(HMI_WIDTH - 20 < ball_position || 20 - HMI_WIDTH > ball_position);
	ball.set_position(ball_position);

	// Check for win
	if (ball_position < -HMI_WIDTH) {
		game.data.last_winner = PLAYER2;
		game.data.p2score += 1;
		game.show_scores();
	}
	if (ball_position > HMI_WIDTH) {
		game.data.last_winner = PLAYER1;
		game.data.p1score += 1;
		game.show_scores();
	}

	// Play pads
	if (hmi.btn1.clicked() && pad1.can_fire()) {
		if (20 - HMI_WIDTH > ball_position) {
			pad1.fire(HMI_WIDTH + ball_position);
			ball_direction = 1;
			ball_local_speed = 15 * (20 - HMI_WIDTH - ball_position);
		} else {
			pad1.fire(20);
		}
	}
	if (hmi.btn2.clicked() && pad2.can_fire()) {
		if (HMI_WIDTH - 20 < ball_position) {
			pad2.fire(HMI_WIDTH - ball_position);
			ball_direction = -1;
			ball_local_speed = 15 * (20 - HMI_WIDTH + ball_position);
		} else {
			pad2.fire(20);
		}
	}

	// Update all
	pad1.animate();
	pad2.animate();
	ball.animate();
}
<commit_msg>Change some speed constants<commit_after>#include "playing_screen.h"

#include "game_manager.h"
#include "../hmi/hmi_manager.h"

void PlayingScreen::init() {
	ball.init();
	ball.set_queue(true);
	pad1.init();
	pad1.reverse(true);
	pad2.init();

	if (game.data.last_winner == PLAYER1) {
		ball_position = HMI_WIDTH;
		ball_direction = -1;
	} else {
		ball_position = -HMI_WIDTH;
		ball_direction = 1;
	}

	ball_speed.init();
	ball_speed.set_duration(60000);
	ball_speed.start();

	hmi.log("C'est (re)parti !\n");
}

void PlayingScreen::animate() {
	hmi.leds.clear();
	ball_speed.animate();

	// Compute ball speed and new position
	float real_speed = HMI_DTMS / (600.0 + (1 - ball_speed) * 1000.0 - ball_local_speed) * HMI_WIDTH * 2;
	if (real_speed > HMI_DTMS / 500.0 * HMI_WIDTH * 2)
		real_speed = HMI_DTMS / 500.0 * HMI_WIDTH * 2;
	ball_position += real_speed * ball_direction;
	ball.set_shiny(
		(HMI_WIDTH - 20 < ball_position && ball_direction > 0) ||
		(20 - HMI_WIDTH > ball_position && ball_direction < 0)
	);
	ball.set_position(ball_position);

	// Check for win
	if (ball_position < -HMI_WIDTH) {
		game.data.last_winner = PLAYER2;
		game.data.p2score += 1;
		game.show_scores();
	}
	if (ball_position > HMI_WIDTH) {
		game.data.last_winner = PLAYER1;
		game.data.p1score += 1;
		game.show_scores();
	}

	// Play pads
	if (hmi.btn1.clicked() && pad1.can_fire()) {
		if (20 - HMI_WIDTH > ball_position) {
			pad1.fire(HMI_WIDTH + ball_position);
			ball_direction = 1;
			ball_local_speed = 50 * (20 - HMI_WIDTH - ball_position);
		} else {
			pad1.fire(20);
		}
	}
	if (hmi.btn2.clicked() && pad2.can_fire()) {
		if (HMI_WIDTH - 20 < ball_position) {
			pad2.fire(HMI_WIDTH - ball_position);
			ball_direction = -1;
			ball_local_speed = 50 * (20 - HMI_WIDTH + ball_position);
		} else {
			pad2.fire(20);
		}
	}

	// Update all
	pad1.animate();
	pad2.animate();
	ball.animate();
}
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * 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 "MediaSet.hpp"

#include "utils/utils.hpp"
#include <gst/gst.h>
#include <KurentoException.hpp>

/* This is included to avoid problems with slots and lamdas */
#include <type_traits>
#include <sigc++/sigc++.h>
namespace sigc
{
template <typename Functor>
struct functor_trait<Functor, false> {
  typedef decltype (::sigc::mem_fun (std::declval<Functor &> (),
                                     &Functor::operator() ) ) _intermediate;

  typedef typename _intermediate::result_type result_type;
  typedef Functor functor_type;
};
}

#define GST_CAT_DEFAULT kurento_media_set
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaSet"

#define COLLECTOR_INTERVAL 240000

namespace kurento
{

MediaSet &
MediaSet::getMediaSet()
{
  static MediaSet mediaSet;

  return mediaSet;
}

MediaSet::MediaSet()
{
  Glib::RefPtr<Glib::TimeoutSource> timeout;

  context = Glib::MainContext::create();
  loop = Glib::MainLoop::create (context, true);

  thread = Glib::Thread::create ([&] () {
    context->acquire();
    loop->run();
    GST_DEBUG ("Main loop thread stopped");
  });

  timeout = Glib::TimeoutSource::create (COLLECTOR_INTERVAL);
  timeout->connect ( [&] () -> bool {
    Glib::Threads::RecMutex::Lock lock (mutex);
    auto sessions = sessionInUse;

    lock.release();

    GST_DEBUG ("Running garbage collector");

    for (auto it : sessions) {
      if (it.second) {
        lock.acquire();
        sessionInUse[it.first] = false;
        lock.release();
      } else {
        GST_WARNING ("Session timeout: %s", it.first.c_str() );
        unrefSession (it.first);
      }
    }
    return true;
  });
  timeout->attach (context);
}

MediaSet::~MediaSet ()
{
  Monitor monitor (mutex);

  terminated = true;

  if (!objectsMap.empty() ) {
    std::cerr << "Warning: Still " + std::to_string (objectsMap.size() ) +
              " object/s alive" << std::endl;
  }

  if (!sessionMap.empty() ) {
    std::cerr << "Warning: Still " + std::to_string (sessionMap.size() ) +
              " session/s alive" << std::endl;
  }

  if (!sessionInUse.empty() ) {
    std::cerr << "Warning: Still " + std::to_string (sessionInUse.size() ) +
              " session/s with timeout" << std::endl;
  }

  childrenMap.clear();
  sessionMap.clear();

  if (Glib::Thread::self() != thread) {
    Glib::RefPtr<Glib::IdleSource> source = Glib::IdleSource::create ();
    source->connect (sigc::mem_fun (*this, &MediaSet::finishLoop) );
    source->attach (context);

    thread->join();
  } else {
    finishLoop();
  }

  threadPool.shutdown (true);
}

void
MediaSet::executeOnMainLoop (std::function<void () > func, bool force)
{
  if (terminated || !loop->is_running() ) {
    if (force) {
      func ();
    }

    return;
  }

  if (Glib::Thread::self() == thread) {
    func ();
  } else {
    Glib::Threads::Cond cond;
    Glib::Threads::Mutex mutex;
    Glib::Threads::Mutex::Lock lock (mutex);
    bool done = false;
    Glib::RefPtr<Glib::IdleSource> source = Glib::IdleSource::create ();

    source->connect ([&] ()->bool {
      Glib::Threads::Mutex::Lock lock (mutex);

      try {
        func ();
      } catch (...) {
      }

      done = true;
      cond.signal();

      return false;
    });

    source->attach (context);

    while (!done) {
      cond.wait (mutex);
    }
  }
}

bool
MediaSet::finishLoop ()
{
  loop->quit();

  return false;
}

std::shared_ptr<MediaObjectImpl>
MediaSet::ref (MediaObjectImpl *mediaObjectPtr)
{
  Monitor monitor (mutex);

  std::shared_ptr<MediaObjectImpl> mediaObject;

  if (mediaObjectPtr == NULL) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Invalid object");
  }

  try {
    mediaObject = std::dynamic_pointer_cast<MediaObjectImpl>
                  (mediaObjectPtr->shared_from_this() );
  } catch (std::bad_weak_ptr e) {
    mediaObject =  std::shared_ptr<MediaObjectImpl> (mediaObjectPtr, [] (
    MediaObjectImpl * obj) {
      if (!MediaSet::getMediaSet().terminated) {
        MediaSet::getMediaSet().releasePointer (obj);
      } else {
        delete obj;
      }
    });
  }

  objectsMap[mediaObject->getId()] = std::weak_ptr<MediaObjectImpl> (mediaObject);

  if (mediaObject->getParent() ) {
    std::shared_ptr<MediaObjectImpl> parent = std::dynamic_pointer_cast
        <MediaObjectImpl> (mediaObject->getParent() );

    ref (parent.get() );
    childrenMap[parent->getId()][mediaObject->getId()] = mediaObject;
  }

  return mediaObject;
}

void
MediaSet::ref (const std::string &sessionId,
               std::shared_ptr<MediaObjectImpl> mediaObject)
{
  Monitor monitor (mutex);

  keepAliveSession (sessionId, true);

  if (mediaObject->getParent() ) {
    ref (sessionId,
         std::dynamic_pointer_cast<MediaObjectImpl> (mediaObject->getParent() ) );
  }

  sessionMap[sessionId][mediaObject->getId()] = mediaObject;
  reverseSessionMap[mediaObject->getId()].insert (sessionId);
  ref (mediaObject.get() );
}

void
MediaSet::keepAliveSession (const std::string &sessionId)
{
  keepAliveSession (sessionId, false);
}

void
MediaSet::keepAliveSession (const std::string &sessionId, bool create)
{
  Monitor monitor (mutex);;

  auto it = sessionInUse.find (sessionId);

  if (it == sessionInUse.end() ) {
    if (create) {
      sessionInUse[sessionId] = true;
    } else {
      throw KurentoException (INVALID_SESSION, "Invalid session");
    }
  } else {
    it->second = true;
  }
}

void
MediaSet::releaseSession (const std::string &sessionId)
{
  Monitor monitor (mutex);

  auto it = sessionMap.find (sessionId);

  if (it != sessionMap.end() ) {
    auto objects = it->second;

    for (auto it2 : objects) {
      release (it2.second);
    }
  }

  sessionMap.erase (sessionId);
  sessionInUse.erase (sessionId);
}

void
MediaSet::unrefSession (const std::string &sessionId)
{
  Monitor monitor (mutex);

  auto it = sessionMap.find (sessionId);

  if (it != sessionMap.end() ) {
    auto objects = it->second;

    for (auto it2 : objects) {
      unref (sessionId, it2.second);
    }
  }

  sessionMap.erase (sessionId);
  sessionInUse.erase (sessionId);
}

void
MediaSet::unref (const std::string &sessionId,
                 std::shared_ptr< MediaObjectImpl > mediaObject)
{
  Monitor monitor (mutex);

  if (!mediaObject) {
    return;
  }

  auto it = sessionMap.find (sessionId);

  if (it != sessionMap.end() ) {
    auto it2 = it->second.find (mediaObject->getId() );

    if (it2 == it->second.end() ) {
      return;
    }

    it->second.erase (it2);

    if (it->second.size() == 0) {
      unrefSession (sessionId);
    }
  }

  auto it3 = reverseSessionMap.find (mediaObject->getId() );

  if (it3 != reverseSessionMap.end() ) {
    it3->second.erase (sessionId);

    if (it3->second.empty() ) {
      std::shared_ptr<MediaObjectImpl> parent;

      // Object has been removed from all the sessions, remove it from childrenMap
      childrenMap.erase (mediaObject->getId() );

      parent = std::dynamic_pointer_cast<MediaObjectImpl> (mediaObject->getParent() );

      if (parent) {
        childrenMap[parent->getId()].erase (mediaObject->getId() );
      }

      auto childrenIt = childrenMap.find (mediaObject->getId() );

      if (childrenIt != childrenMap.end() ) {
        auto childMap = childrenIt->second;

        for (auto child : childMap) {
          unref (sessionId, child.second);
        }
      }
    }
  }
}

void
MediaSet::unref (const std::string &sessionId,
                 const std::string &mediaObjectRef)
{
  std::shared_ptr <MediaObjectImpl> object;

  try {
    object = getMediaObject (mediaObjectRef);
  } catch (KurentoException e) {
    return;
  }

  unref (sessionId, object);
}

void
MediaSet::unref (const std::string &sessionId, const uint64_t &mediaObjectRef)
{
  std::shared_ptr <MediaObjectImpl> object;

  try {
    object = getMediaObject (mediaObjectRef);
  } catch (KurentoException e) {
    return;
  }

  unref (sessionId, object);
}

void MediaSet::releasePointer (MediaObjectImpl *mediaObject)
{
  Monitor monitor (mutex);

  objectsMap.erase (mediaObject->getId() );

  if (!terminated) {
    threadPool.push ( [mediaObject] () {
      GST_DEBUG ("Destroying %s", mediaObject->getIdStr().c_str() );
      delete mediaObject;
    });
  } else {
    GST_DEBUG ("Thread pool finished, destroying on the same thread %s",
               mediaObject->getIdStr().c_str() );
    delete mediaObject;
  }
}

void MediaSet::release (std::shared_ptr< MediaObjectImpl > mediaObject)
{
  Monitor monitor (mutex);

  auto it = reverseSessionMap.find (mediaObject->getId() );

  if (it == reverseSessionMap.end() ) {
    /* Already released */
    return;
  }

  auto sessions = it->second;

  for (auto it2 : sessions) {
    unref (it2, mediaObject);
  }

  objectsMap.erase (mediaObject->getId() );
}

void MediaSet::release (const std::string &mediaObjectRef)
{
  try {
    std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

    release (obj);
  } catch (...) {
    /* Do not raise exception if it is already released*/
  }
}

void MediaSet::release (const uint64_t &mediaObjectRef)
{
  try {
    std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

    release (obj);
  } catch (...) {
    /* Do not raise exception if it is already released*/
  }
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const std::string &mediaObjectRef)
{
  uint64_t objectRef;

  try {
#if __WORDSIZE == 64
    objectRef = std::stoul (mediaObjectRef);
#else
    objectRef = std::stoull (mediaObjectRef);
#endif
  } catch (...) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND,
                            "Invalid object reference");
  }

  return getMediaObject (objectRef);
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const uint64_t &mediaObjectRef)
{
  std::shared_ptr <MediaObjectImpl> objectLocked;
  Monitor monitor (mutex);

  auto it = objectsMap.find (mediaObjectRef);

  if (it == objectsMap.end() ) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Object not found");
  }

  try {
    objectLocked = it->second.lock();
  } catch (...) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Object not found");
  }

  if (!objectLocked) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Object not found");
  }

  return objectLocked;
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const std::string &sessionId,
                          const std::string &mediaObjectRef)
{
  std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

  ref (sessionId, obj);
  return obj;
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const std::string &sessionId,
                          const uint64_t &mediaObjectRef)
{
  std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

  ref (sessionId, obj);
  return obj;
}

MediaSet::Monitor::Monitor (Glib::Threads::RecMutex &mutex) : mutex (mutex)
{
  mutex.lock();
}

MediaSet::Monitor::~Monitor()
{
  mutex.unlock();
}

void MediaSet::deleter (MediaObjectImpl *mo)
{
  GST_ERROR ("Deleting");
}

MediaSet::StaticConstructor MediaSet::staticConstructor;

MediaSet::StaticConstructor::StaticConstructor()
{
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
}

} // kurento
<commit_msg>MediaSet: Do not erase element from children map before unref its children<commit_after>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * 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 "MediaSet.hpp"

#include "utils/utils.hpp"
#include <gst/gst.h>
#include <KurentoException.hpp>

/* This is included to avoid problems with slots and lamdas */
#include <type_traits>
#include <sigc++/sigc++.h>
namespace sigc
{
template <typename Functor>
struct functor_trait<Functor, false> {
  typedef decltype (::sigc::mem_fun (std::declval<Functor &> (),
                                     &Functor::operator() ) ) _intermediate;

  typedef typename _intermediate::result_type result_type;
  typedef Functor functor_type;
};
}

#define GST_CAT_DEFAULT kurento_media_set
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaSet"

#define COLLECTOR_INTERVAL 240000

namespace kurento
{

MediaSet &
MediaSet::getMediaSet()
{
  static MediaSet mediaSet;

  return mediaSet;
}

MediaSet::MediaSet()
{
  Glib::RefPtr<Glib::TimeoutSource> timeout;

  context = Glib::MainContext::create();
  loop = Glib::MainLoop::create (context, true);

  thread = Glib::Thread::create ([&] () {
    context->acquire();
    loop->run();
    GST_DEBUG ("Main loop thread stopped");
  });

  timeout = Glib::TimeoutSource::create (COLLECTOR_INTERVAL);
  timeout->connect ( [&] () -> bool {
    Glib::Threads::RecMutex::Lock lock (mutex);
    auto sessions = sessionInUse;

    lock.release();

    GST_DEBUG ("Running garbage collector");

    for (auto it : sessions) {
      if (it.second) {
        lock.acquire();
        sessionInUse[it.first] = false;
        lock.release();
      } else {
        GST_WARNING ("Session timeout: %s", it.first.c_str() );
        unrefSession (it.first);
      }
    }
    return true;
  });
  timeout->attach (context);
}

MediaSet::~MediaSet ()
{
  Monitor monitor (mutex);

  terminated = true;

  if (!objectsMap.empty() ) {
    std::cerr << "Warning: Still " + std::to_string (objectsMap.size() ) +
              " object/s alive" << std::endl;
  }

  if (!sessionMap.empty() ) {
    std::cerr << "Warning: Still " + std::to_string (sessionMap.size() ) +
              " session/s alive" << std::endl;
  }

  if (!sessionInUse.empty() ) {
    std::cerr << "Warning: Still " + std::to_string (sessionInUse.size() ) +
              " session/s with timeout" << std::endl;
  }

  childrenMap.clear();
  sessionMap.clear();

  if (Glib::Thread::self() != thread) {
    Glib::RefPtr<Glib::IdleSource> source = Glib::IdleSource::create ();
    source->connect (sigc::mem_fun (*this, &MediaSet::finishLoop) );
    source->attach (context);

    thread->join();
  } else {
    finishLoop();
  }

  threadPool.shutdown (true);
}

void
MediaSet::executeOnMainLoop (std::function<void () > func, bool force)
{
  if (terminated || !loop->is_running() ) {
    if (force) {
      func ();
    }

    return;
  }

  if (Glib::Thread::self() == thread) {
    func ();
  } else {
    Glib::Threads::Cond cond;
    Glib::Threads::Mutex mutex;
    Glib::Threads::Mutex::Lock lock (mutex);
    bool done = false;
    Glib::RefPtr<Glib::IdleSource> source = Glib::IdleSource::create ();

    source->connect ([&] ()->bool {
      Glib::Threads::Mutex::Lock lock (mutex);

      try {
        func ();
      } catch (...) {
      }

      done = true;
      cond.signal();

      return false;
    });

    source->attach (context);

    while (!done) {
      cond.wait (mutex);
    }
  }
}

bool
MediaSet::finishLoop ()
{
  loop->quit();

  return false;
}

std::shared_ptr<MediaObjectImpl>
MediaSet::ref (MediaObjectImpl *mediaObjectPtr)
{
  Monitor monitor (mutex);

  std::shared_ptr<MediaObjectImpl> mediaObject;

  if (mediaObjectPtr == NULL) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Invalid object");
  }

  try {
    mediaObject = std::dynamic_pointer_cast<MediaObjectImpl>
                  (mediaObjectPtr->shared_from_this() );
  } catch (std::bad_weak_ptr e) {
    mediaObject =  std::shared_ptr<MediaObjectImpl> (mediaObjectPtr, [] (
    MediaObjectImpl * obj) {
      if (!MediaSet::getMediaSet().terminated) {
        MediaSet::getMediaSet().releasePointer (obj);
      } else {
        delete obj;
      }
    });
  }

  objectsMap[mediaObject->getId()] = std::weak_ptr<MediaObjectImpl> (mediaObject);

  if (mediaObject->getParent() ) {
    std::shared_ptr<MediaObjectImpl> parent = std::dynamic_pointer_cast
        <MediaObjectImpl> (mediaObject->getParent() );

    ref (parent.get() );
    childrenMap[parent->getId()][mediaObject->getId()] = mediaObject;
  }

  return mediaObject;
}

void
MediaSet::ref (const std::string &sessionId,
               std::shared_ptr<MediaObjectImpl> mediaObject)
{
  Monitor monitor (mutex);

  keepAliveSession (sessionId, true);

  if (mediaObject->getParent() ) {
    ref (sessionId,
         std::dynamic_pointer_cast<MediaObjectImpl> (mediaObject->getParent() ) );
  }

  sessionMap[sessionId][mediaObject->getId()] = mediaObject;
  reverseSessionMap[mediaObject->getId()].insert (sessionId);
  ref (mediaObject.get() );
}

void
MediaSet::keepAliveSession (const std::string &sessionId)
{
  keepAliveSession (sessionId, false);
}

void
MediaSet::keepAliveSession (const std::string &sessionId, bool create)
{
  Monitor monitor (mutex);;

  auto it = sessionInUse.find (sessionId);

  if (it == sessionInUse.end() ) {
    if (create) {
      sessionInUse[sessionId] = true;
    } else {
      throw KurentoException (INVALID_SESSION, "Invalid session");
    }
  } else {
    it->second = true;
  }
}

void
MediaSet::releaseSession (const std::string &sessionId)
{
  Monitor monitor (mutex);

  auto it = sessionMap.find (sessionId);

  if (it != sessionMap.end() ) {
    auto objects = it->second;

    for (auto it2 : objects) {
      release (it2.second);
    }
  }

  sessionMap.erase (sessionId);
  sessionInUse.erase (sessionId);
}

void
MediaSet::unrefSession (const std::string &sessionId)
{
  Monitor monitor (mutex);

  auto it = sessionMap.find (sessionId);

  if (it != sessionMap.end() ) {
    auto objects = it->second;

    for (auto it2 : objects) {
      unref (sessionId, it2.second);
    }
  }

  sessionMap.erase (sessionId);
  sessionInUse.erase (sessionId);
}

void
MediaSet::unref (const std::string &sessionId,
                 std::shared_ptr< MediaObjectImpl > mediaObject)
{
  Monitor monitor (mutex);

  if (!mediaObject) {
    return;
  }

  auto it = sessionMap.find (sessionId);

  if (it != sessionMap.end() ) {
    auto it2 = it->second.find (mediaObject->getId() );

    if (it2 == it->second.end() ) {
      return;
    }

    it->second.erase (it2);

    if (it->second.size() == 0) {
      unrefSession (sessionId);
    }
  }

  auto it3 = reverseSessionMap.find (mediaObject->getId() );

  if (it3 != reverseSessionMap.end() ) {
    it3->second.erase (sessionId);

    if (it3->second.empty() ) {
      std::shared_ptr<MediaObjectImpl> parent;

      parent = std::dynamic_pointer_cast<MediaObjectImpl> (mediaObject->getParent() );

      if (parent) {
        childrenMap[parent->getId()].erase (mediaObject->getId() );
      }

      auto childrenIt = childrenMap.find (mediaObject->getId() );

      if (childrenIt != childrenMap.end() ) {
        auto childMap = childrenIt->second;

        for (auto child : childMap) {
          unref (sessionId, child.second);
        }
      }

      childrenMap.erase (mediaObject->getId() );
    }
  }
}

void
MediaSet::unref (const std::string &sessionId,
                 const std::string &mediaObjectRef)
{
  std::shared_ptr <MediaObjectImpl> object;

  try {
    object = getMediaObject (mediaObjectRef);
  } catch (KurentoException e) {
    return;
  }

  unref (sessionId, object);
}

void
MediaSet::unref (const std::string &sessionId, const uint64_t &mediaObjectRef)
{
  std::shared_ptr <MediaObjectImpl> object;

  try {
    object = getMediaObject (mediaObjectRef);
  } catch (KurentoException e) {
    return;
  }

  unref (sessionId, object);
}

void MediaSet::releasePointer (MediaObjectImpl *mediaObject)
{
  Monitor monitor (mutex);

  objectsMap.erase (mediaObject->getId() );

  if (!terminated) {
    threadPool.push ( [mediaObject] () {
      GST_DEBUG ("Destroying %s", mediaObject->getIdStr().c_str() );
      delete mediaObject;
    });
  } else {
    GST_DEBUG ("Thread pool finished, destroying on the same thread %s",
               mediaObject->getIdStr().c_str() );
    delete mediaObject;
  }
}

void MediaSet::release (std::shared_ptr< MediaObjectImpl > mediaObject)
{
  Monitor monitor (mutex);

  auto it = reverseSessionMap.find (mediaObject->getId() );

  if (it == reverseSessionMap.end() ) {
    /* Already released */
    return;
  }

  auto sessions = it->second;

  for (auto it2 : sessions) {
    unref (it2, mediaObject);
  }

  objectsMap.erase (mediaObject->getId() );
}

void MediaSet::release (const std::string &mediaObjectRef)
{
  try {
    std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

    release (obj);
  } catch (...) {
    /* Do not raise exception if it is already released*/
  }
}

void MediaSet::release (const uint64_t &mediaObjectRef)
{
  try {
    std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

    release (obj);
  } catch (...) {
    /* Do not raise exception if it is already released*/
  }
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const std::string &mediaObjectRef)
{
  uint64_t objectRef;

  try {
#if __WORDSIZE == 64
    objectRef = std::stoul (mediaObjectRef);
#else
    objectRef = std::stoull (mediaObjectRef);
#endif
  } catch (...) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND,
                            "Invalid object reference");
  }

  return getMediaObject (objectRef);
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const uint64_t &mediaObjectRef)
{
  std::shared_ptr <MediaObjectImpl> objectLocked;
  Monitor monitor (mutex);

  auto it = objectsMap.find (mediaObjectRef);

  if (it == objectsMap.end() ) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Object not found");
  }

  try {
    objectLocked = it->second.lock();
  } catch (...) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Object not found");
  }

  if (!objectLocked) {
    throw KurentoException (MEDIA_OBJECT_NOT_FOUND, "Object not found");
  }

  return objectLocked;
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const std::string &sessionId,
                          const std::string &mediaObjectRef)
{
  std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

  ref (sessionId, obj);
  return obj;
}

std::shared_ptr< MediaObjectImpl >
MediaSet::getMediaObject (const std::string &sessionId,
                          const uint64_t &mediaObjectRef)
{
  std::shared_ptr< MediaObjectImpl > obj = getMediaObject (mediaObjectRef);

  ref (sessionId, obj);
  return obj;
}

MediaSet::Monitor::Monitor (Glib::Threads::RecMutex &mutex) : mutex (mutex)
{
  mutex.lock();
}

MediaSet::Monitor::~Monitor()
{
  mutex.unlock();
}

void MediaSet::deleter (MediaObjectImpl *mo)
{
  GST_ERROR ("Deleting");
}

MediaSet::StaticConstructor MediaSet::staticConstructor;

MediaSet::StaticConstructor::StaticConstructor()
{
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
}

} // kurento
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////

#include <cppcoro/file.hpp>
#include <cppcoro/io_service.hpp>

#include <system_error>
#include <cassert>

#if CPPCORO_OS_WINNT
# define WIN32_LEAN_AND_MEAN
# include <Windows.h>
#endif

cppcoro::file::~file()
{}

std::uint64_t cppcoro::file::size() const
{
#if CPPCORO_OS_WINNT
	LARGE_INTEGER size;
	BOOL ok = ::GetFileSizeEx(m_fileHandle.handle(), &size);
	if (!ok)
	{
		DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error getting file size: GetFileSizeEx"
		};
	}

	return size.QuadPart;
#endif
}

cppcoro::file::file(detail::win32::safe_handle&& fileHandle) noexcept
	: m_fileHandle(std::move(fileHandle))
{
}

cppcoro::detail::win32::safe_handle cppcoro::file::open(
	detail::win32::dword_t fileAccess,
	io_context& ioContext,
	const std::experimental::filesystem::path& path,
	file_open_mode openMode,
	file_share_mode shareMode,
	file_buffering_mode bufferingMode)
{
	DWORD flags = FILE_FLAG_OVERLAPPED;
	if ((bufferingMode & file_buffering_mode::random_access) == file_buffering_mode::random_access)
	{
		flags |= FILE_FLAG_RANDOM_ACCESS;
	}
	if ((bufferingMode & file_buffering_mode::sequential) == file_buffering_mode::sequential)
	{
		flags |= FILE_FLAG_SEQUENTIAL_SCAN;
	}
	if ((bufferingMode & file_buffering_mode::write_through) == file_buffering_mode::write_through)
	{
		flags |= FILE_FLAG_WRITE_THROUGH;
	}
	if ((bufferingMode & file_buffering_mode::temporary) == file_buffering_mode::temporary)
	{
		flags |= FILE_ATTRIBUTE_TEMPORARY;
	}
	if ((bufferingMode & file_buffering_mode::unbuffered) == file_buffering_mode::unbuffered)
	{
		flags |= FILE_FLAG_NO_BUFFERING;
	}

	DWORD shareFlags = 0;
	if ((shareMode & file_share_mode::read) == file_share_mode::read)
	{
		shareFlags |= FILE_SHARE_READ;
	}
	if ((shareMode & file_share_mode::write) == file_share_mode::write)
	{
		shareFlags |= FILE_SHARE_WRITE;
	}
	if ((shareMode & file_share_mode::delete_) == file_share_mode::delete_)
	{
		shareFlags |= FILE_SHARE_DELETE;
	}

	DWORD creationDisposition = 0;
	switch (openMode)
	{
	case file_open_mode::create_or_open:
		creationDisposition = OPEN_ALWAYS;
		break;
	case file_open_mode::create_always:
		creationDisposition = CREATE_ALWAYS;
		break;
	case file_open_mode::create_new:
		creationDisposition = CREATE_NEW;
		break;
	case file_open_mode::open_existing:
		creationDisposition = OPEN_EXISTING;
		break;
	case file_open_mode::truncate_existing:
		creationDisposition = TRUNCATE_EXISTING;
		break;
	}

	// Open the file
	detail::win32::safe_handle fileHandle(
		::CreateFileW(
			path.c_str(),
			fileAccess,
			shareFlags,
			nullptr,
			creationDisposition,
			flags,
			nullptr));
	if (fileHandle.handle() == INVALID_HANDLE_VALUE)
	{
		const DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error opening file: CreateFileW"
		};
	}

	// Associate with the I/O service's completion port.
	const HANDLE result = ::CreateIoCompletionPort(
		fileHandle.handle(),
		ioContext.native_iocp_handle(),
		0,
		0);
	if (result == nullptr)
	{
		const DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error opening file: CreateIoCompletionPort"
		};
	}

	// Configure I/O operations to avoid dispatching a completion event
	// to the I/O service if the operation completes synchronously.
	// This avoids unnecessary suspension/resuption of the awaiting coroutine.
	const BOOL ok = ::SetFileCompletionNotificationModes(
		fileHandle.handle(),
		FILE_SKIP_COMPLETION_PORT_ON_SUCCESS |
		FILE_SKIP_SET_EVENT_ON_HANDLE);
	if (!ok)
	{
		const DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error opening file: SetFileCompletionNotificationModes"
		};
	}

	return std::move(fileHandle);
}
<commit_msg>Fix usage of filesystem::path under libc++.<commit_after>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////

#include <cppcoro/file.hpp>
#include <cppcoro/io_service.hpp>

#include <system_error>
#include <cassert>

#if CPPCORO_OS_WINNT
# define WIN32_LEAN_AND_MEAN
# include <Windows.h>
#endif

cppcoro::file::~file()
{}

std::uint64_t cppcoro::file::size() const
{
#if CPPCORO_OS_WINNT
	LARGE_INTEGER size;
	BOOL ok = ::GetFileSizeEx(m_fileHandle.handle(), &size);
	if (!ok)
	{
		DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error getting file size: GetFileSizeEx"
		};
	}

	return size.QuadPart;
#endif
}

cppcoro::file::file(detail::win32::safe_handle&& fileHandle) noexcept
	: m_fileHandle(std::move(fileHandle))
{
}

cppcoro::detail::win32::safe_handle cppcoro::file::open(
	detail::win32::dword_t fileAccess,
	io_context& ioContext,
	const std::experimental::filesystem::path& path,
	file_open_mode openMode,
	file_share_mode shareMode,
	file_buffering_mode bufferingMode)
{
	DWORD flags = FILE_FLAG_OVERLAPPED;
	if ((bufferingMode & file_buffering_mode::random_access) == file_buffering_mode::random_access)
	{
		flags |= FILE_FLAG_RANDOM_ACCESS;
	}
	if ((bufferingMode & file_buffering_mode::sequential) == file_buffering_mode::sequential)
	{
		flags |= FILE_FLAG_SEQUENTIAL_SCAN;
	}
	if ((bufferingMode & file_buffering_mode::write_through) == file_buffering_mode::write_through)
	{
		flags |= FILE_FLAG_WRITE_THROUGH;
	}
	if ((bufferingMode & file_buffering_mode::temporary) == file_buffering_mode::temporary)
	{
		flags |= FILE_ATTRIBUTE_TEMPORARY;
	}
	if ((bufferingMode & file_buffering_mode::unbuffered) == file_buffering_mode::unbuffered)
	{
		flags |= FILE_FLAG_NO_BUFFERING;
	}

	DWORD shareFlags = 0;
	if ((shareMode & file_share_mode::read) == file_share_mode::read)
	{
		shareFlags |= FILE_SHARE_READ;
	}
	if ((shareMode & file_share_mode::write) == file_share_mode::write)
	{
		shareFlags |= FILE_SHARE_WRITE;
	}
	if ((shareMode & file_share_mode::delete_) == file_share_mode::delete_)
	{
		shareFlags |= FILE_SHARE_DELETE;
	}

	DWORD creationDisposition = 0;
	switch (openMode)
	{
	case file_open_mode::create_or_open:
		creationDisposition = OPEN_ALWAYS;
		break;
	case file_open_mode::create_always:
		creationDisposition = CREATE_ALWAYS;
		break;
	case file_open_mode::create_new:
		creationDisposition = CREATE_NEW;
		break;
	case file_open_mode::open_existing:
		creationDisposition = OPEN_EXISTING;
		break;
	case file_open_mode::truncate_existing:
		creationDisposition = TRUNCATE_EXISTING;
		break;
	}

	// Open the file
	detail::win32::safe_handle fileHandle(
		::CreateFileW(
			path.wstring().c_str(),
			fileAccess,
			shareFlags,
			nullptr,
			creationDisposition,
			flags,
			nullptr));
	if (fileHandle.handle() == INVALID_HANDLE_VALUE)
	{
		const DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error opening file: CreateFileW"
		};
	}

	// Associate with the I/O service's completion port.
	const HANDLE result = ::CreateIoCompletionPort(
		fileHandle.handle(),
		ioContext.native_iocp_handle(),
		0,
		0);
	if (result == nullptr)
	{
		const DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error opening file: CreateIoCompletionPort"
		};
	}

	// Configure I/O operations to avoid dispatching a completion event
	// to the I/O service if the operation completes synchronously.
	// This avoids unnecessary suspension/resuption of the awaiting coroutine.
	const BOOL ok = ::SetFileCompletionNotificationModes(
		fileHandle.handle(),
		FILE_SKIP_COMPLETION_PORT_ON_SUCCESS |
		FILE_SKIP_SET_EVENT_ON_HANDLE);
	if (!ok)
	{
		const DWORD errorCode = ::GetLastError();
		throw std::system_error
		{
			static_cast<int>(errorCode),
			std::system_category(),
			"error opening file: SetFileCompletionNotificationModes"
		};
	}

	return std::move(fileHandle);
}
<|endoftext|>


<commit_before>/*
#include <string.h>
#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include "rply/rply.h"

using namespace std;
using namespace CGAL;

typedef Exact_predicates_inexact_constructions_kernel K;
typedef Point_3<K> Point;
typedef Delaunay_triangulation_3<K> Delaunay;

// PLC:
map <unsigned, Point> plcVertices; // mapping between vertex coordinates and corresponding unique id

class TriangleFace
{
	public:
		unsigned int vertexIds[3];
}tempFace;

class TetrahedronCell
{
	public:
		unsigned int vertexIds[4];
}tempTet;

list <TriangleFace> plcFaces; // contains ids of vertices/points making the triangle

// Delaunay tetrahedralization:
Delaunay DT;

// CDT:
map <unsigned, Point> cdtVertices;
list <TriangleFace> cdtTets; // contains ids of vertices making tetrahedron

static unsigned tempPoint[3];
unsigned int dimensionId = 0;
static unsigned pointId = 0;

static int vertex_cb(p_ply_argument argument)
{
	long eol;
	ply_get_argument_user_data(argument, NULL, &eol);
	tempPoint[dimensionId++] = ply_get_argument_value(argument);

	// insert the vertex into plcVertex
	if (eol)
	{
		plcVertices[pointId++] = Point(tempPoint[0],tempPoint[1],tempPoint[2]);
		dimensionId = 0;
	}
}

static int face_cb(p_ply_argument argument)
{
	long length, value_index;

	ply_get_argument_property(argument, NULL, &length, &value_index);

        switch (value_index)
	{
        	case 0:
	        case 1:
        		tempFace.vertexIds[pointId++] = ply_get_argument_value(argument);
	                break;
        	case 2:
			tempFace.vertexIds[pointId] = ply_get_argument_value(argument);
			pointId = 0;
			plcFaces.push_front(tempFace);
	                break;
        	default:
                	cout << "Invalid number of points in a face specified :(";
			break;
        }

}

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
	string fileName;

	cout << "\nPlease enter input filename:\t";
	cin >> fileName;

	p_ply inputPLY = ply_open(fileName.c_str(), NULL, 0, NULL);

	if (!inputPLY) exit(0);

        if (!ply_read_header(inputPLY)) exit(0);

	if (!ply_read(inputPLY))
	{
		cout << "Cannot read the PLY file :(";
		exit(0);
	}

	// Initialize plcVertex and plcFaces
  	ply_set_read_cb(inputPLY, "vertex", "x", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "y", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "z", vertex_cb, NULL, 1);

	ply_set_read_cb(inputPLY, "face", "vertex_indices", face_cb, NULL, 0);

	ply_close(inputPLY);

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

	list <Point> tempPointList;
	unsigned int i = 0;

	for (map<unsigned, Point>::iterator pit = plcVertices.begin(); pit != plcVertices.end(); pit++, i++)
		tempPointList.push_front(plcVertices.find(i)->second);

    	DT.insert(tempPointList.begin(), tempPointList.end());

	cout << "\nInitial Delaunay tetrahedralization computed!!";
}
*/

class DegenerateVertexSetCandidate
{
	Vertex_handle degenSetVertex[5];
};

// returns true if  given vertices are co-spherical
bool areCospherical()
{

}

// removes duplicate vertex sets from global degeneracyQueue
void removeDuplicateDegenracies()
{

}

// finds all local degeneracies in DT and adds them to a global queue
void addLocalDegeneraciesToQueue(queue<DegenerateVertexSetCandidate> degeneracyQueue)
{

	DegenerateVertexSetCandidate degenerateSet;

	for (delaunay::Finite_cell_iterator cellIter = DT.finite_cells_begin(); cellIter != DT.finite_cells_end(); cellIter++)
	{
		for (unsigned int n = 0; n < 4; n++)
			degenerateSet.degenSetVertex[n] = cellIter->vertex(n);

		for (unsigned int j = 0; j < 4; j++)
			for (unsigned int k = 0; k < 4; k++)
				{
					if ((cellId->neighbor(j))->neighbor(k) == cellId)
						degenerateSet.degenSetVertex[4] = (cellIter->neighbor)->vertex(k);

					if (areCospherical(degenerateSet))
						degeneracyQueue.push_front(degenerateSet);
				}
	}

	removeDuplicateDegeneracies();
}

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{

	cout << "\nStarting local degeneracy removal...";

	// compute all local degeneracies in DT and add them to Q
	queue<DegenerateVertexSetCandidate> degeneracyQueue;
	addLocalDegeneraciesToQueue(degeneracyQueue);

	// repeat while Q != NULL
	while (degenercyQueue.size() != 0)
		for (queue<>::iterator qIter = degeneracyQueue.begin(); qIter != degeneracyQueue.end(); qIter++)
			{
				if (isDegeneracyRemovable(qIter))
					perturbRemove(qIter, degeneracyQueue);
				else
				{
					Vertex vb;
					computeBreakPoint(vb, qIter, degeneracyQueue);
					if (isEncroachingPLC(vb))
						boundaryProtection(vb);
					else
						inputPLCVertices.push(vb);
				}
			}

	cout << "Local degeneracy removal completed";
}

/*
// recovers the constraint faces
void recoverConstraintFaces()
{

}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}
*/
<commit_msg>added function to perform co-spherical test<commit_after>/*
#include <string.h>
#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include "rply/rply.h"

using namespace std;
using namespace CGAL;

typedef Exact_predicates_inexact_constructions_kernel K;
typedef Point_3<K> Point;
typedef Delaunay_triangulation_3<K> Delaunay;

// PLC:
map <unsigned, Point> plcVertices; // mapping between vertex coordinates and corresponding unique id

class TriangleFace
{
	public:
		unsigned int vertexIds[3];
}tempFace;

class TetrahedronCell
{
	public:
		unsigned int vertexIds[4];
}tempTet;

list <TriangleFace> plcFaces; // contains ids of vertices/points making the triangle

// Delaunay tetrahedralization:
Delaunay DT;

// CDT:
map <unsigned, Point> cdtVertices;
list <TriangleFace> cdtTets; // contains ids of vertices making tetrahedron

static unsigned tempPoint[3];
unsigned int dimensionId = 0;
static unsigned pointId = 0;

static int vertex_cb(p_ply_argument argument)
{
	long eol;
	ply_get_argument_user_data(argument, NULL, &eol);
	tempPoint[dimensionId++] = ply_get_argument_value(argument);

	// insert the vertex into plcVertex
	if (eol)
	{
		plcVertices[pointId++] = Point(tempPoint[0],tempPoint[1],tempPoint[2]);
		dimensionId = 0;
	}
}

static int face_cb(p_ply_argument argument)
{
	long length, value_index;

	ply_get_argument_property(argument, NULL, &length, &value_index);

        switch (value_index)
	{
        	case 0:
	        case 1:
        		tempFace.vertexIds[pointId++] = ply_get_argument_value(argument);
	                break;
        	case 2:
			tempFace.vertexIds[pointId] = ply_get_argument_value(argument);
			pointId = 0;
			plcFaces.push_front(tempFace);
	                break;
        	default:
                	cout << "Invalid number of points in a face specified :(";
			break;
        }

}

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
	string fileName;

	cout << "\nPlease enter input filename:\t";
	cin >> fileName;

	p_ply inputPLY = ply_open(fileName.c_str(), NULL, 0, NULL);

	if (!inputPLY) exit(0);

        if (!ply_read_header(inputPLY)) exit(0);

	if (!ply_read(inputPLY))
	{
		cout << "Cannot read the PLY file :(";
		exit(0);
	}

	// Initialize plcVertex and plcFaces
  	ply_set_read_cb(inputPLY, "vertex", "x", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "y", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "z", vertex_cb, NULL, 1);

	ply_set_read_cb(inputPLY, "face", "vertex_indices", face_cb, NULL, 0);

	ply_close(inputPLY);

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

	list <Point> tempPointList;
	unsigned int i = 0;

	for (map<unsigned, Point>::iterator pit = plcVertices.begin(); pit != plcVertices.end(); pit++, i++)
		tempPointList.push_front(plcVertices.find(i)->second);

    	DT.insert(tempPointList.begin(), tempPointList.end());

	cout << "\nInitial Delaunay tetrahedralization computed!!";
}
*/

class DegenerateVertexSetCandidate
{
	Vertex_handle degenSetVertex[5];
};

// returns true if  given vertices are co-spherical
bool areCospherical(DegenerateVertexSetCandidate degenSet)
{

	Point_3 p[5];

	for (unsigned int i = 0; i < 5; i++)
		p[i] = (degenSet.degenSetVertex[i])->point();

	if (CGAL::side_of_bounded_sphere(p[0],p[1],p[2],p[3],p[4]) == CGAL::ON_BOUNDARY)
		return true;
	else
		return false;
}

// removes duplicate vertex sets from global degeneracyQueue
void removeDuplicateDegenracies()
{

}

// finds all local degeneracies in DT and adds them to a global queue
void addLocalDegeneraciesToQueue(queue<DegenerateVertexSetCandidate> degeneracyQueue)
{

	DegenerateVertexSetCandidate degenerateSet;

	for (delaunay::Finite_cell_iterator cellIter = DT.finite_cells_begin(); cellIter != DT.finite_cells_end(); cellIter++)
	{
		for (unsigned int n = 0; n < 4; n++)
			degenerateSet.degenSetVertex[n] = cellIter->vertex(n);

		for (unsigned int j = 0; j < 4; j++)
			for (unsigned int k = 0; k < 4; k++)
				{
					if ((cellId->neighbor(j))->neighbor(k) == cellId)
						degenerateSet.degenSetVertex[4] = (cellIter->neighbor)->vertex(k);

					if (areCospherical(degenerateSet))
						degeneracyQueue.push_front(degenerateSet);
				}
	}

	removeDuplicateDegeneracies();
}

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{

	cout << "\nStarting local degeneracy removal...";

	// compute all local degeneracies in DT and add them to Q
	queue<DegenerateVertexSetCandidate> degeneracyQueue;
	addLocalDegeneraciesToQueue(degeneracyQueue);

	// repeat while Q != NULL
	while (degenercyQueue.size() != 0)
		for (queue<>::iterator qIter = degeneracyQueue.begin(); qIter != degeneracyQueue.end(); qIter++)
			{
				if (isDegeneracyRemovable(qIter))
					perturbRemove(qIter, degeneracyQueue);
				else
				{
					Vertex vb;
					computeBreakPoint(vb, qIter, degeneracyQueue);
					if (isEncroachingPLC(vb))
						boundaryProtection(vb);
					else
						inputPLCVertices.push(vb);
				}
			}

	cout << "Local degeneracy removal completed";
}

/*
// recovers the constraint faces
void recoverConstraintFaces()
{

}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}
*/
<|endoftext|>


<commit_before>/**
 * Master header of the `d2` library.
 */

#ifndef D2_HPP
#define D2_HPP

#include <d2/analysis.hpp>
#include <d2/concepts.hpp>
#include <d2/event_repository.hpp>
#include <d2/event_traits.hpp>
#include <d2/events.hpp>
#include <d2/filesystem_dispatcher.hpp>
#include <d2/lock_graph.hpp>
#include <d2/logging.hpp>
#include <d2/repository.hpp>
#include <d2/segment.hpp>
#include <d2/segmentation_graph.hpp>
#include <d2/sync_object.hpp>
#include <d2/thread.hpp>

#endif // !D2_HPP
<commit_msg>Remove the d2 master header, which is quite useless.<commit_after><|endoftext|>


<commit_before>#include <node.h>
#include <nan.h>
#include <rpcsvc/ypclnt.h>

using namespace v8;

extern "C" {
    int foreach_all(unsigned long instatus, char *inkey, int inkeylen, char *inval, int invallen, void *data);
}

NAN_METHOD(Match) {
    HandleScope scope;
    int error;

    if (args.Length() < 2) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("Second argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value inkey(args[1]->ToString());
    char *outval;
    int outvallen;

    error = yp_match(*domain, *mapname, *inkey, inkey.length(), &outval, &outvallen);
    if (YPERR_KEY == error) {
        return scope.Close(Undefined());
    } else if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(String::New(outval, outvallen));
}

NAN_METHOD(Next) {
    HandleScope scope;
    int error;

    if (args.Length() < 2) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("Second argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value inkey(args[1]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    char *outkey, *outval;
    int outkeylen, outvallen;

    error = yp_next(*domain, *mapname, *inkey, inkey.length(), &outkey, &outkeylen, &outval, &outvallen);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    Local<Object> obj = Object::New();
    obj->Set(String::New("key"), String::New(outkey, outkeylen));
    obj->Set(String::New("value"), String::New(outval, outvallen));
    return scope.Close(obj);
}

NAN_METHOD(First) {
    HandleScope scope;
    int error;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    char *outkey, *outval;
    int outkeylen, outvallen;

    error = yp_first(*domain, *mapname, &outkey, &outkeylen, &outval, &outvallen);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    Local<Object> obj = Object::New();
    obj->Set(String::New("key"), String::New(outkey, outkeylen));
    obj->Set(String::New("value"), String::New(outval, outvallen));
    return scope.Close(obj);
}

NAN_METHOD(Master) {
    HandleScope scope;
    int error;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    char *outname;

    error = yp_master(*domain, *mapname, &outname);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(String::New(outname));
}

NAN_METHOD(Order) {
    HandleScope scope;
    int error;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    int outorder;

    error = yp_order(*domain, *mapname, &outorder);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(Integer::New(outorder));
}

NAN_METHOD(All) {
    HandleScope scope;
    int error;

    if (args.Length() < 2) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }
    if (!args[1]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Second argument must be function")));
        return scope.Close(Undefined());
    }
    Local<Function> user_cb = Local<Function>::Cast(args[1]);
    struct ypall_callback cb;
    cb.foreach = foreach_all;
    cb.data = (char *) &user_cb;

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));

    error = yp_all(*domain, *mapname, &cb);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(Undefined());
}

extern "C"
int foreach_all(unsigned long instatus, char *inkey, int inkeylen, char *inval, int invallen, void *data) {
    Local<Function> *cb = static_cast<Local<Function> *>(data);
    const int argc = 3;
    Local<Value> argv[argc] = {
        Local<Value>::New(Integer::NewFromUnsigned(instatus)),
        Local<Value>::New(String::New(inkey, inkeylen)),
        Local<Value>::New(String::New(inval, invallen))
    };
    Local<Value> ret = (*cb)->Call(Context::GetCurrent()->Global(), argc, argv);
    return ret->Int32Value();
}

NAN_METHOD(CreateObject) {
    HandleScope scope;
    char* domp;
    int error;

    Local<Object> obj = Object::New();
    error = yp_get_default_domain(&domp);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }
    obj->Set(NanSymbol("domain_name"), String::New(domp));
    obj->Set(NanSymbol("all"), FunctionTemplate::New(All)->GetFunction());
    obj->Set(NanSymbol("order"), FunctionTemplate::New(Order)->GetFunction());
    obj->Set(NanSymbol("master"), FunctionTemplate::New(Master)->GetFunction());
    obj->Set(NanSymbol("first"), FunctionTemplate::New(First)->GetFunction());
    obj->Set(NanSymbol("next"), FunctionTemplate::New(Next)->GetFunction());
    obj->Set(NanSymbol("match"), FunctionTemplate::New(Match)->GetFunction());
    return scope.Close(obj);
}

void Initialize(Handle<Object> exports, Handle<Object> module) {
    module->Set(
        NanSymbol("exports"),
        FunctionTemplate::New(CreateObject)->GetFunction()
    );
}

NODE_MODULE(nis, Initialize);
<commit_msg>Return undefined if no more records in database<commit_after>#include <node.h>
#include <nan.h>
#include <rpcsvc/ypclnt.h>

using namespace v8;

extern "C" {
    int foreach_all(unsigned long instatus, char *inkey, int inkeylen, char *inval, int invallen, void *data);
}

NAN_METHOD(Match) {
    HandleScope scope;
    int error;

    if (args.Length() < 2) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("Second argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value inkey(args[1]->ToString());
    char *outval;
    int outvallen;

    error = yp_match(*domain, *mapname, *inkey, inkey.length(), &outval, &outvallen);
    if (YPERR_KEY == error) {
        return scope.Close(Undefined());
    } else if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(String::New(outval, outvallen));
}

NAN_METHOD(Next) {
    HandleScope scope;
    int error;

    if (args.Length() < 2) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("Second argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value inkey(args[1]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    char *outkey, *outval;
    int outkeylen, outvallen;

    error = yp_next(*domain, *mapname, *inkey, inkey.length(), &outkey, &outkeylen, &outval, &outvallen);
    if (YPERR_NOMORE == error) {
        return scope.Close(Undefined());
    } else if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    Local<Object> obj = Object::New();
    obj->Set(String::New("key"), String::New(outkey, outkeylen));
    obj->Set(String::New("value"), String::New(outval, outvallen));
    return scope.Close(obj);
}

NAN_METHOD(First) {
    HandleScope scope;
    int error;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    char *outkey, *outval;
    int outkeylen, outvallen;

    error = yp_first(*domain, *mapname, &outkey, &outkeylen, &outval, &outvallen);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    Local<Object> obj = Object::New();
    obj->Set(String::New("key"), String::New(outkey, outkeylen));
    obj->Set(String::New("value"), String::New(outval, outvallen));
    return scope.Close(obj);
}

NAN_METHOD(Master) {
    HandleScope scope;
    int error;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    char *outname;

    error = yp_master(*domain, *mapname, &outname);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(String::New(outname));
}

NAN_METHOD(Order) {
    HandleScope scope;
    int error;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));
    int outorder;

    error = yp_order(*domain, *mapname, &outorder);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(Integer::New(outorder));
}

NAN_METHOD(All) {
    HandleScope scope;
    int error;

    if (args.Length() < 2) {
        ThrowException(Exception::TypeError(String::New("Not enough arguments")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("First argument must be string")));
        return scope.Close(Undefined());
    }
    if (!args[1]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Second argument must be function")));
        return scope.Close(Undefined());
    }
    Local<Function> user_cb = Local<Function>::Cast(args[1]);
    struct ypall_callback cb;
    cb.foreach = foreach_all;
    cb.data = (char *) &user_cb;

    String::Utf8Value mapname(args[0]->ToString());
    String::Utf8Value domain(args.Holder()->Get(NanSymbol("domain_name")));

    error = yp_all(*domain, *mapname, &cb);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }

    return scope.Close(Undefined());
}

extern "C"
int foreach_all(unsigned long instatus, char *inkey, int inkeylen, char *inval, int invallen, void *data) {
    Local<Function> *cb = static_cast<Local<Function> *>(data);
    const int argc = 3;
    Local<Value> argv[argc] = {
        Local<Value>::New(Integer::NewFromUnsigned(instatus)),
        Local<Value>::New(String::New(inkey, inkeylen)),
        Local<Value>::New(String::New(inval, invallen))
    };
    Local<Value> ret = (*cb)->Call(Context::GetCurrent()->Global(), argc, argv);
    return ret->Int32Value();
}

NAN_METHOD(CreateObject) {
    HandleScope scope;
    char* domp;
    int error;

    Local<Object> obj = Object::New();
    error = yp_get_default_domain(&domp);
    if (error) {
        Local<String> errorMessage = String::New(yperr_string(error));
        ThrowException(Exception::Error(errorMessage));
    }
    obj->Set(NanSymbol("domain_name"), String::New(domp));
    obj->Set(NanSymbol("all"), FunctionTemplate::New(All)->GetFunction());
    obj->Set(NanSymbol("order"), FunctionTemplate::New(Order)->GetFunction());
    obj->Set(NanSymbol("master"), FunctionTemplate::New(Master)->GetFunction());
    obj->Set(NanSymbol("first"), FunctionTemplate::New(First)->GetFunction());
    obj->Set(NanSymbol("next"), FunctionTemplate::New(Next)->GetFunction());
    obj->Set(NanSymbol("match"), FunctionTemplate::New(Match)->GetFunction());
    return scope.Close(obj);
}

void Initialize(Handle<Object> exports, Handle<Object> module) {
    module->Set(
        NanSymbol("exports"),
        FunctionTemplate::New(CreateObject)->GetFunction()
    );
}

NODE_MODULE(nis, Initialize);
<|endoftext|>