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

// Authors: Jiashun Zhu(zhujiashun@bilibili.com)

#include <vector>
#include <iomanip>
#include <map>
#include "brpc/controller.h"                // Controller
#include "brpc/server.h"                    // Server
#include "brpc/closure_guard.h"             // ClosureGuard
#include "brpc/builtin/prometheus_metrics_service.h"
#include "brpc/builtin/common.h"
#include "bvar/bvar.h"

namespace bvar {
DECLARE_int32(bvar_latency_p1);
DECLARE_int32(bvar_latency_p2);
DECLARE_int32(bvar_latency_p3);
}

namespace brpc {

// This is a class that convert bvar result to prometheus output.
// Currently the output only includes gauge and summary for two
// reasons:
// 1) We cannot tell gauge and counter just from name and what's
// more counter is just another gauge.
// 2) Histogram and summary is equivalent except that histogram
// calculates quantiles in the server side.
class PrometheusMetricsDumper : public bvar::Dumper {
public:
    explicit PrometheusMetricsDumper(butil::IOBufBuilder* os,
                                     const std::string& server_prefix)
        : _os(os)
        , _server_prefix(server_prefix) {
    }

    bool dump(const std::string& name, const butil::StringPiece& desc) override;

private:
    DISALLOW_COPY_AND_ASSIGN(PrometheusMetricsDumper);

    // Return true iff name ends with suffix output by LatencyRecorder.
    bool DumpLatencyRecorderSuffix(const butil::StringPiece& name,
                                   const butil::StringPiece& desc);

    // 6 is the number of bvars in LatencyRecorder that indicating percentiles
    static const int NPERCENTILES = 6;

    struct SummaryItems {
        std::string latency_percentiles[NPERCENTILES];
        std::string latency_avg;
        std::string count;
        std::string metric_name;
    };
    // Return true iff name ends with suffix output by LatencyRecorder.
    // If all bvars in LatencyRecorder have been gathered and are ready
    // to output a summary, *si_out is set properly.
    bool ProcessLatencyRecorderSuffix(const butil::StringPiece& name,
                                      const butil::StringPiece& desc,
                                      SummaryItems** si_out);

private:
    butil::IOBufBuilder* _os;
    const std::string _server_prefix;
    std::map<std::string, SummaryItems> _m;
};

bool PrometheusMetricsDumper::dump(const std::string& name,
                                   const butil::StringPiece& desc) {
    if (!desc.empty() && desc[0] == '"') {
        // there is no necessary to monitor string in prometheus
        return true;
    }
    if (DumpLatencyRecorderSuffix(name, desc)) {
        // Has encountered name with suffix exposed by LatencyRecorder,
        // Leave it to DumpLatencyRecorderSuffix to output Summary.
        return true;
    }
    *_os << "# HELP " << name << '\n'
         << "# TYPE " << name << " gauge" << '\n'
         << name << " " << desc << '\n';
    return true;
}

bool PrometheusMetricsDumper::ProcessLatencyRecorderSuffix(
    const butil::StringPiece& name,
    const butil::StringPiece& desc,
    PrometheusMetricsDumper::SummaryItems** si_out) {
    static std::string latency_names[] = {
        butil::string_printf("_latency_%d", (int)bvar::FLAGS_bvar_latency_p1),
        butil::string_printf("_latency_%d", (int)bvar::FLAGS_bvar_latency_p2),
        butil::string_printf("_latency_%d", (int)bvar::FLAGS_bvar_latency_p3),
        "_latency_999", "_latency_9999", "_max_latency"
    };
    CHECK(NPERCENTILES == arraysize(latency_names));
    butil::StringPiece metric_name(name);
    for (int i = 0; i < NPERCENTILES; ++i) {
        if (!metric_name.ends_with(latency_names[i])) {
            continue;
        }
        metric_name.remove_suffix(latency_names[i].size());
        SummaryItems* si = &_m[metric_name.as_string()];
        si->latency_percentiles[i] = desc.as_string();
        if (i == NPERCENTILES - 1) {
            // 'max_latency' is the last suffix name that appear in the sorted bvar
            // list, which means all related percentiles have been gathered and we are
            // ready to output a Summary.
            si->metric_name = metric_name.as_string();
            *si_out = si;
        }
        return true;
    }
    // Get the average of latency in recent window size
    if (metric_name.ends_with("_latency")) {
        metric_name.remove_suffix(8);
        _m[metric_name.as_string()].latency_avg = desc.as_string();
        return true;
    }
    if (metric_name.ends_with("_count")) {
        metric_name.remove_suffix(6);
        _m[metric_name.as_string()].count = desc.as_string();
        return true;
    }
    return false;
}

bool PrometheusMetricsDumper::DumpLatencyRecorderSuffix(
    const butil::StringPiece& name,
    const butil::StringPiece& desc) {
    if (!name.starts_with(_server_prefix)) {
        return false;
    }
    SummaryItems* si = NULL;
    if (!ProcessLatencyRecorderSuffix(name, desc, &si)) {
        return false;
    }
    if (!si) {
        return true;
    }
    *_os << "# HELP " << si->metric_name << '\n'
         << "# TYPE " << si->metric_name << " summary\n"
         << si->metric_name << "{quantile=\""
         << (double)(bvar::FLAGS_bvar_latency_p1) / 100 << "\"} "
         << si->latency_percentiles[0] << '\n'
         << si->metric_name << "{quantile=\""
         << (double)(bvar::FLAGS_bvar_latency_p2) / 100 << "\"} "
         << si->latency_percentiles[1] << '\n'
         << si->metric_name << "{quantile=\""
         << (double)(bvar::FLAGS_bvar_latency_p3) / 100 << "\"} "
         << si->latency_percentiles[2] << '\n'
         << si->metric_name << "{quantile=\"0.999\"} "
         << si->latency_percentiles[3] << '\n'
         << si->metric_name << "{quantile=\"0.9999\"} "
         << si->latency_percentiles[4] << '\n'
         << si->metric_name << "{quantile=\"1\"} "
         << si->latency_percentiles[5] << '\n'
         << si->metric_name << "_sum "
         // There is no sum of latency in bvar output, just use
         // average * count as approximation
         << strtoll(si->latency_avg.data(), NULL, 10) *
                strtoll(si->count.data(), NULL, 10) << '\n'
         << si->metric_name << "_count " << si->count << '\n';
    return true;
}

void PrometheusMetricsService::default_method(::google::protobuf::RpcController* cntl_base,
                                              const ::brpc::MetricsRequest*,
                                              ::brpc::MetricsResponse*,
                                              ::google::protobuf::Closure* done) {
    ClosureGuard done_guard(done);
    Controller *cntl = static_cast<Controller*>(cntl_base);
    cntl->http_response().set_content_type("text/plain");
    butil::IOBufBuilder os;
    PrometheusMetricsDumper dumper(&os, _server->ServerPrefix());
    const int ndump = bvar::Variable::dump_exposed(&dumper, NULL);
    if (ndump < 0) {
        cntl->SetFailed("Fail to dump metrics");
        return;
    }
    os.move_to(cntl->response_attachment());
}

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

// Authors: Jiashun Zhu(zhujiashun@bilibili.com)

#include <vector>
#include <iomanip>
#include <map>
#include "brpc/controller.h"                // Controller
#include "brpc/server.h"                    // Server
#include "brpc/closure_guard.h"             // ClosureGuard
#include "brpc/builtin/prometheus_metrics_service.h"
#include "brpc/builtin/common.h"
#include "bvar/bvar.h"

namespace bvar {
DECLARE_int32(bvar_latency_p1);
DECLARE_int32(bvar_latency_p2);
DECLARE_int32(bvar_latency_p3);
}

namespace brpc {

// This is a class that convert bvar result to prometheus output.
// Currently the output only includes gauge and summary for two
// reasons:
// 1) We cannot tell gauge and counter just from name and what's
// more counter is just another gauge.
// 2) Histogram and summary is equivalent except that histogram
// calculates quantiles in the server side.
class PrometheusMetricsDumper : public bvar::Dumper {
public:
    explicit PrometheusMetricsDumper(butil::IOBufBuilder* os,
                                     const std::string& server_prefix)
        : _os(os)
        , _server_prefix(server_prefix) {
    }

    bool dump(const std::string& name, const butil::StringPiece& desc) override;

private:
    DISALLOW_COPY_AND_ASSIGN(PrometheusMetricsDumper);

    // Return true iff name ends with suffix output by LatencyRecorder.
    bool DumpLatencyRecorderSuffix(const butil::StringPiece& name,
                                   const butil::StringPiece& desc);

    // 6 is the number of bvars in LatencyRecorder that indicating percentiles
    static const int NPERCENTILES = 6;

    struct SummaryItems {
        std::string latency_percentiles[NPERCENTILES];
        std::string latency_avg;
        std::string count;
        std::string metric_name;

        bool IsComplete() const { return !metric_name.empty(); }
    };
    const SummaryItems* ProcessLatencyRecorderSuffix(const butil::StringPiece& name,
                                                     const butil::StringPiece& desc);

private:
    butil::IOBufBuilder* _os;
    const std::string _server_prefix;
    std::map<std::string, SummaryItems> _m;
};

bool PrometheusMetricsDumper::dump(const std::string& name,
                                   const butil::StringPiece& desc) {
    if (!desc.empty() && desc[0] == '"') {
        // there is no necessary to monitor string in prometheus
        return true;
    }
    if (DumpLatencyRecorderSuffix(name, desc)) {
        // Has encountered name with suffix exposed by LatencyRecorder,
        // Leave it to DumpLatencyRecorderSuffix to output Summary.
        return true;
    }
    *_os << "# HELP " << name << '\n'
         << "# TYPE " << name << " gauge" << '\n'
         << name << " " << desc << '\n';
    return true;
}

const PrometheusMetricsDumper::SummaryItems*
PrometheusMetricsDumper::ProcessLatencyRecorderSuffix(const butil::StringPiece& name,
                                                      const butil::StringPiece& desc) {
    static std::string latency_names[] = {
        butil::string_printf("_latency_%d", (int)bvar::FLAGS_bvar_latency_p1),
        butil::string_printf("_latency_%d", (int)bvar::FLAGS_bvar_latency_p2),
        butil::string_printf("_latency_%d", (int)bvar::FLAGS_bvar_latency_p3),
        "_latency_999", "_latency_9999", "_max_latency"
    };
    CHECK(NPERCENTILES == arraysize(latency_names));
    butil::StringPiece metric_name(name);
    for (int i = 0; i < NPERCENTILES; ++i) {
        if (!metric_name.ends_with(latency_names[i])) {
            continue;
        }
        metric_name.remove_suffix(latency_names[i].size());
        SummaryItems* si = &_m[metric_name.as_string()];
        si->latency_percentiles[i] = desc.as_string();
        if (i == NPERCENTILES - 1) {
            // '_max_latency' is the last suffix name that appear in the sorted bvar
            // list, which means all related percentiles have been gathered and we are
            // ready to output a Summary.
            si->metric_name = metric_name.as_string();
        }
        return si;
    }
    // Get the average of latency in recent window size
    if (metric_name.ends_with("_latency")) {
        metric_name.remove_suffix(8);
        SummaryItems* si = &_m[metric_name.as_string()];
        si->latency_avg = desc.as_string();
        return si;
    }
    if (metric_name.ends_with("_count")) {
        metric_name.remove_suffix(6);
        SummaryItems* si = &_m[metric_name.as_string()];
        si->count = desc.as_string();
        return si;
    }
    return NULL;
}

bool PrometheusMetricsDumper::DumpLatencyRecorderSuffix(
    const butil::StringPiece& name,
    const butil::StringPiece& desc) {
    if (!name.starts_with(_server_prefix)) {
        return false;
    }
    const SummaryItems* si = ProcessLatencyRecorderSuffix(name, desc);
    if (!si) {
        return false;
    }
    if (!si->IsComplete()) {
        return true;
    }
    *_os << "# HELP " << si->metric_name << '\n'
         << "# TYPE " << si->metric_name << " summary\n"
         << si->metric_name << "{quantile=\""
         << (double)(bvar::FLAGS_bvar_latency_p1) / 100 << "\"} "
         << si->latency_percentiles[0] << '\n'
         << si->metric_name << "{quantile=\""
         << (double)(bvar::FLAGS_bvar_latency_p2) / 100 << "\"} "
         << si->latency_percentiles[1] << '\n'
         << si->metric_name << "{quantile=\""
         << (double)(bvar::FLAGS_bvar_latency_p3) / 100 << "\"} "
         << si->latency_percentiles[2] << '\n'
         << si->metric_name << "{quantile=\"0.999\"} "
         << si->latency_percentiles[3] << '\n'
         << si->metric_name << "{quantile=\"0.9999\"} "
         << si->latency_percentiles[4] << '\n'
         << si->metric_name << "{quantile=\"1\"} "
         << si->latency_percentiles[5] << '\n'
         << si->metric_name << "_sum "
         // There is no sum of latency in bvar output, just use
         // average * count as approximation
         << strtoll(si->latency_avg.data(), NULL, 10) *
                strtoll(si->count.data(), NULL, 10) << '\n'
         << si->metric_name << "_count " << si->count << '\n';
    return true;
}

void PrometheusMetricsService::default_method(::google::protobuf::RpcController* cntl_base,
                                              const ::brpc::MetricsRequest*,
                                              ::brpc::MetricsResponse*,
                                              ::google::protobuf::Closure* done) {
    ClosureGuard done_guard(done);
    Controller *cntl = static_cast<Controller*>(cntl_base);
    cntl->http_response().set_content_type("text/plain");
    butil::IOBufBuilder os;
    PrometheusMetricsDumper dumper(&os, _server->ServerPrefix());
    const int ndump = bvar::Variable::dump_exposed(&dumper, NULL);
    if (ndump < 0) {
        cntl->SetFailed("Fail to dump metrics");
        return;
    }
    os.move_to(cntl->response_attachment());
}

} // namespace brpc
<|endoftext|>


<commit_before>// Copyright 2014 BVLC and contributors.

#include <cuda_runtime.h>
#include <cmath>
#include <cstring>

#include "gtest/gtest.h"
#include "caffe/common.hpp"
#include "caffe/syncedmem.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/test/test_caffe_main.hpp"

namespace caffe {

template <typename Dtype>
class RandomNumberGeneratorTest : public ::testing::Test {
 public:
  virtual ~RandomNumberGeneratorTest() {}

  Dtype sample_mean(const Dtype* const seqs, const size_t sample_size) {
      double sum = 0;
      for (int i = 0; i < sample_size; ++i) {
          sum += seqs[i];
      }
      return sum / sample_size;
  }

  Dtype sample_mean(const int* const seqs, const size_t sample_size) {
      Dtype sum = 0;
      for (int i = 0; i < sample_size; ++i) {
          sum += Dtype(seqs[i]);
      }
      return sum / sample_size;
  }

  Dtype mean_bound(const Dtype std, const size_t sample_size) {
      return  std/sqrt(static_cast<double>(sample_size));
  }
};

typedef ::testing::Types<float, double> Dtypes;
TYPED_TEST_CASE(RandomNumberGeneratorTest, Dtypes);

TYPED_TEST(RandomNumberGeneratorTest, TestRngGaussian) {
  size_t sample_size = 10000;
  SyncedMemory data_a(sample_size * sizeof(TypeParam));
  Caffe::set_random_seed(1701);
  TypeParam mu = 0;
  TypeParam sigma = 1;
  caffe_vRngGaussian(sample_size,
      reinterpret_cast<TypeParam*>(data_a.mutable_cpu_data()), mu, sigma);
  TypeParam true_mean = mu;
  TypeParam true_std = sigma;
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean =
      this->sample_mean(reinterpret_cast<const TypeParam*>(data_a.cpu_data()),
          sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngUniform) {
  size_t sample_size = 10000;
  SyncedMemory data_a(sample_size * sizeof(TypeParam));
  Caffe::set_random_seed(1701);
  TypeParam lower = 0;
  TypeParam upper = 1;
  caffe_vRngUniform(sample_size,
      reinterpret_cast<TypeParam*>(data_a.mutable_cpu_data()), lower, upper);
  TypeParam true_mean = (lower + upper) / 2;
  TypeParam true_std = (upper - lower) / sqrt(12);
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean =
      this->sample_mean(reinterpret_cast<const TypeParam*>(data_a.cpu_data()),
          sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngBernoulli) {
  size_t sample_size = 10000;
  SyncedMemory data_a(sample_size * sizeof(int));
  Caffe::set_random_seed(1701);
  double p = 0.3;
  caffe_vRngBernoulli(sample_size,
      static_cast<int*>(data_a.mutable_cpu_data()), p);
  TypeParam true_mean = p;
  TypeParam true_std = sqrt(p * (1 - p));
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean =
      this->sample_mean((const int *)data_a.cpu_data(), sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngGaussianTimesBernoulli) {
  size_t sample_size = 10000;
  SyncedMemory gaussian_data(sample_size * sizeof(TypeParam));
  SyncedMemory bernoulli_data(sample_size * sizeof(int));
  Caffe::set_random_seed(1701);
  // Sample from 0 mean Gaussian
  TypeParam mu = 0;
  TypeParam sigma = 1;
  caffe_vRngGaussian(sample_size, reinterpret_cast<TypeParam*>(
      gaussian_data.mutable_cpu_data()), mu, sigma);
  TypeParam true_mean = mu;
  TypeParam true_std = sigma;
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean = this->sample_mean(
      reinterpret_cast<const TypeParam*>(gaussian_data.cpu_data()),
      sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int num_pos = 0;
  int num_neg = 0;
  int num_zeros = 0;
  TypeParam* samples =
      static_cast<TypeParam*>(gaussian_data.mutable_cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have no zeros (possible to generate 0s, but highly
  // improbable), and roughly half positives and half negatives (with bound
  // computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(0, num_zeros);
  double p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  TypeParam expected_num_each_sign = sample_size * p;
  LOG(INFO) << "Gaussian: Expected " << expected_num_each_sign << " positives"
            << "; got " << num_pos;
  LOG(INFO) << "Gaussian: Expected " << expected_num_each_sign << " negatives"
            << "; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
  // Sample from Bernoulli with p = 0.3
  p = 0.3;
  caffe_vRngBernoulli(sample_size,
      reinterpret_cast<int*>(bernoulli_data.mutable_cpu_data()), p);
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  empirical_mean =
      this->sample_mean((const int *)bernoulli_data.cpu_data(), sample_size);
  LOG(INFO) << "Bernoulli: Expected mean = " << true_mean
            << "; sample mean = " << empirical_mean;
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int bernoulli_num_zeros = 0;
  int num_ones = 0;
  int num_other = 0;
  const int* bernoulli_samples =
      reinterpret_cast<const int*>(bernoulli_data.cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (bernoulli_samples[i] == 0) {
      ++bernoulli_num_zeros;
    } else if (bernoulli_samples[i] == 1) {
      ++num_ones;
    } else {
      ++num_other;
    }
  }
  LOG(INFO) << "Bernoulli: zeros: "  << bernoulli_num_zeros
            << "; ones: " << num_ones << "; other: " << num_other;
  EXPECT_EQ(0, num_other);
  EXPECT_EQ(sample_size * empirical_mean, num_ones);
  EXPECT_EQ(sample_size * (1.0 - empirical_mean), bernoulli_num_zeros);
  // Multiply Gaussian by Bernoulli
  for (int i = 0; i < sample_size; ++i) {
    samples[i] *= bernoulli_samples[i];
  }
  num_pos = 0;
  num_neg = 0;
  num_zeros = 0;
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have as many zeros as Bernoulli, and roughly half positives
  // and half negatives (with bound computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(bernoulli_num_zeros, num_zeros);
  p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  int sub_sample_size = sample_size - bernoulli_num_zeros;
  bound = this->mean_bound(true_std, sub_sample_size);
  expected_num_each_sign = sub_sample_size * p;
  LOG(INFO) << "Gaussian: Expected " << expected_num_each_sign << " positives"
            << "; got " << num_pos;
  LOG(INFO) << "Gaussian: Expected " << expected_num_each_sign << " negatives"
            << "; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngUniformTimesBernoulli) {
  size_t sample_size = 10000;
  SyncedMemory uniform_data(sample_size * sizeof(TypeParam));
  SyncedMemory bernoulli_data(sample_size * sizeof(int));
  Caffe::set_random_seed(1701);
  // Sample from Uniform on [-1, 1]
  TypeParam a = -1;
  TypeParam b = 1;
  caffe_vRngUniform(sample_size, reinterpret_cast<TypeParam*>(
      uniform_data.mutable_cpu_data()), a, b);
  TypeParam true_mean = (a + b) / 2;
  TypeParam true_std = (b - a) / sqrt(12);
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean = this->sample_mean(
      reinterpret_cast<const TypeParam*>(uniform_data.cpu_data()),
      sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int num_pos = 0;
  int num_neg = 0;
  int num_zeros = 0;
  TypeParam* samples =
      static_cast<TypeParam*>(uniform_data.mutable_cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have no zeros (possible to generate 0s, but highly
  // improbable), and roughly half positives and half negatives (with bound
  // computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(0, num_zeros);
  double p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  TypeParam expected_num_each_sign = sample_size * p;
  LOG(INFO) << "Uniform: Expected " << expected_num_each_sign << " positives"
            << "; got " << num_pos;
  LOG(INFO) << "Uniform: Expected " << expected_num_each_sign << " negatives"
            << "; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
  // Sample from Bernoulli with p = 0.3
  p = 0.3;
  caffe_vRngBernoulli(sample_size,
      reinterpret_cast<int*>(bernoulli_data.mutable_cpu_data()), p);
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  empirical_mean =
      this->sample_mean((const int *)bernoulli_data.cpu_data(), sample_size);
  LOG(INFO) << "Bernoulli: Expected mean = " << true_mean
            << "; sample mean = " << empirical_mean;
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int bernoulli_num_zeros = 0;
  int num_ones = 0;
  int num_other = 0;
  const int* bernoulli_samples =
      reinterpret_cast<const int*>(bernoulli_data.cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (bernoulli_samples[i] == 0) {
      ++bernoulli_num_zeros;
    } else if (bernoulli_samples[i] == 1) {
      ++num_ones;
    } else {
      ++num_other;
    }
  }
  LOG(INFO) << "Bernoulli: zeros: "  << bernoulli_num_zeros
            << "; ones: " << num_ones << "; other: " << num_other;
  EXPECT_EQ(0, num_other);
  EXPECT_EQ(sample_size * empirical_mean, num_ones);
  EXPECT_EQ(sample_size * (1.0 - empirical_mean), bernoulli_num_zeros);
  // Multiply Uniform by Bernoulli
  for (int i = 0; i < sample_size; ++i) {
    samples[i] *= bernoulli_samples[i];
  }
  num_pos = 0;
  num_neg = 0;
  num_zeros = 0;
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have as many zeros as Bernoulli, and roughly half positives
  // and half negatives (with bound computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(bernoulli_num_zeros, num_zeros);
  p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  int sub_sample_size = sample_size - bernoulli_num_zeros;
  bound = this->mean_bound(true_std, sub_sample_size);
  expected_num_each_sign = sub_sample_size * p;
  LOG(INFO) << "Uniform: Expected " << expected_num_each_sign << " positives"
            << "; got " << num_pos;
  LOG(INFO) << "Uniform: Expected " << expected_num_each_sign << " negatives"
            << "; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
}

}  // namespace caffe
<commit_msg>cleanup log messages<commit_after>// Copyright 2014 BVLC and contributors.

#include <cuda_runtime.h>
#include <cmath>
#include <cstring>

#include "gtest/gtest.h"
#include "caffe/common.hpp"
#include "caffe/syncedmem.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/test/test_caffe_main.hpp"

namespace caffe {

template <typename Dtype>
class RandomNumberGeneratorTest : public ::testing::Test {
 public:
  virtual ~RandomNumberGeneratorTest() {}

  Dtype sample_mean(const Dtype* const seqs, const size_t sample_size) {
      double sum = 0;
      for (int i = 0; i < sample_size; ++i) {
          sum += seqs[i];
      }
      return sum / sample_size;
  }

  Dtype sample_mean(const int* const seqs, const size_t sample_size) {
      Dtype sum = 0;
      for (int i = 0; i < sample_size; ++i) {
          sum += Dtype(seqs[i]);
      }
      return sum / sample_size;
  }

  Dtype mean_bound(const Dtype std, const size_t sample_size) {
      return  std/sqrt(static_cast<double>(sample_size));
  }
};

typedef ::testing::Types<float, double> Dtypes;
TYPED_TEST_CASE(RandomNumberGeneratorTest, Dtypes);

TYPED_TEST(RandomNumberGeneratorTest, TestRngGaussian) {
  size_t sample_size = 10000;
  SyncedMemory data_a(sample_size * sizeof(TypeParam));
  Caffe::set_random_seed(1701);
  TypeParam mu = 0;
  TypeParam sigma = 1;
  caffe_vRngGaussian(sample_size,
      reinterpret_cast<TypeParam*>(data_a.mutable_cpu_data()), mu, sigma);
  TypeParam true_mean = mu;
  TypeParam true_std = sigma;
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean =
      this->sample_mean(reinterpret_cast<const TypeParam*>(data_a.cpu_data()),
          sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngUniform) {
  size_t sample_size = 10000;
  SyncedMemory data_a(sample_size * sizeof(TypeParam));
  Caffe::set_random_seed(1701);
  TypeParam lower = 0;
  TypeParam upper = 1;
  caffe_vRngUniform(sample_size,
      reinterpret_cast<TypeParam*>(data_a.mutable_cpu_data()), lower, upper);
  TypeParam true_mean = (lower + upper) / 2;
  TypeParam true_std = (upper - lower) / sqrt(12);
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean =
      this->sample_mean(reinterpret_cast<const TypeParam*>(data_a.cpu_data()),
          sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngBernoulli) {
  size_t sample_size = 10000;
  SyncedMemory data_a(sample_size * sizeof(int));
  Caffe::set_random_seed(1701);
  double p = 0.3;
  caffe_vRngBernoulli(sample_size,
      static_cast<int*>(data_a.mutable_cpu_data()), p);
  TypeParam true_mean = p;
  TypeParam true_std = sqrt(p * (1 - p));
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean =
      this->sample_mean((const int *)data_a.cpu_data(), sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngGaussianTimesBernoulli) {
  size_t sample_size = 10000;
  SyncedMemory gaussian_data(sample_size * sizeof(TypeParam));
  SyncedMemory bernoulli_data(sample_size * sizeof(int));
  Caffe::set_random_seed(1701);
  // Sample from 0 mean Gaussian
  TypeParam mu = 0;
  TypeParam sigma = 1;
  caffe_vRngGaussian(sample_size, reinterpret_cast<TypeParam*>(
      gaussian_data.mutable_cpu_data()), mu, sigma);
  TypeParam true_mean = mu;
  TypeParam true_std = sigma;
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean = this->sample_mean(
      reinterpret_cast<const TypeParam*>(gaussian_data.cpu_data()),
      sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int num_pos = 0;
  int num_neg = 0;
  int num_zeros = 0;
  TypeParam* samples =
      static_cast<TypeParam*>(gaussian_data.mutable_cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have no zeros (possible to generate 0s, but highly
  // improbable), and roughly half positives and half negatives (with bound
  // computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(0, num_zeros);
  double p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  TypeParam expected_num_each_sign = sample_size * p;
  LOG(INFO) << "Gaussian: Expected " << expected_num_each_sign << " positives"
            << "; got " << num_pos;
  LOG(INFO) << "Gaussian: Expected " << expected_num_each_sign << " negatives"
            << "; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
  // Sample from Bernoulli with p = 0.3
  p = 0.3;
  caffe_vRngBernoulli(sample_size,
      reinterpret_cast<int*>(bernoulli_data.mutable_cpu_data()), p);
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  empirical_mean =
      this->sample_mean((const int *)bernoulli_data.cpu_data(), sample_size);
  LOG(INFO) << "Bernoulli: Expected mean = " << true_mean
            << "; sample mean = " << empirical_mean;
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int bernoulli_num_zeros = 0;
  int num_ones = 0;
  int num_other = 0;
  const int* bernoulli_samples =
      reinterpret_cast<const int*>(bernoulli_data.cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (bernoulli_samples[i] == 0) {
      ++bernoulli_num_zeros;
    } else if (bernoulli_samples[i] == 1) {
      ++num_ones;
    } else {
      ++num_other;
    }
  }
  LOG(INFO) << "Bernoulli: zeros: "  << bernoulli_num_zeros
            << "; ones: " << num_ones << "; other: " << num_other;
  EXPECT_EQ(0, num_other);
  EXPECT_EQ(sample_size * empirical_mean, num_ones);
  EXPECT_EQ(sample_size * (1.0 - empirical_mean), bernoulli_num_zeros);
  // Multiply Gaussian by Bernoulli
  for (int i = 0; i < sample_size; ++i) {
    samples[i] *= bernoulli_samples[i];
  }
  num_pos = 0;
  num_neg = 0;
  num_zeros = 0;
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have as many zeros as Bernoulli, and roughly half positives
  // and half negatives (with bound computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(bernoulli_num_zeros, num_zeros);
  p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  int sub_sample_size = sample_size - bernoulli_num_zeros;
  bound = this->mean_bound(true_std, sub_sample_size);
  expected_num_each_sign = sub_sample_size * p;
  LOG(INFO) << "Gaussian*Bernoulli: Expected " << expected_num_each_sign
            << " positives; got " << num_pos;
  LOG(INFO) << "Gaussian*Bernoulli: Expected " << expected_num_each_sign
            << " negatives; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
}

TYPED_TEST(RandomNumberGeneratorTest, TestRngUniformTimesBernoulli) {
  size_t sample_size = 10000;
  SyncedMemory uniform_data(sample_size * sizeof(TypeParam));
  SyncedMemory bernoulli_data(sample_size * sizeof(int));
  Caffe::set_random_seed(1701);
  // Sample from Uniform on [-1, 1]
  TypeParam a = -1;
  TypeParam b = 1;
  caffe_vRngUniform(sample_size, reinterpret_cast<TypeParam*>(
      uniform_data.mutable_cpu_data()), a, b);
  TypeParam true_mean = (a + b) / 2;
  TypeParam true_std = (b - a) / sqrt(12);
  TypeParam bound = this->mean_bound(true_std, sample_size);
  TypeParam empirical_mean = this->sample_mean(
      reinterpret_cast<const TypeParam*>(uniform_data.cpu_data()),
      sample_size);
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int num_pos = 0;
  int num_neg = 0;
  int num_zeros = 0;
  TypeParam* samples =
      static_cast<TypeParam*>(uniform_data.mutable_cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have no zeros (possible to generate 0s, but highly
  // improbable), and roughly half positives and half negatives (with bound
  // computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(0, num_zeros);
  double p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  TypeParam expected_num_each_sign = sample_size * p;
  LOG(INFO) << "Uniform: Expected " << expected_num_each_sign << " positives"
            << "; got " << num_pos;
  LOG(INFO) << "Uniform: Expected " << expected_num_each_sign << " negatives"
            << "; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
  // Sample from Bernoulli with p = 0.3
  p = 0.3;
  caffe_vRngBernoulli(sample_size,
      reinterpret_cast<int*>(bernoulli_data.mutable_cpu_data()), p);
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  bound = this->mean_bound(true_std, sample_size);
  empirical_mean =
      this->sample_mean((const int *)bernoulli_data.cpu_data(), sample_size);
  LOG(INFO) << "Bernoulli: Expected mean = " << true_mean
            << "; sample mean = " << empirical_mean;
  EXPECT_NEAR(empirical_mean, true_mean, bound);
  int bernoulli_num_zeros = 0;
  int num_ones = 0;
  int num_other = 0;
  const int* bernoulli_samples =
      reinterpret_cast<const int*>(bernoulli_data.cpu_data());
  for (int i = 0; i < sample_size; ++i) {
    if (bernoulli_samples[i] == 0) {
      ++bernoulli_num_zeros;
    } else if (bernoulli_samples[i] == 1) {
      ++num_ones;
    } else {
      ++num_other;
    }
  }
  LOG(INFO) << "Bernoulli: zeros: "  << bernoulli_num_zeros
            << "; ones: " << num_ones << "; other: " << num_other;
  EXPECT_EQ(0, num_other);
  EXPECT_EQ(sample_size * empirical_mean, num_ones);
  EXPECT_EQ(sample_size * (1.0 - empirical_mean), bernoulli_num_zeros);
  // Multiply Uniform by Bernoulli
  for (int i = 0; i < sample_size; ++i) {
    samples[i] *= bernoulli_samples[i];
  }
  num_pos = 0;
  num_neg = 0;
  num_zeros = 0;
  for (int i = 0; i < sample_size; ++i) {
    if (samples[i] == TypeParam(0)) {
      ++num_zeros;
    } else if (samples[i] > TypeParam(0)) {
      ++num_pos;
    } else if (samples[i] < TypeParam(0)) {
      ++num_neg;
    }
  }
  // Check that we have as many zeros as Bernoulli, and roughly half positives
  // and half negatives (with bound computed from a Bernoulli with p = 0.5).
  EXPECT_EQ(bernoulli_num_zeros, num_zeros);
  p = 0.5;
  true_mean = p;
  true_std = sqrt(p * (1 - p));
  int sub_sample_size = sample_size - bernoulli_num_zeros;
  bound = this->mean_bound(true_std, sub_sample_size);
  expected_num_each_sign = sub_sample_size * p;
  LOG(INFO) << "Uniform*Bernoulli: Expected " << expected_num_each_sign
            << " positives; got " << num_pos;
  LOG(INFO) << "Uniform*Bernoulli: Expected " << expected_num_each_sign
            << " negatives; got " << num_neg;
  EXPECT_NEAR(expected_num_each_sign, num_pos, sample_size * bound);
  EXPECT_NEAR(expected_num_each_sign, num_neg, sample_size * bound);
}

}  // namespace caffe
<|endoftext|>


<commit_before>#include "./chunk.h"

namespace mcmap {

Chunk::Chunk() : data_version(-1) {}

Chunk::Chunk(const nbt::NBT &data, const Colors::Palette &palette) : Chunk() {
  // If there is nothing to render
  if (data.is_end() || !assert_chunk(data))
    return;

  // This value is primordial: it states which version of minecraft the chunk
  // was created under, and we use it to know which interpreter to use later
  // in the sections
  data_version = data["DataVersion"].get<int>();

  for (const auto &raw_section : data["Level"]["Sections"]) {
    Section section(raw_section, data_version);
    section.loadPalette(palette);
    sections.push_back(std::move(section));
  }
}

Chunk::Chunk(Chunk &&other) { *this = std::move(other); }

Chunk &Chunk::operator=(Chunk &&other) {
  data_version = other.data_version;
  sections = std::move(other.sections);

  return *this;
}

bool Chunk::assert_chunk(const nbt::NBT &chunk) {
  if (chunk.is_end()                          // Catch uninitialized chunks
      || !chunk.contains("DataVersion")       // Dataversion is required
      || !chunk.contains("Level")             // Level data is required
      || !chunk["Level"].contains("Sections") // No sections mean no blocks
      || !chunk["Level"].contains("Status"))  // Ensure the status is `full`
    return false;

  long version = chunk["DataVersion"].get<long>();

  if (version > 1628) {
    return chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() == "full";
  }

  return chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() ==
         "postprocessed";
}

} // namespace mcmap

mcmap::Chunk::coordinates left_in(Map::Orientation o) {
  switch (o) {
  case Map::NW:
    return {0, 1};
  case Map::SW:
    return {1, 0};
  case Map::SE:
    return {0, -1};
  case Map::NE:
    return {-1, 0};
  }

  return {0, 0};
}

mcmap::Chunk::coordinates right_in(Map::Orientation o) {
  switch (o) {
  case Map::NW:
    return {1, 0};
  case Map::SW:
    return {0, -1};
  case Map::SE:
    return {-1, 0};
  case Map::NE:
    return {0, 1};
  }

  return {0, 0};
}
<commit_msg>Support for 21w43<commit_after>#include "./chunk.h"

namespace mcmap {

namespace versions {
namespace assert {
bool v2844(const nbt::NBT &chunk) {
  // Snapshot 21w43a
  return chunk.contains("sections")  // No sections mean no blocks
         && chunk.contains("Status") // Ensure the status is `full`
         && chunk["Status"].get<nbt::NBT::tag_string_t>() == "full";
}

bool v2840(const nbt::NBT &chunk) {
  // Snapshot 21w42a
  return chunk.contains("Level") &&           // Level data is required
         chunk["Level"].contains("Sections")  // No sections mean no blocks
         && chunk["Level"].contains("Status") // Ensure the status is `full`
         && chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() == "full";
}

bool vbetween(const nbt::NBT &chunk) {
  return chunk.contains("Level")                // Level data is required
         && chunk["Level"].contains("Sections") // No sections mean no blocks
         && chunk["Level"].contains("Status")   // Ensure the status is `full`
         && chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() == "full";
}

bool v1628(const nbt::NBT &chunk) {
  // 1.13
  return chunk.contains("Level")                // Level data is required
         && chunk["Level"].contains("Sections") // No sections mean no blocks
         && chunk["Level"].contains("Status")   // Ensure the status is `full`
         && chunk["Level"]["Status"].get<nbt::NBT::tag_string_t>() ==
                "postprocessed";
}
} // namespace assert

namespace sections {
nbt::NBT v2844(const nbt::NBT &chunk) { return chunk["sections"]; }
nbt::NBT v1628(const nbt::NBT &chunk) { return chunk["Level"]["Sections"]; }
} // namespace sections
} // namespace versions

Chunk::Chunk() : data_version(-1) {}

Chunk::Chunk(const nbt::NBT &data, const Colors::Palette &palette) : Chunk() {
  // If there is nothing to render
  if (data.is_end() || !assert_chunk(data))
    return;

  // This value is primordial: it states which version of minecraft the chunk
  // was created under, and we use it to know which interpreter to use later
  // in the sections
  data_version = data["DataVersion"].get<int>();

  nbt::NBT sections_list;

  if (data_version >= 2844) {
    sections_list = versions::sections::v2844(data);
  } else {
    sections_list = versions::sections::v1628(data);
  }

  for (const auto &raw_section : sections_list) {
    Section section(raw_section, data_version);
    section.loadPalette(palette);
    sections.push_back(std::move(section));
  }
}

Chunk::Chunk(Chunk &&other) { *this = std::move(other); }

Chunk &Chunk::operator=(Chunk &&other) {
  data_version = other.data_version;
  sections = std::move(other.sections);

  return *this;
}

bool Chunk::assert_chunk(const nbt::NBT &chunk) {
  if (chunk.is_end()                     // Catch uninitialized chunks
      || !chunk.contains("DataVersion")) // Dataversion is required
    return false;

  const int version = chunk["DataVersion"].get<int>();

  if (version >= 2844) {
    return versions::assert::v2844(chunk);
  } else if (version >= 2840) {
    return versions::assert::v2840(chunk);
  } else if (version > 1628) {
    return versions::assert::vbetween(chunk);
  } else {
    return versions::assert::v1628(chunk);
  }
}

} // namespace mcmap

mcmap::Chunk::coordinates left_in(Map::Orientation o) {
  switch (o) {
  case Map::NW:
    return {0, 1};
  case Map::SW:
    return {1, 0};
  case Map::SE:
    return {0, -1};
  case Map::NE:
    return {-1, 0};
  }

  return {0, 0};
}

mcmap::Chunk::coordinates right_in(Map::Orientation o) {
  switch (o) {
  case Map::NW:
    return {1, 0};
  case Map::SW:
    return {0, -1};
  case Map::SE:
    return {-1, 0};
  case Map::NE:
    return {0, 1};
  }

  return {0, 0};
}
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS dba30c (1.12.10); FILE MERGED 2008/05/08 07:15:27 oj 1.12.10.2: #i87131# collect keys only once, getKeys always refetch keys 2008/05/05 10:57:50 oj 1.12.10.1: #i87131# collect keys only once, getKeys always refetch the keys<commit_after><|endoftext|>


<commit_before>/**
 * @file posix/net.cpp
 * @brief POSIX network access layer (using cURL)
 *
 * (c) 2013-2014 by Mega Limited, Wellsford, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#include "mega.h"

namespace mega {
CurlHttpIO::CurlHttpIO()
{
    curl_global_init(CURL_GLOBAL_DEFAULT);

    curlm = curl_multi_init();

    curlsh = curl_share_init();
    curl_share_setopt(curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    curl_share_setopt(curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);

    contenttypejson = curl_slist_append(NULL, "Content-Type: application/json");
    contenttypejson = curl_slist_append(contenttypejson, "Expect:");

    contenttypebinary = curl_slist_append(NULL, "Content-Type: application/octet-stream");
    contenttypebinary = curl_slist_append(contenttypebinary, "Expect:");
}

CurlHttpIO::~CurlHttpIO()
{
    curl_global_cleanup();
}

void CurlHttpIO::setuseragent(string* u)
{
    useragent = u;
}

// wake up from cURL I/O
void CurlHttpIO::addevents(Waiter* w, int flags)
{
    int t;
    PosixWaiter* pw = (PosixWaiter*)w;

    curl_multi_fdset(curlm, &pw->rfds, &pw->wfds, &pw->efds, &t);

    pw->bumpmaxfd(t);
}

// POST request to URL
void CurlHttpIO::post(HttpReq* req, const char* data, unsigned len)
{
    if (debug)
    {
        cout << "POST target URL: " << req->posturl << endl;

        if (req->binary)
        {
            cout << "[sending " << req->out->size() << " bytes of raw data]" << endl;
        }
        else
        {
            cout << "Sending: " << *req->out << endl;
        }
    }

    CURL* curl;

    req->in.clear();

    if ((curl = curl_easy_init()))
    {
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1);
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 10);
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 5);
        curl_easy_setopt(curl, CURLOPT_URL, req->posturl.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data ? data : req->out->data());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data ? len : req->out->size());
        curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent->c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req->type == REQ_JSON ? contenttypejson : contenttypebinary);
        curl_easy_setopt(curl, CURLOPT_ENCODING, "");
        curl_easy_setopt(curl, CURLOPT_SHARE, curlsh);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)req);
        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, check_header);
        curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)req);
        curl_easy_setopt(curl, CURLOPT_PRIVATE, (void*)req);
        curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_function);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);

        curl_multi_add_handle(curlm, curl);

        req->status = REQ_INFLIGHT;

        req->httpiohandle = (void*)curl;
    }
    else
    {
        req->status = REQ_FAILURE;
    }
}

// cancel pending HTTP request
void CurlHttpIO::cancel(HttpReq* req)
{
    if (req->httpiohandle)
    {
        curl_multi_remove_handle(curlm, (CURL*)req->httpiohandle);
        curl_easy_cleanup((CURL*)req->httpiohandle);

        req->httpstatus = 0;
        req->status = REQ_FAILURE;
        req->httpiohandle = NULL;
    }
}

// real-time progress information on POST data
m_off_t CurlHttpIO::postpos(void* handle)
{
    double bytes;

    curl_easy_getinfo(handle, CURLINFO_SIZE_UPLOAD, &bytes);

    return (m_off_t)bytes;
}

// process events
bool CurlHttpIO::doio()
{
    bool done = false;

    CURLMsg *msg;
    int dummy;

    curl_multi_perform(curlm, &dummy);

    while ((msg = curl_multi_info_read(curlm, &dummy)))
    {
        HttpReq* req;

        if ((curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&req) == CURLE_OK) && req)
        {
            req->httpio = NULL;

            if (msg->msg == CURLMSG_DONE)
            {
                curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &req->httpstatus);

                if (debug)
                {
                    cout << "CURLMSG_DONE with HTTP status: " << req->httpstatus << endl;

                    if (req->httpstatus)
                    {
                        if (req->binary)
                        {
                            cout << "[received " << req->in.size() << " bytes of raw data]" << endl;
                        }
                        else
                        {
                            cout << "Received: " << req->in.c_str() << endl;
                        }
                    }
                }

                // check httpstatus and response length
                req->status = (req->httpstatus == 200
                            && req->contentlength == (req->buf ? req->bufpos : req->in.size()))
                             ? REQ_SUCCESS : REQ_FAILURE;

                inetstatus(req->status);

                success = true;
                done = true;
            }
            else
            {
                req->status = REQ_FAILURE;
            }
        }

        curl_multi_remove_handle(curlm, msg->easy_handle);
        curl_easy_cleanup(msg->easy_handle);
    }

    return done;
}

// callback for incoming HTTP payload
size_t CurlHttpIO::write_data(void* ptr, size_t, size_t nmemb, void* target)
{
    ((HttpReq*)target)->put(ptr, nmemb);

    return nmemb;
}

// set contentlength according to Original-Content-Length header
size_t CurlHttpIO::check_header(void* ptr, size_t, size_t nmemb, void* target)
{
    if (!memcmp(ptr, "Content-Length:", 15))
    {
        if (((HttpReq*)target)->contentlength < 0) ((HttpReq*)target)->setcontentlength(atol((char*)ptr+15));
    }
    else
    {
        if (!memcmp(ptr, "Original-Content-Length:", 24))
        {
            ((HttpReq*)target)->setcontentlength(atol((char*)ptr+24));
        }
    }

    return nmemb;
}

CURLcode CurlHttpIO::ssl_ctx_function(CURL* curl, void* sslctx, void*)
{
    SSL_CTX_set_cert_verify_callback((SSL_CTX*)sslctx, cert_verify_callback, NULL);

    return CURLE_OK;
}

// SSL public key pinning
int CurlHttpIO::cert_verify_callback(X509_STORE_CTX* ctx, void*)
{
    unsigned char buf[sizeof(APISSLMODULUS) - 1];
    EVP_PKEY* evp;
    int ok = 0;

    if ((evp = X509_PUBKEY_get(X509_get_X509_PUBKEY(ctx->cert))))
    {
        if (BN_num_bytes(evp->pkey.rsa->n) == sizeof APISSLMODULUS - 1
         && BN_num_bytes(evp->pkey.rsa->e) == sizeof APISSLEXPONENT - 1)
        {
            BN_bn2bin(evp->pkey.rsa->n, buf);

            if (!memcmp(buf, APISSLMODULUS, sizeof APISSLMODULUS - 1))
            {
                BN_bn2bin(evp->pkey.rsa->e, buf);

                if (!memcmp(buf, APISSLEXPONENT, sizeof APISSLEXPONENT - 1))
                {
                    ok = 1;
                }
            }
        }

        EVP_PKEY_free(evp);
    }

    return ok;
}

} // namespace
<commit_msg>POSIX/Net: Formatting<commit_after>/**
 * @file posix/net.cpp
 * @brief POSIX network access layer (using cURL)
 *
 * (c) 2013-2014 by Mega Limited, Wellsford, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#include "mega.h"

namespace mega {
CurlHttpIO::CurlHttpIO()
{
    curl_global_init(CURL_GLOBAL_DEFAULT);

    curlm = curl_multi_init();

    curlsh = curl_share_init();
    curl_share_setopt(curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    curl_share_setopt(curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);

    contenttypejson = curl_slist_append(NULL, "Content-Type: application/json");
    contenttypejson = curl_slist_append(contenttypejson, "Expect:");

    contenttypebinary = curl_slist_append(NULL, "Content-Type: application/octet-stream");
    contenttypebinary = curl_slist_append(contenttypebinary, "Expect:");
}

CurlHttpIO::~CurlHttpIO()
{
    curl_global_cleanup();
}

void CurlHttpIO::setuseragent(string* u)
{
    useragent = u;
}

// wake up from cURL I/O
void CurlHttpIO::addevents(Waiter* w, int flags)
{
    int t;
    PosixWaiter* pw = (PosixWaiter*)w;

    curl_multi_fdset(curlm, &pw->rfds, &pw->wfds, &pw->efds, &t);

    pw->bumpmaxfd(t);
}

// POST request to URL
void CurlHttpIO::post(HttpReq* req, const char* data, unsigned len)
{
    if (debug)
    {
        cout << "POST target URL: " << req->posturl << endl;

        if (req->binary)
        {
            cout << "[sending " << req->out->size() << " bytes of raw data]" << endl;
        }
        else
        {
            cout << "Sending: " << *req->out << endl;
        }
    }

    CURL* curl;

    req->in.clear();

    if ((curl = curl_easy_init()))
    {
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1);
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 10);
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 5);
        curl_easy_setopt(curl, CURLOPT_URL, req->posturl.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data ? data : req->out->data());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data ? len : req->out->size());
        curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent->c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req->type == REQ_JSON ? contenttypejson : contenttypebinary);
        curl_easy_setopt(curl, CURLOPT_ENCODING, "");
        curl_easy_setopt(curl, CURLOPT_SHARE, curlsh);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)req);
        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, check_header);
        curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)req);
        curl_easy_setopt(curl, CURLOPT_PRIVATE, (void*)req);
        curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_function);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);

        curl_multi_add_handle(curlm, curl);

        req->status = REQ_INFLIGHT;

        req->httpiohandle = (void*)curl;
    }
    else
    {
        req->status = REQ_FAILURE;
    }
}

// cancel pending HTTP request
void CurlHttpIO::cancel(HttpReq* req)
{
    if (req->httpiohandle)
    {
        curl_multi_remove_handle(curlm, (CURL*)req->httpiohandle);
        curl_easy_cleanup((CURL*)req->httpiohandle);

        req->httpstatus = 0;
        req->status = REQ_FAILURE;
        req->httpiohandle = NULL;
    }
}

// real-time progress information on POST data
m_off_t CurlHttpIO::postpos(void* handle)
{
    double bytes;

    curl_easy_getinfo(handle, CURLINFO_SIZE_UPLOAD, &bytes);

    return (m_off_t)bytes;
}

// process events
bool CurlHttpIO::doio()
{
    bool done = false;

    CURLMsg *msg;
    int dummy;

    curl_multi_perform(curlm, &dummy);

    while ((msg = curl_multi_info_read(curlm, &dummy)))
    {
        HttpReq* req;

        if ((curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&req) == CURLE_OK) && req)
        {
            req->httpio = NULL;

            if (msg->msg == CURLMSG_DONE)
            {
                curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &req->httpstatus);

                if (debug)
                {
                    cout << "CURLMSG_DONE with HTTP status: " << req->httpstatus << endl;

                    if (req->httpstatus)
                    {
                        if (req->binary)
                        {
                            cout << "[received " << req->in.size() << " bytes of raw data]" << endl;
                        }
                        else
                        {
                            cout << "Received: " << req->in.c_str() << endl;
                        }
                    }
                }

                // check httpstatus and response length
                req->status = (req->httpstatus == 200
                            && req->contentlength == (req->buf ? req->bufpos : req->in.size()))
                             ? REQ_SUCCESS : REQ_FAILURE;

                inetstatus(req->status);

                success = true;
                done = true;
            }
            else
            {
                req->status = REQ_FAILURE;
            }
        }

        curl_multi_remove_handle(curlm, msg->easy_handle);
        curl_easy_cleanup(msg->easy_handle);
    }

    return done;
}

// callback for incoming HTTP payload
size_t CurlHttpIO::write_data(void* ptr, size_t, size_t nmemb, void* target)
{
    ((HttpReq*)target)->put(ptr, nmemb);

    return nmemb;
}

// set contentlength according to Original-Content-Length header
size_t CurlHttpIO::check_header(void* ptr, size_t, size_t nmemb, void* target)
{
    if (!memcmp(ptr, "Content-Length:", 15))
    {
        if (((HttpReq*)target)->contentlength < 0) ((HttpReq*)target)->setcontentlength(atol((char*)ptr + 15));
    }
    else
    {
        if (!memcmp(ptr, "Original-Content-Length:", 24))
        {
            ((HttpReq*)target)->setcontentlength(atol((char*)ptr + 24));
        }
    }

    return nmemb;
}

CURLcode CurlHttpIO::ssl_ctx_function(CURL* curl, void* sslctx, void*)
{
    SSL_CTX_set_cert_verify_callback((SSL_CTX*)sslctx, cert_verify_callback, NULL);

    return CURLE_OK;
}

// SSL public key pinning
int CurlHttpIO::cert_verify_callback(X509_STORE_CTX* ctx, void*)
{
    unsigned char buf[sizeof(APISSLMODULUS) - 1];
    EVP_PKEY* evp;
    int ok = 0;

    if ((evp = X509_PUBKEY_get(X509_get_X509_PUBKEY(ctx->cert))))
    {
        if (BN_num_bytes(evp->pkey.rsa->n) == sizeof APISSLMODULUS - 1
         && BN_num_bytes(evp->pkey.rsa->e) == sizeof APISSLEXPONENT - 1)
        {
            BN_bn2bin(evp->pkey.rsa->n, buf);

            if (!memcmp(buf, APISSLMODULUS, sizeof APISSLMODULUS - 1))
            {
                BN_bn2bin(evp->pkey.rsa->e, buf);

                if (!memcmp(buf, APISSLEXPONENT, sizeof APISSLEXPONENT - 1))
                {
                    ok = 1;
                }
            }
        }

        EVP_PKEY_free(evp);
    }

    return ok;
}
} // namespace
<|endoftext|>


<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/thread.hpp"
#include <boost/tuple/tuple.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

void test_lsd()
{
	using namespace libtorrent;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48100, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49100, 50000), "0.0.0.0", 0);
	session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50100, 51000), "0.0.0.0", 0);

	// this is to avoid everything finish from a single peer
	// immediately. To make the swarm actually connect all
	// three peers before finishing.
	float rate_limit = 180000;
	ses1.set_upload_rate_limit(int(rate_limit));
	ses2.set_download_rate_limit(int(rate_limit));
	ses3.set_download_rate_limit(int(rate_limit));
	ses2.set_upload_rate_limit(int(rate_limit / 2));
	ses3.set_upload_rate_limit(int(rate_limit / 2));

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	settings.ignore_limits_on_local_network = false;
	ses1.set_settings(settings);
	ses2.set_settings(settings);
	ses3.set_settings(settings);

	ses1.start_lsd();
	ses2.start_lsd();
	ses3.start_lsd();

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
	ses3.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;
	torrent_handle tor3;

	boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, false, "_lsd");

	for (int i = 0; i < 30; ++i)
	{
		print_alerts(ses1, "ses1", true);
		print_alerts(ses2, "ses2", true);
		print_alerts(ses3, "ses3", true);

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();
		torrent_status st3 = tor3.status();

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< st1.num_peers << ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers << " - "
			<< "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st3.progress * 100) << "% "
			<< st3.num_peers
			<< std::endl;

		if (tor2.is_seed() && tor3.is_seed()) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());
	TEST_CHECK(tor3.is_seed());

	if (tor2.is_seed() && tor3.is_seed()) std::cerr << "done\n";
}

int test_main()
{
	using namespace libtorrent;

	// in case the previous run was terminated
	error_code ec;
	remove_all("./tmp1_lsd", ec);
	remove_all("./tmp2_lsd", ec);
	remove_all("./tmp3_lsd", ec);

	test_lsd();

	remove_all("./tmp1_lsd", ec);
	remove_all("./tmp2_lsd", ec);
	remove_all("./tmp3_lsd", ec);

	return 0;
}

<commit_msg>optimized local peer discovery unittest<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/thread.hpp"
#include <boost/tuple/tuple.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

void test_lsd()
{
	using namespace libtorrent;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48100, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49100, 50000), "0.0.0.0", 0);

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	ses1.set_settings(settings);
	ses2.set_settings(settings);

	ses1.start_lsd();
	ses2.start_lsd();
	torrent_handle tor1;
	torrent_handle tor2;

	using boost::tuples::ignore;
	boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, false, "_lsd");

	for (int i = 0; i < 30; ++i)
	{
		print_alerts(ses1, "ses1", true);
		print_alerts(ses2, "ses2", true);

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< std::endl;

		if (tor2.is_seed() /*&& tor3.is_seed()*/) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());

	if (tor2.is_seed()) std::cerr << "done\n";
}

int test_main()
{
	using namespace libtorrent;

	// in case the previous run was terminated
	error_code ec;
	remove_all("./tmp1_lsd", ec);
	remove_all("./tmp2_lsd", ec);
	remove_all("./tmp3_lsd", ec);

	test_lsd();

	remove_all("./tmp1_lsd", ec);
	remove_all("./tmp2_lsd", ec);
	remove_all("./tmp3_lsd", ec);

	return 0;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <mist/io/wav.h>
#include <mist/io/mp3.h>

int main( int argc, char *argv[] )
{
	using namespace std;

	unsigned int sampling_rate = 44100;

	if( argc == 2 )
	{
		mist::array< signed int > sound;
		mist::read_wav( sound, argv[1], sampling_rate );
		mist::write_wav( sound, std::string( argv[1] ) + ".wav", 16, sampling_rate );
	}

	return( 0 );
}
<commit_msg>includeファイルミス<commit_after>#include <iostream>
#include <mist/io/wav.h>

int main( int argc, char *argv[] )
{
	using namespace std;

	unsigned int sampling_rate = 44100;

	if( argc == 2 )
	{
		mist::array< signed int > sound;
		mist::read_wav( sound, argv[1], sampling_rate );
		mist::write_wav( sound, std::string( argv[1] ) + ".wav", 16, sampling_rate );
	}

	return( 0 );
}
<|endoftext|>


<commit_before>#pragma once

#include "arm.hpp"

namespace bandit{

class BernoulliArm : public Arm{
  const double theta;
  std::uniform_real_distribution<double> unif;
public:
  BernoulliArm(double theta): theta(theta), unif(0.0, 1.0) {
  }
  virtual double pull(){
    double rand = unif(randomEngine);
    if(rand <= theta){
      return 1;
    }else{
      return 0;
    }
  }
  virtual double getExpectedReward(){
    return theta;
  }
  virtual std::string toString(){
    std::string str="Bernoulli Arm with theta="+dtos(theta);
    return str;
  }
};

} //namespace
<commit_msg>notation: theta->mu<commit_after>#pragma once

#include "arm.hpp"

namespace bandit{

class BernoulliArm : public Arm{
  const double mu;
  std::uniform_real_distribution<double> unif;
public:
  BernoulliArm(double mu): mu(mu), unif(0.0, 1.0) {
  }
  virtual double pull(){
    double rand = unif(randomEngine);
    if(rand <= mu){
      return 1;
    }else{
      return 0;
    }
  }
  virtual double getExpectedReward(){
    return mu;
  }
  virtual std::string toString(){
    std::string str="Bernoulli Arm with mu="+dtos(mu);
    return str;
  }
};

} //namespace
<|endoftext|>


<commit_before><commit_msg>core: keep old index file resolution<commit_after><|endoftext|>


<commit_before>
/*!
	\file

	\author Igor Mironchik (igor.mironchik at gmail dot com).

	Copyright (c) 2014 Igor Mironchik

	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.
*/

// QtMWidgets include.
#include "color.hpp"

namespace QtMWidgets {

//
// lighterColor
//

QColor
lighterColor( const QColor & c, int b )
{
	if( b <= 0 )
		return c;

	int h = 0;
	int s = 0;
	int v = 0;
	int a = 0;

	QColor hsv = c.toHsv();
	hsv.getHsv( &h, &s, &v, &a );

	v += b;

	if( v > 255 )
	{
		s -= v - 255;

		if( s < 0 ) s = 0;

		v = 255;
	}

	hsv.setHsv( h, s, v, a );

	return hsv.convertTo( c.spec() );
}

//
// darkerColor
//

QColor
darkerColor( const QColor & c, int b )
{
	if( b <= 0 )
		return c;

	int h = 0;
	int s = 0;
	int v = 0;
	int a = 0;

	QColor hsv = c.toHsv();
	hsv.getHsv( &h, &s, &v, &a );

	v -= b;

	hsv.setHsv( h, s, v, a );

	return hsv.convertTo( c.spec() );
}

} /* namespace QtMWidgets */
<commit_msg>Fixed issue in darkerColor().<commit_after>
/*!
	\file

	\author Igor Mironchik (igor.mironchik at gmail dot com).

	Copyright (c) 2014 Igor Mironchik

	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.
*/

// QtMWidgets include.
#include "color.hpp"

namespace QtMWidgets {

//
// lighterColor
//

QColor
lighterColor( const QColor & c, int b )
{
	if( b <= 0 )
		return c;

	int h = 0;
	int s = 0;
	int v = 0;
	int a = 0;

	QColor hsv = c.toHsv();
	hsv.getHsv( &h, &s, &v, &a );

	v += b;

	if( v > 255 )
	{
		s -= v - 255;

		if( s < 0 ) s = 0;

		v = 255;
	}

	hsv.setHsv( h, s, v, a );

	return hsv.convertTo( c.spec() );
}

//
// darkerColor
//

QColor
darkerColor( const QColor & c, int b )
{
	if( b <= 0 )
		return c;

	int h = 0;
	int s = 0;
	int v = 0;
	int a = 0;

	QColor hsv = c.toHsv();
	hsv.getHsv( &h, &s, &v, &a );

	v -= b;

	if( v < 0 )
		v = 0;

	hsv.setHsv( h, s, v, a );

	return hsv.convertTo( c.spec() );
}

} /* namespace QtMWidgets */
<|endoftext|>


<commit_before>#include <cstdio>
#include <cstdlib>
#include <QtCore/QBasicTimer>
#include <QtCore/QMetaType>
#include <QtCore/QMutexLocker>
#include <QtCore/QThread>
#ifdef Q_OS_MAC
#include <pthread.h>
#endif

#include "Canvas.h"
#include "Manager.h"
#include "Object.h"

// Declarations for part of Qt's internal API
Q_DECL_IMPORT const QVariant::Handler* qcoreVariantHandler();
namespace QVariantPrivate {
Q_DECL_IMPORT void registerHandler(
    const int name, const QVariant::Handler *handler);
}

static const char* cCounterNames[] = {
    "ClassCounter",
    "ObjectCounter",
    "QObjectCounter",
    "VariantCounter",
    "ClassSerial",
    "ObjectSerial"
};

extern "C" void hsqml_dump_counters()
{
    Q_ASSERT (gManager);
    if (gManager->checkLogLevel(1)) {
        for (int i=0; i<HsQMLManager::TotalCounters; i++) {
            gManager->log(QString().sprintf("%s = %d.",
                cCounterNames[i], gManager->updateCounter(
                    static_cast<HsQMLManager::CounterId>(i), 0)));
        }
    }
}

static void hooked_construct(QVariant::Private* p, const void* copy)
{
    gManager->hookedConstruct(p, copy);
}

static void hooked_clear(QVariant::Private* p)
{
    gManager->hookedClear(p);
}

ManagerPointer gManager;

HsQMLManager::HsQMLManager(
    void (*freeFun)(HsFunPtr),
    void (*freeStable)(HsStablePtr))
    : mLogLevel(0)
    , mAtExit(false)
    , mFreeFun(freeFun)
    , mFreeStable(freeStable)
    , mOriginalHandler(qcoreVariantHandler())
    , mHookedHandler(*mOriginalHandler)
    , mApp(NULL)
    , mLock(QMutex::Recursive)
    , mRunning(false)
    , mRunCount(0)
    , mStackBase(NULL)
    , mStartCb(NULL)
    , mJobsCb(NULL)
    , mYieldCb(NULL)
    , mActiveEngine(NULL)
{
    // Get log level from environment
    const char* env = std::getenv("HSQML_DEBUG_LOG_LEVEL");
    if (env) {
        setLogLevel(QString(env).toInt());
    }

    // Set hooked handler functions
    mHookedHandler.construct = &hooked_construct;
    mHookedHandler.clear = &hooked_clear;
}

void HsQMLManager::setLogLevel(int ll)
{
    mLogLevel = ll;
    if (ll > 0 && !mAtExit) {
        if (atexit(&hsqml_dump_counters) == 0) {
            mAtExit = true;
        }
        else {
            log("Failed to register callback with atexit().");
        }
    }
}

bool HsQMLManager::checkLogLevel(int ll)
{
    return mLogLevel >= ll;
}

void HsQMLManager::log(const QString& msg)
{
    QMutexLocker locker(&mLogLock);
    fprintf(stderr, "HsQML: %s\n", msg.toUtf8().data());
}

int HsQMLManager::updateCounter(CounterId id, int delta)
{
    return mCounters[id].fetchAndAddRelaxed(delta);
}

void HsQMLManager::freeFun(HsFunPtr funPtr)
{
    mFreeFun(funPtr);
}

void HsQMLManager::freeStable(HsStablePtr stablePtr)
{
    mFreeStable(stablePtr);
}

void HsQMLManager::registerObject(const QObject* obj)
{
    mObjectSet.insert(obj);
}

void HsQMLManager::unregisterObject(const QObject* obj)
{
    bool removed = mObjectSet.remove(obj);
    Q_ASSERT(removed);
}

void HsQMLManager::hookedConstruct(QVariant::Private* p, const void* copy)
{
    char guard;
    mOriginalHandler->construct(p, copy);
    void* pp = reinterpret_cast<void*>(p);
    // The QVariant internals sometimes use a special code path for pointer
    // values which avoids calling the handler functions. This makes it
    // difficult to use them to keep a reference count. However, it's my
    // observation that this only affects transient QVariants created on the
    // stack inside the JavaScript engine's marshalling code. The persistent
    // QVariants stored in the heap are manipulated using a more restricted set
    // of operations which always use the handler functions. Hence, by assuming
    // that the stack can be discounted, it's possible to keep an accurate
    // count of heap references using these hooks.
    if ((pp < &guard || pp > mStackBase) && p->type == QMetaType::QObjectStar) {
        if (isEventThread() && mObjectSet.contains(p->data.o)) {
            HsQMLObject* obj = static_cast<HsQMLObject*>(p->data.o);
            HsQMLObjectProxy* proxy = obj->proxy();
            proxy->ref(HsQMLObjectProxy::Variant);
            proxy->tryGCLock();
            updateCounter(VariantCount, 1);
        }
    }
}

void HsQMLManager::hookedClear(QVariant::Private* p)
{
    char guard;
    void* pp = reinterpret_cast<void*>(p);
    if ((pp < &guard || pp > mStackBase) && p->type == QMetaType::QObjectStar) {
        if (isEventThread() && mObjectSet.contains(p->data.o)) {
            HsQMLObject* obj = static_cast<HsQMLObject*>(p->data.o);
            obj->proxy()->deref(HsQMLObjectProxy::Variant);
            updateCounter(VariantCount, -1);
        }
    }
    mOriginalHandler->clear(p);
}

bool HsQMLManager::isEventThread()
{
    return mApp && mApp->thread() == QThread::currentThread();
}

HsQMLManager::EventLoopStatus HsQMLManager::runEventLoop(
    HsQMLTrivialCb startCb,
    HsQMLTrivialCb jobsCb,
    HsQMLTrivialCb yieldCb)
{
    QMutexLocker locker(&mLock);

    // Check if already running
    if (mRunning) {
        return HSQML_EVLOOP_ALREADY_RUNNING;
    }

    // Check if event loop bound to a different thread
    if (mApp && !isEventThread()) {
        return HSQML_EVLOOP_WRONG_THREAD;
    }

#ifdef Q_OS_MAC
    if (!pthread_main_np()) {
        // Cocoa can only be run on the primordial thread and exec() doesn't
        // check this.
        return HSQML_EVLOOP_WRONG_THREAD;
    }
#endif

    // Check for non-threaded RTS
    if (yieldCb) {
        HSQML_LOG(0,
            "Warning: CPU cannot idle when using the non-threaded RTS.");
    }

    // Perform one-time initialisation
    if (!mApp) {
        // Install hooked handler for QVariants
        QVariantPrivate::registerHandler(0, &mHookedHandler);

        // Register custom types
        qRegisterMetaType<HsQMLEngineConfig>("HsQMLEngineConfig");
        qmlRegisterType<HsQMLCanvas>("HsQML.Canvas", 1, 0, "HaskellCanvas");

        // Create application object
        mApp = new HsQMLManagerApp();
    }

    // Save stack base and callbacks
    mStackBase = &locker;
    mStartCb = startCb;
    mJobsCb = jobsCb;
    mYieldCb = yieldCb;

    // Setup events
    QCoreApplication::postEvent(
        mApp, new QEvent(HsQMLManagerApp::StartedLoopEvent),
        Qt::HighEventPriority);
    QBasicTimer idleTimer;
    if (yieldCb) {
        idleTimer.start(0, mApp);
    }

    // Run loop
    int ret;
    do {
        ret = mApp->exec();

        // Kill all engines
        const QObjectList& cs = gManager->mApp->children();
        while (!cs.empty()) {
            delete cs.front();
        }

        // Cmd-Q on MacOS can kill the event loop before we're ready
        // Keep it running until a StopLoopEvent is received
    } while (ret == 0 && mRunning);

    // Remove redundant events
    QCoreApplication::removePostedEvents(
        mApp, HsQMLManagerApp::RemoveGCLockEvent);

    // Cleanup callbacks
    freeFun(startCb);
    mStartCb = NULL;
    freeFun(jobsCb);
    mJobsCb = NULL;
    if (yieldCb) {
        freeFun(yieldCb);
        mYieldCb = NULL;
    }

    // Return
    if (ret == 0) {
        return HSQML_EVLOOP_OK;
    }
    else {
        QCoreApplication::removePostedEvents(
            mApp, HsQMLManagerApp::StartedLoopEvent);
        return HSQML_EVLOOP_OTHER_ERROR;
    }
}

HsQMLManager::EventLoopStatus HsQMLManager::requireEventLoop()
{
    QMutexLocker locker(&mLock);
    if (mRunCount > 0) {
        mRunCount++;
        return HSQML_EVLOOP_OK;
    }
    else {
        return HSQML_EVLOOP_NOT_RUNNING;
    }
}

void HsQMLManager::releaseEventLoop()
{
    QMutexLocker locker(&mLock);
    if (--mRunCount == 0) {
        QCoreApplication::postEvent(
            mApp, new QEvent(HsQMLManagerApp::StopLoopEvent),
            Qt::LowEventPriority);
    }
}

void HsQMLManager::notifyJobs()
{
    QMutexLocker locker(&mLock);
    if (mRunCount > 0) {
        QCoreApplication::postEvent(
            mApp, new QEvent(HsQMLManagerApp::PendingJobsEvent));
    }
}

void HsQMLManager::createEngine(const HsQMLEngineConfig& config)
{
    Q_ASSERT (mApp);
    QMetaObject::invokeMethod(
        mApp, "createEngine", Q_ARG(HsQMLEngineConfig, config));
}

void HsQMLManager::setActiveEngine(HsQMLEngine* engine)
{
    Q_ASSERT(!mActiveEngine || !engine);
    mActiveEngine = engine;
}

HsQMLEngine* HsQMLManager::activeEngine()
{
    return mActiveEngine;
}

void HsQMLManager::postObjectEvent(HsQMLObjectEvent* ev)
{
    QCoreApplication::postEvent(mApp, ev);
}

HsQMLManagerApp::HsQMLManagerApp()
    : mArgC(1)
    , mArg0(0)
    , mArgV(&mArg0)
    , mApp(mArgC, &mArgV)
{
    mApp.setQuitOnLastWindowClosed(false);
}

HsQMLManagerApp::~HsQMLManagerApp()
{}

void HsQMLManagerApp::customEvent(QEvent* ev)
{
    switch (ev->type()) {
    case HsQMLManagerApp::StartedLoopEvent: {
        gManager->mRunning = true;
        gManager->mRunCount++;
        gManager->mLock.unlock();
        gManager->mStartCb();
        gManager->mJobsCb();
        break;}
    case HsQMLManagerApp::StopLoopEvent: {
        gManager->mLock.lock();
        gManager->mRunning = false;
        gManager->mApp->mApp.quit();
        break;}
    case HsQMLManagerApp::PendingJobsEvent: {
        gManager->mJobsCb();
        break;}
    case HsQMLManagerApp::RemoveGCLockEvent: {
        static_cast<HsQMLObjectEvent*>(ev)->process();
        break;}
    }
}

void HsQMLManagerApp::timerEvent(QTimerEvent*)
{
    Q_ASSERT(gManager->mYieldCb);
    gManager->mYieldCb();
}

void HsQMLManagerApp::createEngine(HsQMLEngineConfig config)
{
    HsQMLEngine* engine = new HsQMLEngine(config);
    engine->setParent(this);
}

int HsQMLManagerApp::exec()
{
    return mApp.exec();
}

extern "C" void hsqml_init(
    void (*freeFun)(HsFunPtr),
    void (*freeStable)(HsStablePtr))
{
    if (gManager == NULL) {
        HsQMLManager* manager = new HsQMLManager(freeFun, freeStable);
        if (!gManager.testAndSetOrdered(NULL, manager)) {
            delete manager;
        }
    }
}

extern "C" HsQMLEventLoopStatus hsqml_evloop_run(
    HsQMLTrivialCb startCb,
    HsQMLTrivialCb jobsCb,
    HsQMLTrivialCb yieldCb)
{
    return gManager->runEventLoop(startCb, jobsCb, yieldCb);
}

extern "C" HsQMLEventLoopStatus hsqml_evloop_require()
{
    return gManager->requireEventLoop();
}

extern "C" void hsqml_evloop_release()
{
    gManager->releaseEventLoop();
}

extern "C" void hsqml_evloop_notify_jobs()
{
    gManager->notifyJobs();
}

extern "C" void hsqml_set_debug_loglevel(int ll)
{
    Q_ASSERT (gManager);
    gManager->setLogLevel(ll);
}
<commit_msg>Change to not export dump counters function.<commit_after>#include <cstdio>
#include <cstdlib>
#include <QtCore/QBasicTimer>
#include <QtCore/QMetaType>
#include <QtCore/QMutexLocker>
#include <QtCore/QThread>
#ifdef Q_OS_MAC
#include <pthread.h>
#endif

#include "Canvas.h"
#include "Manager.h"
#include "Object.h"

// Declarations for part of Qt's internal API
Q_DECL_IMPORT const QVariant::Handler* qcoreVariantHandler();
namespace QVariantPrivate {
Q_DECL_IMPORT void registerHandler(
    const int name, const QVariant::Handler *handler);
}

static const char* cCounterNames[] = {
    "ClassCounter",
    "ObjectCounter",
    "QObjectCounter",
    "VariantCounter",
    "ClassSerial",
    "ObjectSerial"
};

static void dump_counters()
{
    Q_ASSERT (gManager);
    if (gManager->checkLogLevel(1)) {
        for (int i=0; i<HsQMLManager::TotalCounters; i++) {
            gManager->log(QString().sprintf("%s = %d.",
                cCounterNames[i], gManager->updateCounter(
                    static_cast<HsQMLManager::CounterId>(i), 0)));
        }
    }
}

static void hooked_construct(QVariant::Private* p, const void* copy)
{
    gManager->hookedConstruct(p, copy);
}

static void hooked_clear(QVariant::Private* p)
{
    gManager->hookedClear(p);
}

ManagerPointer gManager;

HsQMLManager::HsQMLManager(
    void (*freeFun)(HsFunPtr),
    void (*freeStable)(HsStablePtr))
    : mLogLevel(0)
    , mAtExit(false)
    , mFreeFun(freeFun)
    , mFreeStable(freeStable)
    , mOriginalHandler(qcoreVariantHandler())
    , mHookedHandler(*mOriginalHandler)
    , mApp(NULL)
    , mLock(QMutex::Recursive)
    , mRunning(false)
    , mRunCount(0)
    , mStackBase(NULL)
    , mStartCb(NULL)
    , mJobsCb(NULL)
    , mYieldCb(NULL)
    , mActiveEngine(NULL)
{
    // Get log level from environment
    const char* env = std::getenv("HSQML_DEBUG_LOG_LEVEL");
    if (env) {
        setLogLevel(QString(env).toInt());
    }

    // Set hooked handler functions
    mHookedHandler.construct = &hooked_construct;
    mHookedHandler.clear = &hooked_clear;
}

void HsQMLManager::setLogLevel(int ll)
{
    mLogLevel = ll;
    if (ll > 0 && !mAtExit) {
        if (atexit(&dump_counters) == 0) {
            mAtExit = true;
        }
        else {
            log("Failed to register callback with atexit().");
        }
    }
}

bool HsQMLManager::checkLogLevel(int ll)
{
    return mLogLevel >= ll;
}

void HsQMLManager::log(const QString& msg)
{
    QMutexLocker locker(&mLogLock);
    fprintf(stderr, "HsQML: %s\n", msg.toUtf8().data());
}

int HsQMLManager::updateCounter(CounterId id, int delta)
{
    return mCounters[id].fetchAndAddRelaxed(delta);
}

void HsQMLManager::freeFun(HsFunPtr funPtr)
{
    mFreeFun(funPtr);
}

void HsQMLManager::freeStable(HsStablePtr stablePtr)
{
    mFreeStable(stablePtr);
}

void HsQMLManager::registerObject(const QObject* obj)
{
    mObjectSet.insert(obj);
}

void HsQMLManager::unregisterObject(const QObject* obj)
{
    bool removed = mObjectSet.remove(obj);
    Q_ASSERT(removed);
}

void HsQMLManager::hookedConstruct(QVariant::Private* p, const void* copy)
{
    char guard;
    mOriginalHandler->construct(p, copy);
    void* pp = reinterpret_cast<void*>(p);
    // The QVariant internals sometimes use a special code path for pointer
    // values which avoids calling the handler functions. This makes it
    // difficult to use them to keep a reference count. However, it's my
    // observation that this only affects transient QVariants created on the
    // stack inside the JavaScript engine's marshalling code. The persistent
    // QVariants stored in the heap are manipulated using a more restricted set
    // of operations which always use the handler functions. Hence, by assuming
    // that the stack can be discounted, it's possible to keep an accurate
    // count of heap references using these hooks.
    if ((pp < &guard || pp > mStackBase) && p->type == QMetaType::QObjectStar) {
        if (isEventThread() && mObjectSet.contains(p->data.o)) {
            HsQMLObject* obj = static_cast<HsQMLObject*>(p->data.o);
            HsQMLObjectProxy* proxy = obj->proxy();
            proxy->ref(HsQMLObjectProxy::Variant);
            proxy->tryGCLock();
            updateCounter(VariantCount, 1);
        }
    }
}

void HsQMLManager::hookedClear(QVariant::Private* p)
{
    char guard;
    void* pp = reinterpret_cast<void*>(p);
    if ((pp < &guard || pp > mStackBase) && p->type == QMetaType::QObjectStar) {
        if (isEventThread() && mObjectSet.contains(p->data.o)) {
            HsQMLObject* obj = static_cast<HsQMLObject*>(p->data.o);
            obj->proxy()->deref(HsQMLObjectProxy::Variant);
            updateCounter(VariantCount, -1);
        }
    }
    mOriginalHandler->clear(p);
}

bool HsQMLManager::isEventThread()
{
    return mApp && mApp->thread() == QThread::currentThread();
}

HsQMLManager::EventLoopStatus HsQMLManager::runEventLoop(
    HsQMLTrivialCb startCb,
    HsQMLTrivialCb jobsCb,
    HsQMLTrivialCb yieldCb)
{
    QMutexLocker locker(&mLock);

    // Check if already running
    if (mRunning) {
        return HSQML_EVLOOP_ALREADY_RUNNING;
    }

    // Check if event loop bound to a different thread
    if (mApp && !isEventThread()) {
        return HSQML_EVLOOP_WRONG_THREAD;
    }

#ifdef Q_OS_MAC
    if (!pthread_main_np()) {
        // Cocoa can only be run on the primordial thread and exec() doesn't
        // check this.
        return HSQML_EVLOOP_WRONG_THREAD;
    }
#endif

    // Check for non-threaded RTS
    if (yieldCb) {
        HSQML_LOG(0,
            "Warning: CPU cannot idle when using the non-threaded RTS.");
    }

    // Perform one-time initialisation
    if (!mApp) {
        // Install hooked handler for QVariants
        QVariantPrivate::registerHandler(0, &mHookedHandler);

        // Register custom types
        qRegisterMetaType<HsQMLEngineConfig>("HsQMLEngineConfig");
        qmlRegisterType<HsQMLCanvas>("HsQML.Canvas", 1, 0, "HaskellCanvas");

        // Create application object
        mApp = new HsQMLManagerApp();
    }

    // Save stack base and callbacks
    mStackBase = &locker;
    mStartCb = startCb;
    mJobsCb = jobsCb;
    mYieldCb = yieldCb;

    // Setup events
    QCoreApplication::postEvent(
        mApp, new QEvent(HsQMLManagerApp::StartedLoopEvent),
        Qt::HighEventPriority);
    QBasicTimer idleTimer;
    if (yieldCb) {
        idleTimer.start(0, mApp);
    }

    // Run loop
    int ret;
    do {
        ret = mApp->exec();

        // Kill all engines
        const QObjectList& cs = gManager->mApp->children();
        while (!cs.empty()) {
            delete cs.front();
        }

        // Cmd-Q on MacOS can kill the event loop before we're ready
        // Keep it running until a StopLoopEvent is received
    } while (ret == 0 && mRunning);

    // Remove redundant events
    QCoreApplication::removePostedEvents(
        mApp, HsQMLManagerApp::RemoveGCLockEvent);

    // Cleanup callbacks
    freeFun(startCb);
    mStartCb = NULL;
    freeFun(jobsCb);
    mJobsCb = NULL;
    if (yieldCb) {
        freeFun(yieldCb);
        mYieldCb = NULL;
    }

    // Return
    if (ret == 0) {
        return HSQML_EVLOOP_OK;
    }
    else {
        QCoreApplication::removePostedEvents(
            mApp, HsQMLManagerApp::StartedLoopEvent);
        return HSQML_EVLOOP_OTHER_ERROR;
    }
}

HsQMLManager::EventLoopStatus HsQMLManager::requireEventLoop()
{
    QMutexLocker locker(&mLock);
    if (mRunCount > 0) {
        mRunCount++;
        return HSQML_EVLOOP_OK;
    }
    else {
        return HSQML_EVLOOP_NOT_RUNNING;
    }
}

void HsQMLManager::releaseEventLoop()
{
    QMutexLocker locker(&mLock);
    if (--mRunCount == 0) {
        QCoreApplication::postEvent(
            mApp, new QEvent(HsQMLManagerApp::StopLoopEvent),
            Qt::LowEventPriority);
    }
}

void HsQMLManager::notifyJobs()
{
    QMutexLocker locker(&mLock);
    if (mRunCount > 0) {
        QCoreApplication::postEvent(
            mApp, new QEvent(HsQMLManagerApp::PendingJobsEvent));
    }
}

void HsQMLManager::createEngine(const HsQMLEngineConfig& config)
{
    Q_ASSERT (mApp);
    QMetaObject::invokeMethod(
        mApp, "createEngine", Q_ARG(HsQMLEngineConfig, config));
}

void HsQMLManager::setActiveEngine(HsQMLEngine* engine)
{
    Q_ASSERT(!mActiveEngine || !engine);
    mActiveEngine = engine;
}

HsQMLEngine* HsQMLManager::activeEngine()
{
    return mActiveEngine;
}

void HsQMLManager::postObjectEvent(HsQMLObjectEvent* ev)
{
    QCoreApplication::postEvent(mApp, ev);
}

HsQMLManagerApp::HsQMLManagerApp()
    : mArgC(1)
    , mArg0(0)
    , mArgV(&mArg0)
    , mApp(mArgC, &mArgV)
{
    mApp.setQuitOnLastWindowClosed(false);
}

HsQMLManagerApp::~HsQMLManagerApp()
{}

void HsQMLManagerApp::customEvent(QEvent* ev)
{
    switch (ev->type()) {
    case HsQMLManagerApp::StartedLoopEvent: {
        gManager->mRunning = true;
        gManager->mRunCount++;
        gManager->mLock.unlock();
        gManager->mStartCb();
        gManager->mJobsCb();
        break;}
    case HsQMLManagerApp::StopLoopEvent: {
        gManager->mLock.lock();
        gManager->mRunning = false;
        gManager->mApp->mApp.quit();
        break;}
    case HsQMLManagerApp::PendingJobsEvent: {
        gManager->mJobsCb();
        break;}
    case HsQMLManagerApp::RemoveGCLockEvent: {
        static_cast<HsQMLObjectEvent*>(ev)->process();
        break;}
    }
}

void HsQMLManagerApp::timerEvent(QTimerEvent*)
{
    Q_ASSERT(gManager->mYieldCb);
    gManager->mYieldCb();
}

void HsQMLManagerApp::createEngine(HsQMLEngineConfig config)
{
    HsQMLEngine* engine = new HsQMLEngine(config);
    engine->setParent(this);
}

int HsQMLManagerApp::exec()
{
    return mApp.exec();
}

extern "C" void hsqml_init(
    void (*freeFun)(HsFunPtr),
    void (*freeStable)(HsStablePtr))
{
    if (gManager == NULL) {
        HsQMLManager* manager = new HsQMLManager(freeFun, freeStable);
        if (!gManager.testAndSetOrdered(NULL, manager)) {
            delete manager;
        }
    }
}

extern "C" HsQMLEventLoopStatus hsqml_evloop_run(
    HsQMLTrivialCb startCb,
    HsQMLTrivialCb jobsCb,
    HsQMLTrivialCb yieldCb)
{
    return gManager->runEventLoop(startCb, jobsCb, yieldCb);
}

extern "C" HsQMLEventLoopStatus hsqml_evloop_require()
{
    return gManager->requireEventLoop();
}

extern "C" void hsqml_evloop_release()
{
    gManager->releaseEventLoop();
}

extern "C" void hsqml_evloop_notify_jobs()
{
    gManager->notifyJobs();
}

extern "C" void hsqml_set_debug_loglevel(int ll)
{
    Q_ASSERT (gManager);
    gManager->setLogLevel(ll);
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2013-2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef SPINLOCK_H
#define SPINLOCK_H

struct spinlock {
private:
    volatile size_t lock = 0;

public:
    void acquire(){
        while(!__sync_bool_compare_and_swap(&lock, 0, 1));
    }

    void release(){
        __sync_synchronize();
        lock = 0;
    }
};

#endif
<commit_msg>More safety for spinlock<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2013-2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef SPINLOCK_H
#define SPINLOCK_H

struct spinlock {
private:
    volatile size_t lock = 0;

public:
    void acquire(){
        while(!__sync_bool_compare_and_swap(&lock, 0, 1));
        __sync_synchronize();
    }

    void release(){
        __sync_synchronize();
        lock = 0;
    }
};

#endif
<|endoftext|>