<commit_before>#include "core/physics/physics_manager.h"
#include <catch.hpp>
#include "core/physics/body_template.h"
#include <SDL2/SDL_stdinc.h>
#include <random>

using namespace std;
using namespace glm;
using namespace eversim::core::physics;

namespace {
	class mock_constraint : public constraint {
		bool* constructed;
	public:
		explicit mock_constraint(size_t N, bool* ptr = nullptr)
			: constraint(N), constructed(ptr)
		{
			if (constructed)
				*constructed = true;
		}

		~mock_constraint()
		{
			if (constructed)
				*constructed = false;
		}

		float operator()() const override { return 0.f; }
		vector<vec2> grad() const override { return {}; }
	};

	class mock_constraint_factory : public constraint_factory {
	public:
		data_container parse(istream&) const override { return {}; }

		unique_ptr<constraint> build(constraint_descriptor const& d, body const* b) const override
		{
			bool* ptr = nullptr;
			if (!d.extra_data.empty())
				ptr = boost::any_cast<bool*>(d.extra_data);
			auto c = make_unique<mock_constraint>(d.arity);
			for (int i = 0; i < d.arity; ++i)
			{
				c->particles[i] = {b, d.particles[i]};
			}
			return c;
		}
	};

	class physics_test_fixture {
	public:
		physics_test_fixture()
		{
			loader.register_factory("mock", make_unique<mock_constraint_factory>());
			single_particle.particles = {{}};

			auto N = 25;
			auto delta = float(2 * M_PI / (N + 1));
			boulder.particles.resize(N);
			for(int i = 0; i < N; ++i)
			{
				auto& p = boulder.particles[i];
				p.pos = { cosf(i*delta), sinf(i*delta) };
			}
		}

	protected:
		physics_manager man;
		body_template_loader loader;
		body_template single_particle;
		body_template boulder;
		body_template rand_body;

		void check_particle_pointer_integrity()
		{
			for(auto const& p : man.get_particles())
			{
				auto owner = p.owner;
				REQUIRE(owner->get_particles().begin() <= &p);
				REQUIRE(&p < owner->get_particles().end());
			}
		}
	};
}

TEST_CASE_METHOD(physics_test_fixture,"velocity", "[physics][physics_manager]")
{
	auto* b = man.add_body(single_particle, {});
	b->velocity = { 1.f,0.f };
	b->gravity_scale = 0;

	SECTION("movement without damping"){
		man.set_damping_coefficient(0);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x == Approx(1.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x == Approx(2.f));
		REQUIRE(b->position.y == Approx(0.f));
	}

	SECTION("fully damped movement")
	{
		man.set_damping_coefficient(1);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
	}

	SECTION("some damping")
	{
		man.set_damping_coefficient(0.5);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x > 0.f);
		REQUIRE(b->position.x < 1.f);
		REQUIRE(b->position.y == Approx(0.f));
	}
}

TEST_CASE_METHOD(physics_test_fixture, "gravity","[physics][physics_manager]")
{
	man.set_damping_coefficient(0);
	SECTION("change gravity"){
		const auto g = vec2(0, -10);
		man.set_gravity(g);
		REQUIRE(man.get_gravity() == g);
	}
	SECTION("object falls")
	{
		REQUIRE(man.get_gravity().y < 0);
		auto* b = man.add_body(single_particle,{});
		auto old_pos = b->position;
		man.integrate(1);
		auto new_pos = b->position;
		REQUIRE(new_pos.y < old_pos.y);
	}
	SECTION("gravity scale")
	{
		man.set_gravity({ 0.f,-1.f });

		auto* b1 = man.add_body(single_particle, {-1.f,0});
		REQUIRE(b1->gravity_scale == Approx(1.f));

		auto* b2 = man.add_body(single_particle, {1.f, 0});
		b2->gravity_scale = 0;

		auto* b3 = man.add_body(single_particle, {2.f, 0 });
		b3->gravity_scale = 2;

		man.integrate(1);

		REQUIRE(b1->position.y == Approx(-1.f));
		REQUIRE(b2->position.y == Approx(0.f));
		REQUIRE(b3->position.y == Approx(-2.f));
	}
}

TEST_CASE_METHOD(physics_test_fixture, "body management", "[physics][physics_manager]")
{
	check_particle_pointer_integrity();
	SECTION("single body")
	{
		auto* b = man.add_body(boulder, {});
		REQUIRE(man.get_num_bodies() == 1);
		REQUIRE(man.get_particles().size() == boulder.particles.size());
		for(auto const& p : man.get_particles())
		{
			REQUIRE(p.owner == b);
		}
		SECTION("add"){
			check_particle_pointer_integrity();
			man.integrate(1.f);
		}
		SECTION("remove")
		{
			REQUIRE(man.get_num_bodies() == 1);
			auto const& ps = b->get_particles();
			man.remove_body(b);
			REQUIRE(man.get_num_bodies() == 0);
			for(auto const& _ : man.get_bodies())
			{
				FAIL("Found body where nobody was expected! " << &_);
			}
			for(auto const& p : ps)
			{
				REQUIRE(!p.is_alive());
			}
			man.integrate(1.f);
		}
	}
	SECTION("multiple bodies")
	{
		const int N = 20;

		mt19937 rng{42}; // the one and only seed
		const auto insert_random = [&] {
			auto dist = uniform_int_distribution<>(3, 12);
			rand_body.particles.resize(dist(rng));
			return man.add_body(rand_body, {});
		};

		for(int i = 0; i < N; ++i) {
			auto old_num_particles = man.get_particles().size();
			auto b = insert_random();
			REQUIRE(man.get_num_bodies() == i+1);
			REQUIRE(man.get_particles().size() == old_num_particles + b->get_particles().size());
		}
		SECTION("insert only") {
			check_particle_pointer_integrity();
		}

		const auto remove_random = [&] {
			auto dist = uniform_int_distribution<>(0, man.get_num_bodies());
			auto it = man.get_bodies().begin();
			advance(it, dist(rng));
			man.remove_body(&*it);
		};

		SECTION("remove only")
		{
			REQUIRE(man.get_num_bodies() == N);

			while(man.get_num_bodies() > 0)
			{
				auto oldsize = man.get_num_bodies();
				remove_random();
				man.cleanup_dead_bodies();
				REQUIRE(man.get_num_bodies() == oldsize - 1);
			}
			check_particle_pointer_integrity();
			man.integrate(1);
			check_particle_pointer_integrity();
		}

		SECTION("insert & remove")
		{
			REQUIRE(man.get_num_bodies() == N);
			auto coinflip = bernoulli_distribution(0.5);
			for(int i = 0; i < N; ++i)
			{
				auto remove = coinflip(rng);
				auto old_num_bodies = man.get_num_bodies();
				if(remove)
				{
					remove_random();
					REQUIRE(man.get_num_bodies() == old_num_bodies - 1);
				}
				else
				{
					insert_random();
					REQUIRE(man.get_num_bodies() == old_num_bodies + 1);
				}
				if(coinflip(rng))
				{
					man.cleanup_dead_bodies();
				}
				man.integrate(1.f);
			}
			check_particle_pointer_integrity();
		}
	}
}<commit_msg>fixed a few bugs in physics_manager test<commit_after>#include "core/physics/physics_manager.h"
#include <catch.hpp>
#include "core/physics/body_template.h"
#include <SDL2/SDL_stdinc.h>
#include <random>

using namespace std;
using namespace glm;
using namespace eversim::core::physics;

namespace {
	class mock_constraint : public constraint {
		bool* constructed;
	public:
		explicit mock_constraint(size_t N, bool* ptr = nullptr)
			: constraint(N), constructed(ptr)
		{
			if (constructed)
				*constructed = true;
		}

		~mock_constraint()
		{
			if (constructed)
				*constructed = false;
		}

		float operator()() const override { return 0.f; }
		vector<vec2> grad() const override { return {}; }
	};

	class mock_constraint_factory : public constraint_factory {
	public:
		data_container parse(istream&) const override { return {}; }

		unique_ptr<constraint> build(constraint_descriptor const& d, body const* b) const override
		{
			bool* ptr = nullptr;
			if (!d.extra_data.empty())
				ptr = boost::any_cast<bool*>(d.extra_data);
			auto c = make_unique<mock_constraint>(d.arity);
			for (int i = 0; i < d.arity; ++i)
			{
				c->particles[i] = {b, d.particles[i]};
			}
			return c;
		}
	};

	class physics_test_fixture {
	public:
		physics_test_fixture()
		{
			loader.register_factory("mock", make_unique<mock_constraint_factory>());
			single_particle.particles = {{}};

			auto N = 25;
			auto delta = float(2 * M_PI / (N + 1));
			boulder.particles.resize(N);
			for(int i = 0; i < N; ++i)
			{
				auto& p = boulder.particles[i];
				p.pos = { cosf(i*delta), sinf(i*delta) };
			}
		}

	protected:
		physics_manager man;
		body_template_loader loader;
		body_template single_particle;
		body_template boulder;
		body_template rand_body;

		void check_particle_pointer_integrity()
		{
			for(auto const& p : man.get_particles())
			{
				if (!p.is_alive()) continue;
				auto owner = p.owner;
				REQUIRE(owner->get_particles().begin() <= &p);
				REQUIRE(&p < owner->get_particles().end());
			}
		}
	};
}

TEST_CASE_METHOD(physics_test_fixture,"velocity", "[physics][physics_manager]")
{
	auto* b = man.add_body(single_particle, {});
	b->velocity = { 1.f,0.f };
	b->gravity_scale = 0;

	SECTION("movement without damping"){
		man.set_damping_coefficient(0);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x == Approx(1.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x == Approx(2.f));
		REQUIRE(b->position.y == Approx(0.f));
	}

	SECTION("fully damped movement")
	{
		man.set_damping_coefficient(1);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
	}

	SECTION("some damping")
	{
		man.set_damping_coefficient(0.5);
		REQUIRE(b->position.x == Approx(0.f));
		REQUIRE(b->position.y == Approx(0.f));
		man.integrate(1);
		REQUIRE(b->position.x > 0.f);
		REQUIRE(b->position.x < 1.f);
		REQUIRE(b->position.y == Approx(0.f));
	}
}

TEST_CASE_METHOD(physics_test_fixture, "gravity","[physics][physics_manager]")
{
	man.set_damping_coefficient(0);
	SECTION("change gravity"){
		const auto g = vec2(0, -10);
		man.set_gravity(g);
		REQUIRE(man.get_gravity() == g);
	}
	SECTION("object falls")
	{
		REQUIRE(man.get_gravity().y < 0);
		auto* b = man.add_body(single_particle,{});
		auto old_pos = b->position;
		man.integrate(1);
		auto new_pos = b->position;
		REQUIRE(new_pos.y < old_pos.y);
	}
	SECTION("gravity scale")
	{
		man.set_gravity({ 0.f,-1.f });

		auto* b1 = man.add_body(single_particle, {-1.f,0});
		REQUIRE(b1->gravity_scale == Approx(1.f));

		auto* b2 = man.add_body(single_particle, {1.f, 0});
		b2->gravity_scale = 0;

		auto* b3 = man.add_body(single_particle, {2.f, 0 });
		b3->gravity_scale = 2;

		man.integrate(1);

		REQUIRE(b1->position.y == Approx(-1.f));
		REQUIRE(b2->position.y == Approx(0.f));
		REQUIRE(b3->position.y == Approx(-2.f));
	}
}

TEST_CASE_METHOD(physics_test_fixture, "body management", "[physics][physics_manager]")
{
	check_particle_pointer_integrity();
	SECTION("single body")
	{
		auto* b = man.add_body(boulder, {});
		REQUIRE(man.get_num_bodies() == 1);
		REQUIRE(man.get_particles().size() == boulder.particles.size());
		for(auto const& p : man.get_particles())
		{
			REQUIRE(p.owner == b);
		}
		SECTION("add"){
			check_particle_pointer_integrity();
			man.integrate(1.f);
		}
		SECTION("remove")
		{
			REQUIRE(man.get_num_bodies() == 1);
			auto const& ps = b->get_particles();
			man.remove_body(b);
			REQUIRE(man.get_num_bodies() == 0);
			for(auto const& _ : man.get_bodies())
			{
				FAIL("Found body where nobody was expected! " << &_);
			}
			for(auto const& p : ps)
			{
				REQUIRE(!p.is_alive());
			}
			man.cleanup_dead_bodies();
			man.integrate(1.f);
		}
	}
	SECTION("multiple bodies")
	{
		const int N = 20;

		mt19937 rng{42}; // the one and only seed
		const auto insert_random = [&] {
			auto dist = uniform_int_distribution<>(3, 12);
			rand_body.particles.resize(dist(rng));
			return man.add_body(rand_body, {});
		};

		for(int i = 0; i < N; ++i) {
			auto old_num_particles = man.get_particles().size();
			auto b = insert_random();
			REQUIRE(man.get_num_bodies() == i+1);
			REQUIRE(man.get_particles().size() == old_num_particles + b->get_particles().size());
		}
		SECTION("insert only") {
			check_particle_pointer_integrity();
		}

		const auto remove_random = [&] {
			auto dist = uniform_int_distribution<>(0, man.get_num_bodies()-1);
			auto it = man.get_bodies().begin();
			advance(it, dist(rng));
			man.remove_body(&*it);
		};

		SECTION("remove only")
		{
			REQUIRE(man.get_num_bodies() == N);

			while(man.get_num_bodies() > 0)
			{
				auto oldsize = man.get_num_bodies();
				remove_random();
				man.cleanup_dead_bodies();
				REQUIRE(man.get_num_bodies() == oldsize - 1);
			}
			check_particle_pointer_integrity();
			man.integrate(1);
			check_particle_pointer_integrity();
		}

		SECTION("insert & remove")
		{
			REQUIRE(man.get_num_bodies() == N);
			auto coinflip = bernoulli_distribution(0.5);
			for(int i = 0; i < N; ++i)
			{
				auto remove = coinflip(rng);
				auto old_num_bodies = man.get_num_bodies();
				if(remove)
				{
					remove_random();
					REQUIRE(man.get_num_bodies() == old_num_bodies - 1);
				}
				else
				{
					insert_random();
					REQUIRE(man.get_num_bodies() == old_num_bodies + 1);
				}
				if(coinflip(rng))
				{
					man.cleanup_dead_bodies();
				}
				man.integrate(1.f);
			}
			check_particle_pointer_integrity();
		}
	}
}<|endoftext|>


<commit_before>#include <nan.h>
#include <v8.h>
#include <poppler.h>
#include <cairo.h>
#include <cairo-svg.h>
#include <vector>

namespace sspdf {

using v8::Local;
using v8::Value;
using v8::Function;
using v8::Exception;
using v8::Isolate;
using v8::Object;
using v8::Array;
using v8::Uint8Array;
using v8::ArrayBuffer;
using Nan::Callback;
using Nan::GetFunction;
using Nan::Set;
using Nan::Null;
using Nan::New;
using Nan::AsyncWorker;
using Nan::HandleScope;
using Nan::ThrowTypeError;
using Nan::Persistent;
using Nan::ObjectWrap;
using std::vector;

const char* MSG_EXCEPTION_ZEROLEN =  "Zero length buffer provided.";
const char* MSG_EXCEPTION_PAGE_FAIL =  "Unable to open that page.";
const char* MSG_EXCEPTION_PAGE_OUT_OF_RANGE =  "Page index out of range.";

class PdfssWorker : public AsyncWorker {
  public:
    char* pdfData = NULL;
    size_t pdfLen = 0;
    double pw, ph;
    char* txt;
    PopplerRectangle* rects;
    guint rectLen;
    char* error = NULL; // Error stored here from {constructor, Execute} freed in HandleOKCallback.
    int destPage;
    vector<char> svgData;
    // pdf data copied.
    PdfssWorker(Callback* cb, Local<Uint8Array> hPdf, int destPage)
      : AsyncWorker(cb) {
      this->destPage = destPage;
      this->pdfLen = (*hPdf)->ByteLength();
      if (this->pdfLen == 0) {
        this->error = new char[strlen(MSG_EXCEPTION_ZEROLEN) + 1];
        strcpy(this->error, MSG_EXCEPTION_ZEROLEN);
      } else {
        char* pdfData = (((char*)(*(*hPdf)->Buffer())->GetContents().Data())) + (*hPdf)->ByteOffset();
        this->pdfData = new char[this->pdfLen];
        memcpy(this->pdfData, pdfData, this->pdfLen);
      }
    }

    void Execute () {
      if (this->error != NULL) {
        return;
      }
      GError* gerror = NULL;
      PopplerDocument* popperDoc = poppler_document_new_from_data(this->pdfData, this->pdfLen, NULL, &gerror);
      if (popperDoc == NULL) {
        this->error = new char[strlen(gerror->message) + 1];
        strcpy(this->error, gerror->message);
        g_error_free(gerror);
        gerror = NULL;
        return;
      }
      int nPages = poppler_document_get_n_pages(popperDoc);
      if (this->destPage >= nPages) {
        this->error = new char[strlen(MSG_EXCEPTION_PAGE_OUT_OF_RANGE) + 1];
        strcpy(this->error, MSG_EXCEPTION_PAGE_OUT_OF_RANGE);
        g_object_unref(popperDoc);
        return;
      }
      PopplerPage* page = poppler_document_get_page(popperDoc, this->destPage);
      g_object_unref(popperDoc);
      popperDoc = NULL;
      if (page == NULL) {
        this->error = new char[strlen(MSG_EXCEPTION_PAGE_FAIL) + 1];
        strcpy(this->error, MSG_EXCEPTION_PAGE_FAIL);
        return;
      }
      poppler_page_get_size(page, &this->pw, &this->ph);
      this->txt = poppler_page_get_text(page);
      poppler_page_get_text_layout(page, &this->rects, &this->rectLen);
      // this->rects require freeing by us, freed on HandleOKCallback.

      cairo_surface_t* svgSurface = cairo_svg_surface_create_for_stream((cairo_write_func_t) PdfssWorker::writeFunc, this, this->pw, this->ph);
      cairo_t* svg = cairo_create(svgSurface);
      poppler_page_render(page, svg);
      cairo_surface_destroy(svgSurface);
      cairo_destroy(svg);
      g_object_unref(page);
    }

    void HandleOKCallback () {
      HandleScope scope;
      Local<Object> obj;
      Local<Array> rects;
      Local<Value> argv[] = {
        Null(), Null()
      };
      if (this->error == NULL) {
        obj = New<Object>();
        Set(obj, New<v8::String>("width").ToLocalChecked(), New<v8::Number>(this->pw));
        Set(obj, New<v8::String>("height").ToLocalChecked(), New<v8::Number>(this->ph));
        Set(obj, New<v8::String>("text").ToLocalChecked(), New<v8::String>(this->txt).ToLocalChecked());
        rects = New<v8::Array>(this->rectLen);
        for (guint i = 0; i < this->rectLen; i ++) {
          Local<Object> xy = New<Object>();
          Set(xy, New<v8::String>("x1").ToLocalChecked(), New<v8::Number>(this->rects[i].x1));
          Set(xy, New<v8::String>("y1").ToLocalChecked(), New<v8::Number>(this->rects[i].y1));
          Set(xy, New<v8::String>("x2").ToLocalChecked(), New<v8::Number>(this->rects[i].x2));
          Set(xy, New<v8::String>("y2").ToLocalChecked(), New<v8::Number>(this->rects[i].y2));
          Set(rects, New<v8::Number>(i), xy);
        }
        Set(obj, New<v8::String>("rects").ToLocalChecked(), rects);
        g_free(this->rects);
        g_free(this->txt);
        argv[1] = obj;

        if (this->svgData.size() > 0) {
          auto ml = node::Buffer::Copy(Isolate::GetCurrent(), &(*this->svgData.begin()), this->svgData.size());
          if (ml.IsEmpty()) {
            argv[0] = v8::Exception::Error(New<v8::String>("Can't return svg data.").ToLocalChecked());
          } else {
            Set(obj, New<v8::String>("svg").ToLocalChecked(), ml.ToLocalChecked());
          }
        } else {
          Set(obj, New<v8::String>("svg").ToLocalChecked(), Null());
        }
      } else {
        argv[0] = v8::Exception::Error(New<v8::String>(this->error).ToLocalChecked());
        delete this->error;
        this->error = NULL;
      }
      this->callback->Call(2, argv);
      if (this->pdfData != NULL) {
        delete this->pdfData;
        this->pdfData = NULL;
      }
    }
    static cairo_status_t writeFunc (void* closure, const unsigned char* data, unsigned int length) {
      PdfssWorker* worker = (PdfssWorker*) closure;
      worker->svgData.reserve(worker->svgData.size() + length);
      for (unsigned int i = 0; i < length; i ++) {
        worker->svgData.push_back(data[i]);
      }
      return CAIRO_STATUS_SUCCESS;
    }
};

NAN_METHOD(getPage) {
  if (info.Length() != 3) {
    ThrowTypeError("getPage(pdfBuffer, pageNum, callback)");
    return;
  }
  if (!info[0]->IsUint8Array()) {
    ThrowTypeError("arg[0] is not a buffer.");
    return;
  }
  if (!info[1]->IsInt32()) {
    ThrowTypeError("arg[1] is not a int32.");
    return;
  }
  if (!info[2]->IsFunction()) {
    ThrowTypeError("arg[2] is not a function.");
    return;
  }
  int pn = (int)(*info[1].As<v8::Number>())->Value();
  if (pn < 0) {
    ThrowTypeError("arg[1] shouldn't be < 0.");
    return;
  }
  auto pdfBuffer = info[0].As<Uint8Array>();
  if (pdfBuffer.IsEmpty()) {
    ThrowTypeError("arg[0] can't resolve.");
    return;
  }
  Callback *callback = new Callback(info[2].As<Function>());
  AsyncQueueWorker(new PdfssWorker(callback, pdfBuffer, pn));
}

NAN_MODULE_INIT(Init) {
  Set(target
    , New<v8::String>("getPage").ToLocalChecked()
    , New<v8::FunctionTemplate>(getPage)->GetFunction());
}

NODE_MODULE(sspdf, Init)

}
<commit_msg>Fix double free when document contains no text<commit_after>#include <nan.h>
#include <v8.h>
#include <poppler.h>
#include <cairo.h>
#include <cairo-svg.h>
#include <vector>

namespace sspdf {

using v8::Local;
using v8::Value;
using v8::Function;
using v8::Exception;
using v8::Isolate;
using v8::Object;
using v8::Array;
using v8::Uint8Array;
using v8::ArrayBuffer;
using Nan::Callback;
using Nan::GetFunction;
using Nan::Set;
using Nan::Null;
using Nan::New;
using Nan::AsyncWorker;
using Nan::HandleScope;
using Nan::ThrowTypeError;
using Nan::Persistent;
using Nan::ObjectWrap;
using std::vector;

const char* MSG_EXCEPTION_ZEROLEN =  "Zero length buffer provided.";
const char* MSG_EXCEPTION_PAGE_FAIL =  "Unable to open that page.";
const char* MSG_EXCEPTION_PAGE_OUT_OF_RANGE =  "Page index out of range.";

class PdfssWorker : public AsyncWorker {
  public:
    char* pdfData = NULL;
    size_t pdfLen = 0;
    double pw, ph;
    char* txt;
    PopplerRectangle* rects;
    guint rectLen;
    char* error = NULL; // Error stored here from {constructor, Execute} freed in HandleOKCallback.
    int destPage;
    vector<char> svgData;
    // pdf data copied.
    PdfssWorker(Callback* cb, Local<Uint8Array> hPdf, int destPage)
      : AsyncWorker(cb) {
      this->destPage = destPage;
      this->pdfLen = (*hPdf)->ByteLength();
      if (this->pdfLen == 0) {
        this->error = new char[strlen(MSG_EXCEPTION_ZEROLEN) + 1];
        strcpy(this->error, MSG_EXCEPTION_ZEROLEN);
      } else {
        char* pdfData = (((char*)(*(*hPdf)->Buffer())->GetContents().Data())) + (*hPdf)->ByteOffset();
        this->pdfData = new char[this->pdfLen];
        memcpy(this->pdfData, pdfData, this->pdfLen);
      }
    }

    void Execute () {
      if (this->error != NULL) {
        return;
      }
      GError* gerror = NULL;
      PopplerDocument* popperDoc = poppler_document_new_from_data(this->pdfData, this->pdfLen, NULL, &gerror);
      if (popperDoc == NULL) {
        this->error = new char[strlen(gerror->message) + 1];
        strcpy(this->error, gerror->message);
        g_error_free(gerror);
        gerror = NULL;
        return;
      }
      int nPages = poppler_document_get_n_pages(popperDoc);
      if (this->destPage >= nPages) {
        this->error = new char[strlen(MSG_EXCEPTION_PAGE_OUT_OF_RANGE) + 1];
        strcpy(this->error, MSG_EXCEPTION_PAGE_OUT_OF_RANGE);
        g_object_unref(popperDoc);
        return;
      }
      PopplerPage* page = poppler_document_get_page(popperDoc, this->destPage);
      g_object_unref(popperDoc);
      popperDoc = NULL;
      if (page == NULL) {
        this->error = new char[strlen(MSG_EXCEPTION_PAGE_FAIL) + 1];
        strcpy(this->error, MSG_EXCEPTION_PAGE_FAIL);
        return;
      }
      poppler_page_get_size(page, &this->pw, &this->ph);
      this->txt = poppler_page_get_text(page);
      poppler_page_get_text_layout(page, &this->rects, &this->rectLen);
      // this->rects require freeing by us, freed on HandleOKCallback.

      cairo_surface_t* svgSurface = cairo_svg_surface_create_for_stream((cairo_write_func_t) PdfssWorker::writeFunc, this, this->pw, this->ph);
      cairo_t* svg = cairo_create(svgSurface);
      poppler_page_render(page, svg);
      cairo_surface_destroy(svgSurface);
      cairo_destroy(svg);
      g_object_unref(page);
    }

    void HandleOKCallback () {
      HandleScope scope;
      Local<Object> obj;
      Local<Array> rects;
      Local<Value> argv[] = {
        Null(), Null()
      };
      if (this->error == NULL) {
        obj = New<Object>();
        Set(obj, New<v8::String>("width").ToLocalChecked(), New<v8::Number>(this->pw));
        Set(obj, New<v8::String>("height").ToLocalChecked(), New<v8::Number>(this->ph));
        Set(obj, New<v8::String>("text").ToLocalChecked(), New<v8::String>(this->txt).ToLocalChecked());
        rects = New<v8::Array>(this->rectLen);
        for (guint i = 0; i < this->rectLen; i ++) {
          Local<Object> xy = New<Object>();
          Set(xy, New<v8::String>("x1").ToLocalChecked(), New<v8::Number>(this->rects[i].x1));
          Set(xy, New<v8::String>("y1").ToLocalChecked(), New<v8::Number>(this->rects[i].y1));
          Set(xy, New<v8::String>("x2").ToLocalChecked(), New<v8::Number>(this->rects[i].x2));
          Set(xy, New<v8::String>("y2").ToLocalChecked(), New<v8::Number>(this->rects[i].y2));
          Set(rects, New<v8::Number>(i), xy);
        }
        Set(obj, New<v8::String>("rects").ToLocalChecked(), rects);
        if (this->rectLen) g_free(this->rects);
        if (this->txt) g_free(this->txt);
        argv[1] = obj;

        if (this->svgData.size() > 0) {
          auto ml = node::Buffer::Copy(Isolate::GetCurrent(), &(*this->svgData.begin()), this->svgData.size());
          if (ml.IsEmpty()) {
            argv[0] = v8::Exception::Error(New<v8::String>("Can't return svg data.").ToLocalChecked());
          } else {
            Set(obj, New<v8::String>("svg").ToLocalChecked(), ml.ToLocalChecked());
          }
        } else {
          Set(obj, New<v8::String>("svg").ToLocalChecked(), Null());
        }
      } else {
        argv[0] = v8::Exception::Error(New<v8::String>(this->error).ToLocalChecked());
        delete this->error;
        this->error = NULL;
      }
      this->callback->Call(2, argv);
      if (this->pdfData != NULL) {
        delete this->pdfData;
        this->pdfData = NULL;
      }
    }
    static cairo_status_t writeFunc (void* closure, const unsigned char* data, unsigned int length) {
      PdfssWorker* worker = (PdfssWorker*) closure;
      worker->svgData.reserve(worker->svgData.size() + length);
      for (unsigned int i = 0; i < length; i ++) {
        worker->svgData.push_back(data[i]);
      }
      return CAIRO_STATUS_SUCCESS;
    }
};

NAN_METHOD(getPage) {
  if (info.Length() != 3) {
    ThrowTypeError("getPage(pdfBuffer, pageNum, callback)");
    return;
  }
  if (!info[0]->IsUint8Array()) {
    ThrowTypeError("arg[0] is not a buffer.");
    return;
  }
  if (!info[1]->IsInt32()) {
    ThrowTypeError("arg[1] is not a int32.");
    return;
  }
  if (!info[2]->IsFunction()) {
    ThrowTypeError("arg[2] is not a function.");
    return;
  }
  int pn = (int)(*info[1].As<v8::Number>())->Value();
  if (pn < 0) {
    ThrowTypeError("arg[1] shouldn't be < 0.");
    return;
  }
  auto pdfBuffer = info[0].As<Uint8Array>();
  if (pdfBuffer.IsEmpty()) {
    ThrowTypeError("arg[0] can't resolve.");
    return;
  }
  Callback *callback = new Callback(info[2].As<Function>());
  AsyncQueueWorker(new PdfssWorker(callback, pdfBuffer, pn));
}

NAN_MODULE_INIT(Init) {
  Set(target
    , New<v8::String>("getPage").ToLocalChecked()
    , New<v8::FunctionTemplate>(getPage)->GetFunction());
}

NODE_MODULE(sspdf, Init)

}
<|endoftext|>


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

#include "test_lru.h"

#include "../src/lru.h"
#include "utils.h"

using namespace lru;

int test_lru() {
	INIT_LOG
	LRU<std::string, int> lru(3);
	lru.insert(std::make_pair("test1", 111));
	lru.insert(std::make_pair("test2", 222));
	lru.insert(std::make_pair("test3", 333));
	lru.insert(std::make_pair("test4", 444));  // this pushes 'test1' out of the lru

	try {
		if (lru.at("test1")) {
			L_ERR("ERROR: LRU::insert with limit is not working");
			RETURN(1);
		}
	} catch (const std::out_of_range&) { }

	if (lru.at("test4") != 444 || lru.at("test3") != 333 || lru.at("test2") != 222) {
		L_ERR("ERROR: LRU::at is not working");
		RETURN(1);
	}

	lru.insert(std::make_pair("test5", 555));  // this pushes 'test4' out of the lru

	try {
		if (lru.at("test4")) {
			L_ERR("ERROR: LRU::insert with limit is not working");
			RETURN(1);
		}
	} catch (const std::out_of_range&) { }

	if (lru.at("test2") != 222 || lru.at("test3") != 333 || lru.at("test5") != 555) {
		L_ERR("ERROR: LRU::at is not working");
		RETURN(1);
	}

	L_ERR("Test LRU is correct!");

	RETURN(0);
}

int test_lru_emplace() {
	INIT_LOG
	LRU<std::string, int> lru(1);
	lru.emplace("test1", 111);
	lru.emplace_and([](int&, ssize_t, ssize_t){ return DropAction::leave; }, "test2", 222);

	if (lru.at("test1") != 111 || lru.at("test2") != 222) {
		L_ERR("ERROR: LRU emplace is not working");
		RETURN(1);
	}

	L_ERR("Test LRU emplace is correct!");

	RETURN(0);
}

int test_lru_actions() {
	INIT_LOG
	try {
		LRU<std::string, int> lru(3);
		lru.insert(std::make_pair("test1", 111));
		lru.insert(std::make_pair("test2", 222));
		lru.insert(std::make_pair("test3", 333));
		lru.insert_and([](int&, ssize_t, ssize_t){ return DropAction::leave; }, std::make_pair("test4", 444));  // this DOES NOT push 'test1' out of the lru

		if (lru.size() != 4) {
			L_ERR("ERROR: LRU::insert_and is not working");
			RETURN(1);
		}

		// this gets, but doesn't renew 'test1'
		if (lru.at_and([](int&){ return GetAction::leave; }, "test1") != 111) {
			L_ERR("ERROR: LRU::at_and is not working");
			RETURN(1);
		}

		lru.insert(std::make_pair("test5", 555));  // this pushes 'test1' *and* 'test2' out of the lru

		try {
			if (lru.at("test1")) {
				L_ERR("ERROR: LRU::insert with limit is not working");
				RETURN(1);
			}
		} catch (std::out_of_range) { }

		if (lru.size() != 3) {
			L_ERR("ERROR: LRU::insert with limit is not working");
			RETURN(1);
		}

		lru.insert_and([](int&, ssize_t, ssize_t){ return DropAction::renew; }, std::make_pair("test6", 666));  // this renews 'test3'

		if (lru.size() != 4) {
			L_ERR("ERROR: LRU::insert_and  is not working");
			RETURN(1);
		}

		if (lru.at("test3") != 333 || lru.at("test4") != 444 || lru.at("test5") != 555 || lru.at("test6") != 666) {
			L_ERR("ERROR: LRU insert is not working");
			RETURN(1);
		}
	} catch (const std::exception& exc) {
		L_EXC("%s\n", exc.what());
	}

	L_ERR("Test LRU with actions is correct!");

	RETURN(0);
}

int test_lru_mutate() {
	INIT_LOG
	LRU<std::string, int> lru(3);
	lru.insert(std::make_pair("test1", 111));

	if (lru.at_and([](int& o){ o = 123; return GetAction::leave; }, "test1") != 123 ||
		lru.get_and([](int& o){ o = 456; return GetAction::leave; }, [](int&, size_t, size_t){ return DropAction::leave; }, "test1") != 456 ||
		lru.at("test1") != 456) {
		L_ERR("ERROR: LRU mutate is not working");
		RETURN(1);
	}

	L_ERR("Test LRU mutate is correct!");

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

#include "test_lru.h"

#include "../src/lru.h"
#include "utils.h"

using namespace lru;

int test_lru() {
	INIT_LOG
	LRU<std::string, int> lru(3);
	lru.insert(std::make_pair("test1", 111));
	lru.insert(std::make_pair("test2", 222));
	lru.insert(std::make_pair("test3", 333));
	lru.insert(std::make_pair("test4", 444));  // this pushes 'test1' out of the lru

	try {
		if (lru.at("test1")) {
			L_ERR("ERROR: LRU::insert with limit is not working");
			RETURN(1);
		}
	} catch (const std::out_of_range&) { }

	if (lru.at("test4") != 444 || lru.at("test3") != 333 || lru.at("test2") != 222) {
		L_ERR("ERROR: LRU::at is not working");
		RETURN(1);
	}

	lru.insert(std::make_pair("test5", 555));  // this pushes 'test4' out of the lru

	try {
		if (lru.at("test4")) {
			L_ERR("ERROR: LRU::insert with limit is not working");
			RETURN(1);
		}
	} catch (const std::out_of_range&) { }

	if (lru.at("test2") != 222 || lru.at("test3") != 333 || lru.at("test5") != 555) {
		L_ERR("ERROR: LRU::at is not working");
		RETURN(1);
	}

	RETURN(0);
}

int test_lru_emplace() {
	INIT_LOG
	LRU<std::string, int> lru(1);
	lru.emplace("test1", 111);
	lru.emplace_and([](int&, ssize_t, ssize_t){ return DropAction::leave; }, "test2", 222);

	if (lru.at("test1") != 111 || lru.at("test2") != 222) {
		L_ERR("ERROR: LRU emplace is not working");
		RETURN(1);
	}

	L_ERR("Test LRU emplace is correct!");

	RETURN(0);
}

int test_lru_actions() {
	INIT_LOG
	try {
		LRU<std::string, int> lru(3);
		lru.insert(std::make_pair("test1", 111));
		lru.insert(std::make_pair("test2", 222));
		lru.insert(std::make_pair("test3", 333));
		lru.insert_and([](int&, ssize_t, ssize_t){ return DropAction::leave; }, std::make_pair("test4", 444));  // this DOES NOT push 'test1' out of the lru

		if (lru.size() != 4) {
			L_ERR("ERROR: LRU::insert_and is not working");
			RETURN(1);
		}

		// this gets, but doesn't renew 'test1'
		if (lru.at_and([](int&){ return GetAction::leave; }, "test1") != 111) {
			L_ERR("ERROR: LRU::at_and is not working");
			RETURN(1);
		}

		lru.insert(std::make_pair("test5", 555));  // this pushes 'test1' *and* 'test2' out of the lru

		try {
			if (lru.at("test1")) {
				L_ERR("ERROR: LRU::insert with limit is not working");
				RETURN(1);
			}
		} catch (std::out_of_range) { }

		if (lru.size() != 3) {
			L_ERR("ERROR: LRU::insert with limit is not working");
			RETURN(1);
		}

		lru.insert_and([](int&, ssize_t, ssize_t){ return DropAction::renew; }, std::make_pair("test6", 666));  // this renews 'test3'

		if (lru.size() != 4) {
			L_ERR("ERROR: LRU::insert_and  is not working");
			RETURN(1);
		}

		if (lru.at("test3") != 333 || lru.at("test4") != 444 || lru.at("test5") != 555 || lru.at("test6") != 666) {
			L_ERR("ERROR: LRU insert is not working");
			RETURN(1);
		}
	} catch (const std::exception& exc) {
		L_EXC("%s\n", exc.what());
	}

	L_ERR("Test LRU with actions is correct!");

	RETURN(0);
}

int test_lru_mutate() {
	INIT_LOG
	LRU<std::string, int> lru(3);
	lru.insert(std::make_pair("test1", 111));

	if (lru.at_and([](int& o){ o = 123; return GetAction::leave; }, "test1") != 123 ||
		lru.get_and([](int& o){ o = 456; return GetAction::leave; }, [](int&, size_t, size_t){ return DropAction::leave; }, "test1") != 456 ||
		lru.at("test1") != 456) {
		L_ERR("ERROR: LRU mutate is not working");
		RETURN(1);
	}

	L_ERR("Test LRU mutate is correct!");

	RETURN(0);
}
<|endoftext|>


<commit_before>/* square using vertex buffer objects */

#include "include/Angel.h"

const int NumPoints = 4;

//----------------------------------------------------------------------------

void init()
{
    GLuint VertexArrayID;
    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);

    // Specify the vertices for a square
    float ext = 0.5;
    vec3 points[] = {
        vec3(-ext, -ext, 0.0), //v1
        vec3( ext, -ext, 0.0), //v2
        vec3( ext,  ext, 0.0), //v3
        vec3(-ext,  ext, 0.0)  //v4
    };

    // Create and initialize a buffer object
    GLuint buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    // Load shaders and use the resulting shader program
    GLuint program = InitShader("vshader.glsl", "fshader.glsl");

    // Initialize the vertex position attribute from the vertex shader
    GLuint loc = glGetAttribLocation(program, "vPosition");
    glEnableVertexAttribArray(loc);
    glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0,
                          BUFFER_OFFSET(0));

    glClearColor(1.0, 1.0, 1.0, 1.0); // white background
}

//----------------------------------------------------------------------------

void display()
{
    // Clear the window
    glClear(GL_COLOR_BUFFER_BIT);

    // Polygon mode and face culling
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glEnable(GL_CULL_FACE);

    // Draw the points as triangle strip
    glDrawArrays(GL_TRIANGLE_STRIP, 0, NumPoints);

    // Force OpenGL to render
    glFlush();
}

//----------------------------------------------------------------------------

void keyboard(unsigned char key, int x, int y)
{
    switch (key) {
        case 033: //ESC
            exit(EXIT_SUCCESS);
            break;
    }
}

//----------------------------------------------------------------------------

int main(int argc, char **argv)
{
    // Initialize glut library
    glutInit(&argc, argv);

    // Create the window
    glutInitDisplayMode(GLUT_3_2_CORE_PROFILE | GLUT_RGBA);
    glutInitWindowSize(512, 512);
    glutCreateWindow("Hello, Square!");

    // Initialize glew library
    // glewInit();

    // Your own initialization
    init();

    // Set callback functions
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);

    // Start the main loop
    glutMainLoop();

    return 0;
}
<commit_msg>Show grids.<commit_after>/* square using vertex buffer objects */

#include "include/Angel.h"

const int NUM_OF_H_BLOCKS = 20;
const int NUM_OF_H_LINES = NUM_OF_H_BLOCKS + 1;
const int NUM_OF_V_BLOCKS = 10;
const int NUM_OF_V_LINES = NUM_OF_V_BLOCKS + 1;
const float H = 0.9;
const float W = 0.82;

//----------------------------------------------------------------------------

void init()
{
    GLuint VertexArrayID;
    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);

    vec4 line_points[NUM_OF_H_LINES + NUM_OF_V_LINES];
    for (int i = 0; i < NUM_OF_H_LINES; ++i) {
        line_points[i] = vec4(-W, -H + H / 10 * i,
                                 W, -H + H / 10 * i);
    }
    for (int i = 0; i < NUM_OF_V_LINES; ++i) {
        line_points[NUM_OF_H_LINES + i] = vec4(-W + W / 5 * i, -H,
                                               -W + W / 5 * i,  H);
    }

    // Create and initialize a buffer object
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(line_points), line_points, GL_STATIC_DRAW);

    // Load shaders and use the resulting shader program
    GLuint program = InitShader("vshader.glsl", "fshader.glsl");

    // Initialize the vertex position attribute from the vertex shader
    GLuint loc = glGetAttribLocation(program, "vPosition");
    glEnableVertexAttribArray(loc);
    glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glClearColor(1.0, 1.0, 1.0, 1.0); // white background
}

//----------------------------------------------------------------------------

void display()
{
    // Clear the window
    glClear(GL_COLOR_BUFFER_BIT);

    // Polygon mode and face culling
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glEnable(GL_CULL_FACE);

    glDrawArrays(GL_LINES, 0, NUM_OF_H_LINES * 2 + NUM_OF_V_LINES * 2);

    // Force OpenGL to render
    glFlush();
}

//----------------------------------------------------------------------------

void keyboard(unsigned char key, int x, int y)
{
    switch (key) {
        case 033: //ESC
            exit(EXIT_SUCCESS);
            break;
    }
}

//----------------------------------------------------------------------------

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_3_2_CORE_PROFILE | GLUT_RGBA);
    glutInitWindowSize(340, 600);
    glutCreateWindow("Xetris!");

    // glewInit();

    init();

    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);

    glutMainLoop();

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016, Simula Research Laboratory
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string/trim.hpp>

#include "pgmread.h"

#define RGB2GRAY_IN_INT

#ifdef RGB2GRAY_IN_INT
// #define RATE_SHIFT 24
// #define R_RATE (uint32_t)(0.298912F * (float)(1<<RATE_SHIFT))
// #define G_RATE (uint32_t)(0.586611F * (float)(1<<RATE_SHIFT))
// #define B_RATE (uint32_t)(0.114478F * (float)(1<<RATE_SHIFT))
// the following are OpenCV's numbers
#define RATE_SHIFT 14
#define R_RATE (uint32_t)4899
#define G_RATE (uint32_t)9617
#define B_RATE (uint32_t)1868
#else
#define R_RATE 0.298912f
#define G_RATE 0.586611f
#define B_RATE 0.114478f
#endif

using namespace std;

unsigned char* readPGMfile( const string& filename, int& w, int& h )
{
    boost::filesystem::path input_file( filename );

    if( not boost::filesystem::exists( input_file ) ) {
        cerr << "File " << input_file << " does not exist" << endl;
        return nullptr;
    }

    ifstream pgmfile( filename.c_str(), ios::binary );
    if( not pgmfile.is_open() ) {
        cerr << "File " << input_file << " could not be opened for reading" << endl;
        return nullptr;
    }

    string pgmtype;
    do {
        getline( pgmfile, pgmtype ); // this is the string version of getline()
        if( pgmfile.fail() ) {
            cerr << "File " << input_file << " is too short" << endl;
            return nullptr;
        }
        boost::algorithm::trim_left( pgmtype ); // nice because of trim
    } while( pgmtype.at(0) == '#' );

    int type;
    if( pgmtype.substr(0,2) == "P2" ) type = 2;
    else if( pgmtype.substr(0,2) == "P3" ) type = 3;
    else if( pgmtype.substr(0,2) == "P5" ) type = 5;
    else if( pgmtype.substr(0,2) == "P6" ) type = 6;
    else {
        cerr << "File " << input_file << " can only contain P2, P3, P5 or P6 PGM images" << endl;
        return nullptr;
    }

    const int maxLineSize{1000};
    char  line[maxLineSize];
    char* parse{nullptr};
    int   maxval{};

    do {
        pgmfile.getline( line, maxLineSize );

        if( pgmfile.fail() ) {
            cerr << "File " << input_file << " is too short" << endl;
            return nullptr;
        }
        int num = pgmfile.gcount();
        parse = line;
        while( (num-- > 0) && isspace( *parse ) ) {
            parse++;
        }
        if( *parse == '#' ) continue;
        int ct = sscanf( parse, "%d %d", &w, &h );
        if( ct != 2 ) {
            cerr << "Error in " << __FILE__ << ":" << __LINE__ << endl
                 << "File " << input_file << " PGM type header (" << type << ") must be followed by comments and WxH info" << endl
                 << "but line contains " << parse << endl;
            return nullptr;
        }
    } while( *parse == '#' );

    if( w <= 0 || h <= 0 ) {
        cerr << "File " << input_file << " has meaningless image size" << endl;
        return nullptr;
    }

    do {
        pgmfile.getline( line, maxLineSize );
        if( pgmfile.fail() ) {
            cerr << "File " << input_file << " is too short" << endl;
            return nullptr;
        }
        int num = pgmfile.gcount();
        parse = line;
        while( (num-- > 0) && isspace( *parse ) ) {
            parse++;
        }
        if( *parse == '#' ) continue;
        int ct = sscanf( parse, "%d", &maxval );
        if( ct != 1 ) {
            cerr << "File " << input_file << " PGM dimensions must be followed by comments and max value info" << endl;
            return nullptr;
        }
    } while( *parse == '#' );

    unsigned char* input_data = new unsigned char[ w * h ];

    switch( type )
    {
    case 2 :
        for( int i=0; i<w*h; i++ ) {
            int input;
            pgmfile >> input;
            if( maxval == 255 ) {
                input_data[i] = input;
            } else {
                input_data[i] = (unsigned char)(input * 255.0 / maxval );
            }
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] input_data;
                return nullptr;
            }
        }
        break;
    case 3 :
        {
            unsigned char* i2 = new unsigned char[ w * h * 3 ];
            unsigned char* src = i2;
            for( int i=0; i<w*h*3; i++ ) {
                int input;
                pgmfile >> input;
                if( maxval == 255 ) {
                    i2[i] = input;
                } else {
                    i2[i] = (unsigned char)(input * 255.0 / maxval );
                }
                if( pgmfile.fail() ) {
                    cerr << "File " << input_file << " file too short" << endl;
                    delete [] i2;
                    delete [] input_data;
                    return nullptr;
                }
            }
            for( int i=0; i<w*h; i++ ) {
#ifdef RGB2GRAY_IN_INT
                unsigned int r = *src; src++;
                unsigned int g = *src; src++;
                unsigned int b = *src; src++;
                unsigned int res = ( ( R_RATE*r+G_RATE*g+B_RATE*b ) >> RATE_SHIFT );
                input_data[i] = (unsigned char)res;
#else // RGB2GRAY_IN_INT
                float r = *src; src++;
                float g = *src; src++;
                float b = *src; src++;
                input_data[i] = (unsigned char)( R_RATE*r+G_RATE*g+B_RATE*b );
#endif // RGB2GRAY_IN_INT
            }
            delete [] i2;
        }
        break;
    case 5 :
        if( maxval < 256 ) {
            pgmfile.read( (char*)input_data, w*h );
        } else {
            unsigned short* i2 = new unsigned short[ w * h ];
            pgmfile.read( (char*)i2, w*h*2 );
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] i2;
                delete [] input_data;
                return nullptr;
            }
            for( int i=0; i<w*h; i++ ) {
                input_data[i] = (unsigned char)(i2[i] * 255.0 / maxval );
            }
            delete [] i2;
        }
        break;
    case 6 :
        if( maxval < 256 ) {
            unsigned char* i2 = new unsigned char[ w * h * 3 ];
            unsigned char* src = i2;
            pgmfile.read( (char*)i2, w*h*3 );
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] i2;
                delete [] input_data;
                return nullptr;
            }
            for( int i=0; i<w*h; i++ ) {
#ifdef RGB2GRAY_IN_INT
                unsigned int r = *src; src++;
                unsigned int g = *src; src++;
                unsigned int b = *src; src++;
                unsigned int res = ( ( R_RATE*r+G_RATE*g+B_RATE*b ) >> RATE_SHIFT );
                input_data[i] = (unsigned char)res;
#else // RGB2GRAY_IN_INT
                float r = *src; src++;
                float g = *src; src++;
                float b = *src; src++;
                input_data[i] = (unsigned char)( R_RATE*r+G_RATE*g+B_RATE*b );
#endif // RGB2GRAY_IN_INT
            }
            delete [] i2;
        } else {
            unsigned short* i2 = new unsigned short[ w * h * 2 * 3 ];
            unsigned short* src = i2;
            pgmfile.read( (char*)i2, w*h*2*3 );
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] i2;
                delete [] input_data;
                return 0;
            }
            for( int i=0; i<w*h; i++ ) {
#ifdef RGB2GRAY_IN_INT
                unsigned int r = *src; src++;
                unsigned int g = *src; src++;
                unsigned int b = *src; src++;
                unsigned int res = ( ( R_RATE*r+G_RATE*g+B_RATE*b ) >> RATE_SHIFT );
                input_data[i] = (unsigned char)res;
#else // RGB2GRAY_IN_INT
                float r = *src; src++;
                float g = *src; src++;
                float b = *src; src++;
                input_data[i] = (unsigned char)( R_RATE*r+G_RATE*g+B_RATE*b );
#endif // RGB2GRAY_IN_INT
            }
            delete [] i2;
        }
        break;
    }

    return input_data;
}

<commit_msg>[apps] const<commit_after>/*
 * Copyright 2016, Simula Research Laboratory
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string/trim.hpp>

#include "pgmread.h"

#define RGB2GRAY_IN_INT

#ifdef RGB2GRAY_IN_INT
// #define RATE_SHIFT 24
// #define R_RATE (uint32_t)(0.298912F * (float)(1<<RATE_SHIFT))
// #define G_RATE (uint32_t)(0.586611F * (float)(1<<RATE_SHIFT))
// #define B_RATE (uint32_t)(0.114478F * (float)(1<<RATE_SHIFT))
// the following are OpenCV's numbers
#define RATE_SHIFT 14
#define R_RATE (uint32_t)4899
#define G_RATE (uint32_t)9617
#define B_RATE (uint32_t)1868
#else
#define R_RATE 0.298912f
#define G_RATE 0.586611f
#define B_RATE 0.114478f
#endif

using namespace std;

unsigned char* readPGMfile( const string& filename, int& w, int& h )
{
    boost::filesystem::path input_file( filename );

    if( not boost::filesystem::exists( input_file ) ) {
        cerr << "File " << input_file << " does not exist" << endl;
        return nullptr;
    }

    ifstream pgmfile( filename.c_str(), ios::binary );
    if( not pgmfile.is_open() ) {
        cerr << "File " << input_file << " could not be opened for reading" << endl;
        return nullptr;
    }

    string pgmtype;
    do {
        getline( pgmfile, pgmtype ); // this is the string version of getline()
        if( pgmfile.fail() ) {
            cerr << "File " << input_file << " is too short" << endl;
            return nullptr;
        }
        boost::algorithm::trim_left( pgmtype ); // nice because of trim
    } while( pgmtype.at(0) == '#' );

    int type;
    if( pgmtype.substr(0,2) == "P2" ) type = 2;
    else if( pgmtype.substr(0,2) == "P3" ) type = 3;
    else if( pgmtype.substr(0,2) == "P5" ) type = 5;
    else if( pgmtype.substr(0,2) == "P6" ) type = 6;
    else {
        cerr << "File " << input_file << " can only contain P2, P3, P5 or P6 PGM images" << endl;
        return nullptr;
    }

    const int maxLineSize{1000};
    char  line[maxLineSize];
    char* parse{nullptr};
    int   maxval{};

    do {
        pgmfile.getline( line, maxLineSize );

        if( pgmfile.fail() ) {
            cerr << "File " << input_file << " is too short" << endl;
            return nullptr;
        }
        int num = pgmfile.gcount();
        parse = line;
        while( (num-- > 0) && isspace( *parse ) ) {
            parse++;
        }
        if( *parse == '#' ) continue;
        const int ct = sscanf( parse, "%d %d", &w, &h );
        if( ct != 2 ) {
            cerr << "Error in " << __FILE__ << ":" << __LINE__ << endl
                 << "File " << input_file << " PGM type header (" << type << ") must be followed by comments and WxH info" << endl
                 << "but line contains " << parse << endl;
            return nullptr;
        }
    } while( *parse == '#' );

    if( w <= 0 || h <= 0 ) {
        cerr << "File " << input_file << " has meaningless image size" << endl;
        return nullptr;
    }

    do {
        pgmfile.getline( line, maxLineSize );
        if( pgmfile.fail() ) {
            cerr << "File " << input_file << " is too short" << endl;
            return nullptr;
        }
        int num = pgmfile.gcount();
        parse = line;
        while( (num-- > 0) && isspace( *parse ) ) {
            parse++;
        }
        if( *parse == '#' ) continue;
        const int ct = sscanf( parse, "%d", &maxval );
        if( ct != 1 ) {
            cerr << "File " << input_file << " PGM dimensions must be followed by comments and max value info" << endl;
            return nullptr;
        }
    } while( *parse == '#' );

    unsigned char* input_data = new unsigned char[ w * h ];

    switch( type )
    {
    case 2 :
        for( int i=0; i<w*h; i++ ) {
            int input;
            pgmfile >> input;
            if( maxval == 255 ) {
                input_data[i] = input;
            } else {
                input_data[i] = (unsigned char)(input * 255.0 / maxval );
            }
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] input_data;
                return nullptr;
            }
        }
        break;
    case 3 :
        {
            unsigned char* i2 = new unsigned char[ w * h * 3 ];
            unsigned char* src = i2;
            for( int i=0; i<w*h*3; i++ ) {
                int input;
                pgmfile >> input;
                if( maxval == 255 ) {
                    i2[i] = input;
                } else {
                    i2[i] = (unsigned char)(input * 255.0 / maxval );
                }
                if( pgmfile.fail() ) {
                    cerr << "File " << input_file << " file too short" << endl;
                    delete [] i2;
                    delete [] input_data;
                    return nullptr;
                }
            }
            for( int i=0; i<w*h; i++ ) {
#ifdef RGB2GRAY_IN_INT
                const unsigned int r = *src; src++;
                const unsigned int g = *src; src++;
                const unsigned int b = *src; src++;
                const unsigned int res = ( ( R_RATE*r+G_RATE*g+B_RATE*b ) >> RATE_SHIFT );
                input_data[i] = (unsigned char)res;
#else // RGB2GRAY_IN_INT
                const float r = *src; src++;
                const float g = *src; src++;
                const float b = *src; src++;
                input_data[i] = (unsigned char)( R_RATE*r+G_RATE*g+B_RATE*b );
#endif // RGB2GRAY_IN_INT
            }
            delete [] i2;
        }
        break;
    case 5 :
        if( maxval < 256 ) {
            pgmfile.read( (char*)input_data, w*h );
        } else {
            unsigned short* i2 = new unsigned short[ w * h ];
            pgmfile.read( (char*)i2, w*h*2 );
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] i2;
                delete [] input_data;
                return nullptr;
            }
            for( int i=0; i<w*h; i++ ) {
                input_data[i] = (unsigned char)(i2[i] * 255.0 / maxval );
            }
            delete [] i2;
        }
        break;
    case 6 :
        if( maxval < 256 ) {
            unsigned char* i2 = new unsigned char[ w * h * 3 ];
            unsigned char* src = i2;
            pgmfile.read( (char*)i2, w*h*3 );
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] i2;
                delete [] input_data;
                return nullptr;
            }
            for( int i=0; i<w*h; i++ ) {
#ifdef RGB2GRAY_IN_INT
                unsigned int r = *src; src++;
                unsigned int g = *src; src++;
                unsigned int b = *src; src++;
                unsigned int res = ( ( R_RATE*r+G_RATE*g+B_RATE*b ) >> RATE_SHIFT );
                input_data[i] = (unsigned char)res;
#else // RGB2GRAY_IN_INT
                float r = *src; src++;
                float g = *src; src++;
                float b = *src; src++;
                input_data[i] = (unsigned char)( R_RATE*r+G_RATE*g+B_RATE*b );
#endif // RGB2GRAY_IN_INT
            }
            delete [] i2;
        } else {
            unsigned short* i2 = new unsigned short[ w * h * 2 * 3 ];
            unsigned short* src = i2;
            pgmfile.read( (char*)i2, w*h*2*3 );
            if( pgmfile.fail() ) {
                cerr << "File " << input_file << " file too short" << endl;
                delete [] i2;
                delete [] input_data;
                return 0;
            }
            for( int i=0; i<w*h; i++ ) {
#ifdef RGB2GRAY_IN_INT
                unsigned int r = *src; src++;
                unsigned int g = *src; src++;
                unsigned int b = *src; src++;
                unsigned int res = ( ( R_RATE*r+G_RATE*g+B_RATE*b ) >> RATE_SHIFT );
                input_data[i] = (unsigned char)res;
#else // RGB2GRAY_IN_INT
                float r = *src; src++;
                float g = *src; src++;
                float b = *src; src++;
                input_data[i] = (unsigned char)( R_RATE*r+G_RATE*g+B_RATE*b );
#endif // RGB2GRAY_IN_INT
            }
            delete [] i2;
        }
        break;
    }

    return input_data;
}

<|endoftext|>


<commit_before>/**
 * @file discrete_distribution.cpp
 * @author Ryan Curtin
 *
 * Implementation of DiscreteDistribution probability distribution.
 */
#include "discrete_distribution.hpp"

using namespace mlpack;
using namespace mlpack::distribution;

/**
 * Return a randomly generated observation according to the probability
 * distribution defined by this object.
 */
size_t DiscreteDistribution::Random() const
{
  // Generate a random number.
  double randObs = (double) rand() / (double) RAND_MAX;

  double sumProb = 0;
  for (size_t obs = 0; obs < probabilities.n_elem; obs++)
    if ((sumProb += probabilities[obs]) >= randObs)
      return obs;
}

/**
 * Estimate the probability distribution directly from the given observations.
 */
void DiscreteDistribution::Estimate(const std::vector<size_t> observations)
{
  // Clear old probabilities.
  probabilities.zeros();

  // Add the probability of each observation.
  for (std::vector<size_t>::const_iterator it = observations.begin();
       it != observations.end(); it++)
    probabilities(*it)++;

  // Now normalize the distribution.
  probabilities /= accu(probabilities);
}

/**
 * Estimate the probability distribution from the given observations when also
 * given probabilities that each observation is from this distribution.
 */
void DiscreteDistribution::Estimate(const std::vector<size_t> observations,
                                    const std::vector<double> probObs)
{
  // Clear old probabilities.
  probabilities.zeros();

  // Add the probability of each observation.
  for (size_t i = 0; i < observations.size(); i++)
    probabilities(observations[i]) += probObs[i];

  // Now normalize the distribution.
  probabilities /= accu(probabilities);
}

/**
 * Set the vector of probabilities correctly.
 */
void DiscreteDistribution::Probabilities(const arma::vec& probabilities)
{
  this->probabilities = probabilities / accu(probabilities);
}
<commit_msg>Fix compiler warning.<commit_after>/**
 * @file discrete_distribution.cpp
 * @author Ryan Curtin
 *
 * Implementation of DiscreteDistribution probability distribution.
 */
#include "discrete_distribution.hpp"

using namespace mlpack;
using namespace mlpack::distribution;

/**
 * Return a randomly generated observation according to the probability
 * distribution defined by this object.
 */
size_t DiscreteDistribution::Random() const
{
  // Generate a random number.
  double randObs = (double) rand() / (double) RAND_MAX;

  double sumProb = 0;
  for (size_t obs = 0; obs < probabilities.n_elem; obs++)
    if ((sumProb += probabilities[obs]) >= randObs)
      return obs;

  // This shouldn't happen.
  return probabilities.n_elem - 1;
}

/**
 * Estimate the probability distribution directly from the given observations.
 */
void DiscreteDistribution::Estimate(const std::vector<size_t> observations)
{
  // Clear old probabilities.
  probabilities.zeros();

  // Add the probability of each observation.
  for (std::vector<size_t>::const_iterator it = observations.begin();
       it != observations.end(); it++)
    probabilities(*it)++;

  // Now normalize the distribution.
  probabilities /= accu(probabilities);
}

/**
 * Estimate the probability distribution from the given observations when also
 * given probabilities that each observation is from this distribution.
 */
void DiscreteDistribution::Estimate(const std::vector<size_t> observations,
                                    const std::vector<double> probObs)
{
  // Clear old probabilities.
  probabilities.zeros();

  // Add the probability of each observation.
  for (size_t i = 0; i < observations.size(); i++)
    probabilities(observations[i]) += probObs[i];

  // Now normalize the distribution.
  probabilities /= accu(probabilities);
}

/**
 * Set the vector of probabilities correctly.
 */
void DiscreteDistribution::Probabilities(const arma::vec& probabilities)
{
  this->probabilities = probabilities / accu(probabilities);
}
<|endoftext|>


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

#define __TITLE__ "TwoPhaseCommit"

namespace dsn { namespace replication {

void replica::on_client_write(int code, message_ptr& request)
{
    check_hashed_access();

    if (PS_PRIMARY != status())
    {
        response_client_message(request, ERR_INVALID_STATE);
        return;
    }

    mutation_ptr mu = new_mutation(_prepare_list->max_decree() + 1);
    mu->set_client_request(code, request);
    init_prepare(mu);
}

void replica::init_prepare(mutation_ptr& mu)
{
    dassert (PS_PRIMARY == status(), "");

    error_code err = ERR_SUCCESS;
    if (static_cast<int>(_primary_states.membership.secondaries.size()) + 1 < _options.mutation_2pc_min_replica_count)
    {
        err = ERR_NOT_ENOUGH_MEMBER;
        goto ErrOut;
    }

    mu->data.header.last_committed_decree = last_committed_decree();
    if (mu->data.header.decree == invalid_decree)
    {
        mu->set_id(get_ballot(), _prepare_list->max_decree() + 1);
    }
    else
    {
        mu->set_id(get_ballot(), mu->data.header.decree);
    }

    if (mu->data.header.decree > _prepare_list->max_decree() && _prepare_list->count() >= _options.staleness_for_commit)
    {
        err = ERR_CAPACITY_EXCEEDED;
        goto ErrOut;
    }

    dassert (mu->data.header.decree > last_committed_decree(), "");

    // local prepare without log
    err = _prepare_list->prepare(mu, PS_PRIMARY);
    if (err != ERR_SUCCESS)
    {
        goto ErrOut;
    }

    ddebug("%s: mutation %s init_prepare", name(), mu->name());

    //
    // TODO: bounded staleness on secondaries
    //
    dassert (mu->data.header.decree <= last_committed_decree() + _options.staleness_for_commit, "");

    // remote prepare
    dassert (mu->remote_tasks().size() == 0, "");
    mu->set_left_secondary_ack_count((unsigned int)_primary_states.membership.secondaries.size());
    for (auto it = _primary_states.membership.secondaries.begin(); it != _primary_states.membership.secondaries.end(); it++)
    {
        send_prepare_message(*it, PS_SECONDARY, mu, _options.prepare_timeout_ms_for_secondaries);
    }

    uint8_t count = 0;
    for (auto it = _primary_states.learners.begin(); it != _primary_states.learners.end(); it++)
    {
        if (it->second.prepare_start_decree != invalid_decree && mu->data.header.decree >= it->second.prepare_start_decree)
        {
            send_prepare_message(it->first, PS_POTENTIAL_SECONDARY, mu, _options.prepare_timeout_ms_for_potential_secondaries);
            count++;
        }
    }
    mu->set_left_potential_secondary_ack_count(count);

    // local log
    dassert (mu->data.header.log_offset == invalid_offset, "");
    dassert (mu->log_task() == nullptr, "");
    mu->log_task() = _stub->_log->append(mu,
        LPC_WRITE_REPLICATION_LOG,
        this,
        std::bind(&replica::on_append_log_completed, this, mu,
            std::placeholders::_1,
            std::placeholders::_2),
        gpid_to_hash(get_gpid())
        );

    dassert(nullptr != mu->log_task(), "");
    return;

ErrOut:
    response_client_message(mu->client_request, err);
    return;
}

void replica::send_prepare_message(const end_point& addr, partition_status status, mutation_ptr& mu, int timeout_milliseconds)
{
    message_ptr msg = message::create_request(RPC_PREPARE, timeout_milliseconds, gpid_to_hash(get_gpid()));
    marshall(msg, get_gpid());

    replica_configuration rconfig;
    _primary_states.get_replica_config(status, rconfig);

    marshall(msg, rconfig);
    mu->write_to(msg);

    dbg_dassert (mu->remote_tasks().find(addr) == mu->remote_tasks().end());

    mu->remote_tasks()[addr] = rpc::call(addr, msg,
        this,
        std::bind(&replica::on_prepare_reply,
            this,
            std::make_pair(mu, rconfig.status),
            std::placeholders::_1,
            std::placeholders::_2,
            std::placeholders::_3),
        gpid_to_hash(get_gpid())
        );

    ddebug(
        "%s: mutation %s send_prepare_message to %s:%d as %s",
        name(), mu->name(),
        addr.name.c_str(), static_cast<int>(addr.port),
        enum_to_string(rconfig.status)
        );
}

void replica::do_possible_commit_on_primary(mutation_ptr& mu)
{
    dassert (_config.ballot == mu->data.header.ballot, "");
    dassert (PS_PRIMARY == status(), "");

    if (mu->is_ready_for_commit(_options.prepare_ack_on_secondary_before_logging_allowed))
    {
        _prepare_list->commit(mu->data.header.decree, false);
    }
}

void replica::on_prepare(message_ptr& request)
{
    check_hashed_access();

    replica_configuration rconfig;
    unmarshall(request, rconfig);

    mutation_ptr mu = mutation::read_from(request);
    decree decree = mu->data.header.decree;

    ddebug( "%s: mutation %s on_prepare", name(), mu->name());

    dassert (mu->data.header.ballot == rconfig.ballot, "");

    if (mu->data.header.ballot < get_ballot())
    {
        ddebug( "%s: mutation %s on_prepare skipped due to old view", name(), mu->name());
        return;
    }

    // update configuration when necessary
    else if (rconfig.ballot > get_ballot())
    {
        update_local_configuration(rconfig);
    }

    if (PS_INACTIVE == status() || PS_ERROR == status())
    {
        ddebug(
            "%s: mutation %s on_prepare  to %s skipped",
            name(), mu->name(),
            enum_to_string(status())
            );
        ack_prepare_message(ERR_INVALID_STATE, mu);
        return;
    }

    else if (PS_POTENTIAL_SECONDARY == status())
    {
        if (_potential_secondary_states.learning_status != LearningWithPrepare && _potential_secondary_states.learning_status != LearningSucceeded)
        {
            ddebug(
                "%s: mutation %s on_prepare to %s skipped, learnings state = %s",
                name(), mu->name(),
                enum_to_string(status()),
                enum_to_string(_potential_secondary_states.learning_status)
                );

            // do not retry as there may retries later
            return;
        }
    }

    dassert (rconfig.status == status(), "");
    if (decree <= last_committed_decree())
    {
        ack_prepare_message(ERR_SUCCESS, mu);
        return;
    }

    // real prepare start
    auto mu2 = _prepare_list->get_mutation_by_decree(decree);
    if (mu2 != nullptr && mu2->data.header.ballot == mu->data.header.ballot)
    {
        ddebug( "%s: mutation %s redundant prepare skipped", name(), mu->name());

        if (mu2->is_prepared() || _options.prepare_ack_on_secondary_before_logging_allowed)
        {
            ack_prepare_message(ERR_SUCCESS, mu);
        }
        return;
    }

    int err = _prepare_list->prepare(mu, status());
    dassert (err == ERR_SUCCESS, "");

    if (PS_POTENTIAL_SECONDARY == status())
    {
        dassert (mu->data.header.decree <= last_committed_decree() + _options.staleness_for_start_prepare_for_potential_secondary, "");
    }
    else
    {
        dassert (PS_SECONDARY == status(), "");
        dassert (mu->data.header.decree <= last_committed_decree() + _options.staleness_for_commit, "");
    }

    // ack without logging
    if (_options.prepare_ack_on_secondary_before_logging_allowed)
    {
        ack_prepare_message(err, mu);
    }

    // write log
    dassert (mu->log_task() == nullptr, "");
    mu->log_task() = _stub->_log->append(mu,
        LPC_WRITE_REPLICATION_LOG,
        this,
        std::bind(&replica::on_append_log_completed, this, mu, std::placeholders::_1, std::placeholders::_2),
        gpid_to_hash(get_gpid())
        );

    dassert(mu->log_task() != nullptr, "");
}

void replica::on_append_log_completed(mutation_ptr& mu, uint32_t err, uint32_t size)
{
    check_hashed_access();

    ddebug( "%s: mutation %s on_append_log_completed, err = %u", name(), mu->name(), err);

    if (err == ERR_SUCCESS)
    {
        mu->set_logged();
    }

    // skip old mutations
    if (mu->data.header.ballot < get_ballot() || status() == PS_INACTIVE)
    {
        return;
    }

    switch (status())
    {
    case PS_PRIMARY:
        if (err == ERR_SUCCESS)
        {
            do_possible_commit_on_primary(mu);
        }
        else
        {
            handle_local_failure(err);
        }
        break;
    case PS_SECONDARY:
    case PS_POTENTIAL_SECONDARY:
        if (err != ERR_SUCCESS)
        {
            handle_local_failure(err);
        }

        if (!_options.prepare_ack_on_secondary_before_logging_allowed)
        {
            ack_prepare_message(err, mu);
        }
        break;
    case PS_ERROR:
        break;
    default:
        dassert (false, "");
        break;
    }
}

void replica::on_prepare_reply(std::pair<mutation_ptr, partition_status> pr, int err, message_ptr& request, message_ptr& reply)
{
    check_hashed_access();

    mutation_ptr& mu = pr.first;
    partition_status targetStatus = pr.second;

    // skip callback for old mutations
    if (mu->data.header.ballot < get_ballot() || PS_PRIMARY != status())
        return;

    dassert (mu->data.header.ballot == get_ballot(), "");

    end_point node = request->header().to_address;
    partition_status st = _primary_states.GetNodeStatus(node);

    // handle reply
    prepare_ack resp;

    // handle error
    if (err)
    {
        resp.err = err;
    }
    else
    {
        unmarshall(reply, resp);

        ddebug(
            "%s: mutation %s on_prepare_reply from %s:%d",
            name(), mu->name(),
            node.name.c_str(), static_cast<int>(node.port)
            );
    }

    if (resp.err == ERR_SUCCESS)
    {
        dassert (resp.ballot == get_ballot(), "");
        dassert (resp.decree == mu->data.header.decree, "");

        switch (targetStatus)
        {
        case PS_SECONDARY:
            dassert (_primary_states.check_exist(node, PS_SECONDARY), "");
            dassert (mu->left_secondary_ack_count() > 0, "");
            if (0 == mu->decrease_left_secondary_ack_count())
            {
                do_possible_commit_on_primary(mu);
            }
            break;
        case PS_POTENTIAL_SECONDARY:
            dassert (mu->left_potential_secondary_ack_count() > 0, "");
            if (0 == mu->decrease_left_potential_secondary_ack_count())
            {
                do_possible_commit_on_primary(mu);
            }
            break;
        default:
            ddebug(
                "%s: mutation %s prepare ack skipped coz the node is now inactive", name(), mu->name()
                );
            break;
        }
    }

    // failure handling
    else
    {
        // note targetStatus and (curent) status may diff
        if (targetStatus == PS_POTENTIAL_SECONDARY)
        {
            dassert (mu->left_potential_secondary_ack_count() > 0, "");
            if (0 == mu->decrease_left_potential_secondary_ack_count())
            {
                do_possible_commit_on_primary(mu);
            }
        }
        handle_remote_failure(st, node, resp.err);
    }
}

void replica::ack_prepare_message(int err, mutation_ptr& mu)
{
    prepare_ack resp;
    resp.gpid = get_gpid();
    resp.err = err;
    resp.ballot = get_ballot();
    resp.decree = mu->data.header.decree;

    // for PS_POTENTIAL_SECONDARY ONLY
    resp.last_committed_decree_in_app = _app->last_committed_decree();
    resp.last_committed_decree_in_prepare_list = last_committed_decree();

    dassert (nullptr != mu->owner_message(), "");
    reply(mu->owner_message(), resp);

    ddebug( "%s: mutation %s ack_prepare_message", name(), mu->name());
}

void replica::cleanup_preparing_mutations(bool is_primary)
{
    decree start = last_committed_decree() + 1;
    decree end = _prepare_list->max_decree();

    for (decree decree = start; decree <= end; decree++)
    {
        mutation_ptr mu = _prepare_list->get_mutation_by_decree(decree);
        if (mu != nullptr)
        {
            int c = mu->clear_prepare_or_commit_tasks();
            if (!is_primary)
            {
                dassert (0 == c, "");
            }
            else
            {
            }

            mu->clear_log_task();
        }
    }
}

}} // namespace
<commit_msg>fix compile error on ubuntu due to Goto across local vars definition<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "replica.h"
#include "mutation.h"
#include "mutation_log.h"
#include "replica_stub.h"

#define __TITLE__ "TwoPhaseCommit"

namespace dsn { namespace replication {

void replica::on_client_write(int code, message_ptr& request)
{
    check_hashed_access();

    if (PS_PRIMARY != status())
    {
        response_client_message(request, ERR_INVALID_STATE);
        return;
    }

    mutation_ptr mu = new_mutation(_prepare_list->max_decree() + 1);
    mu->set_client_request(code, request);
    init_prepare(mu);
}

void replica::init_prepare(mutation_ptr& mu)
{
    dassert (PS_PRIMARY == status(), "");

    error_code err = ERR_SUCCESS;
    uint8_t count = 0;

    if (static_cast<int>(_primary_states.membership.secondaries.size()) + 1 < _options.mutation_2pc_min_replica_count)
    {
        err = ERR_NOT_ENOUGH_MEMBER;
        goto ErrOut;
    }

    mu->data.header.last_committed_decree = last_committed_decree();
    if (mu->data.header.decree == invalid_decree)
    {
        mu->set_id(get_ballot(), _prepare_list->max_decree() + 1);
    }
    else
    {
        mu->set_id(get_ballot(), mu->data.header.decree);
    }

    if (mu->data.header.decree > _prepare_list->max_decree() && _prepare_list->count() >= _options.staleness_for_commit)
    {
        err = ERR_CAPACITY_EXCEEDED;
        goto ErrOut;
    }

    dassert (mu->data.header.decree > last_committed_decree(), "");

    // local prepare without log
    err = _prepare_list->prepare(mu, PS_PRIMARY);
    if (err != ERR_SUCCESS)
    {
        goto ErrOut;
    }

    ddebug("%s: mutation %s init_prepare", name(), mu->name());

    //
    // TODO: bounded staleness on secondaries
    //
    dassert (mu->data.header.decree <= last_committed_decree() + _options.staleness_for_commit, "");

    // remote prepare
    dassert (mu->remote_tasks().size() == 0, "");
    mu->set_left_secondary_ack_count((unsigned int)_primary_states.membership.secondaries.size());
    for (auto it = _primary_states.membership.secondaries.begin(); it != _primary_states.membership.secondaries.end(); it++)
    {
        send_prepare_message(*it, PS_SECONDARY, mu, _options.prepare_timeout_ms_for_secondaries);
    }

    count = 0;
    for (auto it = _primary_states.learners.begin(); it != _primary_states.learners.end(); it++)
    {
        if (it->second.prepare_start_decree != invalid_decree && mu->data.header.decree >= it->second.prepare_start_decree)
        {
            send_prepare_message(it->first, PS_POTENTIAL_SECONDARY, mu, _options.prepare_timeout_ms_for_potential_secondaries);
            count++;
        }
    }
    mu->set_left_potential_secondary_ack_count(count);

    // local log
    dassert (mu->data.header.log_offset == invalid_offset, "");
    dassert (mu->log_task() == nullptr, "");
    mu->log_task() = _stub->_log->append(mu,
        LPC_WRITE_REPLICATION_LOG,
        this,
        std::bind(&replica::on_append_log_completed, this, mu,
            std::placeholders::_1,
            std::placeholders::_2),
        gpid_to_hash(get_gpid())
        );

    dassert(nullptr != mu->log_task(), "");
    return;

ErrOut:
    response_client_message(mu->client_request, err);
    return;
}

void replica::send_prepare_message(const end_point& addr, partition_status status, mutation_ptr& mu, int timeout_milliseconds)
{
    message_ptr msg = message::create_request(RPC_PREPARE, timeout_milliseconds, gpid_to_hash(get_gpid()));
    marshall(msg, get_gpid());

    replica_configuration rconfig;
    _primary_states.get_replica_config(status, rconfig);

    marshall(msg, rconfig);
    mu->write_to(msg);

    dbg_dassert (mu->remote_tasks().find(addr) == mu->remote_tasks().end());

    mu->remote_tasks()[addr] = rpc::call(addr, msg,
        this,
        std::bind(&replica::on_prepare_reply,
            this,
            std::make_pair(mu, rconfig.status),
            std::placeholders::_1,
            std::placeholders::_2,
            std::placeholders::_3),
        gpid_to_hash(get_gpid())
        );

    ddebug(
        "%s: mutation %s send_prepare_message to %s:%d as %s",
        name(), mu->name(),
        addr.name.c_str(), static_cast<int>(addr.port),
        enum_to_string(rconfig.status)
        );
}

void replica::do_possible_commit_on_primary(mutation_ptr& mu)
{
    dassert (_config.ballot == mu->data.header.ballot, "");
    dassert (PS_PRIMARY == status(), "");

    if (mu->is_ready_for_commit(_options.prepare_ack_on_secondary_before_logging_allowed))
    {
        _prepare_list->commit(mu->data.header.decree, false);
    }
}

void replica::on_prepare(message_ptr& request)
{
    check_hashed_access();

    replica_configuration rconfig;
    unmarshall(request, rconfig);

    mutation_ptr mu = mutation::read_from(request);
    decree decree = mu->data.header.decree;

    ddebug( "%s: mutation %s on_prepare", name(), mu->name());

    dassert (mu->data.header.ballot == rconfig.ballot, "");

    if (mu->data.header.ballot < get_ballot())
    {
        ddebug( "%s: mutation %s on_prepare skipped due to old view", name(), mu->name());
        return;
    }

    // update configuration when necessary
    else if (rconfig.ballot > get_ballot())
    {
        update_local_configuration(rconfig);
    }

    if (PS_INACTIVE == status() || PS_ERROR == status())
    {
        ddebug(
            "%s: mutation %s on_prepare  to %s skipped",
            name(), mu->name(),
            enum_to_string(status())
            );
        ack_prepare_message(ERR_INVALID_STATE, mu);
        return;
    }

    else if (PS_POTENTIAL_SECONDARY == status())
    {
        if (_potential_secondary_states.learning_status != LearningWithPrepare && _potential_secondary_states.learning_status != LearningSucceeded)
        {
            ddebug(
                "%s: mutation %s on_prepare to %s skipped, learnings state = %s",
                name(), mu->name(),
                enum_to_string(status()),
                enum_to_string(_potential_secondary_states.learning_status)
                );

            // do not retry as there may retries later
            return;
        }
    }

    dassert (rconfig.status == status(), "");
    if (decree <= last_committed_decree())
    {
        ack_prepare_message(ERR_SUCCESS, mu);
        return;
    }

    // real prepare start
    auto mu2 = _prepare_list->get_mutation_by_decree(decree);
    if (mu2 != nullptr && mu2->data.header.ballot == mu->data.header.ballot)
    {
        ddebug( "%s: mutation %s redundant prepare skipped", name(), mu->name());

        if (mu2->is_prepared() || _options.prepare_ack_on_secondary_before_logging_allowed)
        {
            ack_prepare_message(ERR_SUCCESS, mu);
        }
        return;
    }

    int err = _prepare_list->prepare(mu, status());
    dassert (err == ERR_SUCCESS, "");

    if (PS_POTENTIAL_SECONDARY == status())
    {
        dassert (mu->data.header.decree <= last_committed_decree() + _options.staleness_for_start_prepare_for_potential_secondary, "");
    }
    else
    {
        dassert (PS_SECONDARY == status(), "");
        dassert (mu->data.header.decree <= last_committed_decree() + _options.staleness_for_commit, "");
    }

    // ack without logging
    if (_options.prepare_ack_on_secondary_before_logging_allowed)
    {
        ack_prepare_message(err, mu);
    }

    // write log
    dassert (mu->log_task() == nullptr, "");
    mu->log_task() = _stub->_log->append(mu,
        LPC_WRITE_REPLICATION_LOG,
        this,
        std::bind(&replica::on_append_log_completed, this, mu, std::placeholders::_1, std::placeholders::_2),
        gpid_to_hash(get_gpid())
        );

    dassert(mu->log_task() != nullptr, "");
}

void replica::on_append_log_completed(mutation_ptr& mu, uint32_t err, uint32_t size)
{
    check_hashed_access();

    ddebug( "%s: mutation %s on_append_log_completed, err = %u", name(), mu->name(), err);

    if (err == ERR_SUCCESS)
    {
        mu->set_logged();
    }

    // skip old mutations
    if (mu->data.header.ballot < get_ballot() || status() == PS_INACTIVE)
    {
        return;
    }

    switch (status())
    {
    case PS_PRIMARY:
        if (err == ERR_SUCCESS)
        {
            do_possible_commit_on_primary(mu);
        }
        else
        {
            handle_local_failure(err);
        }
        break;
    case PS_SECONDARY:
    case PS_POTENTIAL_SECONDARY:
        if (err != ERR_SUCCESS)
        {
            handle_local_failure(err);
        }

        if (!_options.prepare_ack_on_secondary_before_logging_allowed)
        {
            ack_prepare_message(err, mu);
        }
        break;
    case PS_ERROR:
        break;
    default:
        dassert (false, "");
        break;
    }
}

void replica::on_prepare_reply(std::pair<mutation_ptr, partition_status> pr, int err, message_ptr& request, message_ptr& reply)
{
    check_hashed_access();

    mutation_ptr& mu = pr.first;
    partition_status targetStatus = pr.second;

    // skip callback for old mutations
    if (mu->data.header.ballot < get_ballot() || PS_PRIMARY != status())
        return;

    dassert (mu->data.header.ballot == get_ballot(), "");

    end_point node = request->header().to_address;
    partition_status st = _primary_states.GetNodeStatus(node);

    // handle reply
    prepare_ack resp;

    // handle error
    if (err)
    {
        resp.err = err;
    }
    else
    {
        unmarshall(reply, resp);

        ddebug(
            "%s: mutation %s on_prepare_reply from %s:%d",
            name(), mu->name(),
            node.name.c_str(), static_cast<int>(node.port)
            );
    }

    if (resp.err == ERR_SUCCESS)
    {
        dassert (resp.ballot == get_ballot(), "");
        dassert (resp.decree == mu->data.header.decree, "");

        switch (targetStatus)
        {
        case PS_SECONDARY:
            dassert (_primary_states.check_exist(node, PS_SECONDARY), "");
            dassert (mu->left_secondary_ack_count() > 0, "");
            if (0 == mu->decrease_left_secondary_ack_count())
            {
                do_possible_commit_on_primary(mu);
            }
            break;
        case PS_POTENTIAL_SECONDARY:
            dassert (mu->left_potential_secondary_ack_count() > 0, "");
            if (0 == mu->decrease_left_potential_secondary_ack_count())
            {
                do_possible_commit_on_primary(mu);
            }
            break;
        default:
            ddebug(
                "%s: mutation %s prepare ack skipped coz the node is now inactive", name(), mu->name()
                );
            break;
        }
    }

    // failure handling
    else
    {
        // note targetStatus and (curent) status may diff
        if (targetStatus == PS_POTENTIAL_SECONDARY)
        {
            dassert (mu->left_potential_secondary_ack_count() > 0, "");
            if (0 == mu->decrease_left_potential_secondary_ack_count())
            {
                do_possible_commit_on_primary(mu);
            }
        }
        handle_remote_failure(st, node, resp.err);
    }
}

void replica::ack_prepare_message(int err, mutation_ptr& mu)
{
    prepare_ack resp;
    resp.gpid = get_gpid();
    resp.err = err;
    resp.ballot = get_ballot();
    resp.decree = mu->data.header.decree;

    // for PS_POTENTIAL_SECONDARY ONLY
    resp.last_committed_decree_in_app = _app->last_committed_decree();
    resp.last_committed_decree_in_prepare_list = last_committed_decree();

    dassert (nullptr != mu->owner_message(), "");
    reply(mu->owner_message(), resp);

    ddebug( "%s: mutation %s ack_prepare_message", name(), mu->name());
}

void replica::cleanup_preparing_mutations(bool is_primary)
{
    decree start = last_committed_decree() + 1;
    decree end = _prepare_list->max_decree();

    for (decree decree = start; decree <= end; decree++)
    {
        mutation_ptr mu = _prepare_list->get_mutation_by_decree(decree);
        if (mu != nullptr)
        {
            int c = mu->clear_prepare_or_commit_tasks();
            if (!is_primary)
            {
                dassert (0 == c, "");
            }
            else
            {
            }

            mu->clear_log_task();
        }
    }
}

}} // namespace
<|endoftext|>