<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 <cstring>

#include <grpc/support/log.h>

#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/iomgr/sockaddr_utils.h"
#include "test/core/util/test_config.h"

constexpr int kMinResolutionPeriodMs = 1000;

extern grpc_address_resolver_vtable* grpc_resolve_address_impl;
static grpc_address_resolver_vtable* default_resolve_address;

static grpc_combiner* g_combiner;

static grpc_ares_request* (*g_default_dns_lookup_ares_locked)(
    const char* dns_server, const char* name, const char* default_port,
    grpc_pollset_set* interested_parties, grpc_closure* on_done,
    grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
    grpc_combiner* combiner);

// Counter incremented by test_resolve_address_impl indicating the number of
// times a system-level resolution has happened.
static int g_resolution_count;

static struct iomgr_args {
  gpr_event ev;
  gpr_atm done_atm;
  gpr_mu* mu;
  grpc_pollset* pollset;
  grpc_pollset_set* pollset_set;
} g_iomgr_args;

// Wrapper around default resolve_address in order to count the number of
// times we incur in a system-level name resolution.
static void test_resolve_address_impl(const char* name,
                                      const char* default_port,
                                      grpc_pollset_set* interested_parties,
                                      grpc_closure* on_done,
                                      grpc_resolved_addresses** addrs) {
  default_resolve_address->resolve_address(
      name, default_port, g_iomgr_args.pollset_set, on_done, addrs);
  ++g_resolution_count;
  static grpc_millis last_resolution_time = 0;
  if (last_resolution_time == 0) {
    last_resolution_time =
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
  } else {
    grpc_millis now =
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
    GPR_ASSERT(now - last_resolution_time >= kMinResolutionPeriodMs);
    last_resolution_time = now;
  }
}

static grpc_error* test_blocking_resolve_address_impl(
    const char* name, const char* default_port,
    grpc_resolved_addresses** addresses) {
  return default_resolve_address->blocking_resolve_address(name, default_port,
                                                           addresses);
}

static grpc_address_resolver_vtable test_resolver = {
    test_resolve_address_impl, test_blocking_resolve_address_impl};

static grpc_ares_request* test_dns_lookup_ares_locked(
    const char* dns_server, const char* name, const char* default_port,
    grpc_pollset_set* interested_parties, grpc_closure* on_done,
    grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
    grpc_combiner* combiner) {
  grpc_ares_request* result = g_default_dns_lookup_ares_locked(
      dns_server, name, default_port, g_iomgr_args.pollset_set, on_done, addrs,
      check_grpclb, service_config_json, combiner);
  ++g_resolution_count;
  static grpc_millis last_resolution_time = 0;
  if (last_resolution_time == 0) {
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
  } else {
    grpc_millis now =
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
    GPR_ASSERT(now - last_resolution_time >= kMinResolutionPeriodMs);
    last_resolution_time = now;
  }
  return result;
}

static gpr_timespec test_deadline(void) {
  return grpc_timeout_seconds_to_deadline(100);
}

static void do_nothing(void* arg, grpc_error* error) {}

static void iomgr_args_init(iomgr_args* args) {
  gpr_event_init(&args->ev);
  args->pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
  grpc_pollset_init(args->pollset, &args->mu);
  args->pollset_set = grpc_pollset_set_create();
  grpc_pollset_set_add_pollset(args->pollset_set, args->pollset);
  gpr_atm_rel_store(&args->done_atm, 0);
}

static void iomgr_args_finish(iomgr_args* args) {
  GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline()));
  grpc_pollset_set_del_pollset(args->pollset_set, args->pollset);
  grpc_pollset_set_destroy(args->pollset_set);
  grpc_closure do_nothing_cb;
  GRPC_CLOSURE_INIT(&do_nothing_cb, do_nothing, nullptr,
                    grpc_schedule_on_exec_ctx);
  gpr_mu_lock(args->mu);
  grpc_pollset_shutdown(args->pollset, &do_nothing_cb);
  gpr_mu_unlock(args->mu);
  // exec_ctx needs to be flushed before calling grpc_pollset_destroy()
  grpc_core::ExecCtx::Get()->Flush();
  grpc_pollset_destroy(args->pollset);
  gpr_free(args->pollset);
}

static grpc_millis n_sec_deadline(int seconds) {
  return grpc_timespec_to_millis_round_up(
      grpc_timeout_seconds_to_deadline(seconds));
}

static void poll_pollset_until_request_done(iomgr_args* args) {
  grpc_core::ExecCtx exec_ctx;
  grpc_millis deadline = n_sec_deadline(10);
  while (true) {
    bool done = gpr_atm_acq_load(&args->done_atm) != 0;
    if (done) {
      break;
    }
    grpc_millis time_left = deadline - grpc_core::ExecCtx::Get()->Now();
    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64, done, time_left);
    GPR_ASSERT(time_left >= 0);
    grpc_pollset_worker* worker = nullptr;
    gpr_mu_lock(args->mu);
    GRPC_LOG_IF_ERROR("pollset_work", grpc_pollset_work(args->pollset, &worker,
                                                        n_sec_deadline(1)));
    gpr_mu_unlock(args->mu);
    grpc_core::ExecCtx::Get()->Flush();
  }
  gpr_event_set(&args->ev, (void*)1);
}

struct OnResolutionCallbackArg {
  const char* uri_str = nullptr;
  grpc_core::OrphanablePtr<grpc_core::Resolver> resolver;
  grpc_channel_args* result = nullptr;
};

// Set to true by the last callback in the resolution chain.
static bool g_all_callbacks_invoked;

static void on_second_resolution(void* arg, grpc_error* error) {
  OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg);
  grpc_channel_args_destroy(cb_arg->result);
  GPR_ASSERT(error == GRPC_ERROR_NONE);
  gpr_log(GPR_INFO, "2nd: g_resolution_count: %d", g_resolution_count);
  // The resolution callback was not invoked until new data was
  // available, which was delayed until after the cooldown period.
  GPR_ASSERT(g_resolution_count == 2);
  cb_arg->resolver.reset();
  gpr_atm_rel_store(&g_iomgr_args.done_atm, 1);
  gpr_mu_lock(g_iomgr_args.mu);
  GRPC_LOG_IF_ERROR("pollset_kick",
                    grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
  gpr_mu_unlock(g_iomgr_args.mu);
  grpc_core::Delete(cb_arg);
  g_all_callbacks_invoked = true;
}

static void on_first_resolution(void* arg, grpc_error* error) {
  OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg);
  grpc_channel_args_destroy(cb_arg->result);
  GPR_ASSERT(error == GRPC_ERROR_NONE);
  gpr_log(GPR_INFO, "1st: g_resolution_count: %d", g_resolution_count);
  // There's one initial system-level resolution and one invocation of a
  // notification callback (the current function).
  GPR_ASSERT(g_resolution_count == 1);
  cb_arg->resolver->NextLocked(
      &cb_arg->result,
      GRPC_CLOSURE_CREATE(on_second_resolution, arg,
                          grpc_combiner_scheduler(g_combiner)));
  cb_arg->resolver->RequestReresolutionLocked();
  gpr_mu_lock(g_iomgr_args.mu);
  GRPC_LOG_IF_ERROR("pollset_kick",
                    grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
  gpr_mu_unlock(g_iomgr_args.mu);
}

static void start_test_under_combiner(void* arg, grpc_error* error) {
  OnResolutionCallbackArg* res_cb_arg =
      static_cast<OnResolutionCallbackArg*>(arg);

  grpc_core::ResolverFactory* factory =
      grpc_core::ResolverRegistry::LookupResolverFactory("dns");
  grpc_uri* uri = grpc_uri_parse(res_cb_arg->uri_str, 0);
  gpr_log(GPR_DEBUG, "test: '%s' should be valid for '%s'", res_cb_arg->uri_str,
          factory->scheme());
  GPR_ASSERT(uri != nullptr);
  grpc_core::ResolverArgs args;
  args.uri = uri;
  args.combiner = g_combiner;
  g_resolution_count = 0;

  grpc_arg cooldown_arg;
  cooldown_arg.key =
      const_cast<char*>(GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS);
  cooldown_arg.type = GRPC_ARG_INTEGER;
  cooldown_arg.value.integer = kMinResolutionPeriodMs;
  auto* cooldown_channel_args =
      grpc_channel_args_copy_and_add(nullptr, &cooldown_arg, 1);
  args.args = cooldown_channel_args;
  res_cb_arg->resolver = factory->CreateResolver(args);
  grpc_channel_args_destroy(cooldown_channel_args);
  GPR_ASSERT(res_cb_arg->resolver != nullptr);
  // First resolution, would incur in system-level resolution.
  res_cb_arg->resolver->NextLocked(
      &res_cb_arg->result,
      GRPC_CLOSURE_CREATE(on_first_resolution, res_cb_arg,
                          grpc_combiner_scheduler(g_combiner)));
  grpc_uri_destroy(uri);
}

static void test_cooldown() {
  grpc_core::ExecCtx exec_ctx;
  iomgr_args_init(&g_iomgr_args);
  OnResolutionCallbackArg* res_cb_arg =
      grpc_core::New<OnResolutionCallbackArg>();
  res_cb_arg->uri_str = "dns:127.0.0.1";

  GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(start_test_under_combiner, res_cb_arg,
                                         grpc_combiner_scheduler(g_combiner)),
                     GRPC_ERROR_NONE);
  grpc_core::ExecCtx::Get()->Flush();
  poll_pollset_until_request_done(&g_iomgr_args);
  iomgr_args_finish(&g_iomgr_args);
}

int main(int argc, char** argv) {
  grpc_test_init(argc, argv);
  grpc_init();

  g_combiner = grpc_combiner_create();

  g_default_dns_lookup_ares_locked = grpc_dns_lookup_ares_locked;
  grpc_dns_lookup_ares_locked = test_dns_lookup_ares_locked;
  default_resolve_address = grpc_resolve_address_impl;
  grpc_set_resolver_impl(&test_resolver);

  test_cooldown();

  {
    grpc_core::ExecCtx exec_ctx;
    GRPC_COMBINER_UNREF(g_combiner, "test");
  }
  grpc_shutdown();
  GPR_ASSERT(g_all_callbacks_invoked);
  return 0;
}
<commit_msg>Allow more timing slack in dns cooldown test.<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 <cstring>

#include <grpc/support/log.h>

#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/iomgr/sockaddr_utils.h"
#include "test/core/util/test_config.h"

constexpr int kMinResolutionPeriodMs = 1000;
// Provide some slack when checking intervals, to allow for test timing issues.
constexpr int kMinResolutionPeriodForCheckMs = 900;

extern grpc_address_resolver_vtable* grpc_resolve_address_impl;
static grpc_address_resolver_vtable* default_resolve_address;

static grpc_combiner* g_combiner;

static grpc_ares_request* (*g_default_dns_lookup_ares_locked)(
    const char* dns_server, const char* name, const char* default_port,
    grpc_pollset_set* interested_parties, grpc_closure* on_done,
    grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
    grpc_combiner* combiner);

// Counter incremented by test_resolve_address_impl indicating the number of
// times a system-level resolution has happened.
static int g_resolution_count;

static struct iomgr_args {
  gpr_event ev;
  gpr_atm done_atm;
  gpr_mu* mu;
  grpc_pollset* pollset;
  grpc_pollset_set* pollset_set;
} g_iomgr_args;

// Wrapper around default resolve_address in order to count the number of
// times we incur in a system-level name resolution.
static void test_resolve_address_impl(const char* name,
                                      const char* default_port,
                                      grpc_pollset_set* interested_parties,
                                      grpc_closure* on_done,
                                      grpc_resolved_addresses** addrs) {
  default_resolve_address->resolve_address(
      name, default_port, g_iomgr_args.pollset_set, on_done, addrs);
  ++g_resolution_count;
  static grpc_millis last_resolution_time = 0;
  if (last_resolution_time == 0) {
    last_resolution_time =
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
  } else {
    grpc_millis now =
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
    GPR_ASSERT(now - last_resolution_time >= kMinResolutionPeriodForCheckMs);
    last_resolution_time = now;
  }
}

static grpc_error* test_blocking_resolve_address_impl(
    const char* name, const char* default_port,
    grpc_resolved_addresses** addresses) {
  return default_resolve_address->blocking_resolve_address(name, default_port,
                                                           addresses);
}

static grpc_address_resolver_vtable test_resolver = {
    test_resolve_address_impl, test_blocking_resolve_address_impl};

static grpc_ares_request* test_dns_lookup_ares_locked(
    const char* dns_server, const char* name, const char* default_port,
    grpc_pollset_set* interested_parties, grpc_closure* on_done,
    grpc_lb_addresses** addrs, bool check_grpclb, char** service_config_json,
    grpc_combiner* combiner) {
  grpc_ares_request* result = g_default_dns_lookup_ares_locked(
      dns_server, name, default_port, g_iomgr_args.pollset_set, on_done, addrs,
      check_grpclb, service_config_json, combiner);
  ++g_resolution_count;
  static grpc_millis last_resolution_time = 0;
  if (last_resolution_time == 0) {
    last_resolution_time =
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
  } else {
    grpc_millis now =
        grpc_timespec_to_millis_round_up(gpr_now(GPR_CLOCK_MONOTONIC));
    GPR_ASSERT(now - last_resolution_time >= kMinResolutionPeriodForCheckMs);
    last_resolution_time = now;
  }
  return result;
}

static gpr_timespec test_deadline(void) {
  return grpc_timeout_seconds_to_deadline(100);
}

static void do_nothing(void* arg, grpc_error* error) {}

static void iomgr_args_init(iomgr_args* args) {
  gpr_event_init(&args->ev);
  args->pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
  grpc_pollset_init(args->pollset, &args->mu);
  args->pollset_set = grpc_pollset_set_create();
  grpc_pollset_set_add_pollset(args->pollset_set, args->pollset);
  gpr_atm_rel_store(&args->done_atm, 0);
}

static void iomgr_args_finish(iomgr_args* args) {
  GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline()));
  grpc_pollset_set_del_pollset(args->pollset_set, args->pollset);
  grpc_pollset_set_destroy(args->pollset_set);
  grpc_closure do_nothing_cb;
  GRPC_CLOSURE_INIT(&do_nothing_cb, do_nothing, nullptr,
                    grpc_schedule_on_exec_ctx);
  gpr_mu_lock(args->mu);
  grpc_pollset_shutdown(args->pollset, &do_nothing_cb);
  gpr_mu_unlock(args->mu);
  // exec_ctx needs to be flushed before calling grpc_pollset_destroy()
  grpc_core::ExecCtx::Get()->Flush();
  grpc_pollset_destroy(args->pollset);
  gpr_free(args->pollset);
}

static grpc_millis n_sec_deadline(int seconds) {
  return grpc_timespec_to_millis_round_up(
      grpc_timeout_seconds_to_deadline(seconds));
}

static void poll_pollset_until_request_done(iomgr_args* args) {
  grpc_core::ExecCtx exec_ctx;
  grpc_millis deadline = n_sec_deadline(10);
  while (true) {
    bool done = gpr_atm_acq_load(&args->done_atm) != 0;
    if (done) {
      break;
    }
    grpc_millis time_left = deadline - grpc_core::ExecCtx::Get()->Now();
    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64, done, time_left);
    GPR_ASSERT(time_left >= 0);
    grpc_pollset_worker* worker = nullptr;
    gpr_mu_lock(args->mu);
    GRPC_LOG_IF_ERROR("pollset_work", grpc_pollset_work(args->pollset, &worker,
                                                        n_sec_deadline(1)));
    gpr_mu_unlock(args->mu);
    grpc_core::ExecCtx::Get()->Flush();
  }
  gpr_event_set(&args->ev, (void*)1);
}

struct OnResolutionCallbackArg {
  const char* uri_str = nullptr;
  grpc_core::OrphanablePtr<grpc_core::Resolver> resolver;
  grpc_channel_args* result = nullptr;
};

// Set to true by the last callback in the resolution chain.
static bool g_all_callbacks_invoked;

static void on_second_resolution(void* arg, grpc_error* error) {
  OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg);
  grpc_channel_args_destroy(cb_arg->result);
  GPR_ASSERT(error == GRPC_ERROR_NONE);
  gpr_log(GPR_INFO, "2nd: g_resolution_count: %d", g_resolution_count);
  // The resolution callback was not invoked until new data was
  // available, which was delayed until after the cooldown period.
  GPR_ASSERT(g_resolution_count == 2);
  cb_arg->resolver.reset();
  gpr_atm_rel_store(&g_iomgr_args.done_atm, 1);
  gpr_mu_lock(g_iomgr_args.mu);
  GRPC_LOG_IF_ERROR("pollset_kick",
                    grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
  gpr_mu_unlock(g_iomgr_args.mu);
  grpc_core::Delete(cb_arg);
  g_all_callbacks_invoked = true;
}

static void on_first_resolution(void* arg, grpc_error* error) {
  OnResolutionCallbackArg* cb_arg = static_cast<OnResolutionCallbackArg*>(arg);
  grpc_channel_args_destroy(cb_arg->result);
  GPR_ASSERT(error == GRPC_ERROR_NONE);
  gpr_log(GPR_INFO, "1st: g_resolution_count: %d", g_resolution_count);
  // There's one initial system-level resolution and one invocation of a
  // notification callback (the current function).
  GPR_ASSERT(g_resolution_count == 1);
  cb_arg->resolver->NextLocked(
      &cb_arg->result,
      GRPC_CLOSURE_CREATE(on_second_resolution, arg,
                          grpc_combiner_scheduler(g_combiner)));
  cb_arg->resolver->RequestReresolutionLocked();
  gpr_mu_lock(g_iomgr_args.mu);
  GRPC_LOG_IF_ERROR("pollset_kick",
                    grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
  gpr_mu_unlock(g_iomgr_args.mu);
}

static void start_test_under_combiner(void* arg, grpc_error* error) {
  OnResolutionCallbackArg* res_cb_arg =
      static_cast<OnResolutionCallbackArg*>(arg);

  grpc_core::ResolverFactory* factory =
      grpc_core::ResolverRegistry::LookupResolverFactory("dns");
  grpc_uri* uri = grpc_uri_parse(res_cb_arg->uri_str, 0);
  gpr_log(GPR_DEBUG, "test: '%s' should be valid for '%s'", res_cb_arg->uri_str,
          factory->scheme());
  GPR_ASSERT(uri != nullptr);
  grpc_core::ResolverArgs args;
  args.uri = uri;
  args.combiner = g_combiner;
  g_resolution_count = 0;

  grpc_arg cooldown_arg;
  cooldown_arg.key =
      const_cast<char*>(GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS);
  cooldown_arg.type = GRPC_ARG_INTEGER;
  cooldown_arg.value.integer = kMinResolutionPeriodMs;
  auto* cooldown_channel_args =
      grpc_channel_args_copy_and_add(nullptr, &cooldown_arg, 1);
  args.args = cooldown_channel_args;
  res_cb_arg->resolver = factory->CreateResolver(args);
  grpc_channel_args_destroy(cooldown_channel_args);
  GPR_ASSERT(res_cb_arg->resolver != nullptr);
  // First resolution, would incur in system-level resolution.
  res_cb_arg->resolver->NextLocked(
      &res_cb_arg->result,
      GRPC_CLOSURE_CREATE(on_first_resolution, res_cb_arg,
                          grpc_combiner_scheduler(g_combiner)));
  grpc_uri_destroy(uri);
}

static void test_cooldown() {
  grpc_core::ExecCtx exec_ctx;
  iomgr_args_init(&g_iomgr_args);
  OnResolutionCallbackArg* res_cb_arg =
      grpc_core::New<OnResolutionCallbackArg>();
  res_cb_arg->uri_str = "dns:127.0.0.1";

  GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(start_test_under_combiner, res_cb_arg,
                                         grpc_combiner_scheduler(g_combiner)),
                     GRPC_ERROR_NONE);
  grpc_core::ExecCtx::Get()->Flush();
  poll_pollset_until_request_done(&g_iomgr_args);
  iomgr_args_finish(&g_iomgr_args);
}

int main(int argc, char** argv) {
  grpc_test_init(argc, argv);
  grpc_init();

  g_combiner = grpc_combiner_create();

  g_default_dns_lookup_ares_locked = grpc_dns_lookup_ares_locked;
  grpc_dns_lookup_ares_locked = test_dns_lookup_ares_locked;
  default_resolve_address = grpc_resolve_address_impl;
  grpc_set_resolver_impl(&test_resolver);

  test_cooldown();

  {
    grpc_core::ExecCtx exec_ctx;
    GRPC_COMBINER_UNREF(g_combiner, "test");
  }
  grpc_shutdown();
  GPR_ASSERT(g_all_callbacks_invoked);
  return 0;
}
<|endoftext|>


<commit_before>
#include "base/cpuid.hpp"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace principia {
namespace base {

using ::testing::Eq;
using ::testing::Test;

class CPUIDTest : public Test {};

TEST_F(CPUIDTest, Vendor) {
  // This mostly checks that we are getting something from CPUID, since it is
  // hard to expect things from the feature flags.  This could be expanded to an
  // AnyOf as needed if the tests are run on non-Intel processors.
  EXPECT_THAT(CPUVendorIdentificationString(), Eq("GenuineIntel"));
}

TEST_F(CPUIDTest, CPUFeatureFlags) {
  // We require Prescott or later.
  EXPECT_TRUE(HasCPUFeatures(CPUFeatureFlags::FPU | CPUFeatureFlags::SSE |
                             CPUFeatureFlags::SSE2 | CPUFeatureFlags::SSE3));
  // We develop on Sandy Bridge or later.
  EXPECT_TRUE(HasCPUFeatures(CPUFeatureFlags::AVX));
  // Check that we don’t always return true.
  // We are not running these tests on a Pentium III, so we do not have the
  // Processor Serial Number feature.
  EXPECT_FALSE(HasCPUFeatures(CPUFeatureFlags::PSN));
  EXPECT_FALSE(HasCPUFeatures(CPUFeatureFlags::FPU | CPUFeatureFlags::SSE |
                              CPUFeatureFlags::SSE2 | CPUFeatureFlags::SSE3 |
                              CPUFeatureFlags::PSN));
}

}  // namespace base
}  // namespace principia
<commit_msg>Don't require AVX in cpu_test. (My machine does not have AVX)<commit_after>
#include "base/cpuid.hpp"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace principia {
namespace base {

using ::testing::Eq;
using ::testing::Test;

class CPUIDTest : public Test {};

TEST_F(CPUIDTest, Vendor) {
  // This mostly checks that we are getting something from CPUID, since it is
  // hard to expect things from the feature flags.  This could be expanded to an
  // AnyOf as needed if the tests are run on non-Intel processors.
  EXPECT_THAT(CPUVendorIdentificationString(), Eq("GenuineIntel"));
}

TEST_F(CPUIDTest, CPUFeatureFlags) {
  // We require Prescott or later.
  EXPECT_TRUE(HasCPUFeatures(CPUFeatureFlags::FPU | CPUFeatureFlags::SSE |
                             CPUFeatureFlags::SSE2 | CPUFeatureFlags::SSE3));
  // Check that we don’t always return true.
  // We are not running these tests on a Pentium III, so we do not have the
  // Processor Serial Number feature.
  EXPECT_FALSE(HasCPUFeatures(CPUFeatureFlags::PSN));
  EXPECT_FALSE(HasCPUFeatures(CPUFeatureFlags::FPU | CPUFeatureFlags::SSE |
                              CPUFeatureFlags::SSE2 | CPUFeatureFlags::SSE3 |
                              CPUFeatureFlags::PSN));
}

}  // namespace base
}  // namespace principia
<|endoftext|>


<commit_before>template<typename Iterator, typename Container>
Lexer<Iterator, Container>::Lexer(Iterator current, Iterator end, std::string sourceName)
    : current(current), end(end), sourceName(sourceName), line(1), column(0) {

}

template<typename Iterator, typename Container> template<typename Predicate>
std::string Lexer<Iterator, Container>::appendWhile(char initial, Predicate predicate) {
	std::string content;

	if (initial > 0) content += initial;

	while (current != end) {
		char c = nextChar();
		if (!predicate(c)) {
			moveBack();
			break;
		}

		content += c;
	}

	return content;
}

template<typename Iterator, typename Container>
Location Lexer<Iterator, Container>::makeLocation() {
    return Location(sourceName, line, column);
}

template<typename Iterator, typename Container>
char Lexer<Iterator, Container>::nextChar() {
    char c = *current;
    ++current;
    ++column;

    return c;
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::consumeNextCharIf(char c) {
	if (nextChar() == c) {
		return true;
	}

	moveBack();
	return false;
}

template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::moveBack() {
    --current;
    --column;
}

template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::nextLine() {
    line++;
    column = 0;
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::isWhitespace(char c) {
	return std::isspace(c) && c != '\n';
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::isAllowedIdentifierChar(char c) {
    return std::isalnum(c) || c == '_' || c == '?' || c == '.' || c == '$' || c == '#' || c == '@';
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::isAllowedIdentifierFirstChar(char c) {
    return std::isalpha(c) || c == '_' || c == '?' || c == '.';
}

template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::skipWhitespaceAndComments() {
    bool inComment = false;

    for (; current != end; current++, column++) {
        if (!inComment && *current == ';') {
            inComment = true;
        } else if (!inComment && !isWhitespace(*current)) {
            break;
        } else if (inComment && *current == '\n') {
            break;
        }
    }
}

template<typename Iterator, typename Container>
std::shared_ptr<Token> Lexer<Iterator, Container>::parseNumber(Location start, std::string value) {
    std::string unprefixedValue;

    std::uint8_t base = 10;
    if (boost::istarts_with(value, "0x")) {
        base = 16;
        unprefixedValue = value.substr(2);
    } else if (boost::istarts_with(value, "0o")) {
        base = 8;
        unprefixedValue = value.substr(2);
    } else if (boost::istarts_with(value, "0b")) {
        base = 2;
        unprefixedValue = value.substr(2);
    } else {
        unprefixedValue = value;
    }

    // prefix without an actual value
    if (unprefixedValue.length() == 0) {
        return std::make_shared<InvalidIntegerToken>(start, value, base);
    }

    size_t pos;
    try {
        unsigned long parsedValue = std::stoul(unprefixedValue, &pos, base);
        if (pos != unprefixedValue.size()) {
            return std::make_shared<InvalidIntegerToken>(start, value, base);
        }

        if (parsedValue > UINT32_MAX) {
            return std::make_shared<IntegerToken>(start, value, UINT32_MAX, true);
        }

        return std::make_shared<IntegerToken>(start, value, (std::uint32_t)parsedValue, false);
    } catch (std::invalid_argument &ia) {
        return std::make_shared<InvalidIntegerToken>(start, value, base);
    } catch (std::out_of_range &oor) {
        return std::make_shared<IntegerToken>(start, value, UINT32_MAX, true);
    }
}

template<typename Iterator, typename Container>
std::shared_ptr<Token> Lexer<Iterator, Container>::nextToken() {
    skipWhitespaceAndComments();

    if (current == end) {
        return std::make_shared<Token>(makeLocation(), dcpu::TokenType::END_OF_INPUT, "");
    }

    char c = nextChar();
    Location start = makeLocation();

	if (c == '+') {
		if (consumeNextCharIf('+')) {
			return std::make_shared<Token>(start, dcpu::TokenType::INCREMENT, "++");
		}
    } else if (c == '-') {
		if (consumeNextCharIf('-')) {
			return std::make_shared<Token>(start, dcpu::TokenType::DECREMENT, "--");
		}
    } else if (c == '<') {
		if (consumeNextCharIf('<')) {
			return std::make_shared<Token>(start, dcpu::TokenType::SHIFT_LEFT, "<<");
		}
    } else if (c == '>') {
		if (consumeNextCharIf('>')) {
			return std::make_shared<Token>(start, dcpu::TokenType::SHIFT_RIGHT, ">>");
		}
    } else if (isAllowedIdentifierFirstChar(c)) {
        return std::make_shared<Token>(start, dcpu::TokenType::IDENTIFIER,
			appendWhile(c, &Lexer<Iterator, Container>::isAllowedIdentifierChar));
    } else if (std::isdigit(c)) {
        return parseNumber(start, appendWhile(c, &Lexer<Iterator, Container>::isAllowedIdentifierChar));
    } else if (c == '\n') {
        nextLine();

        return std::make_shared<Token>(start, dcpu::TokenType::NEWLINE, "");
    }

    return std::make_shared<Token>(start, dcpu::TokenType::CHARACTER, c);
}
template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::parse() {
    while (true) {
        std::shared_ptr<Token> token = nextToken();
        tokens.push_back(token);

        if (token->type == dcpu::TokenType::END_OF_INPUT) {
            break;
        }
    }
}
<commit_msg>adding content for eol and eoi tokens<commit_after>template<typename Iterator, typename Container>
Lexer<Iterator, Container>::Lexer(Iterator current, Iterator end, std::string sourceName)
    : current(current), end(end), sourceName(sourceName), line(1), column(0) {

}

template<typename Iterator, typename Container> template<typename Predicate>
std::string Lexer<Iterator, Container>::appendWhile(char initial, Predicate predicate) {
	std::string content;

	if (initial > 0) content += initial;

	while (current != end) {
		char c = nextChar();
		if (!predicate(c)) {
			moveBack();
			break;
		}

		content += c;
	}

	return content;
}

template<typename Iterator, typename Container>
Location Lexer<Iterator, Container>::makeLocation() {
    return Location(sourceName, line, column);
}

template<typename Iterator, typename Container>
char Lexer<Iterator, Container>::nextChar() {
    char c = *current;
    ++current;
    ++column;

    return c;
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::consumeNextCharIf(char c) {
	if (nextChar() == c) {
		return true;
	}

	moveBack();
	return false;
}

template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::moveBack() {
    --current;
    --column;
}

template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::nextLine() {
    line++;
    column = 0;
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::isWhitespace(char c) {
	return std::isspace(c) && c != '\n';
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::isAllowedIdentifierChar(char c) {
    return std::isalnum(c) || c == '_' || c == '?' || c == '.' || c == '$' || c == '#' || c == '@';
}

template<typename Iterator, typename Container>
bool Lexer<Iterator, Container>::isAllowedIdentifierFirstChar(char c) {
    return std::isalpha(c) || c == '_' || c == '?' || c == '.';
}

template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::skipWhitespaceAndComments() {
    bool inComment = false;

    for (; current != end; current++, column++) {
        if (!inComment && *current == ';') {
            inComment = true;
        } else if (!inComment && !isWhitespace(*current)) {
            break;
        } else if (inComment && *current == '\n') {
            break;
        }
    }
}

template<typename Iterator, typename Container>
std::shared_ptr<Token> Lexer<Iterator, Container>::parseNumber(Location start, std::string value) {
    std::string unprefixedValue;

    std::uint8_t base = 10;
    if (boost::istarts_with(value, "0x")) {
        base = 16;
        unprefixedValue = value.substr(2);
    } else if (boost::istarts_with(value, "0o")) {
        base = 8;
        unprefixedValue = value.substr(2);
    } else if (boost::istarts_with(value, "0b")) {
        base = 2;
        unprefixedValue = value.substr(2);
    } else {
        unprefixedValue = value;
    }

    // prefix without an actual value
    if (unprefixedValue.length() == 0) {
        return std::make_shared<InvalidIntegerToken>(start, value, base);
    }

    size_t pos;
    try {
        unsigned long parsedValue = std::stoul(unprefixedValue, &pos, base);
        if (pos != unprefixedValue.size()) {
            return std::make_shared<InvalidIntegerToken>(start, value, base);
        }

        if (parsedValue > UINT32_MAX) {
            return std::make_shared<IntegerToken>(start, value, UINT32_MAX, true);
        }

        return std::make_shared<IntegerToken>(start, value, (std::uint32_t)parsedValue, false);
    } catch (std::invalid_argument &ia) {
        return std::make_shared<InvalidIntegerToken>(start, value, base);
    } catch (std::out_of_range &oor) {
        return std::make_shared<IntegerToken>(start, value, UINT32_MAX, true);
    }
}

template<typename Iterator, typename Container>
std::shared_ptr<Token> Lexer<Iterator, Container>::nextToken() {
    skipWhitespaceAndComments();

    if (current == end) {
        return std::make_shared<Token>(makeLocation(), dcpu::TokenType::END_OF_INPUT, "end of file");
    }

    char c = nextChar();
    Location start = makeLocation();

	if (c == '+') {
		if (consumeNextCharIf('+')) {
			return std::make_shared<Token>(start, dcpu::TokenType::INCREMENT, "++");
		}
    } else if (c == '-') {
		if (consumeNextCharIf('-')) {
			return std::make_shared<Token>(start, dcpu::TokenType::DECREMENT, "--");
		}
    } else if (c == '<') {
		if (consumeNextCharIf('<')) {
			return std::make_shared<Token>(start, dcpu::TokenType::SHIFT_LEFT, "<<");
		}
    } else if (c == '>') {
		if (consumeNextCharIf('>')) {
			return std::make_shared<Token>(start, dcpu::TokenType::SHIFT_RIGHT, ">>");
		}
    } else if (isAllowedIdentifierFirstChar(c)) {
        return std::make_shared<Token>(start, dcpu::TokenType::IDENTIFIER,
			appendWhile(c, &Lexer<Iterator, Container>::isAllowedIdentifierChar));
    } else if (std::isdigit(c)) {
        return parseNumber(start, appendWhile(c, &Lexer<Iterator, Container>::isAllowedIdentifierChar));
    } else if (c == '\n') {
        nextLine();

        return std::make_shared<Token>(start, dcpu::TokenType::NEWLINE, "newline");
    }

    return std::make_shared<Token>(start, dcpu::TokenType::CHARACTER, c);
}
template<typename Iterator, typename Container>
void Lexer<Iterator, Container>::parse() {
    while (true) {
        std::shared_ptr<Token> token = nextToken();
        tokens.push_back(token);

        if (token->type == dcpu::TokenType::END_OF_INPUT) {
            break;
        }
    }
}
<|endoftext|>


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

#include "iceoryx_hoofs/testing/logger.hpp"

namespace iox
{
namespace testing
{
void Logger::init() noexcept
{
    static Logger logger;
    log::Logger::setActiveLogger(logger);
    log::Logger::init(log::logLevelFromEnvOr(log::LogLevel::TRACE));
    // disable logger output only after initializing the logger to get error messages from initialization
    // JUSTIFICATION getenv is required for the functionality of the testing logger and will be called only once in main
    // NOLINTNEXTLINE(concurrency-mt-unsafe)
    if (const auto* allowLogString = std::getenv("IOX_TESTING_ALLOW_LOG"))
    {
        logger.m_loggerData->allowLog =
            pbb::equalStrings(allowLogString, "on") || pbb::equalStrings(allowLogString, "ON");
    }
    else
    {
        logger.m_loggerData->allowLog = false;
    }

    auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
    // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) required by the callee
    listeners.Append(new LogPrinter);
}

void Logger::clearLogBuffer() noexcept
{
    m_loggerData->buffer.clear();
}

void Logger::printLogBuffer() noexcept
{
    auto loggerData = m_loggerData.getScopeGuard();
    if (loggerData->buffer.empty())
    {
        return;
    }
    puts("#### Log start ####");
    for (const auto& log : loggerData->buffer)
    {
        puts(log.c_str());
    }
    puts("#### Log end ####");
}

uint64_t Logger::getNumberOfLogMessages() noexcept
{
    auto& logger = dynamic_cast<Logger&>(log::Logger::get());
    return logger.m_loggerData->buffer.size();
}

std::vector<std::string> Logger::getLogMessages() noexcept
{
    auto& logger = dynamic_cast<Logger&>(log::Logger::get());
    return logger.m_loggerData->buffer;
}

void Logger::flush() noexcept
{
    auto loggerData = m_loggerData.getScopeGuard();
    const auto logBuffer = Base::getLogBuffer();
    loggerData->buffer.emplace_back(logBuffer.buffer, logBuffer.writeIndex);

    if (loggerData->allowLog)
    {
        Base::flush();
    }

    Base::assumeFlushed();
}

void LogPrinter::OnTestStart(const ::testing::TestInfo&)
{
    dynamic_cast<Logger&>(log::Logger::get()).clearLogBuffer();
    Logger::setLogLevel(log::LogLevel::TRACE);

    /// @todo iox-#1345 register signal handler for sigterm to flush to logger;
    /// there might be tests to register a handler itself and when this is
    /// done at each start of the test only the tests who use their
    /// own signal handler are affected and don't get an log output on termination
}

void LogPrinter::OnTestPartResult(const ::testing::TestPartResult& result)
{
    if (result.failed())
    {
        dynamic_cast<Logger&>(log::Logger::get()).printLogBuffer();
    }

    /// @todo iox-#1345 de-register the signal handler from 'OnTestStart'
}

} // namespace testing
} // namespace iox
<commit_msg>iox-#1345 Inform the user about valid values for IOX_TESTING_ALLOW_LOG environment variable<commit_after>// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_hoofs/testing/logger.hpp"

#include <iostream>

namespace iox
{
namespace testing
{
void Logger::init() noexcept
{
    static Logger logger;
    log::Logger::setActiveLogger(logger);
    log::Logger::init(log::logLevelFromEnvOr(log::LogLevel::TRACE));
    // disable logger output only after initializing the logger to get error messages from initialization
    // JUSTIFICATION getenv is required for the functionality of the testing logger and will be called only once in main
    // NOLINTNEXTLINE(concurrency-mt-unsafe)
    if (const auto* allowLogString = std::getenv("IOX_TESTING_ALLOW_LOG"))
    {
        if (pbb::equalStrings(allowLogString, "on") || pbb::equalStrings(allowLogString, "ON"))
        {
            logger.m_loggerData->allowLog = true;
        }
        else
        {
            logger.m_loggerData->allowLog = false;
            std::cout << "" << std::endl;
            std::cout << "Invalid value for 'IOX_TESTING_ALLOW_LOG' environment variable!'" << std::endl;
            std::cout << "Found: " << allowLogString << std::endl;
            std::cout << "Allowed is one of: on, ON" << std::endl;
        }
    }
    else
    {
        logger.m_loggerData->allowLog = false;
    }

    auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
    // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) required by the callee
    listeners.Append(new LogPrinter);
}

void Logger::clearLogBuffer() noexcept
{
    m_loggerData->buffer.clear();
}

void Logger::printLogBuffer() noexcept
{
    auto loggerData = m_loggerData.getScopeGuard();
    if (loggerData->buffer.empty())
    {
        return;
    }
    puts("#### Log start ####");
    for (const auto& log : loggerData->buffer)
    {
        puts(log.c_str());
    }
    puts("#### Log end ####");
}

uint64_t Logger::getNumberOfLogMessages() noexcept
{
    auto& logger = dynamic_cast<Logger&>(log::Logger::get());
    return logger.m_loggerData->buffer.size();
}

std::vector<std::string> Logger::getLogMessages() noexcept
{
    auto& logger = dynamic_cast<Logger&>(log::Logger::get());
    return logger.m_loggerData->buffer;
}

void Logger::flush() noexcept
{
    auto loggerData = m_loggerData.getScopeGuard();
    const auto logBuffer = Base::getLogBuffer();
    loggerData->buffer.emplace_back(logBuffer.buffer, logBuffer.writeIndex);

    if (loggerData->allowLog)
    {
        Base::flush();
    }

    Base::assumeFlushed();
}

void LogPrinter::OnTestStart(const ::testing::TestInfo&)
{
    dynamic_cast<Logger&>(log::Logger::get()).clearLogBuffer();
    Logger::setLogLevel(log::LogLevel::TRACE);

    /// @todo iox-#1345 register signal handler for sigterm to flush to logger;
    /// there might be tests to register a handler itself and when this is
    /// done at each start of the test only the tests who use their
    /// own signal handler are affected and don't get an log output on termination
}

void LogPrinter::OnTestPartResult(const ::testing::TestPartResult& result)
{
    if (result.failed())
    {
        dynamic_cast<Logger&>(log::Logger::get()).printLogBuffer();
    }

    /// @todo iox-#1345 de-register the signal handler from 'OnTestStart'
}

} // namespace testing
} // namespace iox
<|endoftext|>


<commit_before>// Copyright 2022 The Pigweed 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
//
//     https://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 <chrono>

#include "gtest/gtest.h"
#include "pw_chrono/system_clock.h"
#include "pw_perf_test/internal/chrono_timer_interface.h"
#include "pw_thread/sleep.h"

namespace pw::perf_test::internal::backend {
namespace {

constexpr chrono::SystemClock::duration kArbitraryDuration =
    chrono::SystemClock::for_at_least(std::chrono::milliseconds(2));

TEST(ChronoTest, DurationIsReasonable) {
  Timestamp start = GetCurrentTimestamp();
  this_thread::sleep_for(kArbitraryDuration);
  Timestamp end = GetCurrentTimestamp();
  uint64_t duration = GetDuration(start, end);
  EXPECT_GE(duration, 2000000u);
  EXPECT_LE(duration, 20000000u);
}

}  // namespace
}  // namespace pw::perf_test::internal::backend
<commit_msg>pw_perf_test: Removed upper limit for chrono_test<commit_after>// Copyright 2022 The Pigweed 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
//
//     https://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 <chrono>

#include "gtest/gtest.h"
#include "pw_chrono/system_clock.h"
#include "pw_perf_test/internal/chrono_timer_interface.h"
#include "pw_thread/sleep.h"

namespace pw::perf_test::internal::backend {
namespace {

constexpr chrono::SystemClock::duration kArbitraryDuration =
    chrono::SystemClock::for_at_least(std::chrono::milliseconds(1));

TEST(ChronoTest, DurationIsReasonable) {
  Timestamp start = GetCurrentTimestamp();
  this_thread::sleep_for(kArbitraryDuration);
  Timestamp end = GetCurrentTimestamp();
  uint64_t duration = GetDuration(start, end);
  EXPECT_GE(duration, 1000000u);
}

}  // namespace
}  // namespace pw::perf_test::internal::backend
<|endoftext|>


<commit_before>//
// Copyright (c) 2020-2022 INRIA
//

#ifndef __eigenpy_user_type_hpp__
#define __eigenpy_user_type_hpp__

#include "eigenpy/fwd.hpp"
#include "eigenpy/numpy-type.hpp"
#include "eigenpy/register.hpp"

#include <iostream>

namespace eigenpy
{
  /// \brief Default cast algo to cast a From to To. Can be specialized for any types.
  template<typename From, typename To>
  struct cast
  {
    static To run(const From & from)
    {
      return static_cast<To>(from);
    }

  };

  namespace internal
  {

    template<typename From, typename To>
    static void cast(void * from_, void * to_, npy_intp n, void * /*fromarr*/, void * /*toarr*/)
    {
//      std::cout << "cast::run" << std::endl;
      const From* from = static_cast<From*>(from_);
      To* to = static_cast<To*>(to_);
      for(npy_intp i = 0; i < n; i++)
      {
        to[i] = eigenpy::cast<From,To>::run(from[i]);
      }
    }

    template<typename T, int type_code = NumpyEquivalentType<T>::type_code>
    struct SpecialMethods
    {
      inline static void copyswap(void * /*dst*/, void * /*src*/, int /*swap*/, void * /*arr*/) /*{}*/;
      inline static PyObject * getitem(void * /*ip*/, void * /*ap*/) /*{ return NULL; }*/;
      inline static int setitem(PyObject * /*op*/, void * /*ov*/, void * /*ap*/) /*{ return -1; }*/;
      inline static void copyswapn(void * /*dest*/, long /*dstride*/, void * /*src*/,
                            long /*sstride*/, long /*n*/, int /*swap*/, void * /*arr*/) /*{}*/;
      inline static npy_bool nonzero(void * /*ip*/, void * /*array*/) /*{ return (npy_bool)false; }*/;
      inline static void dotfunc(void * /*ip0_*/, npy_intp /*is0*/, void * /*ip1_*/, npy_intp /*is1*/,
                          void * /*op*/, npy_intp /*n*/, void * /*arr*/);
      inline static int fill(void* data_, npy_intp length, void* arr);
      inline static int fillwithscalar(void* buffer_, npy_intp length,
                                       void* value, void* arr);
    };

    template<typename T>
    struct OffsetOf
    {
      struct Data
      {
        char c;
        T v;
      };

      enum { value = offsetof(Data, v) };
    };

    template<typename T>
    struct SpecialMethods<T,NPY_USERDEF>
    {
      inline static void copyswap(void * dst, void * src, int swap, void * /*arr*/)
      {
//        std::cout << "copyswap" << std::endl;
        if (src != NULL)
        {
          T & t1 = *static_cast<T*>(dst);
          T & t2 = *static_cast<T*>(src);
          t1 = t2;
        }

        if(swap)
        {
          T & t1 = *static_cast<T*>(dst);
          T & t2 = *static_cast<T*>(src);
          std::swap(t1,t2);
        }
      }

      ///
      /// \brief Get a python object from an array
      ///        It returns a standard Python object from
      ///        a single element of the array object arr pointed to by data.
      /// \param[in] data Pointer to the first element of the C++ data stream
      /// \param[in] arr  Pointer to the first element of the Python object data stream
      ///
      /// \returns PyObject corresponding to the python datastream.
      ///
      inline static PyObject * getitem(void * ip, void * /*ap*/)
      {
//        std::cout << "getitem" << std::endl;
        T * elt_ptr = static_cast<T*>(ip);
        bp::object m(*elt_ptr);
        Py_INCREF(m.ptr());
        return m.ptr();
      }

      ///
      /// \brief Set a python object in an array.
      ///        It sets the Python object "item" into the array, arr, at the position
      ///        pointed to by data. This function deals with “misbehaved” arrays.
      ///        If successful, a zero is returned, otherwise, a negative one is returned
      ///        (and a Python error set).

      /// \param[in] src_obj  Pointer to the location of the python object
      /// \param[in] dest_ptr Pointer to the location in the array where the source object should be saved.
      /// \param[in] array Pointer to the location of the array
      ///
      /// \returns int Success(0) or Failure(-1)
      ///

      inline static int setitem(PyObject * src_obj, void * dest_ptr, void * array)
      {
//        std::cout << "setitem" << std::endl;
        if(array == NULL)
        {
          eigenpy::Exception("Cannot retrieve the type stored in the array.");
          return -1;
        }
        PyArrayObject * py_array = static_cast<PyArrayObject *>(array);
        PyArray_Descr * descr = PyArray_DTYPE(py_array);
        PyTypeObject * array_scalar_type = descr->typeobj;
        PyTypeObject * src_obj_type = Py_TYPE(src_obj);

        if(array_scalar_type != src_obj_type)
        {
          std::stringstream ss;
          ss << "The input type is of wrong type. ";
          ss << "The expected type is " << bp::type_info(typeid(T)).name() << std::endl;
          eigenpy::Exception(ss.str());
          return -1;
        }

        bp::extract<T&> extract_src_obj(src_obj);
        if(!extract_src_obj.check())
        {
          std::stringstream ss;
          ss << "The input type is of wrong type. ";
          ss << "The expected type is " << bp::type_info(typeid(T)).name() << std::endl;
          eigenpy::Exception(ss.str());
          return -1;
        }

        const T & src = extract_src_obj();
        T & dest = *static_cast<T*>(dest_ptr);
        dest = src;

        return 0;
      }

      inline static void copyswapn(void * dst, long dstride, void * src, long sstride,
                                   long n, int swap, void * array)
      {
//        std::cout << "copyswapn" << std::endl;

        char *dstptr = static_cast<char*>(dst);
        char *srcptr = static_cast<char*>(src);

        PyArrayObject * py_array = static_cast<PyArrayObject *>(array);
        PyArray_CopySwapFunc * copyswap = PyArray_DESCR(py_array)->f->copyswap;

        for (npy_intp i = 0; i < n; i++)
        {
          copyswap(dstptr, srcptr, swap, array);
          dstptr += dstride;
          srcptr += sstride;
        }
      }

      inline static npy_bool nonzero(void * ip, void * array)
      {
//        std::cout << "nonzero" << std::endl;
        static const T ZeroValue = T(0);
        PyArrayObject * py_array = static_cast<PyArrayObject *>(array);
        if(py_array == NULL || PyArray_ISBEHAVED_RO(py_array))
        {
          const T & value = *static_cast<T*>(ip);
          return (npy_bool)(value != ZeroValue);
        }
        else
        {
          T tmp_value;
          PyArray_DESCR(py_array)->f->copyswap(&tmp_value, ip, PyArray_ISBYTESWAPPED(py_array),
                                               array);
          return (npy_bool)(tmp_value != ZeroValue);
        }
      }

      inline static void dotfunc(void * ip0_, npy_intp is0, void * ip1_, npy_intp is1,
                                 void * op, npy_intp n, void * /*arr*/)
      {
//        std::cout << "dotfunc" << std::endl;
        typedef Eigen::Matrix<T,Eigen::Dynamic,1> VectorT;
        typedef Eigen::InnerStride<Eigen::Dynamic> InputStride;
        typedef const Eigen::Map<const VectorT,0,InputStride> ConstMapType;

        ConstMapType
        v0(static_cast<T*>(ip0_),n,InputStride(is0/sizeof(T))),
        v1(static_cast<T*>(ip1_),n,InputStride(is1/sizeof(T)));

        *static_cast<T*>(op) = v0.dot(v1);
      }

      inline static int fillwithscalar(void* buffer_, npy_intp length,
                                       void* value, void* /*arr*/)
      {
//        std::cout << "fillwithscalar" << std::endl;
        T r = *static_cast<T*>(value);
        T* buffer = static_cast<T*>(buffer_);
        npy_intp i;
        for (i = 0; i < length; i++) {
          buffer[i] = r;
        }
        return 0;
      }

      static int fill(void* data_, npy_intp length, void* /*arr*/)
      {
//        std::cout << "fillwithscalar" << std::endl;
        T* data = static_cast<T*>(data_);
        const T delta = data[1] - data[0];
        T r = data[1];
        npy_intp i;
        for (i = 2; i < length; i++) {
          r = r + delta;
          data[i] = r;
        }
        return 0;
      }

    };  //     struct SpecialMethods<T,NPY_USERDEF>

  } // namespace internal

  template<typename From, typename To>
  bool registerCast(const bool safe)
  {
    PyArray_Descr* from_array_descr = Register::getPyArrayDescr<From>();
//    int from_typenum = Register::getTypeCode<From>();

//    PyTypeObject * to_py_type = Register::getPyType<To>();
    int to_typenum = Register::getTypeCode<To>();
    assert(to_typenum >= 0 && "to_typenum is not valid");
    assert(from_array_descr != NULL && "from_array_descr is not valid");

    std::cout << "From: " << bp::type_info(typeid(From)).name() << " " << Register::getTypeCode<From>()
    << " to: " << bp::type_info(typeid(To)).name() << " " << Register::getTypeCode<To>()
    << "\n to_typenum: " << to_typenum
    << std::endl;

    if(call_PyArray_RegisterCastFunc(from_array_descr,
                                     to_typenum,
                                     static_cast<PyArray_VectorUnaryFunc *>(&eigenpy::internal::cast<From,To>)) < 0)
    {
      std::stringstream ss;
      ss
      << "PyArray_RegisterCastFunc of the cast from "
      << bp::type_info(typeid(From)).name()
      << " to "
      << bp::type_info(typeid(To)).name()
      << " has failed.";
      eigenpy::Exception(ss.str());
      return false;
    }

    if (safe && call_PyArray_RegisterCanCast(from_array_descr,
                                             to_typenum,
                                             NPY_NOSCALAR) < 0)
    {
      std::stringstream ss;
      ss
      << "PyArray_RegisterCanCast of the cast from "
      << bp::type_info(typeid(From)).name()
      << " to "
      << bp::type_info(typeid(To)).name()
      << " has failed.";
      eigenpy::Exception(ss.str());
      return false;
    }

    return true;
  }

  /// \brief Get the class object for a wrapped type that has been exposed
  ///        through Boost.Python.
  template <typename T>
  boost::python::object getInstanceClass()
  {
    // Query into the registry for type T.
    namespace bp = boost::python;
    bp::type_info type = bp::type_id<T>();
    const bp::converter::registration* registration =
      bp::converter::registry::query(type);

    // If the class is not registered, return None.
    if (!registration) {
      //std::cerr<<"Class Not Registered. Returning Empty."<<std::endl;
      return bp::object();
    }

    bp::handle<PyTypeObject> handle(bp::borrowed(registration->get_class_object()));
    return bp::object(handle);
  }

  template<typename Scalar>
  int registerNewType(PyTypeObject * py_type_ptr = NULL)
  {
    // Check whether the type is a Numpy native type.
    // In this case, the registration is not required.
    if(isNumpyNativeType<Scalar>())
      return NumpyEquivalentType<Scalar>::type_code;

    // Retrieve the registered type for the current Scalar
    if(py_type_ptr == NULL)
    { // retrive the type from Boost.Python
      py_type_ptr = Register::getPyType<Scalar>();
    }

    if(Register::isRegistered(py_type_ptr))
      return Register::getTypeCode(py_type_ptr); // the type is already registered

    PyArray_GetItemFunc * getitem = &internal::SpecialMethods<Scalar>::getitem;
    PyArray_SetItemFunc * setitem = &internal::SpecialMethods<Scalar>::setitem;
    PyArray_NonzeroFunc * nonzero = &internal::SpecialMethods<Scalar>::nonzero;
    PyArray_CopySwapFunc * copyswap = &internal::SpecialMethods<Scalar>::copyswap;
    PyArray_CopySwapNFunc * copyswapn = reinterpret_cast<PyArray_CopySwapNFunc*>(&internal::SpecialMethods<Scalar>::copyswapn);
    PyArray_DotFunc * dotfunc = &internal::SpecialMethods<Scalar>::dotfunc;
    PyArray_FillFunc * fill = &internal::SpecialMethods<Scalar>::fill;
    PyArray_FillWithScalarFunc * fillwithscalar = &internal::SpecialMethods<Scalar>::fillwithscalar;

    int code = Register::registerNewType(py_type_ptr,
                                         &typeid(Scalar),
                                         sizeof(Scalar),
                                         internal::OffsetOf<Scalar>::value,
                                         getitem, setitem, nonzero,
                                         copyswap, copyswapn,
                                         dotfunc,
                                         fill,
                                         fillwithscalar);

    call_PyArray_RegisterCanCast(call_PyArray_DescrFromType(NPY_OBJECT),
                                 code, NPY_NOSCALAR);

    return code;
  }

} // namespace eigenpy

#endif // __eigenpy_user_type_hpp__
<commit_msg>core: fix warning<commit_after>//
// Copyright (c) 2020-2022 INRIA
//

#ifndef __eigenpy_user_type_hpp__
#define __eigenpy_user_type_hpp__

#include "eigenpy/fwd.hpp"
#include "eigenpy/numpy-type.hpp"
#include "eigenpy/register.hpp"

#include <iostream>

namespace eigenpy
{
  /// \brief Default cast algo to cast a From to To. Can be specialized for any types.
  template<typename From, typename To>
  struct cast
  {
    static To run(const From & from)
    {
      return static_cast<To>(from);
    }

  };

  namespace internal
  {

    template<typename From, typename To>
    static void cast(void * from_, void * to_, npy_intp n, void * /*fromarr*/, void * /*toarr*/)
    {
//      std::cout << "cast::run" << std::endl;
      const From* from = static_cast<From*>(from_);
      To* to = static_cast<To*>(to_);
      for(npy_intp i = 0; i < n; i++)
      {
        to[i] = eigenpy::cast<From,To>::run(from[i]);
      }
    }

    template<typename T, int type_code = NumpyEquivalentType<T>::type_code>
    struct SpecialMethods
    {
      inline static void copyswap(void * /*dst*/, void * /*src*/, int /*swap*/, void * /*arr*/) /*{}*/;
      inline static PyObject * getitem(void * /*ip*/, void * /*ap*/) /*{ return NULL; }*/;
      inline static int setitem(PyObject * /*op*/, void * /*ov*/, void * /*ap*/) /*{ return -1; }*/;
      inline static void copyswapn(void * /*dest*/, long /*dstride*/, void * /*src*/,
                            long /*sstride*/, long /*n*/, int /*swap*/, void * /*arr*/) /*{}*/;
      inline static npy_bool nonzero(void * /*ip*/, void * /*array*/) /*{ return (npy_bool)false; }*/;
      inline static void dotfunc(void * /*ip0_*/, npy_intp /*is0*/, void * /*ip1_*/, npy_intp /*is1*/,
                          void * /*op*/, npy_intp /*n*/, void * /*arr*/);
      inline static int fill(void* data_, npy_intp length, void* arr);
      inline static int fillwithscalar(void* buffer_, npy_intp length,
                                       void* value, void* arr);
    };

    template<typename T>
    struct OffsetOf
    {
      struct Data
      {
        char c;
        T v;
      };

      enum { value = offsetof(Data, v) };
    };

    template<typename T>
    struct SpecialMethods<T,NPY_USERDEF>
    {
      inline static void copyswap(void * dst, void * src, int swap, void * /*arr*/)
      {
//        std::cout << "copyswap" << std::endl;
        if (src != NULL)
        {
          T & t1 = *static_cast<T*>(dst);
          T & t2 = *static_cast<T*>(src);
          t1 = t2;
        }

        if(swap)
        {
          T & t1 = *static_cast<T*>(dst);
          T & t2 = *static_cast<T*>(src);
          std::swap(t1,t2);
        }
      }

      ///
      /// \brief Get a python object from an array
      ///        It returns a standard Python object from
      ///        a single element of the array object arr pointed to by data.
      /// \param[in] data Pointer to the first element of the C++ data stream
      /// \param[in] arr  Pointer to the first element of the Python object data stream
      ///
      /// \returns PyObject corresponding to the python datastream.
      ///
      inline static PyObject * getitem(void * ip, void * /*ap*/)
      {
//        std::cout << "getitem" << std::endl;
        T * elt_ptr = static_cast<T*>(ip);
        bp::object m(*elt_ptr);
        Py_INCREF(m.ptr());
        return m.ptr();
      }

      ///
      /// \brief Set a python object in an array.
      ///        It sets the Python object "item" into the array, arr, at the position
      ///        pointed to by data. This function deals with “misbehaved” arrays.
      ///        If successful, a zero is returned, otherwise, a negative one is returned
      ///        (and a Python error set).

      /// \param[in] src_obj  Pointer to the location of the python object
      /// \param[in] dest_ptr Pointer to the location in the array where the source object should be saved.
      /// \param[in] array Pointer to the location of the array
      ///
      /// \returns int Success(0) or Failure(-1)
      ///

      inline static int setitem(PyObject * src_obj, void * dest_ptr, void * array)
      {
//        std::cout << "setitem" << std::endl;
        if(array == NULL)
        {
          eigenpy::Exception("Cannot retrieve the type stored in the array.");
          return -1;
        }
        PyArrayObject * py_array = static_cast<PyArrayObject *>(array);
        PyArray_Descr * descr = PyArray_DTYPE(py_array);
        PyTypeObject * array_scalar_type = descr->typeobj;
        PyTypeObject * src_obj_type = Py_TYPE(src_obj);

        if(array_scalar_type != src_obj_type)
        {
          std::stringstream ss;
          ss << "The input type is of wrong type. ";
          ss << "The expected type is " << bp::type_info(typeid(T)).name() << std::endl;
          eigenpy::Exception(ss.str());
          return -1;
        }

        bp::extract<T&> extract_src_obj(src_obj);
        if(!extract_src_obj.check())
        {
          std::stringstream ss;
          ss << "The input type is of wrong type. ";
          ss << "The expected type is " << bp::type_info(typeid(T)).name() << std::endl;
          eigenpy::Exception(ss.str());
          return -1;
        }

        const T & src = extract_src_obj();
        T & dest = *static_cast<T*>(dest_ptr);
        dest = src;

        return 0;
      }

      inline static void copyswapn(void * dst, long dstride, void * src, long sstride,
                                   long n, int swap, void * array)
      {
//        std::cout << "copyswapn" << std::endl;

        char *dstptr = static_cast<char*>(dst);
        char *srcptr = static_cast<char*>(src);

        PyArrayObject * py_array = static_cast<PyArrayObject *>(array);
        PyArray_CopySwapFunc * copyswap = PyArray_DESCR(py_array)->f->copyswap;

        for (npy_intp i = 0; i < n; i++)
        {
          copyswap(dstptr, srcptr, swap, array);
          dstptr += dstride;
          srcptr += sstride;
        }
      }

      inline static npy_bool nonzero(void * ip, void * array)
      {
//        std::cout << "nonzero" << std::endl;
        static const T ZeroValue = T(0);
        PyArrayObject * py_array = static_cast<PyArrayObject *>(array);
        if(py_array == NULL || PyArray_ISBEHAVED_RO(py_array))
        {
          const T & value = *static_cast<T*>(ip);
          return (npy_bool)(value != ZeroValue);
        }
        else
        {
          T tmp_value;
          PyArray_DESCR(py_array)->f->copyswap(&tmp_value, ip, PyArray_ISBYTESWAPPED(py_array),
                                               array);
          return (npy_bool)(tmp_value != ZeroValue);
        }
      }

      inline static void dotfunc(void * ip0_, npy_intp is0, void * ip1_, npy_intp is1,
                                 void * op, npy_intp n, void * /*arr*/)
      {
//        std::cout << "dotfunc" << std::endl;
        typedef Eigen::Matrix<T,Eigen::Dynamic,1> VectorT;
        typedef Eigen::InnerStride<Eigen::Dynamic> InputStride;
        typedef const Eigen::Map<const VectorT,0,InputStride> ConstMapType;

        ConstMapType
        v0(static_cast<T*>(ip0_),n,InputStride(is0/(Eigen::DenseIndex)sizeof(T))),
        v1(static_cast<T*>(ip1_),n,InputStride(is1/(Eigen::DenseIndex)sizeof(T)));

        *static_cast<T*>(op) = v0.dot(v1);
      }

      inline static int fillwithscalar(void* buffer_, npy_intp length,
                                       void* value, void* /*arr*/)
      {
//        std::cout << "fillwithscalar" << std::endl;
        T r = *static_cast<T*>(value);
        T* buffer = static_cast<T*>(buffer_);
        npy_intp i;
        for (i = 0; i < length; i++) {
          buffer[i] = r;
        }
        return 0;
      }

      static int fill(void* data_, npy_intp length, void* /*arr*/)
      {
//        std::cout << "fillwithscalar" << std::endl;
        T* data = static_cast<T*>(data_);
        const T delta = data[1] - data[0];
        T r = data[1];
        npy_intp i;
        for (i = 2; i < length; i++) {
          r = r + delta;
          data[i] = r;
        }
        return 0;
      }

    };  //     struct SpecialMethods<T,NPY_USERDEF>

  } // namespace internal

  template<typename From, typename To>
  bool registerCast(const bool safe)
  {
    PyArray_Descr* from_array_descr = Register::getPyArrayDescr<From>();
//    int from_typenum = Register::getTypeCode<From>();

//    PyTypeObject * to_py_type = Register::getPyType<To>();
    int to_typenum = Register::getTypeCode<To>();
    assert(to_typenum >= 0 && "to_typenum is not valid");
    assert(from_array_descr != NULL && "from_array_descr is not valid");

    std::cout << "From: " << bp::type_info(typeid(From)).name() << " " << Register::getTypeCode<From>()
    << " to: " << bp::type_info(typeid(To)).name() << " " << Register::getTypeCode<To>()
    << "\n to_typenum: " << to_typenum
    << std::endl;

    if(call_PyArray_RegisterCastFunc(from_array_descr,
                                     to_typenum,
                                     static_cast<PyArray_VectorUnaryFunc *>(&eigenpy::internal::cast<From,To>)) < 0)
    {
      std::stringstream ss;
      ss
      << "PyArray_RegisterCastFunc of the cast from "
      << bp::type_info(typeid(From)).name()
      << " to "
      << bp::type_info(typeid(To)).name()
      << " has failed.";
      eigenpy::Exception(ss.str());
      return false;
    }

    if (safe && call_PyArray_RegisterCanCast(from_array_descr,
                                             to_typenum,
                                             NPY_NOSCALAR) < 0)
    {
      std::stringstream ss;
      ss
      << "PyArray_RegisterCanCast of the cast from "
      << bp::type_info(typeid(From)).name()
      << " to "
      << bp::type_info(typeid(To)).name()
      << " has failed.";
      eigenpy::Exception(ss.str());
      return false;
    }

    return true;
  }

  /// \brief Get the class object for a wrapped type that has been exposed
  ///        through Boost.Python.
  template <typename T>
  boost::python::object getInstanceClass()
  {
    // Query into the registry for type T.
    namespace bp = boost::python;
    bp::type_info type = bp::type_id<T>();
    const bp::converter::registration* registration =
      bp::converter::registry::query(type);

    // If the class is not registered, return None.
    if (!registration) {
      //std::cerr<<"Class Not Registered. Returning Empty."<<std::endl;
      return bp::object();
    }

    bp::handle<PyTypeObject> handle(bp::borrowed(registration->get_class_object()));
    return bp::object(handle);
  }

  template<typename Scalar>
  int registerNewType(PyTypeObject * py_type_ptr = NULL)
  {
    // Check whether the type is a Numpy native type.
    // In this case, the registration is not required.
    if(isNumpyNativeType<Scalar>())
      return NumpyEquivalentType<Scalar>::type_code;

    // Retrieve the registered type for the current Scalar
    if(py_type_ptr == NULL)
    { // retrive the type from Boost.Python
      py_type_ptr = Register::getPyType<Scalar>();
    }

    if(Register::isRegistered(py_type_ptr))
      return Register::getTypeCode(py_type_ptr); // the type is already registered

    PyArray_GetItemFunc * getitem = &internal::SpecialMethods<Scalar>::getitem;
    PyArray_SetItemFunc * setitem = &internal::SpecialMethods<Scalar>::setitem;
    PyArray_NonzeroFunc * nonzero = &internal::SpecialMethods<Scalar>::nonzero;
    PyArray_CopySwapFunc * copyswap = &internal::SpecialMethods<Scalar>::copyswap;
    PyArray_CopySwapNFunc * copyswapn = reinterpret_cast<PyArray_CopySwapNFunc*>(&internal::SpecialMethods<Scalar>::copyswapn);
    PyArray_DotFunc * dotfunc = &internal::SpecialMethods<Scalar>::dotfunc;
    PyArray_FillFunc * fill = &internal::SpecialMethods<Scalar>::fill;
    PyArray_FillWithScalarFunc * fillwithscalar = &internal::SpecialMethods<Scalar>::fillwithscalar;

    int code = Register::registerNewType(py_type_ptr,
                                         &typeid(Scalar),
                                         sizeof(Scalar),
                                         internal::OffsetOf<Scalar>::value,
                                         getitem, setitem, nonzero,
                                         copyswap, copyswapn,
                                         dotfunc,
                                         fill,
                                         fillwithscalar);

    call_PyArray_RegisterCanCast(call_PyArray_DescrFromType(NPY_OBJECT),
                                 code, NPY_NOSCALAR);

    return code;
  }

} // namespace eigenpy

#endif // __eigenpy_user_type_hpp__
<|endoftext|>


<commit_before>#ifndef ELEMEL_STRING_PTR_HPP
#define ELEMEL_STRING_PTR_HPP

#include <elemel/raw_allocator.hpp>
#include <elemel/ref_ptr.hpp>
#include <elemel/detail/string_impl.hpp>

#include <algorithm>
#include <cassert>
#include <string>

namespace elemel {
    template <
        class Char,
        class Traits = std::char_traits<Char>,
        class RefCount = long,
        class RawAllocator = raw_new_allocator
    >
    class basic_string_ptr {
    public:
        typedef Char value_type;
        typedef Traits traits_type;
        typedef RefCount ref_count_type;
        typedef RawAllocator raw_allocator_type;
        typedef detail::string_impl<value_type, ref_count_type, raw_allocator_type>
            impl_type;
        typedef std::size_t size_type;
        typedef value_type const *const_pointer;
        typedef value_type const *const_iterator;

        explicit basic_string_ptr(raw_allocator_type const &alloc =
                                  raw_allocator_type()) :
            impl_(impl_type::create(0, 0, alloc))
        { }

        explicit basic_string_ptr(value_type const *str,
                                  raw_allocator_type const &alloc =
                                  raw_allocator_type()) :
            impl_(impl_type::create(str, Traits::length(str), alloc))
        { }

        basic_string_ptr(value_type const *str, size_type n,
                         raw_allocator_type const &alloc =
                         raw_allocator_type()) :
            impl_(impl_type::create(str, n, alloc))
        { }

        const_pointer data() const
        {
            return impl_->data();
        }

        size_type size() const
        {
            return impl_->size();
        }

        const_pointer c_str() const
        {
            return impl_->data();
        }

        const_iterator begin() const
        {
            return impl_->data();
        }

        const_iterator end() const
        {
            return impl_->data() + impl_->size();
        }

    private:
        ref_ptr<impl_type> impl_;
    };

    template <class C, class T, class N, class A>
    bool operator==(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return (left.size() == right.size() &&
                std::equal(left.begin(), left.end(), right.begin(), T::eq));
    }

    template <class C, class T, class N, class A>
    bool operator!=(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left == right);
    }

    template <class C, class T, class N, class A>
    bool operator<(basic_string_ptr<C, T, N, A> const &left,
                   basic_string_ptr<C, T, N, A> const &right)
    {
        return std::lexicographical_compare(left.begin(), left.end(),
                                            right.begin(), right.end(), T::lt);
    }

    template <class C, class T, class N, class A>
    bool operator<=(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return !(right < left);
    }

    template <class C, class T, class N, class A>
    bool operator>=(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left < right);
    }

    template <class C, class T, class N, class A>
    bool operator>(basic_string_ptr<C, T, N, A> const &left,
                   basic_string_ptr<C, T, N, A> const &right)
    {
        return right < left;
    }

    template <class C, class T, class N, class A>
    bool operator==(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        assert(right);
        std::size_t n = T::length(right);
        return left.size() == n && std::equal(left.begin(), left.end(), right);
    }

    template <class C, class T, class N, class A>
    bool operator!=(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return !(left == right);
    }

    template <class C, class T, class N, class A>
    bool operator<(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        assert(right);
        return std::lexicographical_compare(left.begin(), left.end(),
                                            right, right + T::length(right),
                                            T::lt);
    }

    template <class C, class T, class N, class A>
    bool operator<=(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return !(right < left);
    }

    template <class C, class T, class N, class A>
    bool operator>=(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return !(left < right);
    }

    template <class C, class T, class N, class A>
    bool operator>(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return right < left;
    }

    template <class C, class T, class N, class A>
    bool operator==(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        assert(left);
        std::size_t n = T::length(left);
        return n == right.size() && std::equal(left, left + n, right.begin());
    }

    template <class C, class T, class N, class A>
    bool operator!=(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left == right);
    }

    template <class C, class T, class N, class A>
    bool operator<(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        assert(left);
        return std::lexicographical_compare(left, left + T::length(left),
                                            right.begin(), right.end(),
                                            T::lt);
    }

    template <class C, class T, class N, class A>
    bool operator<=(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return !(right < left);
    }

    template <class C, class T, class N, class A>
    bool operator>=(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left < right);
    }

    template <class C, class T, class N, class A>
    bool operator>(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return right < left;
    }

    typedef basic_string_ptr<char> string_ptr;
    typedef basic_string_ptr<wchar_t> wstring_ptr;
}

#endif // ELEMEL_STRING_PTR_HPP
<commit_msg>cleanup of string_ptr<commit_after>#ifndef ELEMEL_STRING_PTR_HPP
#define ELEMEL_STRING_PTR_HPP

#include <elemel/raw_allocator.hpp>
#include <elemel/ref_ptr.hpp>
#include <elemel/detail/string_impl.hpp>

#include <algorithm>
#include <cassert>
#include <string>

namespace elemel {
    template <
        class Char,
        class Traits = std::char_traits<Char>,
        class RefCount = long,
        class RawAllocator = raw_new_allocator
    >
    class basic_string_ptr {
    public:
        typedef Char value_type;
        typedef Traits traits_type;
        typedef RefCount ref_count_type;
        typedef RawAllocator raw_allocator_type;
        typedef detail::string_impl<value_type, ref_count_type, raw_allocator_type>
            impl_type;
        typedef std::size_t size_type;
        typedef value_type const *const_pointer;
        typedef value_type const *const_iterator;

        explicit basic_string_ptr(raw_allocator_type const &alloc =
                                  raw_allocator_type()) :
            impl_(impl_type::create(0, 0, alloc))
        { }

        explicit basic_string_ptr(const_pointer str,
                                  raw_allocator_type const &alloc =
                                  raw_allocator_type()) :
            impl_(impl_type::create(str, Traits::length(str), alloc))
        { }

        basic_string_ptr(const_pointer str, size_type n,
                         raw_allocator_type const &alloc =
                         raw_allocator_type()) :
            impl_(impl_type::create(str, n, alloc))
        { }

        basic_string_ptr(const_pointer first, const_pointer last,
                         raw_allocator_type const &alloc =
                         raw_allocator_type()) :
            impl_(impl_type::create(first, last - first, alloc))
        { }

        const_pointer data() const
        {
            return impl_->data();
        }

        size_type size() const
        {
            return impl_->size();
        }

        const_pointer c_str() const
        {
            return impl_->data();
        }

        const_iterator begin() const
        {
            return impl_->data();
        }

        const_iterator end() const
        {
            return impl_->data() + impl_->size();
        }

    private:
        ref_ptr<impl_type> impl_;
    };

    template <class C, class T, class N, class A>
    bool operator==(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return (left.size() == right.size() &&
                std::equal(left.begin(), left.end(), right.begin(), T::eq));
    }

    template <class C, class T, class N, class A>
    bool operator!=(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left == right);
    }

    template <class C, class T, class N, class A>
    bool operator<(basic_string_ptr<C, T, N, A> const &left,
                   basic_string_ptr<C, T, N, A> const &right)
    {
        return std::lexicographical_compare(left.begin(), left.end(),
                                            right.begin(), right.end(), T::lt);
    }

    template <class C, class T, class N, class A>
    bool operator<=(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return !(right < left);
    }

    template <class C, class T, class N, class A>
    bool operator>=(basic_string_ptr<C, T, N, A> const &left,
                    basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left < right);
    }

    template <class C, class T, class N, class A>
    bool operator>(basic_string_ptr<C, T, N, A> const &left,
                   basic_string_ptr<C, T, N, A> const &right)
    {
        return right < left;
    }

    template <class C, class T, class N, class A>
    bool operator==(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        assert(right);
        std::size_t n = T::length(right);
        return left.size() == n && std::equal(left.begin(), left.end(), right);
    }

    template <class C, class T, class N, class A>
    bool operator!=(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return !(left == right);
    }

    template <class C, class T, class N, class A>
    bool operator<(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        assert(right);
        return std::lexicographical_compare(left.begin(), left.end(),
                                            right, right + T::length(right),
                                            T::lt);
    }

    template <class C, class T, class N, class A>
    bool operator<=(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return !(right < left);
    }

    template <class C, class T, class N, class A>
    bool operator>=(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return !(left < right);
    }

    template <class C, class T, class N, class A>
    bool operator>(basic_string_ptr<C, T, N, A> const &left, C const *right)
    {
        return right < left;
    }

    template <class C, class T, class N, class A>
    bool operator==(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        assert(left);
        std::size_t n = T::length(left);
        return n == right.size() && std::equal(left, left + n, right.begin());
    }

    template <class C, class T, class N, class A>
    bool operator!=(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left == right);
    }

    template <class C, class T, class N, class A>
    bool operator<(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        assert(left);
        return std::lexicographical_compare(left, left + T::length(left),
                                            right.begin(), right.end(),
                                            T::lt);
    }

    template <class C, class T, class N, class A>
    bool operator<=(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return !(right < left);
    }

    template <class C, class T, class N, class A>
    bool operator>=(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return !(left < right);
    }

    template <class C, class T, class N, class A>
    bool operator>(C const *left, basic_string_ptr<C, T, N, A> const &right)
    {
        return right < left;
    }

    typedef basic_string_ptr<char> string_ptr;
    typedef basic_string_ptr<wchar_t> wstring_ptr;
}

#endif // ELEMEL_STRING_PTR_HPP
<|endoftext|>