<commit_before>#include <gflags/gflags.h>
#include <stddef.h>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "ncode_common/src/common.h"
#include "ncode_common/src/map_util.h"
#include "ncode_common/src/net/net_common.h"
#include "ncode_common/src/net/net_gen.h"
#include "ncode_common/src/strutil.h"
#include "ncode_common/src/viz/grapher.h"
#include "../common.h"
#include "../mean_est/mean_est.h"
#include "../routing_system.h"
#include "ctr.h"
#include "path_provider.h"

DEFINE_double(cd_link_gbps, 1.0, "Capacity of C->D");
DEFINE_double(ab_link_gbps, 1.0, "Capacity of A->B");
DEFINE_double(cd_link_ms, 10, "Delay of C->D");
DEFINE_double(ab_link_ms, 10, "Delay of A->B");
DEFINE_double(cd_aggregate_gbps, 0.9, "Demand of C->D aggregate");
DEFINE_double(ab_aggregate_gbps, 0.9, "Demand of A->B aggregate");
DEFINE_uint64(cd_aggregate_flows, 1000, "Number of C->D flows");
DEFINE_uint64(ab_aggregate_flows, 1000, "Number of A->B flows");
DEFINE_uint64(steps, 20, "Number of steps");
DEFINE_double(decay_factor, 0.0, "How quickly to decay prediction");
DEFINE_string(opt, "CTR", "The optimizer to use");
DEFINE_bool(dampen_ratio, false, "Preserve the flow count / volume ratio?");

namespace ctr {

static constexpr std::chrono::milliseconds kSyntheticBinSize =
    std::chrono::milliseconds(10);
static constexpr size_t kSyntheticBinCount = 600;

class StabilityEvalHarness {
 public:
  StabilityEvalHarness(TrafficMatrix* initial_tm, RoutingSystem* routing_system,
                       size_t cycle_count)
      : initial_tm_(initial_tm),
        routing_system_(routing_system),
        graph_(routing_system_->graph()) {
    Cycle(cycle_count);
  }

  const std::vector<RoutingConfigurationDelta>& deltas() const {
    return deltas_;
  }

  void PlotLinkFractions(const std::string& out) const {
    std::vector<nc::viz::DataSeries2D> series;
    for (const auto& path_and_fractions : path_to_fraction_) {
      const nc::net::Walk* walk = path_and_fractions.first;

      series.emplace_back();
      nc::viz::DataSeries2D& data_series = series.back();
      data_series.data = path_and_fractions.second;
      data_series.label = walk->ToStringNoPorts(*graph_);
    }

    nc::viz::PlotParameters2D params;
    params.x_label = "timestep";
    params.y_label = "fraction";

    nc::viz::PythonGrapher grapher(out);
    grapher.PlotLine(params, series);
  }

  void DumpLinkFractions() const {
    std::cout << path_to_fraction_.size() << "\n";
    for (const auto& path_and_fractions : path_to_fraction_) {
      const nc::net::Walk* walk = path_and_fractions.first;

      std::vector<std::string> to_combine;
      for (const auto& times_and_fractions : path_and_fractions.second) {
        to_combine.emplace_back(nc::StrCat("(", times_and_fractions.first, ",",
                                           times_and_fractions.second, ")"));
      }

      std::cout << walk->ToStringNoPorts(*graph_) << " : ("
                << nc::Join(to_combine, ",") << ")\n";
    }
  }

  void DumpAggregateVolumes() const {
    std::cout << volumes_.size() << "\n";
    for (const auto& aggregate_and_volumes : volumes_) {
      const AggregateId& aggregate_id = aggregate_and_volumes.first;
      const std::vector<nc::net::Bandwidth>& volumes =
          aggregate_and_volumes.second;

      std::string out = nc::Join(volumes, ",", [](nc::net::Bandwidth v) {
        return std::to_string(v.Mbps());
      });
      std::cout << aggregate_id.ToString(*graph_) << " : (" << out << ")\n";
    }
  }

 private:
  // Produces a traffic matrix that is scaled based on each flow's shortest path
  // stretch. If all flows of an aggregate are on a path that is 2x as long as
  // the shortest path then their demand will be 1/2 of what it would be if they
  // were on the shortest path.
  std::unique_ptr<TrafficMatrix> ScaleBasedOnOutput(
      const RoutingConfiguration& routing,
      const TrafficMatrix& original_tm) const {
    std::map<AggregateId, DemandAndFlowCount> out;
    for (const auto& aggregate_and_routes : routing.routes()) {
      const AggregateId& aggregate_id = aggregate_and_routes.first;

      const DemandAndFlowCount& demand_and_flow_count =
          nc::FindOrDieNoPrint(routing.demands(), aggregate_id);
      const DemandAndFlowCount& initial_demand_and_flow_count =
          nc::FindOrDieNoPrint(initial_tm_->demands(), aggregate_id);

      size_t flow_count = demand_and_flow_count.second;
      nc::net::Bandwidth sp_bandwidth = initial_demand_and_flow_count.first;
      nc::net::Delay sp_delay = aggregate_id.GetSPDelay(*graph_);

      double total_mbps = 0;
      double per_flow_sp_mbps = sp_bandwidth.Mbps() / flow_count;
      const std::vector<RouteAndFraction>& routes = aggregate_and_routes.second;
      for (const RouteAndFraction& route_and_fraction : routes) {
        nc::net::Delay path_delay = route_and_fraction.first->delay();
        double sp_ratio =
            static_cast<double>(sp_delay.count()) / path_delay.count();

        // Each flow in the aggregate will get bandwidth proportional to how far
        // away it is from the shortest path.
        total_mbps += route_and_fraction.second * flow_count * sp_ratio *
                      per_flow_sp_mbps;
        CHECK(total_mbps == total_mbps);
      }

      std::mt19937 rnd(1);
      std::uniform_real_distribution<double> dist(1, 10);

      double new_flow_count = flow_count;
      if (FLAGS_dampen_ratio) {
        // The demand in the output will not be the same as the one in the input
        // to the optimizer because of prediction. Need to dampen the ratio
        // based on the original input, not the predicted one.
        const DemandAndFlowCount& original_demand_and_flow_count =
            nc::FindOrDieNoPrint(original_tm.demands(), aggregate_id);

        new_flow_count = total_mbps * flow_count /
                         original_demand_and_flow_count.first.Mbps();
        new_flow_count = std::max(1.0, new_flow_count);
      }

      out[aggregate_id] = {nc::net::Bandwidth::FromMBitsPerSecond(total_mbps),
                           new_flow_count};
    }

    return nc::make_unique<TrafficMatrix>(graph_, out);
  }

  // Performs a number of runs with the optimizer. Returns n - 1 values, one for
  // each delta between a run and its following run.
  void Cycle(size_t n) {
    std::unique_ptr<TrafficMatrix> prev_tm;
    std::unique_ptr<RoutingConfiguration> prev_routing;
    for (size_t i = 0; i < n; ++i) {
      const TrafficMatrix* tm = prev_tm ? prev_tm.get() : initial_tm_;
      UpdateVolumes(*tm);

      RoutingSystemUpdateResult update_result =
          routing_system_->Update(SyntheticHistoryFromTM(*tm));
      auto& routing = update_result.routing;

      for (const auto& aggregate_and_routes : routing->routes()) {
        for (const auto& path_and_fraction : aggregate_and_routes.second) {
          path_to_fraction_[path_and_fraction.first].emplace_back(
              i, path_and_fraction.second);
        }
      }

      if (prev_routing) {
        deltas_.emplace_back(prev_routing->GetDifference(*routing));
      }
      prev_tm = ScaleBasedOnOutput(*routing, *tm);
      prev_routing = std::move(routing);
    }
  }

  void UpdateVolumes(const TrafficMatrix& tm) {
    for (const auto& aggregate_and_demands : tm.demands()) {
      const AggregateId& aggregate = aggregate_and_demands.first;
      volumes_[aggregate].emplace_back(aggregate_and_demands.second.first);
    }
  }

  std::map<AggregateId, AggregateHistory> SyntheticHistoryFromTM(
      const TrafficMatrix& tm) {
    std::map<AggregateId, AggregateHistory> out;
    for (const auto& aggregate_demand_and_flow_count : tm.demands()) {
      const AggregateId& aggregate = aggregate_demand_and_flow_count.first;
      nc::net::Bandwidth demand = aggregate_demand_and_flow_count.second.first;
      size_t flow_count = aggregate_demand_and_flow_count.second.second;

      out.emplace(std::piecewise_construct, std::forward_as_tuple(aggregate),
                  std::forward_as_tuple(demand, kSyntheticBinCount,
                                        kSyntheticBinSize, flow_count));
    }

    return out;
  }

  // In the initial TM each aggregate's demand is what it would be if it were
  // routed on its shortest path.
  TrafficMatrix* initial_tm_;

  // The optimizer.
  RoutingSystem* routing_system_;

  // The graph.
  const nc::net::GraphStorage* graph_;

  // N - 1 deltas for each step to the next one.
  std::vector<RoutingConfigurationDelta> deltas_;

  // Per-aggregate volumes.
  std::map<AggregateId, std::vector<nc::net::Bandwidth>> volumes_;

  // Path to fractions of capacity.
  std::map<const nc::net::Walk*, std::vector<std::pair<double, double>>>
      path_to_fraction_;
};

static void RunWithSimpleTopologyTwoAggregates() {
  nc::net::GraphBuilder builder;
  std::chrono::microseconds ab_delay(
      static_cast<size_t>(FLAGS_ab_link_ms * 1000));
  std::chrono::microseconds cd_delay(
      static_cast<size_t>(FLAGS_cd_link_ms * 1000));

  builder.AddLink({"A", "B",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_ab_link_gbps),
                   ab_delay});
  builder.AddLink({"B", "A",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_ab_link_gbps),
                   ab_delay});
  builder.AddLink({"A", "C", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});
  builder.AddLink({"C", "A", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});
  builder.AddLink({"C", "D",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_cd_link_gbps),
                   cd_delay});
  builder.AddLink({"D", "C",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_cd_link_gbps),
                   cd_delay});
  builder.AddLink({"B", "D", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});
  builder.AddLink({"D", "B", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});

  nc::net::GraphStorage graph(builder);
  TrafficMatrix initial_tm(&graph);
  AggregateId id_one(
      {graph.NodeFromStringOrDie("A"), graph.NodeFromStringOrDie("B")});
  AggregateId id_two(
      {graph.NodeFromStringOrDie("C"), graph.NodeFromStringOrDie("D")});

  initial_tm.AddDemand(
      id_one, {nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_ab_aggregate_gbps),
               FLAGS_ab_aggregate_flows});
  initial_tm.AddDemand(
      id_two, {nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_cd_aggregate_gbps),
               FLAGS_cd_aggregate_flows});

  PathProvider path_provider(&graph);
  std::unique_ptr<Optimizer> opt;
  if (FLAGS_opt == "CTR") {
    opt = nc::make_unique<CTROptimizer>(&path_provider, 1.0, true);
  } else if (FLAGS_opt == "B4") {
    opt = nc::make_unique<B4Optimizer>(&path_provider, false, 1.0);
  } else if (FLAGS_opt == "B4(P)") {
    opt = nc::make_unique<B4Optimizer>(&path_provider, true, 1.0);
  } else if (FLAGS_opt == "MinMax") {
    opt = nc::make_unique<MinMaxOptimizer>(&path_provider, 1.0);
  }

  MeanScaleEstimatorFactory estimator_factory(
      {1.1, FLAGS_decay_factor, FLAGS_decay_factor, 10});
  RoutingSystemConfig routing_system_config;
  routing_system_config.store_to_metrics = false;
  RoutingSystem routing_system(routing_system_config, opt.get(),
                               &estimator_factory);
  StabilityEvalHarness harness(&initial_tm, &routing_system, FLAGS_steps);
  harness.DumpAggregateVolumes();
  harness.DumpLinkFractions();
}

}  // namespace ctr

int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  //  ctr::RunWithSimpleTopology();
  ctr::RunWithSimpleTopologyTwoAggregates();

  return 0;
}
<commit_msg>fixed compilation error<commit_after>#include <gflags/gflags.h>
#include <stddef.h>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "ncode_common/src/common.h"
#include "ncode_common/src/map_util.h"
#include "ncode_common/src/net/net_common.h"
#include "ncode_common/src/net/net_gen.h"
#include "ncode_common/src/strutil.h"
#include "ncode_common/src/viz/grapher.h"
#include "../common.h"
#include "../mean_est/mean_est.h"
#include "../routing_system.h"
#include "ctr.h"
#include "path_provider.h"

DEFINE_double(cd_link_gbps, 1.0, "Capacity of C->D");
DEFINE_double(ab_link_gbps, 1.0, "Capacity of A->B");
DEFINE_double(cd_link_ms, 10, "Delay of C->D");
DEFINE_double(ab_link_ms, 10, "Delay of A->B");
DEFINE_double(cd_aggregate_gbps, 0.9, "Demand of C->D aggregate");
DEFINE_double(ab_aggregate_gbps, 0.9, "Demand of A->B aggregate");
DEFINE_uint64(cd_aggregate_flows, 1000, "Number of C->D flows");
DEFINE_uint64(ab_aggregate_flows, 1000, "Number of A->B flows");
DEFINE_uint64(steps, 20, "Number of steps");
DEFINE_double(decay_factor, 0.0, "How quickly to decay prediction");
DEFINE_string(opt, "CTR", "The optimizer to use");
DEFINE_bool(dampen_ratio, false, "Preserve the flow count / volume ratio?");

namespace ctr {

static constexpr std::chrono::milliseconds kSyntheticBinSize =
    std::chrono::milliseconds(10);
static constexpr size_t kSyntheticBinCount = 600;

class StabilityEvalHarness {
 public:
  StabilityEvalHarness(TrafficMatrix* initial_tm, RoutingSystem* routing_system,
                       size_t cycle_count)
      : initial_tm_(initial_tm),
        routing_system_(routing_system),
        graph_(routing_system_->graph()) {
    Cycle(cycle_count);
  }

  const std::vector<RoutingConfigurationDelta>& deltas() const {
    return deltas_;
  }

  void PlotLinkFractions(const std::string& out) const {
    std::vector<nc::viz::DataSeries2D> series;
    for (const auto& path_and_fractions : path_to_fraction_) {
      const nc::net::Walk* walk = path_and_fractions.first;

      series.emplace_back();
      nc::viz::DataSeries2D& data_series = series.back();
      data_series.data = path_and_fractions.second;
      data_series.label = walk->ToStringNoPorts(*graph_);
    }

    nc::viz::PlotParameters2D params;
    params.x_label = "timestep";
    params.y_label = "fraction";

    nc::viz::PythonGrapher grapher(out);
    grapher.PlotLine(params, series);
  }

  void DumpLinkFractions() const {
    std::cout << path_to_fraction_.size() << "\n";
    for (const auto& path_and_fractions : path_to_fraction_) {
      const nc::net::Walk* walk = path_and_fractions.first;

      std::vector<std::string> to_combine;
      for (const auto& times_and_fractions : path_and_fractions.second) {
        to_combine.emplace_back(nc::StrCat("(", times_and_fractions.first, ",",
                                           times_and_fractions.second, ")"));
      }

      std::cout << walk->ToStringNoPorts(*graph_) << " : ("
                << nc::Join(to_combine, ",") << ")\n";
    }
  }

  void DumpAggregateVolumes() const {
    std::cout << volumes_.size() << "\n";
    for (const auto& aggregate_and_volumes : volumes_) {
      const AggregateId& aggregate_id = aggregate_and_volumes.first;
      const std::vector<nc::net::Bandwidth>& volumes =
          aggregate_and_volumes.second;

      std::function<std::string(const nc::net::Bandwidth&)> format_f = [](
          const nc::net::Bandwidth& bw) { return std::to_string(bw.Mbps()); };

      std::string out = nc::Join(volumes, ",", format_f);
      std::cout << aggregate_id.ToString(*graph_) << " : (" << out << ")\n";
    }
  }

 private:
  // Produces a traffic matrix that is scaled based on each flow's shortest path
  // stretch. If all flows of an aggregate are on a path that is 2x as long as
  // the shortest path then their demand will be 1/2 of what it would be if they
  // were on the shortest path.
  std::unique_ptr<TrafficMatrix> ScaleBasedOnOutput(
      const RoutingConfiguration& routing,
      const TrafficMatrix& original_tm) const {
    std::map<AggregateId, DemandAndFlowCount> out;
    for (const auto& aggregate_and_routes : routing.routes()) {
      const AggregateId& aggregate_id = aggregate_and_routes.first;

      const DemandAndFlowCount& demand_and_flow_count =
          nc::FindOrDieNoPrint(routing.demands(), aggregate_id);
      const DemandAndFlowCount& initial_demand_and_flow_count =
          nc::FindOrDieNoPrint(initial_tm_->demands(), aggregate_id);

      size_t flow_count = demand_and_flow_count.second;
      nc::net::Bandwidth sp_bandwidth = initial_demand_and_flow_count.first;
      nc::net::Delay sp_delay = aggregate_id.GetSPDelay(*graph_);

      double total_mbps = 0;
      double per_flow_sp_mbps = sp_bandwidth.Mbps() / flow_count;
      const std::vector<RouteAndFraction>& routes = aggregate_and_routes.second;
      for (const RouteAndFraction& route_and_fraction : routes) {
        nc::net::Delay path_delay = route_and_fraction.first->delay();
        double sp_ratio =
            static_cast<double>(sp_delay.count()) / path_delay.count();

        // Each flow in the aggregate will get bandwidth proportional to how far
        // away it is from the shortest path.
        total_mbps += route_and_fraction.second * flow_count * sp_ratio *
                      per_flow_sp_mbps;
        CHECK(total_mbps == total_mbps);
      }

      std::mt19937 rnd(1);
      std::uniform_real_distribution<double> dist(1, 10);

      double new_flow_count = flow_count;
      if (FLAGS_dampen_ratio) {
        // The demand in the output will not be the same as the one in the input
        // to the optimizer because of prediction. Need to dampen the ratio
        // based on the original input, not the predicted one.
        const DemandAndFlowCount& original_demand_and_flow_count =
            nc::FindOrDieNoPrint(original_tm.demands(), aggregate_id);

        new_flow_count = total_mbps * flow_count /
                         original_demand_and_flow_count.first.Mbps();
        new_flow_count = std::max(1.0, new_flow_count);
      }

      out[aggregate_id] = {nc::net::Bandwidth::FromMBitsPerSecond(total_mbps),
                           new_flow_count};
    }

    return nc::make_unique<TrafficMatrix>(graph_, out);
  }

  // Performs a number of runs with the optimizer. Returns n - 1 values, one for
  // each delta between a run and its following run.
  void Cycle(size_t n) {
    std::unique_ptr<TrafficMatrix> prev_tm;
    std::unique_ptr<RoutingConfiguration> prev_routing;
    for (size_t i = 0; i < n; ++i) {
      const TrafficMatrix* tm = prev_tm ? prev_tm.get() : initial_tm_;
      UpdateVolumes(*tm);

      RoutingSystemUpdateResult update_result =
          routing_system_->Update(SyntheticHistoryFromTM(*tm));
      auto& routing = update_result.routing;

      for (const auto& aggregate_and_routes : routing->routes()) {
        for (const auto& path_and_fraction : aggregate_and_routes.second) {
          path_to_fraction_[path_and_fraction.first].emplace_back(
              i, path_and_fraction.second);
        }
      }

      if (prev_routing) {
        deltas_.emplace_back(prev_routing->GetDifference(*routing));
      }
      prev_tm = ScaleBasedOnOutput(*routing, *tm);
      prev_routing = std::move(routing);
    }
  }

  void UpdateVolumes(const TrafficMatrix& tm) {
    for (const auto& aggregate_and_demands : tm.demands()) {
      const AggregateId& aggregate = aggregate_and_demands.first;
      volumes_[aggregate].emplace_back(aggregate_and_demands.second.first);
    }
  }

  std::map<AggregateId, AggregateHistory> SyntheticHistoryFromTM(
      const TrafficMatrix& tm) {
    std::map<AggregateId, AggregateHistory> out;
    for (const auto& aggregate_demand_and_flow_count : tm.demands()) {
      const AggregateId& aggregate = aggregate_demand_and_flow_count.first;
      nc::net::Bandwidth demand = aggregate_demand_and_flow_count.second.first;
      size_t flow_count = aggregate_demand_and_flow_count.second.second;

      out.emplace(std::piecewise_construct, std::forward_as_tuple(aggregate),
                  std::forward_as_tuple(demand, kSyntheticBinCount,
                                        kSyntheticBinSize, flow_count));
    }

    return out;
  }

  // In the initial TM each aggregate's demand is what it would be if it were
  // routed on its shortest path.
  TrafficMatrix* initial_tm_;

  // The optimizer.
  RoutingSystem* routing_system_;

  // The graph.
  const nc::net::GraphStorage* graph_;

  // N - 1 deltas for each step to the next one.
  std::vector<RoutingConfigurationDelta> deltas_;

  // Per-aggregate volumes.
  std::map<AggregateId, std::vector<nc::net::Bandwidth>> volumes_;

  // Path to fractions of capacity.
  std::map<const nc::net::Walk*, std::vector<std::pair<double, double>>>
      path_to_fraction_;
};

static void RunWithSimpleTopologyTwoAggregates() {
  nc::net::GraphBuilder builder;
  std::chrono::microseconds ab_delay(
      static_cast<size_t>(FLAGS_ab_link_ms * 1000));
  std::chrono::microseconds cd_delay(
      static_cast<size_t>(FLAGS_cd_link_ms * 1000));

  builder.AddLink({"A", "B",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_ab_link_gbps),
                   ab_delay});
  builder.AddLink({"B", "A",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_ab_link_gbps),
                   ab_delay});
  builder.AddLink({"A", "C", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});
  builder.AddLink({"C", "A", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});
  builder.AddLink({"C", "D",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_cd_link_gbps),
                   cd_delay});
  builder.AddLink({"D", "C",
                   nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_cd_link_gbps),
                   cd_delay});
  builder.AddLink({"B", "D", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});
  builder.AddLink({"D", "B", nc::net::Bandwidth::FromGBitsPerSecond(1),
                   std::chrono::milliseconds(1)});

  nc::net::GraphStorage graph(builder);
  TrafficMatrix initial_tm(&graph);
  AggregateId id_one(
      {graph.NodeFromStringOrDie("A"), graph.NodeFromStringOrDie("B")});
  AggregateId id_two(
      {graph.NodeFromStringOrDie("C"), graph.NodeFromStringOrDie("D")});

  initial_tm.AddDemand(
      id_one, {nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_ab_aggregate_gbps),
               FLAGS_ab_aggregate_flows});
  initial_tm.AddDemand(
      id_two, {nc::net::Bandwidth::FromGBitsPerSecond(FLAGS_cd_aggregate_gbps),
               FLAGS_cd_aggregate_flows});

  PathProvider path_provider(&graph);
  std::unique_ptr<Optimizer> opt;
  if (FLAGS_opt == "CTR") {
    opt = nc::make_unique<CTROptimizer>(&path_provider, 1.0, true);
  } else if (FLAGS_opt == "B4") {
    opt = nc::make_unique<B4Optimizer>(&path_provider, false, 1.0);
  } else if (FLAGS_opt == "B4(P)") {
    opt = nc::make_unique<B4Optimizer>(&path_provider, true, 1.0);
  } else if (FLAGS_opt == "MinMax") {
    opt = nc::make_unique<MinMaxOptimizer>(&path_provider, 1.0);
  }

  MeanScaleEstimatorFactory estimator_factory(
      {1.1, FLAGS_decay_factor, FLAGS_decay_factor, 10});
  RoutingSystemConfig routing_system_config;
  routing_system_config.store_to_metrics = false;
  RoutingSystem routing_system(routing_system_config, opt.get(),
                               &estimator_factory);
  StabilityEvalHarness harness(&initial_tm, &routing_system, FLAGS_steps);
  harness.DumpAggregateVolumes();
  harness.DumpLinkFractions();
}

}  // namespace ctr

int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  //  ctr::RunWithSimpleTopology();
  ctr::RunWithSimpleTopologyTwoAggregates();

  return 0;
}
<|endoftext|>


<commit_before>// $Id$
// -*- C++ -*-

//***************************************************************************
//
// File:
//    loadmodule.cc
//
// Purpose:
//    LoadModule class for reading and gathering symbolic information
//    from a load module.  (based on GNU Binutils).
//
// Description:
//    [The set of functions, macros, etc. defined in the file]
//
// Author:
//    Written by John Mellor-Crummey and Nathan Tallent, Rice University.
//
//    Adapted from parts of The Visual Profiler by Curtis L. Janssen
//    (exec.cc).
//
//***************************************************************************

#ifdef __GNUC__
#pragma implementation
#endif

//************************* System Include Files ****************************

#include <iostream>

//*************************** User Include Files ****************************

#include "loadmodule.h"

//*************************** Forward Declarations **************************

using namespace std;

//*************************** Forward Declarations **************************

bool minisym_is_interesting(bfd *abfd, asymbol *p);

#define valueof(x) ((x)->section->vma + (x)->value)

int val_forward(const void *x, const void *y)
{
  asymbol *xp = *(asymbol **)x;
  asymbol *yp = *(asymbol **)y;
  long yv = valueof(yp);
  long xv = valueof(xp);
  if (xv < yv) return -1;
  if (xv == yv) return 0;
  return 1;
}

//***************************************************************************

/////////////////////////////////////////////////////////////////////////
// LoadModule

LoadModule::LoadModule()
{
  debug_ = 0;
  type_ = Unknown;
}

LoadModule::~LoadModule()
{
}

bool
LoadModule::line_info() const
{
  return true;
}

bool
LoadModule::file_info() const
{
  return true;
}

bool
LoadModule::func_info() const
{
  return true;
}

/////////////////////////////////////////////////////////////////////////
// BFDLoadModule

extern "C" {
  char *cplus_demangle (const char *mangled, int options);
  static int demangle_flags = (1 << 0) | (1 << 1);
  /* FIXME: DMGL_PARAMS | DMGL_ANSI */
}

int BFDLoadModule::bfdinitialized_ = 0;

BFDLoadModule::BFDLoadModule(const string &name)
{
  if (!bfdinitialized_) {
      bfd_init();
      bfdinitialized_ = 1;
    }
  bfd_ = 0;
  symbols_ = 0;

  read(name);
}

BFDLoadModule::~BFDLoadModule()
{
  cleanup();
}

void
BFDLoadModule::cleanup(bool clear_cache)
{
  name_ = "";
  if (bfd_) {
      bfd_close(bfd_);
      bfd_ = 0;
    }
  delete[] (char*)symbols_;
  symbols_ = 0;
  if (clear_cache) locmap_.clear();
}

extern "C" {
    static void
    BFDLoadModule_find_address_in_section(bfd*b, asection* sect, PTR exec)
    {
      BFDLoadModule::find_address_in_section(b, sect, exec);
    }
}

void
BFDLoadModule::read(const string &name)
{
  read_file(name);
}

bool BFDLoadModule::minisyms_;
int BFDLoadModule::dynoffset_;

void
BFDLoadModule::read_file(const string &name, bool clear_cache)
{
  cleanup(clear_cache);
  name_ = name;

#if 0
  cerr << "Reading: " << name << endl;
#endif
  bfd_ = bfd_openr(name_.c_str(), 0);
  minisyms_ = false;
  if (!bfd_) {
      cleanup();
      return;
    }

  if (!bfd_check_format(bfd_, bfd_object)) {
      cleanup();
      return;
    }

  flagword flags = bfd_get_file_flags(bfd_);
  if (flags & EXEC_P) {         // BFD is directly executable
    type_ = Exe;
  } else if (flags & DYNAMIC) { // BFD is a dynamic object
    type_ = DSO;
  }

  if (type_ == DSO) {
    long symcount;
    PTR minisyms;
    unsigned int size;
    struct size_sym *symsizes;
    asymbol *tmp_symbol, *mk_sym;
    int i;

    symcount = bfd_read_minisymbols (bfd_, bfd_tttrue, &minisyms, &size);
    symbols_ = (asymbol**)(new char[(symcount+1)*sizeof(asymbol *)]);
#if 0
    cerr << "Mini symbols semiloaded: " << symcount << endl;
#endif
    tmp_symbol = NULL;
    asymbol *res = NULL;
    int nsyms = 0;
    for (i = 0; i < symcount; i++) {
      if (res == tmp_symbol) {
	tmp_symbol = bfd_make_empty_symbol (bfd_);
      }
      res = bfd_minisymbol_to_symbol(bfd_, bfd_tttrue,
				     (PTR)(((char *)minisyms)+i*size),
				     tmp_symbol);
      if (minisym_is_interesting(bfd_, res)) {
	symbols_[nsyms++] = res;
      } else {
	res = NULL;
      }
    }

    qsort(symbols_, nsyms, sizeof(asymbol*), val_forward);

    symbols_[nsyms] = NULL;
#if 0
    cerr << "Mini symbols loaded: " << nsyms << endl;
#endif
    minisyms_ = true;
  }
  else {
    long int symsize = bfd_get_symtab_upper_bound(bfd_);
    if (!symsize) {
      cleanup();
      return;
    }

    symbols_ = (asymbol**)(new char[symsize]);
    if (!symbols_) {
      cleanup();
      return;
    }

    int numsym = bfd_canonicalize_symtab(bfd_, symbols_);
#if 0
    cerr << "Number of symbols loaded: " << numsym << endl;
#endif

    minisyms_ = false;
  }
}

bfd_vma BFDLoadModule::pc_ = 0;
const char *BFDLoadModule::filename_ = 0;
const char *BFDLoadModule::functionname_ = 0;
unsigned int BFDLoadModule::line_ = 0;
bool BFDLoadModule::found_ = false;

bool
minisym_is_interesting(bfd *abfd, asymbol *p)
{
   symbol_info syminfo;
   bfd_get_symbol_info (abfd, p, &syminfo);
   return (syminfo.type == 'T') || (syminfo.type == 't');
}

void
BFDLoadModule::find_address_in_section(bfd* abfd, asection* section, PTR exec)
{
  BFDLoadModule *e = static_cast<BFDLoadModule*>(exec);

  bfd_vma vma;
  bfd_size_type size;

  if (!e->debug() && e->found_)
    return;

  if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) {
    return;
  }

  vma = bfd_get_section_vma (abfd, section);
  size = bfd_get_section_size_before_reloc (section);

  // FIXME: these debugging messages should removed or placed within
  // debug conditionals
#if 0
  cerr << "Section: vma=" << vma << ", size=" << size << ", dynoffset="
       << dynoffset_ << ", flags=" << bfd_get_section_flags (abfd, section)
       << endl;
#endif

  const char *tmp_filename = 0;
  const char *tmp_funcname = 0;
  unsigned int tmp_line = 0;
  bool tmp_found = false;

#if 0
  cerr << "Looking for pc_: " << e->pc_ << endl;
#endif
  if (minisyms_) {
    asymbol **p;
    unsigned long offset, bestoff;
    asymbol *bestsym;

    if (e->pc_ >= vma && e->pc_ < vma + size) {
      bfd_find_nearest_line (abfd, section, e->symbols_, e->pc_ - vma,
			     &tmp_filename, &tmp_funcname,
			     &tmp_line);
#if 0
      cerr << "FNL: pc=" << e->pc_ << ", funcname=" << tmp_funcname << endl;
#endif
      bestsym = (asymbol *)0;
      offset = e->pc_;
      bestoff = 0;
      for (p = e->symbols_; *p != NULL; p++) {
	symbol_info syminfo;
	bfd_get_symbol_info (abfd, *p, &syminfo);
#if 0
	cerr << "symbol: sym=" << syminfo.value << " offset=" << offset
	     << " best=" << bestoff << " name=" << syminfo.name << endl;
#endif
	if (offset >= syminfo.value && bestoff < syminfo.value) {
	  bestoff = syminfo.value;
#if 0
	  cerr << "better symbol match: sym="<< offset
	       << " best=" << bestoff << "name =" << syminfo.name << endl;
#endif
	  bestsym = (*p);
 	  tmp_funcname = syminfo.name;
	}
      }
      if (bestsym) {
        tmp_found = true;
        tmp_filename = (char *)0;
        tmp_line = 0;
#if 0
        cerr << "pc_: " << e->pc_ << ", vma: " << vma << ",size: " << size
	     << ", bestoff: " << bestoff << ", funcname: " << tmp_funcname
	     << endl;
#endif
      }
    }
    dynoffset_ += size;
  }
  else {
    if (e->pc_ >= vma && e->pc_ < vma + size)
      tmp_found = bfd_find_nearest_line (abfd, section, e->symbols_,
					 e->pc_ - vma, &tmp_filename,
					 &tmp_funcname, &tmp_line);
  }
  if (!tmp_found) {
#if 0
    cerr << "pc_: " << (void*)e->pc_ << " NOT FOUND" << endl;
#endif
  }

  if (e->debug() && (tmp_found || tmp_filename || tmp_funcname || tmp_line)) {
    cerr << "    { bfd sec find: (" << tmp_found << ")"
	 << ", file = " << tmp_filename
	 << ", func = " << tmp_funcname
	 << ", line = " << tmp_line << " }" << endl;
  }
  if (!e->found_) {
    e->found_ = tmp_found;
    e->filename_ = tmp_filename;
    e->functionname_ = tmp_funcname;
    e->line_ = tmp_line;
  }
}

time_t
BFDLoadModule::mtime() const
{
  if (!bfd_) return 0;
  return bfd_get_mtime(bfd_);
}

std::string
BFDLoadModule::name() const
{
  return name_;
}

pprof_off_t
BFDLoadModule::textstart() const
{
  return 0;
}

string
BFDLoadModule::demangle(const string &name) const
{
 char *demangled = cplus_demangle(name.c_str(), demangle_flags);
 string r;
 if (demangled) {
     r = demangled;
     free(demangled);
   }
 else {
     r = name;
   }
 return r;
}

void
BFDLoadModule::find_bfd(pprof_off_t pc, const char **filename,
                        unsigned int *lineno, const char **funcname) const
{
  pc_ = pc;
  dynoffset_ = 0;
  found_ = false;
  functionname_ = 0;
  line_ = 0;
  filename_ = 0;
  bfd_map_over_sections(bfd_, BFDLoadModule_find_address_in_section,
                        (PTR) this);

  if (funcname) *funcname = functionname_;
  if (lineno) *lineno = line_;
  if (filename) *filename = filename_;
}

int
BFDLoadModule::find(pprof_off_t pc, const char **filename,
                    unsigned int *lineno, const char **funcname) const
{
  if (!bfd_) return 0;

  std::map<pprof_off_t,FuncInfo>::iterator ilocmap = locmap_.find(pc);
  if (ilocmap != locmap_.end()) {
      FuncInfo &funcinfo = (*ilocmap).second;
      if (funcinfo.file().size() > 0) *filename = funcinfo.c_file();
      else *filename = 0;
      if (funcinfo.name().size() > 0) *funcname = funcinfo.c_name();
      else *funcname = 0;
      *lineno = funcinfo.index();
      return 1;
    }

  find_bfd(pc, filename, lineno, funcname);

  if (line_ == 0) {
      // possible BFD problem--reinitialize and try again
      const_cast<BFDLoadModule*>(this)->read_file(name(),false);
      find_bfd(pc, filename, lineno, funcname);
      if (debug_ && line_ != 0) {
          cerr << "WARNING: BFD lineno == 0 problem detected" << endl;
        }
    }

  if (debug_) {
#if 0
      const char* pre = "    ";
      cerr << pre << "{ bfd find @ " << (void*)pc << ": " << *funcname << endl
	   << pre << "  [" << *filename << ":" << *lineno << "] }" << endl;
#endif
    }

  locmap_[pc].set_index(*lineno);
  if (*funcname) locmap_[pc].set_name(*funcname);
  if (*filename) locmap_[pc].set_file(*filename);

  return found_;
}

<commit_msg>'(bfd_boolean)true' seems more elegant than 'bfd_tttrue' (Thanks to Nils Smeds)<commit_after>// $Id$
// -*- C++ -*-

//***************************************************************************
//
// File:
//    loadmodule.cc
//
// Purpose:
//    LoadModule class for reading and gathering symbolic information
//    from a load module.  (based on GNU Binutils).
//
// Description:
//    [The set of functions, macros, etc. defined in the file]
//
// Author:
//    Written by John Mellor-Crummey and Nathan Tallent, Rice University.
//
//    Adapted from parts of The Visual Profiler by Curtis L. Janssen
//    (exec.cc).
//
//***************************************************************************

#ifdef __GNUC__
#pragma implementation
#endif

//************************* System Include Files ****************************

#include <iostream>

//*************************** User Include Files ****************************

#include "loadmodule.h"

//*************************** Forward Declarations **************************

using namespace std;

//*************************** Forward Declarations **************************

bool minisym_is_interesting(bfd *abfd, asymbol *p);

#define valueof(x) ((x)->section->vma + (x)->value)

int val_forward(const void *x, const void *y)
{
  asymbol *xp = *(asymbol **)x;
  asymbol *yp = *(asymbol **)y;
  long yv = valueof(yp);
  long xv = valueof(xp);
  if (xv < yv) return -1;
  if (xv == yv) return 0;
  return 1;
}

//***************************************************************************

/////////////////////////////////////////////////////////////////////////
// LoadModule

LoadModule::LoadModule()
{
  debug_ = 0;
  type_ = Unknown;
}

LoadModule::~LoadModule()
{
}

bool
LoadModule::line_info() const
{
  return true;
}

bool
LoadModule::file_info() const
{
  return true;
}

bool
LoadModule::func_info() const
{
  return true;
}

/////////////////////////////////////////////////////////////////////////
// BFDLoadModule

extern "C" {
  char *cplus_demangle (const char *mangled, int options);
  static int demangle_flags = (1 << 0) | (1 << 1);
  /* FIXME: DMGL_PARAMS | DMGL_ANSI */
}

int BFDLoadModule::bfdinitialized_ = 0;

BFDLoadModule::BFDLoadModule(const string &name)
{
  if (!bfdinitialized_) {
      bfd_init();
      bfdinitialized_ = 1;
    }
  bfd_ = 0;
  symbols_ = 0;

  read(name);
}

BFDLoadModule::~BFDLoadModule()
{
  cleanup();
}

void
BFDLoadModule::cleanup(bool clear_cache)
{
  name_ = "";
  if (bfd_) {
      bfd_close(bfd_);
      bfd_ = 0;
    }
  delete[] (char*)symbols_;
  symbols_ = 0;
  if (clear_cache) locmap_.clear();
}

extern "C" {
    static void
    BFDLoadModule_find_address_in_section(bfd*b, asection* sect, PTR exec)
    {
      BFDLoadModule::find_address_in_section(b, sect, exec);
    }
}

void
BFDLoadModule::read(const string &name)
{
  read_file(name);
}

bool BFDLoadModule::minisyms_;
int BFDLoadModule::dynoffset_;

void
BFDLoadModule::read_file(const string &name, bool clear_cache)
{
  cleanup(clear_cache);
  name_ = name;

#if 0
  cerr << "Reading: " << name << endl;
#endif
  bfd_ = bfd_openr(name_.c_str(), 0);
  minisyms_ = false;
  if (!bfd_) {
      cleanup();
      return;
    }

  if (!bfd_check_format(bfd_, bfd_object)) {
      cleanup();
      return;
    }

  flagword flags = bfd_get_file_flags(bfd_);
  if (flags & EXEC_P) {         // BFD is directly executable
    type_ = Exe;
  } else if (flags & DYNAMIC) { // BFD is a dynamic object
    type_ = DSO;
  }

  if (type_ == DSO) {
    long symcount;
    PTR minisyms;
    unsigned int size;
    struct size_sym *symsizes;
    asymbol *tmp_symbol, *mk_sym;
    int i;

    symcount = bfd_read_minisymbols (bfd_, (bfd_boolean)true, &minisyms, &size);
    symbols_ = (asymbol**)(new char[(symcount+1)*sizeof(asymbol *)]);
#if 0
    cerr << "Mini symbols semiloaded: " << symcount << endl;
#endif
    tmp_symbol = NULL;
    asymbol *res = NULL;
    int nsyms = 0;
    for (i = 0; i < symcount; i++) {
      if (res == tmp_symbol) {
	tmp_symbol = bfd_make_empty_symbol (bfd_);
      }
      res = bfd_minisymbol_to_symbol(bfd_, (bfd_boolean)true,
				     (PTR)(((char *)minisyms)+i*size),
				     tmp_symbol);
      if (minisym_is_interesting(bfd_, res)) {
	symbols_[nsyms++] = res;
      } else {
	res = NULL;
      }
    }

    qsort(symbols_, nsyms, sizeof(asymbol*), val_forward);

    symbols_[nsyms] = NULL;
#if 0
    cerr << "Mini symbols loaded: " << nsyms << endl;
#endif
    minisyms_ = true;
  }
  else {
    long int symsize = bfd_get_symtab_upper_bound(bfd_);
    if (!symsize) {
      cleanup();
      return;
    }

    symbols_ = (asymbol**)(new char[symsize]);
    if (!symbols_) {
      cleanup();
      return;
    }

    int numsym = bfd_canonicalize_symtab(bfd_, symbols_);
#if 0
    cerr << "Number of symbols loaded: " << numsym << endl;
#endif

    minisyms_ = false;
  }
}

bfd_vma BFDLoadModule::pc_ = 0;
const char *BFDLoadModule::filename_ = 0;
const char *BFDLoadModule::functionname_ = 0;
unsigned int BFDLoadModule::line_ = 0;
bool BFDLoadModule::found_ = false;

bool
minisym_is_interesting(bfd *abfd, asymbol *p)
{
   symbol_info syminfo;
   bfd_get_symbol_info (abfd, p, &syminfo);
   return (syminfo.type == 'T') || (syminfo.type == 't');
}

void
BFDLoadModule::find_address_in_section(bfd* abfd, asection* section, PTR exec)
{
  BFDLoadModule *e = static_cast<BFDLoadModule*>(exec);

  bfd_vma vma;
  bfd_size_type size;

  if (!e->debug() && e->found_)
    return;

  if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) {
    return;
  }

  vma = bfd_get_section_vma (abfd, section);
  size = bfd_get_section_size_before_reloc (section);

  // FIXME: these debugging messages should removed or placed within
  // debug conditionals
#if 0
  cerr << "Section: vma=" << vma << ", size=" << size << ", dynoffset="
       << dynoffset_ << ", flags=" << bfd_get_section_flags (abfd, section)
       << endl;
#endif

  const char *tmp_filename = 0;
  const char *tmp_funcname = 0;
  unsigned int tmp_line = 0;
  bool tmp_found = false;

#if 0
  cerr << "Looking for pc_: " << e->pc_ << endl;
#endif
  if (minisyms_) {
    asymbol **p;
    unsigned long offset, bestoff;
    asymbol *bestsym;

    if (e->pc_ >= vma && e->pc_ < vma + size) {
      bfd_find_nearest_line (abfd, section, e->symbols_, e->pc_ - vma,
			     &tmp_filename, &tmp_funcname,
			     &tmp_line);
#if 0
      cerr << "FNL: pc=" << e->pc_ << ", funcname=" << tmp_funcname << endl;
#endif
      bestsym = (asymbol *)0;
      offset = e->pc_;
      bestoff = 0;
      for (p = e->symbols_; *p != NULL; p++) {
	symbol_info syminfo;
	bfd_get_symbol_info (abfd, *p, &syminfo);
#if 0
	cerr << "symbol: sym=" << syminfo.value << " offset=" << offset
	     << " best=" << bestoff << " name=" << syminfo.name << endl;
#endif
	if (offset >= syminfo.value && bestoff < syminfo.value) {
	  bestoff = syminfo.value;
#if 0
	  cerr << "better symbol match: sym="<< offset
	       << " best=" << bestoff << "name =" << syminfo.name << endl;
#endif
	  bestsym = (*p);
 	  tmp_funcname = syminfo.name;
	}
      }
      if (bestsym) {
        tmp_found = true;
        tmp_filename = (char *)0;
        tmp_line = 0;
#if 0
        cerr << "pc_: " << e->pc_ << ", vma: " << vma << ",size: " << size
	     << ", bestoff: " << bestoff << ", funcname: " << tmp_funcname
	     << endl;
#endif
      }
    }
    dynoffset_ += size;
  }
  else {
    if (e->pc_ >= vma && e->pc_ < vma + size)
      tmp_found = bfd_find_nearest_line (abfd, section, e->symbols_,
					 e->pc_ - vma, &tmp_filename,
					 &tmp_funcname, &tmp_line);
  }
  if (!tmp_found) {
#if 0
    cerr << "pc_: " << (void*)e->pc_ << " NOT FOUND" << endl;
#endif
  }

  if (e->debug() && (tmp_found || tmp_filename || tmp_funcname || tmp_line)) {
    cerr << "    { bfd sec find: (" << tmp_found << ")"
	 << ", file = " << tmp_filename
	 << ", func = " << tmp_funcname
	 << ", line = " << tmp_line << " }" << endl;
  }
  if (!e->found_) {
    e->found_ = tmp_found;
    e->filename_ = tmp_filename;
    e->functionname_ = tmp_funcname;
    e->line_ = tmp_line;
  }
}

time_t
BFDLoadModule::mtime() const
{
  if (!bfd_) return 0;
  return bfd_get_mtime(bfd_);
}

std::string
BFDLoadModule::name() const
{
  return name_;
}

pprof_off_t
BFDLoadModule::textstart() const
{
  return 0;
}

string
BFDLoadModule::demangle(const string &name) const
{
 char *demangled = cplus_demangle(name.c_str(), demangle_flags);
 string r;
 if (demangled) {
     r = demangled;
     free(demangled);
   }
 else {
     r = name;
   }
 return r;
}

void
BFDLoadModule::find_bfd(pprof_off_t pc, const char **filename,
                        unsigned int *lineno, const char **funcname) const
{
  pc_ = pc;
  dynoffset_ = 0;
  found_ = false;
  functionname_ = 0;
  line_ = 0;
  filename_ = 0;
  bfd_map_over_sections(bfd_, BFDLoadModule_find_address_in_section,
                        (PTR) this);

  if (funcname) *funcname = functionname_;
  if (lineno) *lineno = line_;
  if (filename) *filename = filename_;
}

int
BFDLoadModule::find(pprof_off_t pc, const char **filename,
                    unsigned int *lineno, const char **funcname) const
{
  if (!bfd_) return 0;

  std::map<pprof_off_t,FuncInfo>::iterator ilocmap = locmap_.find(pc);
  if (ilocmap != locmap_.end()) {
      FuncInfo &funcinfo = (*ilocmap).second;
      if (funcinfo.file().size() > 0) *filename = funcinfo.c_file();
      else *filename = 0;
      if (funcinfo.name().size() > 0) *funcname = funcinfo.c_name();
      else *funcname = 0;
      *lineno = funcinfo.index();
      return 1;
    }

  find_bfd(pc, filename, lineno, funcname);

  if (line_ == 0) {
      // possible BFD problem--reinitialize and try again
      const_cast<BFDLoadModule*>(this)->read_file(name(),false);
      find_bfd(pc, filename, lineno, funcname);
      if (debug_ && line_ != 0) {
          cerr << "WARNING: BFD lineno == 0 problem detected" << endl;
        }
    }

  if (debug_) {
#if 0
      const char* pre = "    ";
      cerr << pre << "{ bfd find @ " << (void*)pc << ": " << *funcname << endl
	   << pre << "  [" << *filename << ":" << *lineno << "] }" << endl;
#endif
    }

  locmap_[pc].set_index(*lineno);
  if (*funcname) locmap_[pc].set_name(*funcname);
  if (*filename) locmap_[pc].set_file(*filename);

  return found_;
}

<|endoftext|>


<commit_before>#include <cmath>
#include <allegro5/allegro_primitives.h> // DEBUGTOOL

#include "ingameelements/weapons/hammer.h"
#include "renderer.h"
#include "ingameelements/heroes/reinhardt.h"
#include "ingameelements/projectiles/firestrike.h"

#include "engine.h"

void Hammer::init(uint64_t id_, Gamestate &state, EntityPtr owner_)
{
    Weapon::init(id_, state, owner_);

    barrierptr = state.make_entity<ReinhardtShield>(state, team, owner_);

    firestrikeanim.init(herofolder()+"firestrikebackarm/");
    firestrikeanim.active(false);
    firestrikedelay.init(firestrikeanim.timer.duration * 0.5,
                         std::bind(&Hammer::createfirestrike, this, std::placeholders::_1));
    firestrikedelay.active = false;
    // 6th and 14th frame of an assumed 20 frame animation - ugly but idk better way
    firingdelay1.init(firinganim.timer.duration * 6.0/20.0, std::bind(&Hammer::hitarea, this, std::placeholders::_1));
    firingdelay1.active = false;
    firingdelay2.init(firinganim.timer.duration * 14.0/20.0, std::bind(&Hammer::hitarea, this, std::placeholders::_1));
    firingdelay2.active = false;
}

void Hammer::renderbehind(Renderer &renderer, Gamestate &state)
{
    if (firinganim.active())
    {
        return;
    }

    std::string mainsprite;
    Reinhardt &c = state.get<Reinhardt>(state.get<Player>(owner).character);
    if (firestrikeanim.active())
    {
        mainsprite = firestrikeanim.getframepath();
    }
    else if (barrier(state).active)
    {
        mainsprite = herofolder()+"shield/back";

    }
    else
    {
        mainsprite = herofolder()+"arm/back";
    }
    ALLEGRO_BITMAP *sprite = renderer.spriteloader.requestsprite(mainsprite);
    double spriteoffset_x = renderer.spriteloader.get_spriteoffset_x(mainsprite)*renderer.zoom;
    double spriteoffset_y = renderer.spriteloader.get_spriteoffset_y(mainsprite)*renderer.zoom;
    double rel_x = (x - renderer.cam_x)*renderer.zoom;
    double rel_y = (y - renderer.cam_y)*renderer.zoom;
    double attachpt_x = getbackattachpoint_x(state)*renderer.zoom;
    double attachpt_y = getbackattachpoint_y(state)*renderer.zoom;

    ALLEGRO_BITMAP *outline = renderer.spriteloader.requestspriteoutline(mainsprite);
    ALLEGRO_COLOR outlinecolor = al_map_rgb(225, 17, 17);

    al_set_target_bitmap(renderer.midground);
    if (c.weaponvisible(state))
    {
        if (c.isflipped)
        {
            al_draw_scaled_rotated_bitmap(sprite, spriteoffset_x, spriteoffset_y, rel_x-attachpt_x, rel_y-attachpt_y,
                                          -1, 1, (aimdirection+3.1415)*barrier(state).active, 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_scaled_rotated_bitmap(outline, outlinecolor, attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y,
                                                    -1, 1, (aimdirection+3.1415)*barrier(state).active, 0);
            }
        }
        else
        {
            al_draw_rotated_bitmap(sprite, spriteoffset_x, spriteoffset_y, rel_x-attachpt_x, rel_y-attachpt_y, aimdirection*barrier(state).active, 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_rotated_bitmap(outline, outlinecolor, attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y, aimdirection*barrier(state).active, 0);
            }
        }
    }
}

void Hammer::render(Renderer &renderer, Gamestate &state)
{
    std::string mainsprite;
    Reinhardt &c = state.get<Reinhardt>(state.get<Player>(owner).character);
    if (firinganim.active())
    {
        mainsprite = firinganim.getframepath();
    }
    else if (firestrikeanim.active())
    {
        mainsprite = herofolder()+"firestrikefrontarm/"+std::to_string(firestrikeanim.getframe());
    }
    else if (barrier(state).active)
    {
        mainsprite = herofolder()+"shield/front";

    }
    else
    {
        mainsprite = herofolder()+"arm/front";
    }
    ALLEGRO_BITMAP *sprite = renderer.spriteloader.requestsprite(mainsprite);
    double spriteoffset_x = renderer.spriteloader.get_spriteoffset_x(mainsprite)*renderer.zoom;
    double spriteoffset_y = renderer.spriteloader.get_spriteoffset_y(mainsprite)*renderer.zoom;
    double rel_x = (x - renderer.cam_x)*renderer.zoom;
    double rel_y = (y - renderer.cam_y)*renderer.zoom;
    double attachpt_x = getattachpoint_x(state)*renderer.zoom;
    double attachpt_y = getattachpoint_y(state)*renderer.zoom;

    ALLEGRO_BITMAP *outline = renderer.spriteloader.requestspriteoutline(mainsprite);
    ALLEGRO_COLOR outlinecolor = al_map_rgb(225, 17, 17);

    al_set_target_bitmap(renderer.midground);
    if (c.weaponvisible(state))
    {
        if (c.isflipped)
        {
            al_draw_scaled_rotated_bitmap(sprite, -attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y, -1, 1, 0, 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_scaled_rotated_bitmap(outline, outlinecolor, attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y, -1, 1, 0, 0);
            }
        }
        else
        {
            al_draw_bitmap(sprite, rel_x - (attachpt_x+spriteoffset_x), rel_y - (attachpt_y+spriteoffset_y), 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_bitmap(outline, outlinecolor, rel_x - (attachpt_x+spriteoffset_x), rel_y - (attachpt_y+spriteoffset_y), 0);
            }
        }
    }

    barrier(state).render(renderer, state);

    al_draw_rectangle(rel_x + renderer.zoom*30 * (c.isflipped ? -1 : 1), rel_y - renderer.zoom*30,
                      rel_x + renderer.zoom*59 * (c.isflipped ? -1 : 1), rel_y + renderer.zoom*30, al_map_rgb(255, 0, 0), 0);
}

void Hammer::beginstep(Gamestate &state, double frametime)
{
    Weapon::beginstep(state, frametime);
    barrier(state).beginstep(state, frametime);
    firestrikeanim.update(state, frametime);
    firestrikedelay.update(state, frametime);
}

void Hammer::midstep(Gamestate &state, double frametime)
{
    Weapon::midstep(state, frametime);
    barrier(state).midstep(state, frametime);
}

void Hammer::endstep(Gamestate &state, double frametime)
{
    Weapon::endstep(state, frametime);
    barrier(state).endstep(state, frametime);
}

void Hammer::wantfireprimary(Gamestate &state)
{
    if (state.engine.isserver and not firinganim.active() and not firestrikeanim.active())
    {
        fireprimary(state);
        state.engine.sendbuffer.write<uint8_t>(PRIMARY_FIRED);
        state.engine.sendbuffer.write<uint8_t>(state.findplayerid(owner));
    }
}

void Hammer::fireprimary(Gamestate &state)
{
    firinganim.reset();
    firinganim.active(true);
}

void Hammer::wantfiresecondary(Gamestate &state)
{
    // Do nothing
}

void Hammer::firesecondary(Gamestate &state)
{
    // Do nothing
}

void Hammer::createfirestrike(Gamestate &state)
{
    Firestrike &firestrike = state.get<Firestrike&>(state.make_entity<Firestrike>(state, owner));
    firestrike.x = x + std::cos(aimdirection) * 40;
    firestrike.y = y + std::sin(aimdirection) * 40;
    firestrike.hspeed = firestrike.SPEED * std::cos(aimdirection);
    firestrike.vspeed = firestrike.SPEED * std::sin(aimdirection);

    if (state.currentmap->collideline(x, y, firestrike.x, firestrike.y))
    {
        firestrike.destroy(state);
    }
}

void Hammer::hitarea(Gamestate &state)
{
    for (auto &e : state.entitylist)
    {
        auto &entity = *(e.second);
        if (not entity.destroyentity)
        {
            if (entity.damageableby(team))
            {
                Reinhardt &c = state.get<Reinhardt>(state.get<Player>(owner).character);
                int direction = (c.isflipped ? -1 : 1);
                for (int i=0; i<30; ++i)
                {
                    for (int j=0; j<60; ++j)
                    {
                        if (entity.collides(state, x + direction*(30 + i), y - 30 + direction*j))
                        {
                            Global::logging().print(__FILE__, __LINE__, "Hit some entity");
                            // We hit something, check if it's protected
                            double tmpx, tmpy;
                            if (state.collidelinetarget(state, x, y, state.get<MovingEntity>(entity.id), team,
                                                        PENETRATE_CHARACTER, &tmpx, &tmpy).id == entity.id)
                            {
                                Global::logging().print(__FILE__, __LINE__, "Damage");
                                entity.damage(state, DAMAGE);
                            }
                            else
                            {
                                Global::logging().print(__FILE__, __LINE__, "%i is protected by %i", entity.id, state.collidelinetarget(state, x, y, state.get<MovingEntity>(entity.id), team,
                                                                                PENETRATE_CHARACTER, &tmpx, &tmpy).id);
                            }
                            return;
                        }
                    }
                }
            }
        }
    }
}

double Hammer::getattachpoint_x(Gamestate &state)
{
    return 0;
}

double Hammer::getattachpoint_y(Gamestate &state)
{
    return 8;
}

double Hammer::getbackattachpoint_x(Gamestate &state)
{
    if (barrier(state).active)
    {
        return 6 * (state.get<Player&>(owner).getcharacter(state).isflipped ? 1:-1);
    }
    else
    {
        return 0;
    }
}

double Hammer::getbackattachpoint_y(Gamestate &state)
{
    if (barrier(state).active)
    {
        return 10;
    }
    else
    {
        return 8;
    }
}

ReinhardtShield& Hammer::barrier(Gamestate &state)
{
    return state.get<ReinhardtShield>(barrierptr);
}

void Hammer::destroy(Gamestate &state)
{
    barrier(state).destroy(state);
    Weapon::destroy(state);
}

void Hammer::interpolate(Entity &prev_entity, Entity &next_entity, double alpha)
{
    Weapon::interpolate(prev_entity, next_entity, alpha);

    Hammer &prev_e = static_cast<Hammer&>(prev_entity);
    Hammer &next_e = static_cast<Hammer&>(next_entity);

    firestrikeanim.interpolate(prev_e.firestrikeanim, next_e.firestrikeanim, alpha);
    firestrikedelay.interpolate(prev_e.firestrikedelay, next_e.firestrikedelay, alpha);
    firinganim.interpolate(prev_e.firinganim, next_e.firinganim, alpha);
    firingdelay1.interpolate(prev_e.firingdelay1, next_e.firingdelay1, alpha);
    firingdelay2.interpolate(prev_e.firingdelay2, next_e.firingdelay2, alpha);
}<commit_msg>Finished hammer damage.<commit_after>#include <cmath>

#include "ingameelements/weapons/hammer.h"
#include "renderer.h"
#include "ingameelements/heroes/reinhardt.h"
#include "ingameelements/projectiles/firestrike.h"

#include "engine.h"

void Hammer::init(uint64_t id_, Gamestate &state, EntityPtr owner_)
{
    Weapon::init(id_, state, owner_);

    barrierptr = state.make_entity<ReinhardtShield>(state, team, owner_);

    firestrikeanim.init(herofolder()+"firestrikebackarm/");
    firestrikeanim.active(false);
    firestrikedelay.init(firestrikeanim.timer.duration * 0.5,
                         std::bind(&Hammer::createfirestrike, this, std::placeholders::_1));
    firestrikedelay.active = false;
    // 6th and 14th frame of an assumed 20 frame animation - ugly but idk better way
    firingdelay1.init(firinganim.timer.duration * 6.0/20.0, std::bind(&Hammer::hitarea, this, std::placeholders::_1));
    firingdelay1.active = false;
    firingdelay2.init(firinganim.timer.duration * 14.0/20.0, std::bind(&Hammer::hitarea, this, std::placeholders::_1));
    firingdelay2.active = false;
}

void Hammer::renderbehind(Renderer &renderer, Gamestate &state)
{
    if (firinganim.active())
    {
        return;
    }

    std::string mainsprite;
    Reinhardt &c = state.get<Reinhardt>(state.get<Player>(owner).character);
    if (firestrikeanim.active())
    {
        mainsprite = firestrikeanim.getframepath();
    }
    else if (barrier(state).active)
    {
        mainsprite = herofolder()+"shield/back";

    }
    else
    {
        mainsprite = herofolder()+"arm/back";
    }
    ALLEGRO_BITMAP *sprite = renderer.spriteloader.requestsprite(mainsprite);
    double spriteoffset_x = renderer.spriteloader.get_spriteoffset_x(mainsprite)*renderer.zoom;
    double spriteoffset_y = renderer.spriteloader.get_spriteoffset_y(mainsprite)*renderer.zoom;
    double rel_x = (x - renderer.cam_x)*renderer.zoom;
    double rel_y = (y - renderer.cam_y)*renderer.zoom;
    double attachpt_x = getbackattachpoint_x(state)*renderer.zoom;
    double attachpt_y = getbackattachpoint_y(state)*renderer.zoom;

    ALLEGRO_BITMAP *outline = renderer.spriteloader.requestspriteoutline(mainsprite);
    ALLEGRO_COLOR outlinecolor = al_map_rgb(225, 17, 17);

    al_set_target_bitmap(renderer.midground);
    if (c.weaponvisible(state))
    {
        if (c.isflipped)
        {
            al_draw_scaled_rotated_bitmap(sprite, spriteoffset_x, spriteoffset_y, rel_x-attachpt_x, rel_y-attachpt_y,
                                          -1, 1, (aimdirection+3.1415)*barrier(state).active, 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_scaled_rotated_bitmap(outline, outlinecolor, attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y,
                                                    -1, 1, (aimdirection+3.1415)*barrier(state).active, 0);
            }
        }
        else
        {
            al_draw_rotated_bitmap(sprite, spriteoffset_x, spriteoffset_y, rel_x-attachpt_x, rel_y-attachpt_y, aimdirection*barrier(state).active, 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_rotated_bitmap(outline, outlinecolor, attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y, aimdirection*barrier(state).active, 0);
            }
        }
    }
}

void Hammer::render(Renderer &renderer, Gamestate &state)
{
    std::string mainsprite;
    Reinhardt &c = state.get<Reinhardt>(state.get<Player>(owner).character);
    if (firinganim.active())
    {
        mainsprite = firinganim.getframepath();
    }
    else if (firestrikeanim.active())
    {
        mainsprite = herofolder()+"firestrikefrontarm/"+std::to_string(firestrikeanim.getframe());
    }
    else if (barrier(state).active)
    {
        mainsprite = herofolder()+"shield/front";

    }
    else
    {
        mainsprite = herofolder()+"arm/front";
    }
    ALLEGRO_BITMAP *sprite = renderer.spriteloader.requestsprite(mainsprite);
    double spriteoffset_x = renderer.spriteloader.get_spriteoffset_x(mainsprite)*renderer.zoom;
    double spriteoffset_y = renderer.spriteloader.get_spriteoffset_y(mainsprite)*renderer.zoom;
    double rel_x = (x - renderer.cam_x)*renderer.zoom;
    double rel_y = (y - renderer.cam_y)*renderer.zoom;
    double attachpt_x = getattachpoint_x(state)*renderer.zoom;
    double attachpt_y = getattachpoint_y(state)*renderer.zoom;

    ALLEGRO_BITMAP *outline = renderer.spriteloader.requestspriteoutline(mainsprite);
    ALLEGRO_COLOR outlinecolor = al_map_rgb(225, 17, 17);

    al_set_target_bitmap(renderer.midground);
    if (c.weaponvisible(state))
    {
        if (c.isflipped)
        {
            al_draw_scaled_rotated_bitmap(sprite, -attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y, -1, 1, 0, 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_scaled_rotated_bitmap(outline, outlinecolor, attachpt_x+spriteoffset_x, attachpt_y+spriteoffset_y, rel_x, rel_y, -1, 1, 0, 0);
            }
        }
        else
        {
            al_draw_bitmap(sprite, rel_x - (attachpt_x+spriteoffset_x), rel_y - (attachpt_y+spriteoffset_y), 0);
            if (state.get<Player>(renderer.myself).team != team)
            {
                // Draw enemy outline
                al_draw_tinted_bitmap(outline, outlinecolor, rel_x - (attachpt_x+spriteoffset_x), rel_y - (attachpt_y+spriteoffset_y), 0);
            }
        }
    }

    barrier(state).render(renderer, state);
}

void Hammer::beginstep(Gamestate &state, double frametime)
{
    Weapon::beginstep(state, frametime);
    barrier(state).beginstep(state, frametime);
    firestrikeanim.update(state, frametime);
    firestrikedelay.update(state, frametime);
    firingdelay1.update(state, frametime);
    firingdelay2.update(state, frametime);
}

void Hammer::midstep(Gamestate &state, double frametime)
{
    Weapon::midstep(state, frametime);
    barrier(state).midstep(state, frametime);
}

void Hammer::endstep(Gamestate &state, double frametime)
{
    Weapon::endstep(state, frametime);
    barrier(state).endstep(state, frametime);
}

void Hammer::wantfireprimary(Gamestate &state)
{
    if (state.engine.isserver and not firinganim.active() and not firestrikeanim.active())
    {
        fireprimary(state);
        state.engine.sendbuffer.write<uint8_t>(PRIMARY_FIRED);
        state.engine.sendbuffer.write<uint8_t>(state.findplayerid(owner));
    }
}

void Hammer::fireprimary(Gamestate &state)
{
    firinganim.reset();
    firinganim.active(true);
    firingdelay1.reset();
    firingdelay1.active = true;
    firingdelay2.reset();
    firingdelay2.active = true;
}

void Hammer::wantfiresecondary(Gamestate &state)
{
    // Do nothing
}

void Hammer::firesecondary(Gamestate &state)
{
    // Do nothing
}

void Hammer::createfirestrike(Gamestate &state)
{
    Firestrike &firestrike = state.get<Firestrike&>(state.make_entity<Firestrike>(state, owner));
    firestrike.x = x + std::cos(aimdirection) * 40;
    firestrike.y = y + std::sin(aimdirection) * 40;
    firestrike.hspeed = firestrike.SPEED * std::cos(aimdirection);
    firestrike.vspeed = firestrike.SPEED * std::sin(aimdirection);

    if (state.currentmap->collideline(x, y, firestrike.x, firestrike.y))
    {
        firestrike.destroy(state);
    }
}

void Hammer::hitarea(Gamestate &state)
{
    Reinhardt &reinhardt = state.get<Reinhardt>(state.get<Player>(owner).character);
    for (auto &e : state.entitylist)
    {
        auto &entity = *(e.second);
        if (not entity.destroyentity)
        {
            if (entity.damageableby(team))
            {
                int direction = (reinhardt.isflipped ? -1 : 1);
                for (int i=0; i<30; ++i)
                {
                    for (int j=0; j<60; ++j)
                    {
                        if (entity.collides(state, x + direction*(30 + i), y - 30 + j))
                        {
                            // We hit something, check if it's protected
                            double tmpx, tmpy;
                            if (state.collidelinetarget(state, x, y, state.get<MovingEntity>(entity.id), team,
                                                        PENETRATE_CHARACTER, &tmpx, &tmpy).id == entity.id)
                            {
                                entity.damage(state, DAMAGE);
                            }
                            return;
                        }
                    }
                }
            }
        }
    }
}

double Hammer::getattachpoint_x(Gamestate &state)
{
    return 0;
}

double Hammer::getattachpoint_y(Gamestate &state)
{
    return 8;
}

double Hammer::getbackattachpoint_x(Gamestate &state)
{
    if (barrier(state).active)
    {
        return 6 * (state.get<Player&>(owner).getcharacter(state).isflipped ? 1:-1);
    }
    else
    {
        return 0;
    }
}

double Hammer::getbackattachpoint_y(Gamestate &state)
{
    if (barrier(state).active)
    {
        return 10;
    }
    else
    {
        return 8;
    }
}

ReinhardtShield& Hammer::barrier(Gamestate &state)
{
    return state.get<ReinhardtShield>(barrierptr);
}

void Hammer::destroy(Gamestate &state)
{
    barrier(state).destroy(state);
    Weapon::destroy(state);
}

void Hammer::interpolate(Entity &prev_entity, Entity &next_entity, double alpha)
{
    Weapon::interpolate(prev_entity, next_entity, alpha);

    Hammer &prev_e = static_cast<Hammer&>(prev_entity);
    Hammer &next_e = static_cast<Hammer&>(next_entity);

    firestrikeanim.interpolate(prev_e.firestrikeanim, next_e.firestrikeanim, alpha);
    firestrikedelay.interpolate(prev_e.firestrikedelay, next_e.firestrikedelay, alpha);
    firinganim.interpolate(prev_e.firinganim, next_e.firinganim, alpha);
    firingdelay1.interpolate(prev_e.firingdelay1, next_e.firingdelay1, alpha);
    firingdelay2.interpolate(prev_e.firingdelay2, next_e.firingdelay2, alpha);
}<|endoftext|>


<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2019                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <openspace/interaction/keybindingmanager.h>

#include <openspace/engine/globals.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/scripting/scriptengine.h>
#include <ghoul/glm.h>
#include <sstream>

#include "keybindingmanager_lua.inl"

namespace openspace::interaction {

KeybindingManager::KeybindingManager()
    : DocumentationGenerator(
        "Documentation",
        "keybindings",
        {
            { "keybindingTemplate", "${WEB}/keybindings/keybinding.hbs" },
            { "mainTemplate", "${WEB}/keybindings/main.hbs" }
        },
        "${WEB}/keybindings/script.js"
    )
{}

void KeybindingManager::keyboardCallback(Key key, KeyModifier modifier, KeyAction action)
{
    if (action == KeyAction::Press || action == KeyAction::Repeat) {
        // iterate over key bindings
        auto ret = _keyLua.equal_range({ key, modifier });
        for (auto it = ret.first; it != ret.second; ++it) {
            using RS = scripting::ScriptEngine::RemoteScripting;

            global::scriptEngine.queueScript(
                it->second.command,
                it->second.synchronization ? RS::Yes : RS::No
            );
        }
    }
}

void KeybindingManager::resetKeyBindings() {
    _keyLua.clear();
}

void KeybindingManager::bindKeyLocal(Key key,
                                     KeyModifier modifier,
                                     std::string luaCommand,
                                     std::string documentation,
                                     std::string name,
                                     std::string guiPath)
{
    _keyLua.insert({
        { key, modifier },
        {
            std::move(luaCommand),
            IsSynchronized::No,
            std::move(documentation),
            std::move(name),
            std::move(guiPath)
        }
    });
}

void KeybindingManager::bindKey(Key key,
                                KeyModifier modifier,
                                std::string luaCommand,
                                std::string documentation,
                                std::string name,
                                std::string guiPath)
{
    _keyLua.insert({
        { key, modifier },
        {
            std::move(luaCommand),
            IsSynchronized::Yes,
            std::move(documentation),
            std::move(name),
            std::move(guiPath)
        }
    });
}

void KeybindingManager::removeKeyBinding(const std::string& key) {
    // Erase-remove idiom does not work for std::multimap so we have to do this on foot

    KeyWithModifier k = stringToKey(key);

    for (auto it = _keyLua.begin(); it != _keyLua.end(); ) {
        // If the current iterator is the key that we are looking for, delete it
        // (std::multimap::erase will return the iterator to the next element for us)
        if (it->first == k) {
            it = _keyLua.erase(it);
        }
        else {
            // We if it is not, we continue iteration
            ++it;
        }
    }
}

std::vector<std::pair<KeyWithModifier, KeybindingManager::KeyInformation>>
KeybindingManager::keyBinding(const std::string& key) const
{
    std::vector<std::pair<KeyWithModifier, KeyInformation>> result;

    KeyWithModifier k = stringToKey(key);
    auto itRange = _keyLua.equal_range(k);
    for (auto it = itRange.first; it != itRange.second; ++it) {
        result.emplace_back(it->first, it->second);
    }
    return result;
}

const std::multimap<KeyWithModifier, KeybindingManager::KeyInformation>&
KeybindingManager::keyBindings() const
{
    return _keyLua;
}

std::string KeybindingManager::generateJson() const {
    std::stringstream json;
    json << "[";
    bool first = true;
    for (const std::pair<const KeyWithModifier, KeyInformation>& p : _keyLua) {
        if (!first) {
            json << ",";
        }
        first = false;
        json << "{";
        json << R"("key": ")" << ghoul::to_string(p.first) << "\",";
        json << R"("script": ")" << escapedJson(p.second.command) << "\",";
        json << R"("remoteScripting: ")"
             << (p.second.synchronization ? "true," : "false,");
        json << R"("documentation": ")" << escapedJson(p.second.documentation) << "\",";
        json << R"("name: ")" << escapedJson(p.second.name) << "\"";
        json << "}";
    }
    json << "]";

    return json.str();
}

scripting::LuaLibrary KeybindingManager::luaLibrary() {
    return {
        "",
        {
            {
                "clearKeys",
                &luascriptfunctions::clearKeys,
                {},
                "",
                "Clear all key bindings"
            },
            {
                "clearKey",
                &luascriptfunctions::clearKey,
                {},
                "string or strings",
                "Unbinds the key or keys that have been provided. This function can be "
                "called with a single key or with an array of keys to remove all of the "
                "provided keys at once"
            },
            {
                "bindKey",
                &luascriptfunctions::bindKey,
                {},
                "string, string [,string]",
                "Binds a key by name to a lua string command to execute both locally "
                "and to broadcast to clients if this is the host of a parallel session. "
                "The first argument is the key, the second argument is the Lua command "
                "that is to be executed, and the optional third argument is a human "
                "readable description of the command for documentation purposes."
            },
            {
                "bindKeyLocal",
                &luascriptfunctions::bindKeyLocal,
                {},
                "string, string [,string]",
                "Binds a key by name to a lua string command to execute only locally. "
                "The first argument is the key, the second argument is the Lua command "
                "that is to be executed, and the optional third argument is a human "
                "readable description of the command for documentation purposes."
            },
            {
                "getKeyBinding",
                &luascriptfunctions::getKeyBindings,
                {},
                "string",
                "Returns a list of information about the keybindings for the provided "
                "key. Each element in the list is a table describing the 'Command' that "
                "was bound and whether it was a 'Remote' script or not."

            }
        }
    };
}

} // namespace openspace::interaction
<commit_msg>Fix keybinding documentation (closes #813)<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2019                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <openspace/interaction/keybindingmanager.h>

#include <openspace/engine/globals.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/scripting/scriptengine.h>
#include <ghoul/glm.h>
#include <sstream>

#include "keybindingmanager_lua.inl"

namespace openspace::interaction {

KeybindingManager::KeybindingManager()
    : DocumentationGenerator(
        "Documentation",
        "keybindings",
        {
            { "keybindingTemplate", "${WEB}/keybindings/keybinding.hbs" },
            { "mainTemplate", "${WEB}/keybindings/main.hbs" }
        },
        "${WEB}/keybindings/script.js"
    )
{}

void KeybindingManager::keyboardCallback(Key key, KeyModifier modifier, KeyAction action)
{
    if (action == KeyAction::Press || action == KeyAction::Repeat) {
        // iterate over key bindings
        auto ret = _keyLua.equal_range({ key, modifier });
        for (auto it = ret.first; it != ret.second; ++it) {
            using RS = scripting::ScriptEngine::RemoteScripting;

            global::scriptEngine.queueScript(
                it->second.command,
                it->second.synchronization ? RS::Yes : RS::No
            );
        }
    }
}

void KeybindingManager::resetKeyBindings() {
    _keyLua.clear();
}

void KeybindingManager::bindKeyLocal(Key key,
                                     KeyModifier modifier,
                                     std::string luaCommand,
                                     std::string documentation,
                                     std::string name,
                                     std::string guiPath)
{
    _keyLua.insert({
        { key, modifier },
        {
            std::move(luaCommand),
            IsSynchronized::No,
            std::move(documentation),
            std::move(name),
            std::move(guiPath)
        }
    });
}

void KeybindingManager::bindKey(Key key,
                                KeyModifier modifier,
                                std::string luaCommand,
                                std::string documentation,
                                std::string name,
                                std::string guiPath)
{
    _keyLua.insert({
        { key, modifier },
        {
            std::move(luaCommand),
            IsSynchronized::Yes,
            std::move(documentation),
            std::move(name),
            std::move(guiPath)
        }
    });
}

void KeybindingManager::removeKeyBinding(const std::string& key) {
    // Erase-remove idiom does not work for std::multimap so we have to do this on foot

    KeyWithModifier k = stringToKey(key);

    for (auto it = _keyLua.begin(); it != _keyLua.end(); ) {
        // If the current iterator is the key that we are looking for, delete it
        // (std::multimap::erase will return the iterator to the next element for us)
        if (it->first == k) {
            it = _keyLua.erase(it);
        }
        else {
            // We if it is not, we continue iteration
            ++it;
        }
    }
}

std::vector<std::pair<KeyWithModifier, KeybindingManager::KeyInformation>>
KeybindingManager::keyBinding(const std::string& key) const
{
    std::vector<std::pair<KeyWithModifier, KeyInformation>> result;

    KeyWithModifier k = stringToKey(key);
    auto itRange = _keyLua.equal_range(k);
    for (auto it = itRange.first; it != itRange.second; ++it) {
        result.emplace_back(it->first, it->second);
    }
    return result;
}

const std::multimap<KeyWithModifier, KeybindingManager::KeyInformation>&
KeybindingManager::keyBindings() const
{
    return _keyLua;
}

std::string KeybindingManager::generateJson() const {
    std::stringstream json;
    json << "[";
    bool first = true;
    for (const std::pair<const KeyWithModifier, KeyInformation>& p : _keyLua) {
        if (!first) {
            json << ",";
        }
        first = false;
        json << "{";
        json << R"("key": ")" << ghoul::to_string(p.first) << "\",";
        json << R"("script": ")" << escapedJson(p.second.command) << "\",";
        json << R"("remoteScripting": )"
             << (p.second.synchronization ? "true," : "false,");
        json << R"("documentation": ")" << escapedJson(p.second.documentation) << "\",";
        json << R"("name": ")" << escapedJson(p.second.name) << "\"";
        json << "}";
    }
    json << "]";

    return json.str();
}

scripting::LuaLibrary KeybindingManager::luaLibrary() {
    return {
        "",
        {
            {
                "clearKeys",
                &luascriptfunctions::clearKeys,
                {},
                "",
                "Clear all key bindings"
            },
            {
                "clearKey",
                &luascriptfunctions::clearKey,
                {},
                "string or strings",
                "Unbinds the key or keys that have been provided. This function can be "
                "called with a single key or with an array of keys to remove all of the "
                "provided keys at once"
            },
            {
                "bindKey",
                &luascriptfunctions::bindKey,
                {},
                "string, string [,string]",
                "Binds a key by name to a lua string command to execute both locally "
                "and to broadcast to clients if this is the host of a parallel session. "
                "The first argument is the key, the second argument is the Lua command "
                "that is to be executed, and the optional third argument is a human "
                "readable description of the command for documentation purposes."
            },
            {
                "bindKeyLocal",
                &luascriptfunctions::bindKeyLocal,
                {},
                "string, string [,string]",
                "Binds a key by name to a lua string command to execute only locally. "
                "The first argument is the key, the second argument is the Lua command "
                "that is to be executed, and the optional third argument is a human "
                "readable description of the command for documentation purposes."
            },
            {
                "getKeyBinding",
                &luascriptfunctions::getKeyBindings,
                {},
                "string",
                "Returns a list of information about the keybindings for the provided "
                "key. Each element in the list is a table describing the 'Command' that "
                "was bound and whether it was a 'Remote' script or not."

            }
        }
    };
}

} // namespace openspace::interaction
<|endoftext|>


<commit_before>#include "Database.h"
#include "Exception.h"
#include "is_identifier.h"

/////////////////////////////////////////////////////////////////////////////
const std::map<Field_Id, std::string> &joedb::Database::get_fields
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id
) const
{
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  throw Exception("get_fields: invalid table_id");
 return table_it->second.field_names;
}

/////////////////////////////////////////////////////////////////////////////
const joedb::Type &joedb::Database::get_field_type
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Field_Id field_id
) const
{
 static Type null_type;
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  return null_type;
 auto &fields = table_it->second.get_fields();
 auto field_it = fields.find(field_id);
 if (field_it == fields.end())
  return null_type;
 return field_it->second.get_type();
}

/////////////////////////////////////////////////////////////////////////////
Record_Id joedb::Database::get_last_record_id(Table_Id table_id) const
/////////////////////////////////////////////////////////////////////////////
{
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  return 0;
 return table_it->second.freedom.size();
}

/////////////////////////////////////////////////////////////////////////////
bool joedb::Database::is_used
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Record_Id record_id
) const
{
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  return false;
 return table_it->second.freedom.is_used(record_id + 1);
}

#define TYPE_MACRO(type, return_type, type_id, R, W)\
return_type joedb::Database::get_##type_id(Table_Id table_id,\
                                           Record_Id record_id,\
                                           Field_Id field_id) const\
{\
 auto table_it = tables.find(table_id);\
 if (table_it == tables.end())\
  throw Exception("get: invalid table_id");\
 return table_it->second.get_##type_id(record_id, field_id);\
}
#include "TYPE_MACRO.h"
#undef TYPE_MACRO

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::create_table(const std::string &name)
/////////////////////////////////////////////////////////////////////////////
{
 if (!is_identifier(name))
  throw Exception("create_table: invalid identifier");
 if (find_table(name))
  throw Exception("create_table: name already used");

 ++current_table_id;
 tables.insert(std::make_pair(current_table_id, Table()));
 table_names[current_table_id] = name;
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::drop_table(Table_Id table_id)
/////////////////////////////////////////////////////////////////////////////
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("drop_table: invalid table_id");
 table_names.erase(table_id);
 tables.erase(it);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::rename_table
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 const std::string &name
)
{
 if (!is_identifier(name))
  throw Exception("rename_table: invalid identifier");

 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  throw Exception("rename_table: invalid table_id");

 if (find_table(name) != 0)
  throw Exception("rename_table: name already used");

 table_names[table_id] = name;
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::add_field
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 const std::string &name,
 Type type
)
{
 if (!is_identifier(name))
  throw Exception("add_field: invalid identifier");

 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("add_field: invalid table_id");

 it->second.add_field(name, type);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::drop_field(Table_Id table_id, Field_Id field_id)
/////////////////////////////////////////////////////////////////////////////
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("drop_field: invalid table_id");

 it->second.drop_field(field_id);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::rename_field
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Field_Id field_id,
 const std::string &name
)
{
 if (!is_identifier(name))
  throw Exception("rename_field: invalid identifier");

 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  throw Exception("rename_field: invalid table_id");

 auto &field_names = table_it->second.field_names;
 auto field_it = field_names.find(field_id);
 if (field_it == field_names.end())
  throw Exception("rename_field: invalid field_id");

 if (table_it->second.find_field(name))
  throw Exception("rename_field: name already used");

 field_it->second = name;
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::insert_into(Table_Id table_id, Record_Id record_id)
/////////////////////////////////////////////////////////////////////////////
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("insert_into: invalid table_id");

 if (record_id <= 0 || (max_record_id && record_id > max_record_id))
  throw Exception("insert_into: too big");

 it->second.insert_record(record_id);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::insert_vector
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Record_Id record_id,
 Record_Id size
)
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("insert_vector: invalid table_id");
 if (record_id <= 0 ||
     size <= 0 ||
     (max_record_id && (record_id > max_record_id || size > max_record_id)))
  throw Exception("insert_vector: too big");

 for (Record_Id i = 0; i < size; i++)
  it->second.insert_record(record_id + i);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::delete_from
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Record_Id record_id
)
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("delete_from: invalid table_id");

 it->second.delete_record(record_id);
}

/////////////////////////////////////////////////////////////////////////////
#define TYPE_MACRO(type, return_type, type_id, R, W)\
void joedb::Database::update_##type_id(Table_Id table_id,\
                                       Record_Id record_id,\
                                       Field_Id field_id,\
                                       return_type value)\
{\
 auto it = tables.find(table_id);\
 if (it == tables.end())\
  throw Exception("update: invalid table_id");\
 it->second.update_##type_id(record_id, field_id, value);\
}\
void joedb::Database::update_vector_##type_id(Table_Id table_id,\
                                              Record_Id record_id,\
                                              Field_Id field_id,\
                                              Record_Id size,\
                                              const type *value)\
{\
 auto it = tables.find(table_id);\
 if (it == tables.end())\
  throw Exception("update_vector: invalid table_id");\
 it->second.update_vector_##type_id(record_id, field_id, size, value);\
}
#include "TYPE_MACRO.h"
#undef TYPE_MACRO
<commit_msg>Inserting vectors of size zero does nothing, but is allowed.<commit_after>#include "Database.h"
#include "Exception.h"
#include "is_identifier.h"

#include <sstream>

/////////////////////////////////////////////////////////////////////////////
const std::map<Field_Id, std::string> &joedb::Database::get_fields
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id
) const
{
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  throw Exception("get_fields: invalid table_id");
 return table_it->second.field_names;
}

/////////////////////////////////////////////////////////////////////////////
const joedb::Type &joedb::Database::get_field_type
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Field_Id field_id
) const
{
 static Type null_type;
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  return null_type;
 auto &fields = table_it->second.get_fields();
 auto field_it = fields.find(field_id);
 if (field_it == fields.end())
  return null_type;
 return field_it->second.get_type();
}

/////////////////////////////////////////////////////////////////////////////
Record_Id joedb::Database::get_last_record_id(Table_Id table_id) const
/////////////////////////////////////////////////////////////////////////////
{
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  return 0;
 return table_it->second.freedom.size();
}

/////////////////////////////////////////////////////////////////////////////
bool joedb::Database::is_used
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Record_Id record_id
) const
{
 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  return false;
 return table_it->second.freedom.is_used(record_id + 1);
}

#define TYPE_MACRO(type, return_type, type_id, R, W)\
return_type joedb::Database::get_##type_id(Table_Id table_id,\
                                           Record_Id record_id,\
                                           Field_Id field_id) const\
{\
 auto table_it = tables.find(table_id);\
 if (table_it == tables.end())\
  throw Exception("get: invalid table_id");\
 return table_it->second.get_##type_id(record_id, field_id);\
}
#include "TYPE_MACRO.h"
#undef TYPE_MACRO

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::create_table(const std::string &name)
/////////////////////////////////////////////////////////////////////////////
{
 if (!is_identifier(name))
  throw Exception("create_table: invalid identifier");
 if (find_table(name))
  throw Exception("create_table: name already used");

 ++current_table_id;
 tables.insert(std::make_pair(current_table_id, Table()));
 table_names[current_table_id] = name;
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::drop_table(Table_Id table_id)
/////////////////////////////////////////////////////////////////////////////
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("drop_table: invalid table_id");
 table_names.erase(table_id);
 tables.erase(it);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::rename_table
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 const std::string &name
)
{
 if (!is_identifier(name))
  throw Exception("rename_table: invalid identifier");

 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  throw Exception("rename_table: invalid table_id");

 if (find_table(name) != 0)
  throw Exception("rename_table: name already used");

 table_names[table_id] = name;
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::add_field
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 const std::string &name,
 Type type
)
{
 if (!is_identifier(name))
  throw Exception("add_field: invalid identifier");

 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("add_field: invalid table_id");

 it->second.add_field(name, type);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::drop_field(Table_Id table_id, Field_Id field_id)
/////////////////////////////////////////////////////////////////////////////
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("drop_field: invalid table_id");

 it->second.drop_field(field_id);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::rename_field
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Field_Id field_id,
 const std::string &name
)
{
 if (!is_identifier(name))
  throw Exception("rename_field: invalid identifier");

 auto table_it = tables.find(table_id);
 if (table_it == tables.end())
  throw Exception("rename_field: invalid table_id");

 auto &field_names = table_it->second.field_names;
 auto field_it = field_names.find(field_id);
 if (field_it == field_names.end())
  throw Exception("rename_field: invalid field_id");

 if (table_it->second.find_field(name))
  throw Exception("rename_field: name already used");

 field_it->second = name;
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::insert_into(Table_Id table_id, Record_Id record_id)
/////////////////////////////////////////////////////////////////////////////
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("insert_into: invalid table_id");

 if (record_id <= 0 || (max_record_id && record_id > max_record_id))
  throw Exception("insert_into: too big");

 it->second.insert_record(record_id);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::insert_vector
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Record_Id record_id,
 Record_Id size
)
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("insert_vector: invalid table_id");
 if (record_id <= 0 ||
     (max_record_id && (record_id > max_record_id || size > max_record_id)))
 {
  std::ostringstream error_message;
  error_message << "insert_vector: ";
  error_message << "record_id = " << record_id << "; ";
  error_message << "size = " << size << "; ";
  error_message << "max = " << max_record_id;
  throw Exception(error_message.str());
 }

 for (Record_Id i = 0; i < size; i++)
  it->second.insert_record(record_id + i);
}

/////////////////////////////////////////////////////////////////////////////
void joedb::Database::delete_from
/////////////////////////////////////////////////////////////////////////////
(
 Table_Id table_id,
 Record_Id record_id
)
{
 auto it = tables.find(table_id);
 if (it == tables.end())
  throw Exception("delete_from: invalid table_id");

 it->second.delete_record(record_id);
}

/////////////////////////////////////////////////////////////////////////////
#define TYPE_MACRO(type, return_type, type_id, R, W)\
void joedb::Database::update_##type_id(Table_Id table_id,\
                                       Record_Id record_id,\
                                       Field_Id field_id,\
                                       return_type value)\
{\
 auto it = tables.find(table_id);\
 if (it == tables.end())\
  throw Exception("update: invalid table_id");\
 it->second.update_##type_id(record_id, field_id, value);\
}\
void joedb::Database::update_vector_##type_id(Table_Id table_id,\
                                              Record_Id record_id,\
                                              Field_Id field_id,\
                                              Record_Id size,\
                                              const type *value)\
{\
 auto it = tables.find(table_id);\
 if (it == tables.end())\
  throw Exception("update_vector: invalid table_id");\
 it->second.update_vector_##type_id(record_id, field_id, size, value);\
}
#include "TYPE_MACRO.h"
#undef TYPE_MACRO
<|endoftext|>