<commit_before>#include "ksp_plugin/interface.hpp"

#include <string>

#include "base/version.hpp"

using principia::quantities::Pow;
using principia::si::Degree;
using principia::si::Metre;
using principia::si::Tonne;

namespace principia {
namespace ksp_plugin {

namespace {

// Takes ownership of |**pointer| and returns it to the caller.  Nulls
// |*pointer|.  |pointer| must not be null.  No transfer of ownership of
// |*pointer|.
template<typename T>
std::unique_ptr<T> TakeOwnership(T** const pointer) {
  CHECK_NOTNULL(pointer);
  std::unique_ptr<T const> owned_pointer(*pointer);
  *pointer = nullptr;
  return owned_pointer;
}

}  // namespace

void InitGoogleLogging() {
  if (google::IsGoogleLoggingInitialized()) {
    LOG(INFO) << "Google logging was already initialized, no action taken";
  } else {
#ifdef _MSC_VER
    FILE* file;
    freopen_s(&file, "stderr.log", "w", stderr);
#else
    std::freopen("stderr.log", "w", stderr);
#endif
    google::SetStderrLogging(google::INFO);
    google::SetLogDestination(google::FATAL, "glog/Principia/FATAL.");
    google::SetLogDestination(google::ERROR, "glog/Principia/ERROR.");
    google::SetLogDestination(google::WARNING, "glog/Principia/WARNING.");
    google::SetLogDestination(google::INFO, "glog/Principia/INFO.");
    FLAGS_v = 1;
    // Buffer severities <= |INFO|, i.e., don't buffer.
    FLAGS_logbuflevel = google::INFO - 1;
    google::InitGoogleLogging("Principia");
    LOG(INFO) << "Initialized Google logging for Principia";
    LOG(INFO) << "Principia version " << base::kVersion
              << " built on " << base::kBuildDate;
    // TODO(egg): by (compiler) for (ARCH, OS).
  }
}

void LogInfo(char const* message) {
  LOG(INFO) << message;
}

void LogWarning(char const* message) {
  LOG(WARNING) << message;
}

void LogError(char const* message) {
  LOG(ERROR) << message;
}

void LogFatal(char const* message) {
  LOG(FATAL) << message;
}

Plugin* NewPlugin(double const initial_time,
                  int const sun_index,
                  double const sun_gravitational_parameter,
                  double const planetarium_rotation_in_degrees) {
  LOG(INFO) << "Constructing Principia plugin";
  std::unique_ptr<Plugin> result = std::make_unique<Plugin>(
      Instant(initial_time * Second),
      sun_index,
      sun_gravitational_parameter * SIUnit<GravitationalParameter>(),
      planetarium_rotation_in_degrees * Degree);
  LOG(INFO) << "Plugin constructed";
  return result.release();
}

void DeletePlugin(Plugin const** const plugin) {
  LOG(INFO) << "Destroying Principia plugin";
  // We want to log before and after destroying the plugin since it is a pretty
  // significant event, so we take ownership inside a block.
  {
    TakeOwnership(plugin);
  }
  LOG(INFO) << "Plugin destroyed";
}

// NOTE(egg): The |* (Metre / Second)| might be slower than |* SIUnit<Speed>()|,
// but it is more readable. This will be resolved once we have constexpr.

void InsertCelestial(Plugin* const plugin,
                     int const celestial_index,
                     double const gravitational_parameter,
                     int const parent_index,
                     XYZ const from_parent_position,
                     XYZ const from_parent_velocity) {
  CHECK_NOTNULL(plugin)->InsertCelestial(
      celestial_index,
      gravitational_parameter * SIUnit<GravitationalParameter>(),
      parent_index,
      Displacement<AliceSun>({from_parent_position.x * Metre,
                              from_parent_position.y * Metre,
                              from_parent_position.z * Metre}),
      Velocity<AliceSun>({from_parent_velocity.x * (Metre / Second),
                          from_parent_velocity.y * (Metre / Second),
                          from_parent_velocity.z * (Metre / Second)}));
}

void UpdateCelestialHierarchy(Plugin const* const plugin,
                              int const celestial_index,
                              int const parent_index) {
  CHECK_NOTNULL(plugin)->UpdateCelestialHierarchy(celestial_index,
                                                  parent_index);
}

void EndInitialization(Plugin* const plugin) {
  CHECK_NOTNULL(plugin)->EndInitialization();
}

bool InsertOrKeepVessel(Plugin* const plugin,
                        char const* vessel_guid,
                        int const parent_index) {
  return CHECK_NOTNULL(plugin)->InsertOrKeepVessel(vessel_guid, parent_index);
}

void SetVesselStateOffset(Plugin* const plugin,
                          char const* vessel_guid,
                          XYZ const from_parent_position,
                          XYZ const from_parent_velocity) {
  CHECK_NOTNULL(plugin)->SetVesselStateOffset(
      vessel_guid,
      Displacement<AliceSun>({from_parent_position.x * Metre,
                              from_parent_position.y * Metre,
                              from_parent_position.z * Metre}),
      Velocity<AliceSun>({from_parent_velocity.x * (Metre / Second),
                          from_parent_velocity.y * (Metre / Second),
                          from_parent_velocity.z * (Metre / Second)}));
}

void AdvanceTime(Plugin* const plugin,
                 double const t,
                 double const planetarium_rotation) {
  CHECK_NOTNULL(plugin)->AdvanceTime(Instant(t * Second),
                                     planetarium_rotation * Degree);
}

XYZ VesselDisplacementFromParent(Plugin const* const plugin,
                                 char const* vessel_guid) {
  R3Element<Length> const result =
      CHECK_NOTNULL(plugin)->
          VesselDisplacementFromParent(vessel_guid).coordinates();
  return {result.x / Metre, result.y / Metre, result.z / Metre};
}

XYZ VesselParentRelativeVelocity(Plugin const* const plugin,
                                 char const* vessel_guid) {
  R3Element<Speed> const result =
      CHECK_NOTNULL(plugin)->
          VesselParentRelativeVelocity(vessel_guid).coordinates();
  return {result.x / (Metre / Second),
          result.y / (Metre / Second),
          result.z / (Metre / Second)};
}

XYZ CelestialDisplacementFromParent(Plugin const* const plugin,
                                    int const celestial_index) {
  R3Element<Length> const result =
      CHECK_NOTNULL(plugin)->
          CelestialDisplacementFromParent(celestial_index).coordinates();
  return {result.x / Metre, result.y / Metre, result.z / Metre};
}

XYZ CelestialParentRelativeVelocity(Plugin const* const plugin,
                                    int const celestial_index) {
  R3Element<Speed> const result =
      CHECK_NOTNULL(plugin)->
          CelestialParentRelativeVelocity(celestial_index).coordinates();
  return {result.x / (Metre / Second),
          result.y / (Metre / Second),
          result.z / (Metre / Second)};
}

BodyCentredNonRotatingFrame const* NewBodyCentredNonRotatingFrame(
    Plugin const* const plugin,
    int const reference_body_index) {
  return CHECK_NOTNULL(plugin)->
      NewBodyCentredNonRotatingFrame(reference_body_index).release();
}

BarycentricRotatingFrame const* NewBarycentricRotatingFrame(
    Plugin const* const plugin,
    int const primary_index,
    int const secondary_index) {
  return CHECK_NOTNULL(plugin)->
      NewBarycentricRotatingFrame(primary_index, secondary_index).release();
}

void DeleteRenderingFrame(RenderingFrame const** const frame) {
  TakeOwnership(frame);
}

LineAndIterator* RenderedVesselTrajectory(Plugin const* const plugin,
                                          char const* vessel_guid,
                                          RenderingFrame const* frame,
                                          XYZ const sun_world_position) {
  RenderedTrajectory<World> rendered_trajectory = CHECK_NOTNULL(plugin)->
      RenderedVesselTrajectory(
          vessel_guid,
          *CHECK_NOTNULL(frame),
          World::origin + Displacement<World>({sun_world_position.x * Metre,
                                               sun_world_position.y * Metre,
                                               sun_world_position.z * Metre}));
  std::unique_ptr<LineAndIterator> result =
      std::make_unique<LineAndIterator>(std::move(rendered_trajectory));
  result->it = result->rendered_trajectory.begin();
  return result.release();
}

int NumberOfSegments(LineAndIterator const* line_and_iterator) {
  return CHECK_NOTNULL(line_and_iterator)->rendered_trajectory.size();
}

XYZSegment FetchAndIncrement(LineAndIterator* const line_and_iterator) {
  CHECK_NOTNULL(line_and_iterator);
  CHECK(line_and_iterator->it != line_and_iterator->rendered_trajectory.end());
  LineSegment<World> const result = *line_and_iterator->it;
  ++line_and_iterator->it;
  R3Element<Length> const begin = (result.begin - World::origin).coordinates();
  R3Element<Length> const end = (result.end - World::origin).coordinates();
  return {XYZ{begin.x / Metre, begin.y / Metre, begin.z / Metre},
          XYZ{end.x / Metre, end.y / Metre, end.z / Metre}};
}

bool AtEnd(LineAndIterator* const line_and_iterator) {
  CHECK_NOTNULL(line_and_iterator);
  return line_and_iterator->it == line_and_iterator->rendered_trajectory.end();
}

void DeleteLineAndIterator(LineAndIterator const** const line_and_iterator) {
  TakeOwnership(line_and_iterator);
}

XYZ VesselWorldPosition(Plugin const* const plugin,
                        char const* vessel_guid,
                        XYZ const parent_world_position) {
  Position<World> result = CHECK_NOTNULL(plugin)->VesselWorldPosition(
      vessel_guid,
      World::origin + Displacement<World>({parent_world_position.x * Metre,
                                           parent_world_position.y * Metre,
                                           parent_world_position.z * Metre}));
  R3Element<Length> const& coordinates = (result - World::origin).coordinates();
  return XYZ{coordinates.x / Metre,
             coordinates.y / Metre,
             coordinates.z / Metre};
}

XYZ VesselWorldVelocity(Plugin const* const plugin,
                        char const* vessel_guid,
                        XYZ const parent_world_velocity,
                        double const parent_rotation_period) {
  Velocity<World> result = CHECK_NOTNULL(plugin)->VesselWorldVelocity(
      vessel_guid,
      Velocity<World>({parent_world_velocity.x * Metre / Second,
                       parent_world_velocity.y * Metre / Second,
                       parent_world_velocity.z * Metre / Second}),
      parent_rotation_period * Second);
  R3Element<Speed> const& coordinates = result.coordinates();
  return XYZ{coordinates.x / (Metre / Second),
             coordinates.y / (Metre / Second),
             coordinates.z / (Metre / Second)};
}

void AddVesselToNextPhysicsBubble(
    Plugin* const plugin,
    char const* vessel_guid,
    KSPPart const* const parts,
    int count) {
  std::vector<std::pair<PartID, std::unique_ptr<Part<World>>>> vessel_parts;
  vessel_parts.reserve(count);
  for (KSPPart const* part = parts; part < parts + count; ++part) {
    vessel_parts.push_back(
        std::make_pair(
            part->id,
            std::make_unique<Part<World>>(
                DegreesOfFreedom<World>{World::origin + Displacement<World>(
                                     {part->world_position.x * Metre,
                                      part->world_position.y * Metre,
                                      part->world_position.z * Metre}),
                 Velocity<World>({part->world_velocity.x * (Metre / Second),
                                  part->world_velocity.y * (Metre / Second),
                                  part->world_velocity.z * (Metre / Second)})},
                part->mass * Tonne,
                Vector<Acceleration, World>(
                    {part->expected_ksp_gravity.x * (Metre / Pow<2>(Second)),
                     part->expected_ksp_gravity.y * (Metre / Pow<2>(Second)),
                     part->expected_ksp_gravity.z *
                         (Metre / Pow<2>(Second))}))));
  }
  CHECK_NOTNULL(plugin)->AddVesselToNextPhysicsBubble(vessel_guid,
                                                      std::move(vessel_parts));
}

char const* SayHello() {
  return "Hello from native C++!";
}

}  // namespace ksp_plugin
}  // namespace principia
<commit_msg>formatting<commit_after>#include "ksp_plugin/interface.hpp"

#include <string>

#include "base/version.hpp"

using principia::quantities::Pow;
using principia::si::Degree;
using principia::si::Metre;
using principia::si::Tonne;

namespace principia {
namespace ksp_plugin {

namespace {

// Takes ownership of |**pointer| and returns it to the caller.  Nulls
// |*pointer|.  |pointer| must not be null.  No transfer of ownership of
// |*pointer|.
template<typename T>
std::unique_ptr<T> TakeOwnership(T** const pointer) {
  CHECK_NOTNULL(pointer);
  std::unique_ptr<T const> owned_pointer(*pointer);
  *pointer = nullptr;
  return owned_pointer;
}

}  // namespace

void InitGoogleLogging() {
  if (google::IsGoogleLoggingInitialized()) {
    LOG(INFO) << "Google logging was already initialized, no action taken";
  } else {
#ifdef _MSC_VER
    FILE* file;
    freopen_s(&file, "stderr.log", "w", stderr);
#else
    std::freopen("stderr.log", "w", stderr);
#endif
    google::SetStderrLogging(google::INFO);
    google::SetLogDestination(google::FATAL, "glog/Principia/FATAL.");
    google::SetLogDestination(google::ERROR, "glog/Principia/ERROR.");
    google::SetLogDestination(google::WARNING, "glog/Principia/WARNING.");
    google::SetLogDestination(google::INFO, "glog/Principia/INFO.");
    FLAGS_v = 1;
    // Buffer severities <= |INFO|, i.e., don't buffer.
    FLAGS_logbuflevel = google::INFO - 1;
    google::InitGoogleLogging("Principia");
    LOG(INFO) << "Initialized Google logging for Principia";
    LOG(INFO) << "Principia version " << base::kVersion
              << " built on " << base::kBuildDate;
    // TODO(egg): by (compiler) for (ARCH, OS).
  }
}

void LogInfo(char const* message) {
  LOG(INFO) << message;
}

void LogWarning(char const* message) {
  LOG(WARNING) << message;
}

void LogError(char const* message) {
  LOG(ERROR) << message;
}

void LogFatal(char const* message) {
  LOG(FATAL) << message;
}

Plugin* NewPlugin(double const initial_time,
                  int const sun_index,
                  double const sun_gravitational_parameter,
                  double const planetarium_rotation_in_degrees) {
  LOG(INFO) << "Constructing Principia plugin";
  std::unique_ptr<Plugin> result = std::make_unique<Plugin>(
      Instant(initial_time * Second),
      sun_index,
      sun_gravitational_parameter * SIUnit<GravitationalParameter>(),
      planetarium_rotation_in_degrees * Degree);
  LOG(INFO) << "Plugin constructed";
  return result.release();
}

void DeletePlugin(Plugin const** const plugin) {
  LOG(INFO) << "Destroying Principia plugin";
  // We want to log before and after destroying the plugin since it is a pretty
  // significant event, so we take ownership inside a block.
  {
    TakeOwnership(plugin);
  }
  LOG(INFO) << "Plugin destroyed";
}

// NOTE(egg): The |* (Metre / Second)| might be slower than |* SIUnit<Speed>()|,
// but it is more readable. This will be resolved once we have constexpr.

void InsertCelestial(Plugin* const plugin,
                     int const celestial_index,
                     double const gravitational_parameter,
                     int const parent_index,
                     XYZ const from_parent_position,
                     XYZ const from_parent_velocity) {
  CHECK_NOTNULL(plugin)->InsertCelestial(
      celestial_index,
      gravitational_parameter * SIUnit<GravitationalParameter>(),
      parent_index,
      Displacement<AliceSun>({from_parent_position.x * Metre,
                              from_parent_position.y * Metre,
                              from_parent_position.z * Metre}),
      Velocity<AliceSun>({from_parent_velocity.x * (Metre / Second),
                          from_parent_velocity.y * (Metre / Second),
                          from_parent_velocity.z * (Metre / Second)}));
}

void UpdateCelestialHierarchy(Plugin const* const plugin,
                              int const celestial_index,
                              int const parent_index) {
  CHECK_NOTNULL(plugin)->UpdateCelestialHierarchy(celestial_index,
                                                  parent_index);
}

void EndInitialization(Plugin* const plugin) {
  CHECK_NOTNULL(plugin)->EndInitialization();
}

bool InsertOrKeepVessel(Plugin* const plugin,
                        char const* vessel_guid,
                        int const parent_index) {
  return CHECK_NOTNULL(plugin)->InsertOrKeepVessel(vessel_guid, parent_index);
}

void SetVesselStateOffset(Plugin* const plugin,
                          char const* vessel_guid,
                          XYZ const from_parent_position,
                          XYZ const from_parent_velocity) {
  CHECK_NOTNULL(plugin)->SetVesselStateOffset(
      vessel_guid,
      Displacement<AliceSun>({from_parent_position.x * Metre,
                              from_parent_position.y * Metre,
                              from_parent_position.z * Metre}),
      Velocity<AliceSun>({from_parent_velocity.x * (Metre / Second),
                          from_parent_velocity.y * (Metre / Second),
                          from_parent_velocity.z * (Metre / Second)}));
}

void AdvanceTime(Plugin* const plugin,
                 double const t,
                 double const planetarium_rotation) {
  CHECK_NOTNULL(plugin)->AdvanceTime(Instant(t * Second),
                                     planetarium_rotation * Degree);
}

XYZ VesselDisplacementFromParent(Plugin const* const plugin,
                                 char const* vessel_guid) {
  R3Element<Length> const result =
      CHECK_NOTNULL(plugin)->
          VesselDisplacementFromParent(vessel_guid).coordinates();
  return {result.x / Metre, result.y / Metre, result.z / Metre};
}

XYZ VesselParentRelativeVelocity(Plugin const* const plugin,
                                 char const* vessel_guid) {
  R3Element<Speed> const result =
      CHECK_NOTNULL(plugin)->
          VesselParentRelativeVelocity(vessel_guid).coordinates();
  return {result.x / (Metre / Second),
          result.y / (Metre / Second),
          result.z / (Metre / Second)};
}

XYZ CelestialDisplacementFromParent(Plugin const* const plugin,
                                    int const celestial_index) {
  R3Element<Length> const result =
      CHECK_NOTNULL(plugin)->
          CelestialDisplacementFromParent(celestial_index).coordinates();
  return {result.x / Metre, result.y / Metre, result.z / Metre};
}

XYZ CelestialParentRelativeVelocity(Plugin const* const plugin,
                                    int const celestial_index) {
  R3Element<Speed> const result =
      CHECK_NOTNULL(plugin)->
          CelestialParentRelativeVelocity(celestial_index).coordinates();
  return {result.x / (Metre / Second),
          result.y / (Metre / Second),
          result.z / (Metre / Second)};
}

BodyCentredNonRotatingFrame const* NewBodyCentredNonRotatingFrame(
    Plugin const* const plugin,
    int const reference_body_index) {
  return CHECK_NOTNULL(plugin)->
      NewBodyCentredNonRotatingFrame(reference_body_index).release();
}

BarycentricRotatingFrame const* NewBarycentricRotatingFrame(
    Plugin const* const plugin,
    int const primary_index,
    int const secondary_index) {
  return CHECK_NOTNULL(plugin)->
      NewBarycentricRotatingFrame(primary_index, secondary_index).release();
}

void DeleteRenderingFrame(RenderingFrame const** const frame) {
  TakeOwnership(frame);
}

LineAndIterator* RenderedVesselTrajectory(Plugin const* const plugin,
                                          char const* vessel_guid,
                                          RenderingFrame const* frame,
                                          XYZ const sun_world_position) {
  RenderedTrajectory<World> rendered_trajectory = CHECK_NOTNULL(plugin)->
      RenderedVesselTrajectory(
          vessel_guid,
          *CHECK_NOTNULL(frame),
          World::origin + Displacement<World>({sun_world_position.x * Metre,
                                               sun_world_position.y * Metre,
                                               sun_world_position.z * Metre}));
  std::unique_ptr<LineAndIterator> result =
      std::make_unique<LineAndIterator>(std::move(rendered_trajectory));
  result->it = result->rendered_trajectory.begin();
  return result.release();
}

int NumberOfSegments(LineAndIterator const* line_and_iterator) {
  return CHECK_NOTNULL(line_and_iterator)->rendered_trajectory.size();
}

XYZSegment FetchAndIncrement(LineAndIterator* const line_and_iterator) {
  CHECK_NOTNULL(line_and_iterator);
  CHECK(line_and_iterator->it != line_and_iterator->rendered_trajectory.end());
  LineSegment<World> const result = *line_and_iterator->it;
  ++line_and_iterator->it;
  R3Element<Length> const begin = (result.begin - World::origin).coordinates();
  R3Element<Length> const end = (result.end - World::origin).coordinates();
  return {XYZ{begin.x / Metre, begin.y / Metre, begin.z / Metre},
          XYZ{end.x / Metre, end.y / Metre, end.z / Metre}};
}

bool AtEnd(LineAndIterator* const line_and_iterator) {
  CHECK_NOTNULL(line_and_iterator);
  return line_and_iterator->it == line_and_iterator->rendered_trajectory.end();
}

void DeleteLineAndIterator(LineAndIterator const** const line_and_iterator) {
  TakeOwnership(line_and_iterator);
}

XYZ VesselWorldPosition(Plugin const* const plugin,
                        char const* vessel_guid,
                        XYZ const parent_world_position) {
  Position<World> result = CHECK_NOTNULL(plugin)->VesselWorldPosition(
      vessel_guid,
      World::origin + Displacement<World>({parent_world_position.x * Metre,
                                           parent_world_position.y * Metre,
                                           parent_world_position.z * Metre}));
  R3Element<Length> const& coordinates = (result - World::origin).coordinates();
  return XYZ{coordinates.x / Metre,
             coordinates.y / Metre,
             coordinates.z / Metre};
}

XYZ VesselWorldVelocity(Plugin const* const plugin,
                        char const* vessel_guid,
                        XYZ const parent_world_velocity,
                        double const parent_rotation_period) {
  Velocity<World> result = CHECK_NOTNULL(plugin)->VesselWorldVelocity(
      vessel_guid,
      Velocity<World>({parent_world_velocity.x * Metre / Second,
                       parent_world_velocity.y * Metre / Second,
                       parent_world_velocity.z * Metre / Second}),
      parent_rotation_period * Second);
  R3Element<Speed> const& coordinates = result.coordinates();
  return XYZ{coordinates.x / (Metre / Second),
             coordinates.y / (Metre / Second),
             coordinates.z / (Metre / Second)};
}

void AddVesselToNextPhysicsBubble(
    Plugin* const plugin,
    char const* vessel_guid,
    KSPPart const* const parts,
    int count) {
  std::vector<std::pair<PartID, std::unique_ptr<Part<World>>>> vessel_parts;
  vessel_parts.reserve(count);
  for (KSPPart const* part = parts; part < parts + count; ++part) {
    vessel_parts.push_back(
        std::make_pair(
            part->id,
            std::make_unique<Part<World>>(
                DegreesOfFreedom<World>(
                    World::origin + Displacement<World>(
                                        {part->world_position.x * Metre,
                                         part->world_position.y * Metre,
                                         part->world_position.z * Metre}),
                    Velocity<World>(
                        {part->world_velocity.x * (Metre / Second),
                         part->world_velocity.y * (Metre / Second),
                         part->world_velocity.z * (Metre / Second)})),
                part->mass * Tonne,
                Vector<Acceleration, World>(
                    {part->expected_ksp_gravity.x * (Metre / Pow<2>(Second)),
                     part->expected_ksp_gravity.y * (Metre / Pow<2>(Second)),
                     part->expected_ksp_gravity.z *
                         (Metre / Pow<2>(Second))}))));
  }
  CHECK_NOTNULL(plugin)->AddVesselToNextPhysicsBubble(vessel_guid,
                                                      std::move(vessel_parts));
}

char const* SayHello() {
  return "Hello from native C++!";
}

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


<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2010, 2011, 2012, 2013, 2014
Raffaello D. Di Napoli

This file is part of Application-Building Components (henceforth referred to as ABC).

ABC is free software: you can redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

ABC 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 General Public
License for more details.

You should have received a copy of the GNU General Public License along with ABC. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#ifndef _ABC_MEMORY_HXX
#define _ABC_MEMORY_HXX

#include <abc/core.hxx>
#ifdef ABC_CXX_PRAGMA_ONCE
   #pragma once
#endif

#include <abc/exception.hxx>

#if ABC_HOST_API_POSIX

   #include <stdlib.h> // free() malloc() realloc()
   #include <memory.h> // memcpy() memmove() memset()

#elif ABC_HOST_API_WIN32 //if ABC_HOST_API_POSIX

   // Clean up pollution caused by previous headers.
   extern "C" {

   // Rtl*Memory*

   #undef RtlZeroMemory
   WINBASEAPI void WINAPI RtlZeroMemory(void UNALIGNED * pDst, size_t cb);

   #undef RtlFillMemory
   WINBASEAPI void WINAPI RtlFillMemory(void UNALIGNED * pDst, size_t cb, UCHAR iValue);

   #undef RtlFillMemoryUlong
   WINBASEAPI void WINAPI RtlFillMemoryUlong(void * pDst, size_t cb, ULONG iValue);

   #undef RtlFillMemoryUlonglong
   WINBASEAPI void WINAPI RtlFillMemoryUlonglong(void * pDst, size_t cb, ULONGLONG iValue);

   #undef RtlCopyMemory
   WINBASEAPI void WINAPI RtlCopyMemory(
      void UNALIGNED * pDst, void UNALIGNED const * pSrc, size_t cb
   );

   #undef RtlMoveMemory
   WINBASEAPI void WINAPI RtlMoveMemory(
      void UNALIGNED * pDst, void UNALIGNED const * pSrc, size_t cb
   );

   } //extern "C"

#endif //if ABC_HOST_API_POSIX … elif ABC_HOST_API_WIN32

/** TODO: comment or remove.
*/
#if ABC_HOST_GCC
   #define _abc_alloca(cb) \
      __builtin_alloca((cb))
#elif ABC_HOST_MSC
   extern "C" void * ABC_STL_CALLCONV _alloca(size_t cb);
   #define _abc_alloca(cb) \
      _alloca(cb)
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// :: globals - standard new/delete operators

#if ABC_HOST_MSC
   #pragma warning(push)
   // “'operator': exception specification does not match previous declaration”
   #pragma warning(disable: 4986)
#endif

void * ABC_STL_CALLCONV operator new(size_t cb) ABC_STL_NOEXCEPT_FALSE((std::bad_alloc));
void * ABC_STL_CALLCONV operator new[](size_t cb) ABC_STL_NOEXCEPT_FALSE((std::bad_alloc));
void * ABC_STL_CALLCONV operator new(size_t cb, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();
void * ABC_STL_CALLCONV operator new[](size_t cb, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();

void ABC_STL_CALLCONV operator delete(void * p) ABC_STL_NOEXCEPT_TRUE();
void ABC_STL_CALLCONV operator delete[](void * p) ABC_STL_NOEXCEPT_TRUE();
void ABC_STL_CALLCONV operator delete(void * p, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();
void ABC_STL_CALLCONV operator delete[](void * p, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();

#if ABC_HOST_MSC
   #pragma warning(pop)
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory::freeing_deleter

namespace abc {

namespace memory {

// Forward declaration.
template <typename T>
void free(T const * pt);

/** Deleter that deallocates memory using memory::free().
*/
template <typename T>
struct freeing_deleter {

   /** Deallocates the specified memory block.

   pt
      Pointer to the object to delete.
   */
   void operator()(T * pt) const {
      free(pt);
   }
};

// Specialization for arrays.
template <typename T>
struct freeing_deleter<T[]> :
   public freeing_deleter<T> {

   /** Deallocates the specified array. See also freeing_deleter<T>::operator()().

   pt
      Pointer to the array to deallocate.
   */
   template <typename T2>
   void operator()(T2 * pt) const {
      freeing_deleter<T>::operator()(pt);
   }
};

} //namespace memory

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory::conditional_deleter

namespace abc {

namespace memory {

/** Wrapper that invokes a deleter if and only if a set condition is true.
*/
template <typename T, typename TDeleter = std::default_delete<T>>
class conditional_deleter :
   public TDeleter {
public:

   /** Constructor.

   bEnabled
      If true, the deleter will delete objects when invoked; if false, it will do nothing.
   cd
      Source deleter.
   */
   conditional_deleter(bool bEnabled) :
      TDeleter(),
      m_bEnabled(bEnabled) {
   }
   template <typename T2, typename TDeleter2>
   conditional_deleter(conditional_deleter<T2, TDeleter2> const & cd) :
      TDeleter(static_cast<TDeleter2 const &>(cd)),
      m_bEnabled(cd.enabled()) {
   }

   /** Deletes the specified object if the condition set in the constructor was true.

   pt
      Pointer to the object to delete.
   */
   void operator()(T * pt) const {
      if (m_bEnabled) {
         TDeleter::operator()(pt);
      }
   }

   /** Returns true if the deleter is enabled.

   return
      true if the deleter is enable, or false otherwise.
   */
   bool enabled() const {
      return m_bEnabled;
   }

protected:

   bool m_bEnabled;
};

// Specialization for arrays.
template <typename T, typename TDeleter>
class conditional_deleter<T[], TDeleter> :
   public conditional_deleter<T, TDeleter> {
public:

   /** See conditional_deleter<T>::conditional_deleter().
   */
   conditional_deleter(bool bEnabled) :
      conditional_deleter<T, TDeleter>(bEnabled) {
   }
   template <typename T2, typename TDeleter2>
   conditional_deleter(conditional_deleter<T2, TDeleter2> const & cd) :
      conditional_deleter<T, TDeleter>(cd) {
   }

   /** Deletes the specified array if the condition set in the constructor was true. See also
   conditional_deleter<T, TDeleter>::operator()().

   pt
      Pointer to the array to delete.
   */
   template <typename T2>
   void operator()(T2 * pt) const {
      if (this->m_bEnabled) {
         TDeleter::operator()(pt);
      }
   }
};

} //namespace memory

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory globals - management

namespace abc {

namespace memory {

/** Requests the dynamic allocation of a memory block of the specified number of bytes.

TODO: comment signature.
*/
inline void * _raw_alloc(size_t cb) {
   void * p(::malloc(cb));
   if (!p) {
      ABC_THROW(memory_allocation_error, ());
   }
   return p;
}

/** Resizes a dynamically allocated memory block.

TODO: comment signature.
*/
inline void * _raw_realloc(void * p, size_t cb) {
   p = ::realloc(p, cb);
   if (!p) {
      ABC_THROW(memory_allocation_error, ());
   }
   return p;
}

/** Requests the dynamic allocation of a memory block large enough to contain c objects of type T,
plus an additional cbExtra bytes. With specialization that ignores types (void) and allocates the
specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline std::unique_ptr<T, freeing_deleter<T>> alloc(size_t c = 1, size_t cbExtra = 0) {
   return std::unique_ptr<T, freeing_deleter<T>>(
      static_cast<T *>(_raw_alloc(sizeof(T) * c + cbExtra))
   );
}
template <>
inline std::unique_ptr<void, freeing_deleter<void>> alloc(
   size_t cb /*= 1*/, size_t cbExtra /*= 0*/
) {
   return std::unique_ptr<void, freeing_deleter<void>>(_raw_alloc(cb + cbExtra));
}

/** Releases a block of dynamically allocated memory.

pt
   Pointer to the memory block to be released.
*/
template <typename T>
inline void free(T const * pt) {
   ::free(const_cast<T *>(pt));
}

/** Changes the size of a block of dynamically allocated memory. Both overloads have a
specialization that ignores pointer types (void), and allocates the specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline T * realloc(T * pt, size_t c, size_t cbExtra = 0) {
   return static_cast<T *>(_raw_realloc(pt, sizeof(T) * c + cbExtra));
}
template <>
inline void * realloc(void * p, size_t cb, size_t cbExtra /*= 0*/) {
   return _raw_realloc(p, cb + cbExtra);
}
template <typename T>
inline void realloc(std::unique_ptr<T, freeing_deleter<T>> * ppt, size_t c, size_t cbExtra = 0) {
   T * pt(static_cast<T *>(_raw_realloc(ppt->get(), sizeof(T) * c + cbExtra)));
   ppt->release();
   ppt->reset(pt);
}
template <>
inline void realloc(
   std::unique_ptr<void, freeing_deleter<void>> * pp, size_t cb, size_t cbExtra /*= 0*/
) {
   void * p(_raw_realloc(pp->get(), cb + cbExtra));
   pp->release();
   pp->reset(p);
}

} //namespace memory

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory globals - manipulation

namespace abc {

namespace memory {

/** Sets to the value 0 every item in an array.

TODO: comment signature.
*/
template <typename T>
inline T * clear(T * ptDst, size_t c = 1) {
   return static_cast<T *>(clear<void>(ptDst, sizeof(T) * c));
}
template <>
inline void * clear(void * pDst, size_t cb /*= 1*/) {
#if ABC_HOST_API_POSIX
   ::memset(pDst, 0, cb);
#elif ABC_HOST_API_WIN32
   ::RtlZeroMemory(pDst, cb);
#else
   #error TODO-PORT: HOST_API
#endif
   return pDst;
}

/** Copies memory, by number of items. With specialization that ignores pointer types (void), and
copies the specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline T * copy(T * ptDst, T const * ptSrc) {
   // Optimization: if the copy can be made by mem-reg-mem transfers, avoid calling a function, so
   // that the compiler can inline the copy.
   switch (sizeof(T)) {
      case sizeof(int8_t):
         *reinterpret_cast<int8_t *>(ptDst) = *reinterpret_cast<int8_t const *>(ptSrc);
         break;
      case sizeof(int16_t):
         *reinterpret_cast<int16_t *>(ptDst) = *reinterpret_cast<int16_t const *>(ptSrc);
         break;
      case sizeof(int32_t):
         *reinterpret_cast<int32_t *>(ptDst) = *reinterpret_cast<int32_t const *>(ptSrc);
         break;
      case sizeof(int64_t):
         *reinterpret_cast<int64_t *>(ptDst) = *reinterpret_cast<int64_t const *>(ptSrc);
         break;
      default:
         copy<void>(ptDst, ptSrc, sizeof(T));
         break;
   }
   return static_cast<T *>(ptDst);
}
// Nobody should want to use this, but it’s here for consistency.
template <>
inline void * copy(void * pDst, void const * pSrc) {
   *reinterpret_cast<int8_t *>(pDst) = *reinterpret_cast<int8_t const *>(pSrc);
   return pDst;
}
template <typename T>
inline T * copy(T * ptDst, T const * ptSrc, size_t c) {
   return static_cast<T *>(copy<void>(ptDst, ptSrc, sizeof(T) * c));
}
template <>
inline void * copy(void * pDst, void const * pSrc, size_t cb) {
#if ABC_HOST_API_POSIX
   ::memcpy(pDst, pSrc, cb);
#elif ABC_HOST_API_WIN32
   ::RtlMoveMemory(pDst, pSrc, cb);
#else
   #error TODO-PORT: HOST_API
#endif
   return pDst;
}

/** Copies possibly overlapping memory, by number of items. With specialization that ignores pointer
types (void), and copies the specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline T * move(T * ptDst, T const * ptSrc, size_t c) {
   return static_cast<T *>(move<void>(ptDst, ptSrc, sizeof(T) * c));
}
template <>
inline void * move(void * pDst, void const * pSrc, size_t cb) {
#if ABC_HOST_API_POSIX
   ::memmove(pDst, pSrc, cb);
#elif ABC_HOST_API_WIN32
   ::RtlMoveMemory(pDst, pSrc, cb);
#else
   #error TODO-PORT: HOST_API
#endif
   return pDst;
}

/** Copies a value over each item of a static array.

TODO: comment signature.
*/
template <typename T>
inline T * set(T * ptDst, T const & tValue, size_t c) {
   switch (sizeof(T)) {
#if ABC_HOST_API_POSIX
      case sizeof(int8_t):
         ::memset(ptDst, tValue, c);
         break;
#elif ABC_HOST_API_WIN32
      case sizeof(UCHAR):
         ::RtlFillMemory(ptDst, c, tValue);
         break;
#endif
      default:
         for (T const * ptDstMax(ptDst + c); ptDst < ptDstMax; ++ptDst) {
            copy(ptDst, &tValue);
         }
         break;
   }
   return ptDst;
}

} //namespace memory

} //namespace abc

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

#endif //ifdef _ABC_MEMORY_HXX

<commit_msg>Refactor abc::memory::realloc() to support std::unique_ptr<T[]><commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2010, 2011, 2012, 2013, 2014
Raffaello D. Di Napoli

This file is part of Application-Building Components (henceforth referred to as ABC).

ABC is free software: you can redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

ABC 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 General Public
License for more details.

You should have received a copy of the GNU General Public License along with ABC. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#ifndef _ABC_MEMORY_HXX
#define _ABC_MEMORY_HXX

#include <abc/core.hxx>
#ifdef ABC_CXX_PRAGMA_ONCE
   #pragma once
#endif

#include <abc/exception.hxx>

#if ABC_HOST_API_POSIX

   #include <stdlib.h> // free() malloc() realloc()
   #include <memory.h> // memcpy() memmove() memset()

#elif ABC_HOST_API_WIN32 //if ABC_HOST_API_POSIX

   // Clean up pollution caused by previous headers.
   extern "C" {

   // Rtl*Memory*

   #undef RtlZeroMemory
   WINBASEAPI void WINAPI RtlZeroMemory(void UNALIGNED * pDst, size_t cb);

   #undef RtlFillMemory
   WINBASEAPI void WINAPI RtlFillMemory(void UNALIGNED * pDst, size_t cb, UCHAR iValue);

   #undef RtlFillMemoryUlong
   WINBASEAPI void WINAPI RtlFillMemoryUlong(void * pDst, size_t cb, ULONG iValue);

   #undef RtlFillMemoryUlonglong
   WINBASEAPI void WINAPI RtlFillMemoryUlonglong(void * pDst, size_t cb, ULONGLONG iValue);

   #undef RtlCopyMemory
   WINBASEAPI void WINAPI RtlCopyMemory(
      void UNALIGNED * pDst, void UNALIGNED const * pSrc, size_t cb
   );

   #undef RtlMoveMemory
   WINBASEAPI void WINAPI RtlMoveMemory(
      void UNALIGNED * pDst, void UNALIGNED const * pSrc, size_t cb
   );

   } //extern "C"

#endif //if ABC_HOST_API_POSIX … elif ABC_HOST_API_WIN32

/** TODO: comment or remove.
*/
#if ABC_HOST_GCC
   #define _abc_alloca(cb) \
      __builtin_alloca((cb))
#elif ABC_HOST_MSC
   extern "C" void * ABC_STL_CALLCONV _alloca(size_t cb);
   #define _abc_alloca(cb) \
      _alloca(cb)
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// :: globals - standard new/delete operators

#if ABC_HOST_MSC
   #pragma warning(push)
   // “'operator': exception specification does not match previous declaration”
   #pragma warning(disable: 4986)
#endif

void * ABC_STL_CALLCONV operator new(size_t cb) ABC_STL_NOEXCEPT_FALSE((std::bad_alloc));
void * ABC_STL_CALLCONV operator new[](size_t cb) ABC_STL_NOEXCEPT_FALSE((std::bad_alloc));
void * ABC_STL_CALLCONV operator new(size_t cb, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();
void * ABC_STL_CALLCONV operator new[](size_t cb, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();

void ABC_STL_CALLCONV operator delete(void * p) ABC_STL_NOEXCEPT_TRUE();
void ABC_STL_CALLCONV operator delete[](void * p) ABC_STL_NOEXCEPT_TRUE();
void ABC_STL_CALLCONV operator delete(void * p, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();
void ABC_STL_CALLCONV operator delete[](void * p, std::nothrow_t const &) ABC_STL_NOEXCEPT_TRUE();

#if ABC_HOST_MSC
   #pragma warning(pop)
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory::freeing_deleter

namespace abc {

namespace memory {

// Forward declaration.
template <typename T>
void free(T const * pt);

/** Deleter that deallocates memory using memory::free().
*/
template <typename T>
struct freeing_deleter {

   /** Deallocates the specified memory block.

   pt
      Pointer to the object to delete.
   */
   void operator()(T * pt) const {
      free(pt);
   }
};

// Specialization for arrays.
template <typename T>
struct freeing_deleter<T[]> :
   public freeing_deleter<T> {

   /** Deallocates the specified array. See also freeing_deleter<T>::operator()().

   pt
      Pointer to the array to deallocate.
   */
   template <typename T2>
   void operator()(T2 * pt) const {
      freeing_deleter<T>::operator()(pt);
   }
};

} //namespace memory

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory::conditional_deleter

namespace abc {

namespace memory {

/** Wrapper that invokes a deleter if and only if a set condition is true.
*/
template <typename T, typename TDeleter = std::default_delete<T>>
class conditional_deleter :
   public TDeleter {
public:

   /** Constructor.

   bEnabled
      If true, the deleter will delete objects when invoked; if false, it will do nothing.
   cd
      Source deleter.
   */
   conditional_deleter(bool bEnabled) :
      TDeleter(),
      m_bEnabled(bEnabled) {
   }
   template <typename T2, typename TDeleter2>
   conditional_deleter(conditional_deleter<T2, TDeleter2> const & cd) :
      TDeleter(static_cast<TDeleter2 const &>(cd)),
      m_bEnabled(cd.enabled()) {
   }

   /** Deletes the specified object if the condition set in the constructor was true.

   pt
      Pointer to the object to delete.
   */
   void operator()(T * pt) const {
      if (m_bEnabled) {
         TDeleter::operator()(pt);
      }
   }

   /** Returns true if the deleter is enabled.

   return
      true if the deleter is enable, or false otherwise.
   */
   bool enabled() const {
      return m_bEnabled;
   }

protected:

   bool m_bEnabled;
};

// Specialization for arrays.
template <typename T, typename TDeleter>
class conditional_deleter<T[], TDeleter> :
   public conditional_deleter<T, TDeleter> {
public:

   /** See conditional_deleter<T>::conditional_deleter().
   */
   conditional_deleter(bool bEnabled) :
      conditional_deleter<T, TDeleter>(bEnabled) {
   }
   template <typename T2, typename TDeleter2>
   conditional_deleter(conditional_deleter<T2, TDeleter2> const & cd) :
      conditional_deleter<T, TDeleter>(cd) {
   }

   /** Deletes the specified array if the condition set in the constructor was true. See also
   conditional_deleter<T, TDeleter>::operator()().

   pt
      Pointer to the array to delete.
   */
   template <typename T2>
   void operator()(T2 * pt) const {
      if (this->m_bEnabled) {
         TDeleter::operator()(pt);
      }
   }
};

} //namespace memory

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory globals - management

namespace abc {

namespace memory {

/** Requests the dynamic allocation of a memory block of the specified number of bytes.

TODO: comment signature.
*/
inline void * _raw_alloc(size_t cb) {
   void * p(::malloc(cb));
   if (!p) {
      ABC_THROW(memory_allocation_error, ());
   }
   return p;
}

/** Resizes a dynamically allocated memory block.

TODO: comment signature.
*/
inline void * _raw_realloc(void * p, size_t cb) {
   p = ::realloc(p, cb);
   if (!p) {
      ABC_THROW(memory_allocation_error, ());
   }
   return p;
}

/** Requests the dynamic allocation of a memory block large enough to contain c objects of type T,
plus an additional cbExtra bytes. With specialization that ignores types (void) and allocates the
specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline std::unique_ptr<T, freeing_deleter<T>> alloc(size_t c = 1, size_t cbExtra = 0) {
   return std::unique_ptr<T, freeing_deleter<T>>(
      static_cast<T *>(_raw_alloc(sizeof(T) * c + cbExtra))
   );
}
template <>
inline std::unique_ptr<void, freeing_deleter<void>> alloc(
   size_t cb /*= 1*/, size_t cbExtra /*= 0*/
) {
   return std::unique_ptr<void, freeing_deleter<void>>(_raw_alloc(cb + cbExtra));
}

/** Releases a block of dynamically allocated memory.

pt
   Pointer to the memory block to be released.
*/
template <typename T>
inline void free(T const * pt) {
   ::free(const_cast<T *>(pt));
}

/** Changes the size of a block of dynamically allocated memory. Both overloads have a
specialization that ignores pointer types (void), and allocates the specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline T * realloc(T * pt, size_t c, size_t cbExtra = 0) {
   return static_cast<T *>(_raw_realloc(pt, sizeof(T) * c + cbExtra));
}
template <>
inline void * realloc(void * p, size_t cb, size_t cbExtra /*= 0*/) {
   return _raw_realloc(p, cb + cbExtra);
}
template <typename T>
inline void realloc(std::unique_ptr<T, freeing_deleter<T>> * ppt, size_t c, size_t cbExtra = 0) {
   typedef typename std::unique_ptr<T, freeing_deleter<T>>::element_type TElt;
   TElt * pt(static_cast<TElt *>(_raw_realloc(ppt->get(), sizeof(TElt) * c + cbExtra)));
   ppt->release();
   ppt->reset(pt);
}
template <>
inline void realloc(
   std::unique_ptr<void, freeing_deleter<void>> * pp, size_t cb, size_t cbExtra /*= 0*/
) {
   void * p(_raw_realloc(pp->get(), cb + cbExtra));
   pp->release();
   pp->reset(p);
}

} //namespace memory

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::memory globals - manipulation

namespace abc {

namespace memory {

/** Sets to the value 0 every item in an array.

TODO: comment signature.
*/
template <typename T>
inline T * clear(T * ptDst, size_t c = 1) {
   return static_cast<T *>(clear<void>(ptDst, sizeof(T) * c));
}
template <>
inline void * clear(void * pDst, size_t cb /*= 1*/) {
#if ABC_HOST_API_POSIX
   ::memset(pDst, 0, cb);
#elif ABC_HOST_API_WIN32
   ::RtlZeroMemory(pDst, cb);
#else
   #error TODO-PORT: HOST_API
#endif
   return pDst;
}

/** Copies memory, by number of items. With specialization that ignores pointer types (void), and
copies the specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline T * copy(T * ptDst, T const * ptSrc) {
   // Optimization: if the copy can be made by mem-reg-mem transfers, avoid calling a function, so
   // that the compiler can inline the copy.
   switch (sizeof(T)) {
      case sizeof(int8_t):
         *reinterpret_cast<int8_t *>(ptDst) = *reinterpret_cast<int8_t const *>(ptSrc);
         break;
      case sizeof(int16_t):
         *reinterpret_cast<int16_t *>(ptDst) = *reinterpret_cast<int16_t const *>(ptSrc);
         break;
      case sizeof(int32_t):
         *reinterpret_cast<int32_t *>(ptDst) = *reinterpret_cast<int32_t const *>(ptSrc);
         break;
      case sizeof(int64_t):
         *reinterpret_cast<int64_t *>(ptDst) = *reinterpret_cast<int64_t const *>(ptSrc);
         break;
      default:
         copy<void>(ptDst, ptSrc, sizeof(T));
         break;
   }
   return static_cast<T *>(ptDst);
}
// Nobody should want to use this, but it’s here for consistency.
template <>
inline void * copy(void * pDst, void const * pSrc) {
   *reinterpret_cast<int8_t *>(pDst) = *reinterpret_cast<int8_t const *>(pSrc);
   return pDst;
}
template <typename T>
inline T * copy(T * ptDst, T const * ptSrc, size_t c) {
   return static_cast<T *>(copy<void>(ptDst, ptSrc, sizeof(T) * c));
}
template <>
inline void * copy(void * pDst, void const * pSrc, size_t cb) {
#if ABC_HOST_API_POSIX
   ::memcpy(pDst, pSrc, cb);
#elif ABC_HOST_API_WIN32
   ::RtlMoveMemory(pDst, pSrc, cb);
#else
   #error TODO-PORT: HOST_API
#endif
   return pDst;
}

/** Copies possibly overlapping memory, by number of items. With specialization that ignores pointer
types (void), and copies the specified number of bytes.

TODO: comment signature.
*/
template <typename T>
inline T * move(T * ptDst, T const * ptSrc, size_t c) {
   return static_cast<T *>(move<void>(ptDst, ptSrc, sizeof(T) * c));
}
template <>
inline void * move(void * pDst, void const * pSrc, size_t cb) {
#if ABC_HOST_API_POSIX
   ::memmove(pDst, pSrc, cb);
#elif ABC_HOST_API_WIN32
   ::RtlMoveMemory(pDst, pSrc, cb);
#else
   #error TODO-PORT: HOST_API
#endif
   return pDst;
}

/** Copies a value over each item of a static array.

TODO: comment signature.
*/
template <typename T>
inline T * set(T * ptDst, T const & tValue, size_t c) {
   switch (sizeof(T)) {
#if ABC_HOST_API_POSIX
      case sizeof(int8_t):
         ::memset(ptDst, tValue, c);
         break;
#elif ABC_HOST_API_WIN32
      case sizeof(UCHAR):
         ::RtlFillMemory(ptDst, c, tValue);
         break;
#endif
      default:
         for (T const * ptDstMax(ptDst + c); ptDst < ptDstMax; ++ptDst) {
            copy(ptDst, &tValue);
         }
         break;
   }
   return ptDst;
}

} //namespace memory

} //namespace abc

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

#endif //ifdef _ABC_MEMORY_HXX

<|endoftext|>


<commit_before>/***************************************************************************
 *  utils/createdisks.cpp
 *
 *  Part of the STXXL. See http://stxxl.sourceforge.net
 *
 *  Copyright (C) 2003 Roman Dementiev <dementiev@mpi-sb.mpg.de>
 *  Copyright (C) 2007 Andreas Beckmann <beckmann@mpi-inf.mpg.de>
 *
 *  Distributed under the Boost Software License, Version 1.0.
 *  (See accompanying file LICENSE_1_0.txt or copy at
 *  http://www.boost.org/LICENSE_1_0.txt)
 **************************************************************************/

#include <cstdio>
#include <iomanip>
#include <vector>

#include <stxxl/io>
#include <stxxl/aligned_alloc>

#ifndef BOOST_MSVC
 #include <unistd.h>
#endif

using stxxl::request_ptr;
using stxxl::file;
using stxxl::timestamp;

#ifdef BLOCK_ALIGN
 #undef BLOCK_ALIGN
#endif

#define BLOCK_ALIGN  4096

#define NOREAD

//#define DO_ONLY_READ

#define POLL_DELAY 1000

#define RAW_ACCESS

//#define WATCH_TIMES

#define CHECK_AFTER_READ 0

#ifdef WATCH_TIMES
void watch_times(request_ptr reqs[], unsigned n, double * out)
{
    bool * finished = new bool[n];
    unsigned count = 0;
    unsigned i = 0;
    for (i = 0; i < n; i++)
        finished[i] = false;

    while (count != n)
    {
        usleep(POLL_DELAY);
        i = 0;
        for (i = 0; i < n; i++)
        {
            if (!finished[i])
                if (reqs[i]->poll())
                {
                    finished[i] = true;
                    out[i] = timestamp();
                    count++;
                }
        }
    }
    delete[] finished;
}

void out_stat(double start, double end, double * times, unsigned n, const std::vector<std::string> & names)
{
    for (unsigned i = 0; i < n; i++)
    {
        std::cout << i << " " << names[i] << " took " <<
        100. * (times[i] - start) / (end - start) << " %" << std::endl;
    }
}
#endif

#define MB (1024 * 1024)
#define GB (1024 * 1024 * 1024)

int main(int argc, char * argv[])
{
    if (argc < 3)
    {
        STXXL_MSG("Usage: " << argv[0] << " filesize_in_MB filename1 [filename2 [filename3 ...]]");
        return -1;
    }

    stxxl::int64 offset = 0;
    stxxl::int64 length = stxxl::int64(atoi(argv[1])) * MB;
    stxxl::int64 endpos = offset + length;

    std::vector<std::string> disks_arr;

    for (int ii = 2; ii < argc; ++ii)
    {
        unlink(argv[ii]);
        std::cout << "# Add disk: " << argv[ii] << std::endl;
        disks_arr.push_back(argv[ii]);
    }

    const unsigned ndisks = disks_arr.size();

    unsigned buffer_size = 256 * MB;
    const unsigned buffer_size_int = buffer_size / sizeof(int);

    unsigned chunks = 2;
    const unsigned chunk_size = buffer_size / chunks;
    const unsigned chunk_size_int = chunk_size / sizeof(int);

    unsigned i = 0, j = 0;

    int * buffer = (int *)stxxl::aligned_alloc<BLOCK_ALIGN>(buffer_size * ndisks);
    file ** disks = new file *[ndisks];
    request_ptr * reqs = new request_ptr[ndisks * chunks];
#ifdef WATCH_TIMES
    double * r_finish_times = new double[ndisks];
    double * w_finish_times = new double[ndisks];
#endif

    for (i = 0; i < ndisks * buffer_size_int; i++)
        buffer[i] = i;

    for (i = 0; i < ndisks; i++)
    {
#ifdef BOOST_MSVC
 #ifdef RAW_ACCESS
        disks[i] = new stxxl::wincall_file(disks_arr[i],
                                           file::CREAT | file::RDWR | file::DIRECT, i);
 #else
        disks[i] = new stxxl::wincall_file(disks_arr[i],
                                           file::CREAT | file::RDWR, i);
 #endif
#else
 #ifdef RAW_ACCESS
        disks[i] = new stxxl::syscall_file(disks_arr[i],
                                           file::CREAT | file::RDWR | file::DIRECT, i);
 #else
        disks[i] = new stxxl::syscall_file(disks_arr[i],
                                           file::CREAT | file::RDWR, i);
 #endif
#endif
    }

    while (offset < endpos)
    {
        const unsigned current_block_size = length ? std::min<stxxl::int64>(buffer_size, endpos - offset) : buffer_size;
        const unsigned current_chunk_size = current_block_size / chunks;

        std::cout << "Disk offset " << std::setw(7) << offset / MB << " MB: " << std::fixed;

        double begin = timestamp(), end;

#ifndef DO_ONLY_READ
        for (i = 0; i < ndisks; i++)
        {
            for (j = 0; j < chunks; j++)
                reqs[i * chunks + j] =
                    disks[i]->awrite(buffer + buffer_size_int * i + j * chunk_size_int,
                                     offset + j * current_chunk_size,
                                     current_chunk_size,
                                     stxxl::default_completion_handler());
        }

 #ifdef WATCH_TIMES
        watch_times(reqs, ndisks, w_finish_times);
 #else
        wait_all(reqs, ndisks * chunks);
 #endif

        end = timestamp();

/*
   std::cout << "WRITE\nDisks: " << ndisks
        <<" \nElapsed time: "<< end-begin
        << " \nThroughput: "<< int(1e-6*(buffer_size*ndisks)/(end-begin))
        << " Mb/s \nPer one disk:"
        << int(1e-6*(buffer_size)/(end-begin)) << " Mb/s"
        << std::endl;
*/

 #ifdef WATCH_TIMES
        out_stat(begin, end, w_finish_times, ndisks, disks_arr);
 #endif
        std::cout << std::setw(7) << int(1e-6 * (current_block_size) / (end - begin)) << " MB/s,";
#endif

#ifndef NOREAD
        begin = timestamp();

        for (i = 0; i < ndisks; i++)
        {
            for (j = 0; j < chunks; j++)
                reqs[i * chunks + j] = disks[i]->aread(buffer + buffer_size_int * i + j * chunk_size_int,
                                                       offset + j * current_chunk_size,
                                                       current_chunk_size,
                                                       stxxl::default_completion_handler());
        }

 #ifdef WATCH_TIMES
        watch_times(reqs, ndisks, r_finish_times);
 #else
        wait_all(reqs, ndisks * chunks);
 #endif

        end = timestamp();

/*
   std::cout << "READ\nDisks: " << ndisks
        <<" \nElapsed time: "<< end-begin
        << " \nThroughput: "<< int(1e-6*(buffer_size*ndisks)/(end-begin))
        << " Mb/s \nPer one disk:"
        << int(1e-6*(buffer_size)/(end-begin)) << " Mb/s"
            << std::endl;
*/

        std::cout << int(1e-6 * (current_block_size) / (end - begin)) << " MB/s" << std::endl;

#ifdef WATCH_TIMES
        out_stat(begin, end, r_finish_times, ndisks, disks_arr);
#endif

        if (CHECK_AFTER_READ) {
            for (int i = 0; unsigned(i) < ndisks * buffer_size_int; i++)
            {
                if (buffer[i] != i)
                {
                    int ibuf = i / buffer_size_int;
                    int pos = i % buffer_size_int;

                    std::cout << "Error on disk " << ibuf << " position " << std::hex << std::setw(8) << offset + pos * sizeof(int)
                              << "  got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << i
                              << std::dec << std::endl;

                    i = (ibuf + 1) * buffer_size_int; // jump to next
                }
            }
        }
#else
        std::cout << std::endl;
#endif

        offset += current_block_size;
    }

#ifdef WATCH_TIMES
    delete[] r_finish_times;
    delete[] w_finish_times;
#endif
    delete[] reqs;
    for (i = 0; i < ndisks; i++)
        delete disks[i];
    delete[] disks;
    stxxl::aligned_dealloc<BLOCK_ALIGN>(buffer);

    return 0;
}
<commit_msg>reduce buffer size because of an I/O error on Windows (the I/O buffer is too large)<commit_after>/***************************************************************************
 *  utils/createdisks.cpp
 *
 *  Part of the STXXL. See http://stxxl.sourceforge.net
 *
 *  Copyright (C) 2003 Roman Dementiev <dementiev@mpi-sb.mpg.de>
 *  Copyright (C) 2007 Andreas Beckmann <beckmann@mpi-inf.mpg.de>
 *
 *  Distributed under the Boost Software License, Version 1.0.
 *  (See accompanying file LICENSE_1_0.txt or copy at
 *  http://www.boost.org/LICENSE_1_0.txt)
 **************************************************************************/

#include <cstdio>
#include <iomanip>
#include <vector>

#include <stxxl/io>
#include <stxxl/aligned_alloc>

#ifndef BOOST_MSVC
 #include <unistd.h>
#endif

using stxxl::request_ptr;
using stxxl::file;
using stxxl::timestamp;

#ifdef BLOCK_ALIGN
 #undef BLOCK_ALIGN
#endif

#define BLOCK_ALIGN  4096

#define NOREAD

//#define DO_ONLY_READ

#define POLL_DELAY 1000

#define RAW_ACCESS

//#define WATCH_TIMES

#define CHECK_AFTER_READ 0

#ifdef WATCH_TIMES
void watch_times(request_ptr reqs[], unsigned n, double * out)
{
    bool * finished = new bool[n];
    unsigned count = 0;
    unsigned i = 0;
    for (i = 0; i < n; i++)
        finished[i] = false;

    while (count != n)
    {
        usleep(POLL_DELAY);
        i = 0;
        for (i = 0; i < n; i++)
        {
            if (!finished[i])
                if (reqs[i]->poll())
                {
                    finished[i] = true;
                    out[i] = timestamp();
                    count++;
                }
        }
    }
    delete[] finished;
}

void out_stat(double start, double end, double * times, unsigned n, const std::vector<std::string> & names)
{
    for (unsigned i = 0; i < n; i++)
    {
        std::cout << i << " " << names[i] << " took " <<
        100. * (times[i] - start) / (end - start) << " %" << std::endl;
    }
}
#endif

#define MB (1024 * 1024)
#define GB (1024 * 1024 * 1024)

int main(int argc, char * argv[])
{
    if (argc < 3)
    {
        STXXL_MSG("Usage: " << argv[0] << " filesize_in_MB filename1 [filename2 [filename3 ...]]");
        return -1;
    }

    stxxl::int64 offset = 0;
    stxxl::int64 length = stxxl::int64(atoi(argv[1])) * MB;
    stxxl::int64 endpos = offset + length;

    std::vector<std::string> disks_arr;

    for (int ii = 2; ii < argc; ++ii)
    {
        unlink(argv[ii]);
        std::cout << "# Add disk: " << argv[ii] << std::endl;
        disks_arr.push_back(argv[ii]);
    }

    const unsigned ndisks = disks_arr.size();

#ifdef BOOST_MSVC
    unsigned buffer_size = 64 * MB;
#else
    unsigned buffer_size = 256 * MB;
#endif
    const unsigned buffer_size_int = buffer_size / sizeof(int);

    unsigned chunks = 2;
    const unsigned chunk_size = buffer_size / chunks;
    const unsigned chunk_size_int = chunk_size / sizeof(int);

    unsigned i = 0, j = 0;

    int * buffer = (int *)stxxl::aligned_alloc<BLOCK_ALIGN>(buffer_size * ndisks);
    file ** disks = new file *[ndisks];
    request_ptr * reqs = new request_ptr[ndisks * chunks];
#ifdef WATCH_TIMES
    double * r_finish_times = new double[ndisks];
    double * w_finish_times = new double[ndisks];
#endif

    for (i = 0; i < ndisks * buffer_size_int; i++)
        buffer[i] = i;

    for (i = 0; i < ndisks; i++)
    {
#ifdef BOOST_MSVC
 #ifdef RAW_ACCESS
        disks[i] = new stxxl::wincall_file(disks_arr[i],
                                           file::CREAT | file::RDWR | file::DIRECT, i);
 #else
        disks[i] = new stxxl::wincall_file(disks_arr[i],
                                           file::CREAT | file::RDWR, i);
 #endif
#else
 #ifdef RAW_ACCESS
        disks[i] = new stxxl::syscall_file(disks_arr[i],
                                           file::CREAT | file::RDWR | file::DIRECT, i);
 #else
        disks[i] = new stxxl::syscall_file(disks_arr[i],
                                           file::CREAT | file::RDWR, i);
 #endif
#endif
    }

    while (offset < endpos)
    {
        const unsigned current_block_size = length ? std::min<stxxl::int64>(buffer_size, endpos - offset) : buffer_size;
        const unsigned current_chunk_size = current_block_size / chunks;

        std::cout << "Disk offset " << std::setw(7) << offset / MB << " MB: " << std::fixed;

        double begin = timestamp(), end;

#ifndef DO_ONLY_READ
        for (i = 0; i < ndisks; i++)
        {
            for (j = 0; j < chunks; j++)
                reqs[i * chunks + j] =
                    disks[i]->awrite(buffer + buffer_size_int * i + j * chunk_size_int,
                                     offset + j * current_chunk_size,
                                     current_chunk_size,
                                     stxxl::default_completion_handler());
        }

 #ifdef WATCH_TIMES
        watch_times(reqs, ndisks, w_finish_times);
 #else
        wait_all(reqs, ndisks * chunks);
 #endif

        end = timestamp();

/*
   std::cout << "WRITE\nDisks: " << ndisks
        <<" \nElapsed time: "<< end-begin
        << " \nThroughput: "<< int(1e-6*(buffer_size*ndisks)/(end-begin))
        << " Mb/s \nPer one disk:"
        << int(1e-6*(buffer_size)/(end-begin)) << " Mb/s"
        << std::endl;
*/

 #ifdef WATCH_TIMES
        out_stat(begin, end, w_finish_times, ndisks, disks_arr);
 #endif
        std::cout << std::setw(7) << int(1e-6 * (current_block_size) / (end - begin)) << " MB/s,";
#endif

#ifndef NOREAD
        begin = timestamp();

        for (i = 0; i < ndisks; i++)
        {
            for (j = 0; j < chunks; j++)
                reqs[i * chunks + j] = disks[i]->aread(buffer + buffer_size_int * i + j * chunk_size_int,
                                                       offset + j * current_chunk_size,
                                                       current_chunk_size,
                                                       stxxl::default_completion_handler());
        }

 #ifdef WATCH_TIMES
        watch_times(reqs, ndisks, r_finish_times);
 #else
        wait_all(reqs, ndisks * chunks);
 #endif

        end = timestamp();

/*
   std::cout << "READ\nDisks: " << ndisks
        <<" \nElapsed time: "<< end-begin
        << " \nThroughput: "<< int(1e-6*(buffer_size*ndisks)/(end-begin))
        << " Mb/s \nPer one disk:"
        << int(1e-6*(buffer_size)/(end-begin)) << " Mb/s"
            << std::endl;
*/

        std::cout << int(1e-6 * (current_block_size) / (end - begin)) << " MB/s" << std::endl;

#ifdef WATCH_TIMES
        out_stat(begin, end, r_finish_times, ndisks, disks_arr);
#endif

        if (CHECK_AFTER_READ) {
            for (int i = 0; unsigned(i) < ndisks * buffer_size_int; i++)
            {
                if (buffer[i] != i)
                {
                    int ibuf = i / buffer_size_int;
                    int pos = i % buffer_size_int;

                    std::cout << "Error on disk " << ibuf << " position " << std::hex << std::setw(8) << offset + pos * sizeof(int)
                              << "  got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << i
                              << std::dec << std::endl;

                    i = (ibuf + 1) * buffer_size_int; // jump to next
                }
            }
        }
#else
        std::cout << std::endl;
#endif

        offset += current_block_size;
    }

#ifdef WATCH_TIMES
    delete[] r_finish_times;
    delete[] w_finish_times;
#endif
    delete[] reqs;
    for (i = 0; i < ndisks; i++)
        delete disks[i];
    delete[] disks;
    stxxl::aligned_dealloc<BLOCK_ALIGN>(buffer);

    return 0;
}
<|endoftext|>


<commit_before>#ifndef _SWITCHBACK_HPP_
#define _SWITCHBACK_HPP_

#include <vector>

#include <boost/array.hpp>
#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/unordered_map.hpp>

#include "search/BucketPriorityQueue.hpp"
#include "util/PointerOps.hpp"

template <
  class Domain,
  class Node
  >
class Switchback
{
private:
  typedef typename Node::Cost Cost;
  typedef typename Node::State State;
  typedef BucketPriorityQueue<Node> Open;
  typedef boost::optional<typename Open::ItemPointer> MaybeItemPointer;

  typedef boost::unordered_map<
    Node *,
    MaybeItemPointer,
    PointerHash<Node>,
    PointerEq<Node>
    , boost::fast_pool_allocator< std::pair<Node * const, MaybeItemPointer> >
    > Closed;

  typedef typename Closed::iterator ClosedIterator;
  typedef typename Closed::const_iterator ClosedConstIterator;

private:
  const Node *goal;
  bool searched;

  const Domain &domain;

  const static unsigned hierarchy_height = Domain::num_abstraction_levels + 1;

  boost::array<unsigned, hierarchy_height> num_expanded;
  boost::array<unsigned, hierarchy_height> num_generated;

  boost::array<Open, hierarchy_height> open;
  boost::array<Closed, hierarchy_height> closed;

private:
  Switchback(const Switchback<Domain, Node> &);
  Switchback<Domain, Node> & operator =(const Switchback<Domain, Node> &);

public:
  Switchback(const Domain &domain)
    : goal(NULL)
    , searched(false)
    , domain(domain)
  {
    num_expanded.assign(0);
    num_generated.assign(0);
  }

  ~Switchback()
  {
  }

  void search()
  {
    if (searched)
      return;
    searched = true;

    init_open_and_closed();
    goal = resume_search(0, domain.get_goal_state());
  }

  const Node * get_goal() const
  {
    std::cerr << "final open/closed sizes:" << std::endl;
    dump_open_sizes(std::cerr);
    dump_closed_sizes(std::cerr);

    return goal;
  }

  const Domain & get_domain() const
  {
    return domain;
  }

  unsigned get_num_generated() const
  {
    unsigned sum = 0;
    for (unsigned i = 0; i < hierarchy_height; i += 1)
      sum += num_generated[i];

    return sum;
  }

  unsigned get_num_generated(const unsigned level) const
  {
    return num_generated[level];
  }

  unsigned get_num_expanded() const
  {
    unsigned sum = 0;
    for (unsigned i = 0; i < hierarchy_height; i += 1)
      sum += num_expanded[i];

    return sum;
  }

  unsigned get_num_expanded(const unsigned level) const
  {
    return num_expanded[level];
  }

private:
  Cost heuristic(const unsigned level, const State &goal_state)
  {
    assert(is_valid_level(level));

    if (level == Domain::num_abstraction_levels)
      return 0;

    // Need to create a dummy goal node to look up in the hash table.
    // This smells of bad design!
    const unsigned next_level = level + 1;
    const State abstract_goal_state = domain.abstract(next_level, goal_state);
    Node abstract_goal_node(abstract_goal_state, 0, 0);

    ClosedIterator closed_it = closed[next_level].find(&abstract_goal_node);
    if (closed_it != closed[next_level].end()) {
      return closed_it->first->get_g();
    }

    Node *result = resume_search(next_level, abstract_goal_state);
    if (result == NULL) {
      std::cerr << "whoops, infinite heuristic estimate!" << std::endl;
      assert(false);  // for the domains I am running on, there should
                      // never be an infinite heuristic estimate.
    }
    assert(closed[next_level].find(&abstract_goal_node) != closed[next_level].end());

    return result->get_g();
  }

  Node * resume_search(const unsigned level, const State &goal_state)
  {
    assert(is_valid_level(level));

    // Dummy goal node, for hash table lookup.
    Node goal_node(goal_state, 0, 0);
    ClosedIterator closed_it = closed[level].find(&goal_node);

    if (closed_it != closed[level].end())
      return closed_it->first;

    std::vector<Node *> children;

    // A*-ish code ahead
    while (!open[level].empty()) {
      if (get_num_expanded() % 500000 == 0) {
        std::cerr << get_num_expanded() << " total nodes expanded" << std::endl
                  << get_num_generated() << " total nodes generated" << std::endl;
        dump_open_sizes(std::cerr);
        dump_closed_sizes(std::cerr);
      }

      Node *n = open[level].top();
      assert(closed[level].find(n) != closed[level].end());
      assert(closed[level].find(n)->second);
      assert(open[level].valid_item_pointer(*closed[level].find(n)->second));
      open[level].pop();

      closed[level][n] = boost::none;
      assert(all_closed_item_ptrs_valid(level));

      if (level % 2 == 0)
        domain.compute_successors(*n, children);
      else
        domain.compute_predecessors(*n, children);
      num_expanded[level] += 1;
      num_generated[level] += children.size();

      for (unsigned child_idx = 0; child_idx < children.size(); child_idx += 1) {
        process_child(level, children[child_idx]);
      }

      if (n->get_state() == goal_state)
        return n;
    } /* end while */

    return NULL;
  }

  void process_child(const unsigned level, Node *child)
  {
    assert(open[level].invariants_satisfied());
    assert(open[level].size() <= closed[level].size());
    assert(all_closed_item_ptrs_valid(level));

    child->set_h(heuristic(level, child->get_state()));

    ClosedIterator closed_it = closed[level].find(child);
    if (closed_it == closed[level].end()) {
      // The child has not been generated before.
      closed[level][child] = open[level].push(child);
    }
    else if (closed_it->second && child->get_f() < closed_it->first->get_f()) {
      // A worse version of the child is in the open list.
      open[level].erase(*closed_it->second);  // knock out the old
                                              // one from the open
                                              // list

      domain.free_node(closed_it->first);     // free the old,
                                              // worse copy
      closed[level].erase(closed_it);

      closed[level][child] = open[level].push(child);  // insert better version of child
    }
    else {
      // The child has either already been expanded, or is worse
      // than the version in the open list.
      domain.free_node(child);
    }

    assert(all_closed_item_ptrs_valid(level));
    assert(open[level].invariants_satisfied());
  }

  void init_open_and_closed()
  {
    for (int level = Domain::num_abstraction_levels; level >= 0; level -= 1) {
      std::cerr << "initializing level " << level << std::endl;
        std::cerr << get_num_expanded() << " total nodes expanded" << std::endl
                  << get_num_generated() << " total nodes generated" << std::endl;
        dump_open_sizes(std::cerr);
        dump_closed_sizes(std::cerr);

      State start = level % 2 == 0
                      ? domain.get_start_state()
                      : domain.get_goal_state();

      State abstract_start = domain.abstract(level, start);

      // I think there is trouble with the heuristic initialization
      // here.  Should it be called with level + 1 and
      // abstract_abstract_start?
      Node *start_node = domain.create_node(abstract_start,
                                            0,
                                            0,
                                            NULL
                                            );
      closed[level][start_node] = open[level].push(start_node);
    }
  }

  bool all_closed_item_ptrs_valid(const unsigned level) const
  {
#ifdef CHECK_ALL_CLOSED_ITEM_PTRS_VALID
    std::cerr << "checking if item pointers are valid" << std::endl
              << "  " << closed.size() << " pointers to check" << std::endl;
    for (ClosedConstIterator closed_it = closed[level].begin();
         closed_it != closed[level].end();
         ++closed_it) {
      if ( closed_it->second && !open[level].valid_item_pointer(*closed_it->second) )
        return false;
    }
    return true;
#else
    return true;
#endif
  }

  static bool is_valid_level(const unsigned level)
  {
    return level <= Domain::num_abstraction_levels;
  }

  void dump_open_sizes(std::ostream &o) const
  {
    o << "open sizes: " << std::endl;
    for (unsigned level = 0; level < hierarchy_height; level += 1)
      o << "  " << level << ": " << open[level].size() << std::endl;
  }

  void dump_closed_sizes(std::ostream &o) const
  {
    o << "closed sizes: " << std::endl;
    for (unsigned level = 0; level < hierarchy_height; level += 1)
      o << "  " << level << ": " << closed[level].size() << std::endl;
  }
};

#endif /* !_SWITCHBACK_HPP_ */
<commit_msg>Working switchback!?!?<commit_after>#ifndef _SWITCHBACK_HPP_
#define _SWITCHBACK_HPP_

#include <vector>

#include <boost/array.hpp>
#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/unordered_map.hpp>

#include "search/BucketPriorityQueue.hpp"
#include "util/PointerOps.hpp"

template <
  class Domain,
  class Node
  >
class Switchback
{
private:
  typedef typename Node::Cost Cost;
  typedef typename Node::State State;
  typedef BucketPriorityQueue<Node> Open;
  typedef boost::optional<typename Open::ItemPointer> MaybeItemPointer;

  typedef boost::unordered_map<
    Node *,
    MaybeItemPointer,
    PointerHash<Node>,
    PointerEq<Node>
    , boost::fast_pool_allocator< std::pair<Node * const, MaybeItemPointer> >
    > Closed;

  typedef typename Closed::iterator ClosedIterator;
  typedef typename Closed::const_iterator ClosedConstIterator;

private:
  const Node *goal;
  bool searched;

  const Domain &domain;

  const static unsigned hierarchy_height = Domain::num_abstraction_levels + 1;

  boost::array<unsigned, hierarchy_height> num_expanded;
  boost::array<unsigned, hierarchy_height> num_generated;

  boost::array<Open, hierarchy_height> open;
  boost::array<Closed, hierarchy_height> closed;

private:
  Switchback(const Switchback<Domain, Node> &);
  Switchback<Domain, Node> & operator =(const Switchback<Domain, Node> &);

public:
  Switchback(const Domain &domain)
    : goal(NULL)
    , searched(false)
    , domain(domain)
  {
    num_expanded.assign(0);
    num_generated.assign(0);
  }

  ~Switchback()
  {
  }

  void search()
  {
    if (searched)
      return;
    searched = true;

    init_open_and_closed();
    goal = resume_search(0, domain.get_goal_state());
  }

  const Node * get_goal() const
  {
    std::cerr << "final open/closed sizes:" << std::endl;
    dump_open_sizes(std::cerr);
    dump_closed_sizes(std::cerr);

    return goal;
  }

  const Domain & get_domain() const
  {
    return domain;
  }

  unsigned get_num_generated() const
  {
    unsigned sum = 0;
    for (unsigned i = 0; i < hierarchy_height; i += 1)
      sum += num_generated[i];

    return sum;
  }

  unsigned get_num_generated(const unsigned level) const
  {
    return num_generated[level];
  }

  unsigned get_num_expanded() const
  {
    unsigned sum = 0;
    for (unsigned i = 0; i < hierarchy_height; i += 1)
      sum += num_expanded[i];

    return sum;
  }

  unsigned get_num_expanded(const unsigned level) const
  {
    return num_expanded[level];
  }

private:
  Cost heuristic(const unsigned level, const State &goal_state)
  {
    assert(is_valid_level(level));

    if (level == Domain::num_abstraction_levels)
      return 0;

    // Need to create a dummy goal node to look up in the hash table.
    // This smells of bad design!
    const unsigned next_level = level + 1;
    const State abstract_goal_state = domain.abstract(next_level, goal_state);
    Node abstract_goal_node(abstract_goal_state, 0, 0);

    ClosedIterator closed_it = closed[next_level].find(&abstract_goal_node);
    if (closed_it != closed[next_level].end() && !closed_it->second) {
      return closed_it->first->get_g();
    }

    Node *result = resume_search(next_level, abstract_goal_state);
    if (result == NULL) {
      std::cerr << "whoops, infinite heuristic estimate!" << std::endl;
      assert(false);  // for the domains I am running on, there should
                      // never be an infinite heuristic estimate.
    }
    assert(closed[next_level].find(&abstract_goal_node) != closed[next_level].end());
    assert(!closed[next_level].find(&abstract_goal_node)->second);

    return result->get_g();
  }

  Node * resume_search(const unsigned level, const State &goal_state)
  {
    assert(is_valid_level(level));

    // Dummy goal node, for hash table lookup.
    Node goal_node(goal_state, 0, 0);
    ClosedIterator closed_it = closed[level].find(&goal_node);

    if (closed_it != closed[level].end() &&!closed_it->second)
      return closed_it->first;

    std::vector<Node *> children;

    // A*-ish code ahead
    while (!open[level].empty()) {
      if (get_num_expanded() % 500000 == 0) {
        std::cerr << get_num_expanded() << " total nodes expanded" << std::endl
                  << get_num_generated() << " total nodes generated" << std::endl;
        dump_open_sizes(std::cerr);
        dump_closed_sizes(std::cerr);
      }

      Node *n = open[level].top();
      assert(closed[level].find(n) != closed[level].end());
      assert(closed[level].find(n)->second);
      assert(open[level].valid_item_pointer(*closed[level].find(n)->second));
      open[level].pop();

      closed[level][n] = boost::none;
      assert(all_closed_item_ptrs_valid(level));

      if (level % 2 == 0)
        domain.compute_successors(*n, children);
      else
        domain.compute_predecessors(*n, children);
      num_expanded[level] += 1;
      num_generated[level] += children.size();

      for (unsigned child_idx = 0; child_idx < children.size(); child_idx += 1) {
        process_child(level, children[child_idx]);
      }

      if (n->get_state() == goal_state)
        return n;
    } /* end while */

    return NULL;
  }

  void process_child(const unsigned level, Node *child)
  {
    assert(open[level].invariants_satisfied());
    assert(open[level].size() <= closed[level].size());
    assert(all_closed_item_ptrs_valid(level));

    child->set_h(heuristic(level, child->get_state()));

    ClosedIterator closed_it = closed[level].find(child);
    if (closed_it == closed[level].end()) {
      // The child has not been generated before.
      closed[level][child] = open[level].push(child);
    }
    else if (closed_it->second && child->get_f() < closed_it->first->get_f()) {
      // A worse version of the child is in the open list.
      open[level].erase(*closed_it->second);  // knock out the old
                                              // one from the open
                                              // list

      domain.free_node(closed_it->first);     // free the old,
                                              // worse copy
      closed[level].erase(closed_it);

      closed[level][child] = open[level].push(child);  // insert better version of child
    }
    else {
      // The child has either already been expanded, or is worse
      // than the version in the open list.
      domain.free_node(child);
    }

    assert(all_closed_item_ptrs_valid(level));
    assert(open[level].invariants_satisfied());
  }

  void init_open_and_closed()
  {
    for (unsigned level = 0; level <= Domain::num_abstraction_levels; level += 1) {
      std::cerr << "initializing level " << level << std::endl;

      num_generated[level] += 1;
      State start = level % 2 == 0
                      ? domain.get_start_state()
                      : domain.get_goal_state();
      Node *start_node = domain.create_node(domain.abstract(level, start),
                                            0,
                                            0,
                                            NULL
                                            );
      closed[level][start_node] = open[level].push(start_node);
    }

    std::cerr << "###### INITIALIZATION COMPLETE ######" << std::endl;
  }

  bool all_closed_item_ptrs_valid(const unsigned level) const
  {
#ifdef CHECK_ALL_CLOSED_ITEM_PTRS_VALID
    std::cerr << "checking if item pointers are valid" << std::endl
              << "  " << closed.size() << " pointers to check" << std::endl;
    for (ClosedConstIterator closed_it = closed[level].begin();
         closed_it != closed[level].end();
         ++closed_it) {
      if ( closed_it->second && !open[level].valid_item_pointer(*closed_it->second) )
        return false;
    }
    return true;
#else
    return true;
#endif
  }

  static bool is_valid_level(const unsigned level)
  {
    return level <= Domain::num_abstraction_levels;
  }

  void dump_open_sizes(std::ostream &o) const
  {
    o << "open sizes: " << std::endl;
    for (unsigned level = 0; level < hierarchy_height; level += 1)
      o << "  " << level << ": " << open[level].size() << std::endl;
  }

  void dump_closed_sizes(std::ostream &o) const
  {
    o << "closed sizes: " << std::endl;
    for (unsigned level = 0; level < hierarchy_height; level += 1)
      o << "  " << level << ": " << closed[level].size() << std::endl;
  }
};

#endif /* !_SWITCHBACK_HPP_ */
<|endoftext|>


<commit_before>/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */

#include <eoslib/eos.hpp>
#include <eoslib/token.hpp>
#include <eoslib/db.hpp>

/**
 *  @defgroup testcontract Test Contract
 *  @brief Test Contract
 *  @ingroup contractapi
 *
 */

namespace rate_limit_auth {

  /**
   *  @defgroup ratelimitcontract Test Contract for rate limiting
   *  @brief Test Contract for verifying rate limiting on all authorities
   *  @ingroup testcontract
   *
   *  @{
   */

   /**
    *  Mesage with 2 authorities
    */
   struct Auths2 {
      AccountName       acct1;
      AccountName       acct2;
   };

   /**
    *  Mesage with 3 authorities
    */
   struct Auths3 {
      AccountName       acct1;
      AccountName       acct2;
      AccountName       acct3;
   };

} /// @} /// ratelimitcontract
<commit_msg>Removing unneeded file.<commit_after><|endoftext|>