<commit_before>#include "server.hpp"

namespace po = boost::program_options;
using std::cout;
using std::cerr;
using std::endl;
using std::string;

sf::UdpSocket socket;
unsigned short client_port;

Game* game;

// callback for interrupt signal
void shutdown(int s)
{
	(void)s; // intentionally unused

	cout << "\nServer shutting down...";
	cout.flush();

	if (game != nullptr)
	{
		cout << "\nNotifying players...";
		for (const auto& pair: game->get_players())
		{
			sf::Packet sorry;
			sorry << sv_disconnect << sf::Uint8(4);
			socket.send(sorry, pair.second.get_ip(), client_port);
		}
		cout << endl;

		delete game;
	}

	exit(0);
}

int main(int argc, char* argv[])
{
	// set up shutdown callback for interrupts
	struct sigaction action;
	action.sa_handler = shutdown;
	sigemptyset(&action.sa_mask);
	action.sa_flags = 0;

	sigaction(SIGINT, &action, nullptr);
	sigaction(SIGTERM, &action, nullptr);
	sigaction(SIGKILL, &action, nullptr);

	// command line arguments
	po::options_description desc("Bananagrams dedicated server options");
	desc.add_options()
		("help",                                                                   "show options")
		("dict",  po::value<string>()->required(),                                 "dictionary file")
		("port",  po::value<unsigned short>()->default_value(default_server_port), "TCP/UDP listening port")
		("bunch", po::value<string>()->default_value("1"),                         "bunch multiplier (0.5 or a positive integer)")
		("limit", po::value<unsigned int>(),                                       "player limit")
	;

	// TODO usage string
	// TODO print help on unrecognized options
	po::variables_map opts;
	po::store(po::parse_command_line(argc, argv, desc), opts);

	if (opts.count("help"))
	{
		cerr << desc << endl;
		return 1;
	}

	try
	{
		po::notify(opts);
	}
	catch (po::required_option& e)
	{
		cerr << "Error: " << e.what() << endl << endl << desc << endl;
		return 1;
	}

	// check port option
	unsigned short server_port {opts["port"].as<unsigned short>()};
	if (server_port == 0)
	{
		cerr << "Invalid listening port: " << server_port << "!\n";
		return 1;
	}

	client_port = server_port + 1;

	// game options
	unsigned int b_num;
	unsigned int b_den;
	unsigned int max_players;
	std::map<string, string> dictionary;

	// load options from command line

	// check bunch multiplier option
	std::stringstream multi_s;
	multi_s << opts["bunch"].as<string>();
	b_num = 1;
	b_den = 1;
	if (multi_s.str() == "0.5")
		b_den = 2;
	else
		multi_s >> b_num;

	max_players = (8 * b_num) / b_den;

	if (opts.count("limit"))
	{
		auto limit = opts["limit"].as<unsigned int>();

		if (limit > max_players)
		{
			cerr << "\nmax player limit is " << max_players << " for this bunch size";
			cerr.flush();
		}
		else if (limit < 2)
		{
			cerr << "\nmin player limit is 2";
			cerr.flush();
		}
		else
		{
			max_players = limit;
		}
	}

	// check dictionary option
	if (!opts.count("dict"))
	{
		cerr << "No dictionary file specified!\n";
		return 1;
	}

	string dict = opts["dict"].as<string>();

	cout << "Loading dictionary... ";
	cout.flush();
	std::ifstream words(dict);
	if (!words.is_open())
	{
		std::cerr << "\nFailed to open " << dict << "!\n";
		return 1;
	}

	// parse dictionary
	string line;
	while (std::getline(words, line))
	{
		auto pos = line.find_first_of(' ');
		if (pos == string::npos)
			dictionary[line] = "";
		else
			dictionary[line.substr(0, pos)] = line.substr(pos + 1, string::npos);
	}
	words.close();
	cout << dictionary.size() << " words found";
	cout.flush();

	// TODO srand
	// LET'S GO!!!

	// TODO catch failure
	socket.bind(server_port);
	socket.setBlocking(false);

	sf::Clock timer;

	string peeler;

	game = new Game(b_num, b_den, max_players);

	cout << "\nWaiting for players to join...";
	cout.flush();
	while (true)
	{
		float elapsed = timer.getElapsedTime().asSeconds();
		timer.restart();

		// send pending packets
		for (auto& pair : game->get_players())
		{
			if (pair.second.has_pending())
			{
				pair.second.step(elapsed);

				if (pair.second.get_timeout() > 5.f)
				{
					cout << endl << pair.second.get_name() << " timed out";
					cout.flush();
					game->remove_player(pair.first);
				}
				else if (pair.second.poll > 0.5f)
				{
					pair.second.poll -= 0.5f;
					sf::Packet pending(pair.second.get_pending());
					socket.send(pending, pair.second.get_ip(), client_port);
				}
			}
		}

		// get new packets
		sf::Packet packet;
		sf::IpAddress client_ip;
		unsigned short client_port;

		if (socket.receive(packet, client_ip, client_port) != sf::Socket::Status::Done)
			continue;

		sf::Uint8 type;
		std::string id;

		// every client packet starts with a type and player id
		packet >> type;
		packet >> id;

		// only process packets if we know the player or they're connecting
		if (type != cl_connect && !game->has_player(id))
			continue;

		switch(type)
		{
			case cl_connect:
			{
				sf::Uint8 version;
				packet >> version;
				if (version != protocol_version)
				{
					sf::Packet sorry;
					sorry << sv_disconnect << sf::Uint8(0);
					socket.send(sorry, client_ip, client_port);

					cout << "\nclient failed to join"
					        "\n\tNeed protocol version " << (int)protocol_version << ", got " << (int)version;
					cout.flush();
					break;
				}

				std::string name;
				packet >> name;

				// if it is an unknown player
				if (!game->has_player(id))
				{
					if (game->is_full())
					{
						sf::Packet sorry;
						sorry << sv_disconnect << sf::Uint8(1);
						socket.send(sorry, client_ip, client_port);

						cout << "\nclient failed to join"
						        "\n\tGame full";
						cout.flush();
						break;
					}

					if (game->in_progress())
					{
						sf::Packet sorry;
						sorry << sv_disconnect << sf::Uint8(3);
						socket.send(sorry, client_ip, client_port);

						cout << "\nclient failed to join"
						        "\n\tGame already started";
						cout.flush();
						break;
					}

					// let everyone else know about the connection
					sf::Packet join;
					join << sv_info << id << sf::Uint8(0) << name;
					for (auto& pair : game->get_players())
					{
						if (!pair.second.has_pending())
							socket.send(join, pair.second.get_ip(), client_port);
						pair.second.add_pending(join);
						game->wait();
					}

					Player& new_player = game->add_player(id, client_ip, name);

					// let new player know about all other players
					for (auto& pair : game->get_players())
					{
						if (pair.first == id) continue;

						sf::Packet join;
						join << sv_info << pair.first << sf::Uint8(0) << pair.second.get_name();
						if (!new_player.has_pending())
							socket.send(join, client_ip, client_port);
						new_player.add_pending(join);
						game->wait();
					}

					cout << "\n" << name << " has joined the game";
					cout.flush();
				}

				// by now we know they are a player, ack connection
				sf::Packet join;
				join << sv_info << id << sf::Uint8(0) << name;
				socket.send(join, client_ip, client_port);

				break;
			}
			case cl_disconnect:
			{
				cout << "\n" << game->get_player_name(id) << " has left the game";
				cout.flush();

				game->remove_player(id);

				// let everyone else know about disconnection
				sf::Packet leave;
				leave << sv_info << id << sf::Uint8(1);
				for (auto& pair : game->get_players())
				{
					if (!pair.second.has_pending())
						socket.send(leave, pair.second.get_ip(), client_port);
					pair.second.add_pending(leave);
					game->wait();
				}

				// TODO end game if now below limit

				break;
			}
			case cl_ready:
			{
				bool ready;
				packet >> ready;

				game->set_ready(id, ready);

				sf::Packet rdy;
				rdy << sv_info << id << (ready ? sf::Uint8(2) : sf::Uint8(3));
				for (const auto& pair : game->get_players())
					socket.send(rdy, pair.second.get_ip(), client_port);

				break;
			}
			case cl_check:
			{
				string word;
				packet >> word;

				sf::Packet lookup;
				lookup << sv_check << word << (dictionary.count(word) == 1);

				socket.send(lookup, client_ip, client_port);

				break;
			}
			case cl_dump:
			{
				sf::Int16 dump_n;
				sf::Int8 chr;
				packet >> dump_n >> chr;

				if (game->check_dump(id, dump_n))
				{
					sf::Packet dump;

					string letters = game->dump(id, dump_n, chr);

					cout << endl << game->get_player_name(id) << " dumped " << chr
					     << "\n\tand received " << letters;
					cout.flush();

					dump << sv_dump << dump_n << letters;

					socket.send(dump, client_ip, client_port);
				}

				break;
			}
			case cl_peel:
			{
				sf::Int16 client_peel;
				packet >> client_peel;

				// make sure peel number is correct
				if (game->check_peel(client_peel))
				{
					// store first peeler id
					if (peeler.size() == 0)
						peeler = id;
					else
					{
						cout << "\n" << game->get_player_name(id) << " peeled too. Waiting on some players...";
						cout.flush();
					}
				}
				else
				{
					cout << "\nPeel out of order: got " << (int)client_peel << ", expected " << (int)game->get_peel();
					cout.flush();
				}

				break;
			}
			case cl_ack:
			{
				sf::Int16 ack_num;
				packet >> ack_num;

				Player& player = game->get_players().at(id);

				// if we were waiting for this ACK
				if (player.acknowledged(ack_num))
				{
					// if they have more pending, send the first
					if (player.has_pending())
					{
						sf::Packet pending(player.get_pending());
						socket.send(pending, player.get_ip(), client_port);
					}
					else // check if game can proceed
						game->check_waiting();
				}
				else
				{
					cout << "\nMismatched ACK (" << (int)ack_num << ")";
					cout.flush();
				}
				break;
			}
			default:
				cout << "\nUnrecognized packet type: " << (int)type;
				cout.flush();
		}

		// peel
		if (game->can_peel())
		{
			cout << endl;
			if (peeler.size() == 0)
				cout << "Split!";
			else
				cout << game->get_player_name(peeler) << ": Peel!";

			peeler.clear();

			// if victory
			if (game->peel())
			{
				cout << "\n" << game->get_player_name(id) << " has won the game!";
				cout.flush();

				// send victory notification
				for (const auto& pair : game->get_players())
				{
					sf::Packet win;
					win << sv_done << sf::Uint8(1) << id;
					socket.send(win, pair.second.get_ip(), client_port);
					// TODO ack, also this is totally broken
				}
			}
			else
			{
				sf::Int16 remaining = game->get_remaining();

				for (auto& pair : game->get_players())
				{
					std::string letters = pair.second.get_peel();

					cout << "\nSending " << pair.second.get_name() << " " << letters;

					sf::Packet peel;
					peel << sv_peel << sf::Int16(game->get_peel() - 1) << remaining << pair.first << letters;

					if (!pair.second.has_pending())
						socket.send(peel, pair.second.get_ip(), client_port);

					pair.second.add_pending(peel);
					game->wait();
				}
			}

			cout.flush();
		}
	}

	return 0;
}
<commit_msg>simplify some message formats<commit_after>#include "server.hpp"

namespace po = boost::program_options;
using std::cout;
using std::cerr;
using std::endl;
using std::string;

sf::UdpSocket socket;
unsigned short client_port;

Game* game;

// callback for interrupt signal
void shutdown(int s)
{
	(void)s; // intentionally unused

	cout << "\nServer shutting down...";
	cout.flush();

	if (game != nullptr)
	{
		cout << "\nNotifying players...";
		for (const auto& pair: game->get_players())
		{
			sf::Packet sorry;
			sorry << sv_disconnect << sf::Uint8(4);
			socket.send(sorry, pair.second.get_ip(), client_port);
		}
		cout << endl;

		delete game;
	}

	exit(0);
}

int main(int argc, char* argv[])
{
	// set up shutdown callback for interrupts
	struct sigaction action;
	action.sa_handler = shutdown;
	sigemptyset(&action.sa_mask);
	action.sa_flags = 0;

	sigaction(SIGINT, &action, nullptr);
	sigaction(SIGTERM, &action, nullptr);
	sigaction(SIGKILL, &action, nullptr);

	// command line arguments
	po::options_description desc("Bananagrams dedicated server options");
	desc.add_options()
		("help",                                                                   "show options")
		("dict",  po::value<string>()->required(),                                 "dictionary file")
		("port",  po::value<unsigned short>()->default_value(default_server_port), "TCP/UDP listening port")
		("bunch", po::value<string>()->default_value("1"),                         "bunch multiplier (0.5 or a positive integer)")
		("limit", po::value<unsigned int>(),                                       "player limit")
	;

	// TODO usage string
	// TODO print help on unrecognized options
	po::variables_map opts;
	po::store(po::parse_command_line(argc, argv, desc), opts);

	if (opts.count("help"))
	{
		cerr << desc << endl;
		return 1;
	}

	try
	{
		po::notify(opts);
	}
	catch (po::required_option& e)
	{
		cerr << "Error: " << e.what() << endl << endl << desc << endl;
		return 1;
	}

	// check port option
	unsigned short server_port {opts["port"].as<unsigned short>()};
	if (server_port == 0)
	{
		cerr << "Invalid listening port: " << server_port << "!\n";
		return 1;
	}

	client_port = server_port + 1;

	// game options
	unsigned int b_num;
	unsigned int b_den;
	unsigned int max_players;
	std::map<string, string> dictionary;

	// load options from command line

	// check bunch multiplier option
	std::stringstream multi_s;
	multi_s << opts["bunch"].as<string>();
	b_num = 1;
	b_den = 1;
	if (multi_s.str() == "0.5")
		b_den = 2;
	else
		multi_s >> b_num;

	max_players = (8 * b_num) / b_den;

	if (opts.count("limit"))
	{
		auto limit = opts["limit"].as<unsigned int>();

		if (limit > max_players)
		{
			cerr << "\nmax player limit is " << max_players << " for this bunch size";
			cerr.flush();
		}
		else if (limit < 2)
		{
			cerr << "\nmin player limit is 2";
			cerr.flush();
		}
		else
		{
			max_players = limit;
		}
	}

	// check dictionary option
	if (!opts.count("dict"))
	{
		cerr << "No dictionary file specified!\n";
		return 1;
	}

	string dict = opts["dict"].as<string>();

	cout << "Loading dictionary... ";
	cout.flush();
	std::ifstream words(dict);
	if (!words.is_open())
	{
		std::cerr << "\nFailed to open " << dict << "!\n";
		return 1;
	}

	// parse dictionary
	string line;
	while (std::getline(words, line))
	{
		auto pos = line.find_first_of(' ');
		if (pos == string::npos)
			dictionary[line] = "";
		else
			dictionary[line.substr(0, pos)] = line.substr(pos + 1, string::npos);
	}
	words.close();
	cout << dictionary.size() << " words found";
	cout.flush();

	// TODO srand
	// LET'S GO!!!

	// TODO catch failure
	socket.bind(server_port);
	socket.setBlocking(false);

	sf::Clock timer;

	string peeler;

	game = new Game(b_num, b_den, max_players);

	cout << "\nWaiting for players to join...";
	cout.flush();
	while (true)
	{
		float elapsed = timer.getElapsedTime().asSeconds();
		timer.restart();

		// send pending packets
		for (auto& pair : game->get_players())
		{
			if (pair.second.has_pending())
			{
				pair.second.step(elapsed);

				if (pair.second.get_timeout() > 5.f)
				{
					cout << endl << pair.second.get_name() << " timed out";
					cout.flush();
					game->remove_player(pair.first);
				}
				else if (pair.second.poll > 0.5f)
				{
					pair.second.poll -= 0.5f;
					sf::Packet pending(pair.second.get_pending());
					socket.send(pending, pair.second.get_ip(), client_port);
				}
			}
		}

		// get new packets
		sf::Packet packet;
		sf::IpAddress client_ip;
		unsigned short client_port;

		if (socket.receive(packet, client_ip, client_port) != sf::Socket::Status::Done)
			continue;

		sf::Uint8 type;
		std::string id;

		// every client packet starts with a type and player id
		packet >> type;
		packet >> id;

		// only process packets if we know the player or they're connecting
		if (type != cl_connect && !game->has_player(id))
			continue;

		switch(type)
		{
			case cl_connect:
			{
				sf::Uint8 version;
				packet >> version;
				if (version != protocol_version)
				{
					sf::Packet sorry;
					sorry << sv_disconnect << sf::Uint8(0);
					socket.send(sorry, client_ip, client_port);

					cout << "\nclient failed to join: "
					        "need protocol version " << (int)protocol_version << ", got " << (int)version;
					cout.flush();
					break;
				}

				std::string name;
				packet >> name;

				// if it is an unknown player
				if (!game->has_player(id))
				{
					if (game->is_full())
					{
						sf::Packet sorry;
						sorry << sv_disconnect << sf::Uint8(1);
						socket.send(sorry, client_ip, client_port);

						cout << "\nclient failed to join: game full";
						cout.flush();
						break;
					}

					if (game->in_progress())
					{
						sf::Packet sorry;
						sorry << sv_disconnect << sf::Uint8(3);
						socket.send(sorry, client_ip, client_port);

						cout << "\nclient failed to join: game already started";
						cout.flush();
						break;
					}

					// let everyone else know about the connection
					sf::Packet join;
					join << sv_info << id << sf::Uint8(0) << name;
					for (auto& pair : game->get_players())
					{
						if (!pair.second.has_pending())
							socket.send(join, pair.second.get_ip(), client_port);
						pair.second.add_pending(join);
						game->wait();
					}

					Player& new_player = game->add_player(id, client_ip, name);

					// let new player know about all other players
					for (auto& pair : game->get_players())
					{
						if (pair.first == id) continue;

						sf::Packet join;
						join << sv_info << pair.first << sf::Uint8(0) << pair.second.get_name();
						if (!new_player.has_pending())
							socket.send(join, client_ip, client_port);
						new_player.add_pending(join);
						game->wait();
					}

					cout << "\n" << name << " has joined the game";
					cout.flush();
				}

				// by now we know they are a player, ack connection
				sf::Packet join;
				join << sv_info << id << sf::Uint8(0) << name;
				socket.send(join, client_ip, client_port);

				break;
			}
			case cl_disconnect:
			{
				cout << "\n" << game->get_player_name(id) << " has left the game";
				cout.flush();

				game->remove_player(id);

				// let everyone else know about disconnection
				sf::Packet leave;
				leave << sv_info << id << sf::Uint8(1);
				for (auto& pair : game->get_players())
				{
					if (!pair.second.has_pending())
						socket.send(leave, pair.second.get_ip(), client_port);
					pair.second.add_pending(leave);
					game->wait();
				}

				// TODO end game if now below limit

				break;
			}
			case cl_ready:
			{
				bool ready;
				packet >> ready;

				game->set_ready(id, ready);

				sf::Packet rdy;
				rdy << sv_info << id << (ready ? sf::Uint8(2) : sf::Uint8(3));
				for (const auto& pair : game->get_players())
					socket.send(rdy, pair.second.get_ip(), client_port);

				break;
			}
			case cl_check:
			{
				string word;
				packet >> word;

				sf::Packet lookup;
				lookup << sv_check << word << (dictionary.count(word) == 1);

				socket.send(lookup, client_ip, client_port);

				break;
			}
			case cl_dump:
			{
				sf::Int16 dump_n;
				sf::Int8 chr;
				packet >> dump_n >> chr;

				if (game->check_dump(id, dump_n))
				{
					sf::Packet dump;

					string letters = game->dump(id, dump_n, chr);

					cout << endl << game->get_player_name(id) << " dumped " << chr
					     << " and received " << letters;
					cout.flush();

					dump << sv_dump << dump_n << letters;

					socket.send(dump, client_ip, client_port);
				}

				break;
			}
			case cl_peel:
			{
				sf::Int16 client_peel;
				packet >> client_peel;

				// make sure peel number is correct
				if (game->check_peel(client_peel))
				{
					// store first peeler id
					if (peeler.size() == 0)
						peeler = id;
					else
					{
						cout << "\n" << game->get_player_name(id) << " also peeled. Waiting on some players...";
						cout.flush();
					}
				}
				else
				{
					cout << "\nPeel out of order: got " << (int)client_peel << ", expected " << (int)game->get_peel();
					cout.flush();
				}

				break;
			}
			case cl_ack:
			{
				sf::Int16 ack_num;
				packet >> ack_num;

				Player& player = game->get_players().at(id);

				// if we were waiting for this ACK
				if (player.acknowledged(ack_num))
				{
					// if they have more pending, send the first
					if (player.has_pending())
					{
						sf::Packet pending(player.get_pending());
						socket.send(pending, player.get_ip(), client_port);
					}
					else // check if game can proceed
						game->check_waiting();
				}
				else
				{
					cout << "\nMismatched ACK (" << (int)ack_num << ")";
					cout.flush();
				}
				break;
			}
			default:
				cout << "\nUnrecognized packet type: " << (int)type;
				cout.flush();
		}

		// peel
		if (game->can_peel())
		{
			cout << endl;
			if (peeler.size() == 0)
				cout << "Split!";
			else
				cout << game->get_player_name(peeler) << ": Peel!";

			peeler.clear();

			// if victory
			if (game->peel())
			{
				cout << "\n" << game->get_player_name(id) << " has won the game!";
				cout.flush();

				// send victory notification
				for (const auto& pair : game->get_players())
				{
					sf::Packet win;
					win << sv_done << sf::Uint8(1) << id;
					socket.send(win, pair.second.get_ip(), client_port);
					// TODO ack, also this is totally broken
				}
			}
			else
			{
				sf::Int16 remaining = game->get_remaining();

				for (auto& pair : game->get_players())
				{
					std::string letters = pair.second.get_peel();

					cout << "\nSending " << pair.second.get_name() << " " << letters;

					sf::Packet peel;
					peel << sv_peel << sf::Int16(game->get_peel() - 1) << remaining << pair.first << letters;

					if (!pair.second.has_pending())
						socket.send(peel, pair.second.get_ip(), client_port);

					pair.second.add_pending(peel);
					game->wait();
				}
			}

			cout.flush();
		}
	}

	return 0;
}
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of Sara, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2017 David Ok <david.ok8@gmail.com>
//
// 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/.
// ========================================================================== //

//! @file

#pragma once

#include <DO/Sara/Core/Image.hpp>
#include <DO/Sara/ImageProcessing/ColorFancyPCA.hpp>
#include <DO/Sara/ImageProcessing/Flip.hpp>
#include <DO/Sara/ImageProcessing/Resize.hpp>

namespace DO { namespace Sara {

  struct ImageDataTransform
  {
  public:
    //! @{
    //! @brief Transform types.
    enum FlipType
    {
      Horizontal,
      Vertical
    };

    enum TransformType
    {
      Zoom = 0,
      Shift = 1,
      Flip = 2,
      FancyPCA = 3,
      NumTransformTypes = 4
    };
    //! @}

    // ===================================================================== //
    //! @{
    //! @brief Transform setters.
    void set_zoom(float z)
    {
      use_original = false;
      this->apply_transform[Zoom] = true;
      this->z = z;
    }

    void unset_zoom()
    {
      this->apply_transform[Zoom] = false;
    }

    void set_shift(Vector2i t)
    {
      use_original = false;
      this->apply_transform[Shift] = true;
      this->t = t;
    }

    void set_flip(FlipType flip_type)
    {
      use_original = false;
      this->apply_transform[Flip] = true;
      this->flip_type = flip_type;
    }

    void unset_flip()
    {
      this->apply_transform[Flip] = false;
    }

    void set_fancy_pca(Vector3f alpha)
    {
      use_original = false;
      this->apply_transform[FancyPCA] = true;
      this->alpha = alpha;
    }

    void unset_fancy_pca()
    {
      this->apply_transform[FancyPCA] = false;
    }
    //! @}

    // ===================================================================== //
    //! @{
    //! @brief Transform the image data.
    template <typename T>
    Image<T> extract_patch(const Image<T>& in) const
    {
      if (use_original)
        return reduce(in, out_sizes);

      auto out = in;
      if (apply_transform[Zoom])
      {
        if (z < 1)
          out = reduce(in, 1 / z);
        else
          out = enlarge(in, z);
      }

      out = crop(out, t, t + out_sizes);

      if (apply_transform[Flip])
      {
        if (flip_type == Horizontal)
          flip_horizontally(out);
        else
          flip_vertically(out);
      }

      return out;
    }

    Image<Rgb32f> operator()(const Image<Rgb32f>& in) const
    {
      auto out = extract_patch(in);

      if (apply_transform[FancyPCA])
        ColorFancyPCA{U, S}(out, alpha);

      return out;
    }
    //! @}

    // ===================================================================== //
    // Parameters
    //
    //! @{ Final size.
    Vector2i out_sizes;

    //! @{ Use the original image.
    bool use_original{true};
    //! If not use,
    std::array<bool, NumTransformTypes> apply_transform{
        {false, false, false, false}};

    //! @{
    //! @brief Zoom factor.
    float z{1.f};
    //! @brief Rotation angle.
    float theta{0.f};
    //! Translation vector.
    Vector2i t{Vector2i::Zero()};
    //! @brief Flip type.
    FlipType flip_type{Horizontal};
    //! @}

    //! @{
    //! @brief Color fancy PCA parameters.
    Matrix3f U{Matrix3f::Identity()};
    Vector3f S{Vector3f::Ones()};
    Vector3f alpha{Vector3f::Zero()};
    //! @}
  };

  VectorXf linspace(float a, float b, int num_samples)
  {
    auto range = VectorXf(num_samples);
    for (int i = 0; i < num_samples; ++i)
      range[i] = a + (b - a) * i / (num_samples - 1);
    return range;
  }

  VectorXf logspace(float a, float b, int num_samples)
  {
    return linspace(log(a), log(b), num_samples).array().exp().matrix();
  }

  auto expand_zoom_transforms(const Vector2i& in_image_sizes,
                              const Vector2i& out_image_sizes, float zmin,
                              float zmax, int num_samples)
      -> std::vector<ImageDataTransform>;

  auto expand_crop_transforms(const Vector2i& in_image_sizes,
                              const Vector2i& out_image_sizes,
                              int delta_x, int delta_y)
      -> std::vector<ImageDataTransform>;

} /* namespace Sara */
} /* namespace DO */
<commit_msg>MAINT: fix VS 2015 shared library build.<commit_after>// ========================================================================== //
// This file is part of Sara, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2017 David Ok <david.ok8@gmail.com>
//
// 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/.
// ========================================================================== //

//! @file

#pragma once

#include <array>
#include <vector>

#include <DO/Sara/Core/Image.hpp>
#include <DO/Sara/ImageProcessing/ColorFancyPCA.hpp>
#include <DO/Sara/ImageProcessing/Flip.hpp>
#include <DO/Sara/ImageProcessing/Resize.hpp>

namespace DO { namespace Sara {

  struct ImageDataTransform
  {
  public:
    //! @{
    //! @brief Transform types.
    enum FlipType
    {
      Horizontal,
      Vertical
    };

    enum TransformType
    {
      Zoom = 0,
      Shift = 1,
      Flip = 2,
      FancyPCA = 3,
      NumTransformTypes = 4
    };
    //! @}

    // ===================================================================== //
    //! @{
    //! @brief Transform setters.
    void set_zoom(float z)
    {
      use_original = false;
      this->apply_transform[Zoom] = true;
      this->z = z;
    }

    void unset_zoom()
    {
      this->apply_transform[Zoom] = false;
    }

    void set_shift(Vector2i t)
    {
      use_original = false;
      this->apply_transform[Shift] = true;
      this->t = t;
    }

    void set_flip(FlipType flip_type)
    {
      use_original = false;
      this->apply_transform[Flip] = true;
      this->flip_type = flip_type;
    }

    void unset_flip()
    {
      this->apply_transform[Flip] = false;
    }

    void set_fancy_pca(Vector3f alpha)
    {
      use_original = false;
      this->apply_transform[FancyPCA] = true;
      this->alpha = alpha;
    }

    void unset_fancy_pca()
    {
      this->apply_transform[FancyPCA] = false;
    }
    //! @}

    // ===================================================================== //
    //! @{
    //! @brief Transform the image data.
    template <typename T>
    Image<T> extract_patch(const Image<T>& in) const
    {
      if (use_original)
        return reduce(in, out_sizes);

      auto out = in;
      if (apply_transform[Zoom])
      {
        if (z < 1)
          out = reduce(in, 1 / z);
        else
          out = enlarge(in, z);
      }

      out = crop(out, t, t + out_sizes);

      if (apply_transform[Flip])
      {
        if (flip_type == Horizontal)
          flip_horizontally(out);
        else
          flip_vertically(out);
      }

      return out;
    }

    Image<Rgb32f> operator()(const Image<Rgb32f>& in) const
    {
      auto out = extract_patch(in);

      if (apply_transform[FancyPCA])
        ColorFancyPCA{U, S}(out, alpha);

      return out;
    }
    //! @}

    // ===================================================================== //
    // Parameters
    //
    //! @{ Final size.
    Vector2i out_sizes;

    //! @{ Use the original image.
    bool use_original{true};
    //! If not use,
    std::array<bool, NumTransformTypes> apply_transform{
        {false, false, false, false}};

    //! @{
    //! @brief Zoom factor.
    float z{1.f};
    //! @brief Rotation angle.
    float theta{0.f};
    //! Translation vector.
    Vector2i t{Vector2i::Zero()};
    //! @brief Flip type.
    FlipType flip_type{Horizontal};
    //! @}

    //! @{
    //! @brief Color fancy PCA parameters.
    Matrix3f U{Matrix3f::Identity()};
    Vector3f S{Vector3f::Ones()};
    Vector3f alpha{Vector3f::Zero()};
    //! @}
  };

  DO_SARA_EXPORT
  auto expand_zoom_transforms(const Vector2i& in_image_sizes,
                              const Vector2i& out_image_sizes, float zmin,
                              float zmax, int num_samples)
      -> std::vector<ImageDataTransform>;

  DO_SARA_EXPORT
  auto expand_crop_transforms(const Vector2i& in_image_sizes,
                              const Vector2i& out_image_sizes,
                              int delta_x, int delta_y)
      -> std::vector<ImageDataTransform>;

} /* namespace Sara */
} /* namespace DO */
<|endoftext|>


<commit_before>#include "ServerConfig.hpp"
#include "ConnectionManager.hpp"

#include <tellstore.hpp>
#include <util/Epoch.hpp>
#include <util/Logging.hpp>
#include <util/StorageConfig.hpp>

#include <crossbow/infinio/EventDispatcher.hpp>
#include <crossbow/program_options.hpp>

#include <iostream>
#include <thread>
#include <vector>

int main(int argc, const char** argv) {
    tell::store::StorageConfig storageConfig;
    tell::store::ServerConfig serverConfig;
    bool help = false;

    auto opts = crossbow::program_options::create_options(argv[0],
            crossbow::program_options::toggle<'h'>("help", help),
            crossbow::program_options::value<'p'>("port", serverConfig.port),
            crossbow::program_options::value<'m'>("memory", storageConfig.totalMemory),
            crossbow::program_options::value<'c'>("capacity", storageConfig.hashMapCapacity));

    try {
        crossbow::program_options::parse(opts, argc, argv);
    } catch (crossbow::program_options::argument_not_found e) {
        std::cerr << e.what() << std::endl << std::endl;
        crossbow::program_options::print_help(std::cout, opts);
        return 1;
    }

    if (help) {
        crossbow::program_options::print_help(std::cout, opts);
        return 0;
    }

    LOG_INFO("Starting TellStore server");
    tell::store::init();

    // Initialize storage
    tell::store::Storage storage(storageConfig);

    // Initialize network server
    crossbow::infinio::EventDispatcher dispatcher;
    tell::store::ConnectionManager connectionManager(storage, dispatcher, serverConfig);
    boost::system::error_code ec;
    connectionManager.init(ec);
    if (ec) {
        LOG_FATAL("Failure initializing the connection manager [error = %1% %2%]", ec, ec.message());
        return 1;
    }

    // Start event dispatcher threads
    auto execDispatcher = [&dispatcher] () {
        dispatcher.run();
    };

    std::vector<std::thread> threads;
    for (size_t i = 1; i < serverConfig.serverThreads; ++i) {
        threads.emplace_back(execDispatcher);
    }
    execDispatcher();

    // Join event dispatcher threads
    for (auto& t : threads) {
        t.join();
    }

    return 0;
}
<commit_msg>Use value instead of toggle in server program_options parsing<commit_after>#include "ServerConfig.hpp"
#include "ConnectionManager.hpp"

#include <tellstore.hpp>
#include <util/Epoch.hpp>
#include <util/Logging.hpp>
#include <util/StorageConfig.hpp>

#include <crossbow/infinio/EventDispatcher.hpp>
#include <crossbow/program_options.hpp>

#include <iostream>
#include <thread>
#include <vector>

int main(int argc, const char** argv) {
    tell::store::StorageConfig storageConfig;
    tell::store::ServerConfig serverConfig;
    bool help = false;

    auto opts = crossbow::program_options::create_options(argv[0],
            crossbow::program_options::value<'h'>("help", help),
            crossbow::program_options::value<'p'>("port", serverConfig.port),
            crossbow::program_options::value<'m'>("memory", storageConfig.totalMemory),
            crossbow::program_options::value<'c'>("capacity", storageConfig.hashMapCapacity));

    try {
        crossbow::program_options::parse(opts, argc, argv);
    } catch (crossbow::program_options::argument_not_found e) {
        std::cerr << e.what() << std::endl << std::endl;
        crossbow::program_options::print_help(std::cout, opts);
        return 1;
    }

    if (help) {
        crossbow::program_options::print_help(std::cout, opts);
        return 0;
    }

    LOG_INFO("Starting TellStore server");
    tell::store::init();

    // Initialize storage
    tell::store::Storage storage(storageConfig);

    // Initialize network server
    crossbow::infinio::EventDispatcher dispatcher;
    tell::store::ConnectionManager connectionManager(storage, dispatcher, serverConfig);
    boost::system::error_code ec;
    connectionManager.init(ec);
    if (ec) {
        LOG_FATAL("Failure initializing the connection manager [error = %1% %2%]", ec, ec.message());
        return 1;
    }

    // Start event dispatcher threads
    auto execDispatcher = [&dispatcher] () {
        dispatcher.run();
    };

    std::vector<std::thread> threads;
    for (size_t i = 1; i < serverConfig.serverThreads; ++i) {
        threads.emplace_back(execDispatcher);
    }
    execDispatcher();

    // Join event dispatcher threads
    for (auto& t : threads) {
        t.join();
    }

    return 0;
}
<|endoftext|>


<commit_before>#ifndef __CALLBACK_PARAMETER_HPP_INCLUDED
#define __CALLBACK_PARAMETER_HPP_INCLUDED

#include "cpp/ylikuutio/common/any_value.hpp"
#include "callback_object.hpp"
#include "cpp/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include standard headers
#include <string>        // std::string

namespace ontology
{
    class Universe;
}

namespace callback_system
{
    class CallbackParameter
    {
        public:

            // constructor.
            CallbackParameter(std::string name, datatypes::AnyValue* any_value, bool is_reference, callback_system::CallbackObject* parent_pointer);

            // destructor.
            ~CallbackParameter();

            // getter.
            datatypes::AnyValue* get_any_value();

            friend class CallbackObject;
            template<class T1>
                friend void hierarchy::bind_child_to_parent(T1 child_pointer, std::vector<void*> &child_pointer_vector, std::queue<uint32_t> &free_childID_queue);
            template<class T1, class T2>
                friend void hierarchy::bind_child_to_new_parent(T1 child_pointer, T2 new_parent_pointer, std::vector<void*> &old_child_pointer_vector, std::queue<uint32_t> &old_free_childID_queue);

        private:
            void bind_to_parent();

            uint32_t childID;               // callback parameter ID, returned by `callback_system::CallbackObject->get_callback_parameterID()`.

            callback_system::CallbackObject* parent_pointer; // pointer to the callback object.

            std::string name;
            datatypes::AnyValue* any_value; // this is `private` to make sure that someone does not overwrite it.
            bool is_reference;              // if true, the value is read from the hashmap. if false, then the value is read from the union.
    };

    class BoolCallbackParameter : CallbackParameter
    {
        public:

            // constructor.
            BoolCallbackParameter();
    };
}

#endif
<commit_msg>Removed obsolete `class BoolCallbackParameter : CallbackParameter`.<commit_after>#ifndef __CALLBACK_PARAMETER_HPP_INCLUDED
#define __CALLBACK_PARAMETER_HPP_INCLUDED

#include "cpp/ylikuutio/common/any_value.hpp"
#include "callback_object.hpp"
#include "cpp/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include standard headers
#include <string>        // std::string

namespace ontology
{
    class Universe;
}

namespace callback_system
{
    class CallbackParameter
    {
        public:

            // constructor.
            CallbackParameter(std::string name, datatypes::AnyValue* any_value, bool is_reference, callback_system::CallbackObject* parent_pointer);

            // destructor.
            ~CallbackParameter();

            // getter.
            datatypes::AnyValue* get_any_value();

            friend class CallbackObject;
            template<class T1>
                friend void hierarchy::bind_child_to_parent(T1 child_pointer, std::vector<void*> &child_pointer_vector, std::queue<uint32_t> &free_childID_queue);
            template<class T1, class T2>
                friend void hierarchy::bind_child_to_new_parent(T1 child_pointer, T2 new_parent_pointer, std::vector<void*> &old_child_pointer_vector, std::queue<uint32_t> &old_free_childID_queue);

        private:
            void bind_to_parent();

            uint32_t childID;               // callback parameter ID, returned by `callback_system::CallbackObject->get_callback_parameterID()`.

            callback_system::CallbackObject* parent_pointer; // pointer to the callback object.

            std::string name;
            datatypes::AnyValue* any_value; // this is `private` to make sure that someone does not overwrite it.
            bool is_reference;              // if true, the value is read from the hashmap. if false, then the value is read from the union.
    };
}

#endif
<|endoftext|>


<commit_before>#pragma once

#include "search/v2/house_to_street_table.hpp"

#include "indexer/features_vector.hpp"
#include "indexer/index.hpp"
#include "indexer/scale_index.hpp"

#include "base/macros.hpp"

#include "std/unique_ptr.hpp"

class MwmValue;

namespace search
{
namespace v2
{

void CoverRect(m2::RectD const & rect, int scale, covering::IntervalsT & result);

/// @todo Move this class into "index" library and make it more generic.
/// Now it duplicates "Index" functionality.
struct MwmContext
{
  explicit MwmContext(MwmSet::MwmHandle handle);

  MwmSet::MwmHandle m_handle;
  MwmValue & m_value;
  FeaturesVector m_vector;
  ScaleIndex<ModelReaderPtr> m_index;
  unique_ptr<HouseToStreetTable> m_houseToStreetTable;

  inline MwmSet::MwmId const & GetId() const { return m_handle.GetId(); }
  inline string const & GetName() const { return GetInfo()->GetCountryName(); }
  inline shared_ptr<MwmInfo> const & GetInfo() const { return GetId().GetInfo(); }

  template <class TFn> void ForEachIndex(covering::IntervalsT const & intervals,
                                         uint32_t scale, TFn && fn) const
  {
    ForEachIndexImpl(intervals, scale, [&](uint32_t index)
    {
      // TODO: Optimize deleted checks by getting vector of deleted indexes from the Editor.
      if (GetEditedStatus(index) != osm::Editor::FeatureStatus::Deleted)
        fn(index);
    });
  }

  template <class TFn> void ForEachFeature(m2::RectD const & rect, TFn && fn) const
  {
    uint32_t const scale = m_value.GetHeader().GetLastScale();
    covering::IntervalsT intervals;
    CoverRect(rect, scale, intervals);

    ForEachIndexImpl(intervals, scale,
                     [&](uint32_t index)
                     {
                       FeatureType ft;
                       if (GetFeature(index, ft))
                         fn(ft);
                     });
  }

  // @returns false if feature was deleted by user.
  bool GetFeature(uint32_t index, FeatureType & ft) const;

private:
  osm::Editor::FeatureStatus GetEditedStatus(uint32_t index) const
  {
    return osm::Editor::Instance().GetFeatureStatus(GetId(), index);
  }

  template <class TFn> void ForEachIndexImpl(covering::IntervalsT const & intervals,
                                             uint32_t scale, TFn && fn) const
  {
    CheckUniqueIndexes checkUnique(m_value.GetHeader().GetFormat() >= version::Format::v5);
    for (auto const & i : intervals)
      m_index.ForEachInIntervalAndScale([&] (uint32_t index) { fn(index); }, i.first, i.second, scale);
  }

  DISALLOW_COPY_AND_MOVE(MwmContext);
};

}  // namespace v2
}  // namespace search
<commit_msg>Correct using of CheckUniqueIndexes.<commit_after>#pragma once

#include "search/v2/house_to_street_table.hpp"

#include "indexer/features_vector.hpp"
#include "indexer/index.hpp"
#include "indexer/scale_index.hpp"

#include "base/macros.hpp"

#include "std/unique_ptr.hpp"

class MwmValue;

namespace search
{
namespace v2
{

void CoverRect(m2::RectD const & rect, int scale, covering::IntervalsT & result);

/// @todo Move this class into "index" library and make it more generic.
/// Now it duplicates "Index" functionality.
struct MwmContext
{
  explicit MwmContext(MwmSet::MwmHandle handle);

  MwmSet::MwmHandle m_handle;
  MwmValue & m_value;
  FeaturesVector m_vector;
  ScaleIndex<ModelReaderPtr> m_index;
  unique_ptr<HouseToStreetTable> m_houseToStreetTable;

  inline MwmSet::MwmId const & GetId() const { return m_handle.GetId(); }
  inline string const & GetName() const { return GetInfo()->GetCountryName(); }
  inline shared_ptr<MwmInfo> const & GetInfo() const { return GetId().GetInfo(); }

  template <class TFn> void ForEachIndex(covering::IntervalsT const & intervals,
                                         uint32_t scale, TFn && fn) const
  {
    ForEachIndexImpl(intervals, scale, [&](uint32_t index)
    {
      // TODO: Optimize deleted checks by getting vector of deleted indexes from the Editor.
      if (GetEditedStatus(index) != osm::Editor::FeatureStatus::Deleted)
        fn(index);
    });
  }

  template <class TFn> void ForEachFeature(m2::RectD const & rect, TFn && fn) const
  {
    uint32_t const scale = m_value.GetHeader().GetLastScale();
    covering::IntervalsT intervals;
    CoverRect(rect, scale, intervals);

    ForEachIndexImpl(intervals, scale,
                     [&](uint32_t index)
                     {
                       FeatureType ft;
                       if (GetFeature(index, ft))
                         fn(ft);
                     });
  }

  // @returns false if feature was deleted by user.
  bool GetFeature(uint32_t index, FeatureType & ft) const;

private:
  osm::Editor::FeatureStatus GetEditedStatus(uint32_t index) const
  {
    return osm::Editor::Instance().GetFeatureStatus(GetId(), index);
  }

  template <class TFn> void ForEachIndexImpl(covering::IntervalsT const & intervals,
                                             uint32_t scale, TFn && fn) const
  {
    CheckUniqueIndexes checkUnique(m_value.GetHeader().GetFormat() >= version::Format::v5);
    for (auto const & i : intervals)
      m_index.ForEachInIntervalAndScale(
            [&] (uint32_t index)
            {
              if (checkUnique(index))
                fn(index);
            }, i.first, i.second, scale);
  }

  DISALLOW_COPY_AND_MOVE(MwmContext);
};

}  // namespace v2
}  // namespace search
<|endoftext|>


<commit_before>// ---------------------------------------------------------------------
//
// Copyright (C) 2004 - 2015 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE at
// the top level of the deal.II distribution.
//
// ---------------------------------------------------------------------

// create LA::MPI::SparseMatrix where one CPU has no DoFs

// this bug got fixed in PETScWrappers in r29776

#include "../tests.h"
#include <deal.II/lac/generic_linear_algebra.h>
#include <deal.II/base/index_set.h>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>

#include "gla.h"

template <class LA>
void test ()
{
  unsigned int myid = Utilities::MPI::this_mpi_process (MPI_COMM_WORLD);
  unsigned int numproc = Utilities::MPI::n_mpi_processes (MPI_COMM_WORLD);

  if (myid==0)
    deallog << "numproc=" << numproc << std::endl;

  IndexSet local_active(10);
  if (myid==0)
    local_active.add_range(0,10);
  IndexSet local_relevant= local_active;
  if (myid==1)
    local_relevant.add_range(5,10);
  local_relevant.add_range(0,1);

  CompressedSimpleSparsityPattern csp (local_relevant);

  for (unsigned int i=0; i<10; ++i)
    if (local_relevant.is_element(i))
      csp.add(i,i);

  csp.add(0,1);

  typename LA::MPI::SparseMatrix mat;
  mat.reinit (local_active, local_active, csp, MPI_COMM_WORLD);

  Assert(mat.n()==10, ExcInternalError());
  Assert(mat.m()==10, ExcInternalError());

  mat.set(0,0,0.1);
  mat.set(1,1,0.1);

  MPI_Barrier(MPI_COMM_WORLD);

  mat.compress(VectorOperation::insert);

  mat.add(0,1,1.0);

  mat.compress(VectorOperation::add);

  // check local values
  if (myid==0)
    {
      deallog << "1,1 : " << mat(1,1) << std::endl;
      deallog << "0,1 : " << mat(0,1) << std::endl;
    }

  // done
  if (myid==0)
    deallog << "OK" << std::endl;
}

int main (int argc, char **argv)
{
  Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, 1);
  MPILogInitAll log;
  {
    deallog.push("PETSc");
    test<LA_PETSc>();
    deallog.pop();
    deallog.push("Trilinos");
    test<LA_Trilinos>();
    deallog.pop();
  }

  // compile, don't run
  //if (myid==9999)
  //  test<LA_Dummy>();

}
<commit_msg>extend gla/mat_02 to complex-valued PETSc<commit_after>// ---------------------------------------------------------------------
//
// Copyright (C) 2004 - 2015 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE at
// the top level of the deal.II distribution.
//
// ---------------------------------------------------------------------

// create LA::MPI::SparseMatrix where one CPU has no DoFs

// this bug got fixed in PETScWrappers in r29776

#include "../tests.h"
#include <deal.II/lac/generic_linear_algebra.h>
#include <deal.II/base/index_set.h>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>

#include "gla.h"

template <class LA>
void test ()
{
  unsigned int myid = Utilities::MPI::this_mpi_process (MPI_COMM_WORLD);
  unsigned int numproc = Utilities::MPI::n_mpi_processes (MPI_COMM_WORLD);

  if (myid==0)
    deallog << "numproc=" << numproc << std::endl;

  IndexSet local_active(10);
  if (myid==0)
    local_active.add_range(0,10);
  IndexSet local_relevant= local_active;
  if (myid==1)
    local_relevant.add_range(5,10);
  local_relevant.add_range(0,1);

  CompressedSimpleSparsityPattern csp (local_relevant);

  for (unsigned int i=0; i<10; ++i)
    if (local_relevant.is_element(i))
      csp.add(i,i);

  csp.add(0,1);

  typename LA::MPI::SparseMatrix mat;
  mat.reinit (local_active, local_active, csp, MPI_COMM_WORLD);

  Assert(mat.n()==10, ExcInternalError());
  Assert(mat.m()==10, ExcInternalError());

  mat.set(0,0,0.1);
  mat.set(1,1,0.1);

  MPI_Barrier(MPI_COMM_WORLD);

  mat.compress(VectorOperation::insert);

  mat.add(0,1,1.0);

  mat.compress(VectorOperation::add);

  // check local values
  if (myid==0)
    {
      deallog << "1,1 : " << get_real_assert_zero_imag(mat(1,1)) << std::endl;
      deallog << "0,1 : " << get_real_assert_zero_imag(mat(0,1)) << std::endl;
    }

  // done
  if (myid==0)
    deallog << "OK" << std::endl;
}

int main (int argc, char **argv)
{
  Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, 1);
  MPILogInitAll log;
  {
    deallog.push("PETSc");
    test<LA_PETSc>();
    deallog.pop();
    deallog.push("Trilinos");
    test<LA_Trilinos>();
    deallog.pop();
  }

  // compile, don't run
  //if (myid==9999)
  //  test<LA_Dummy>();

}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 - 2021 gary@drinkingtea.net
 *
 * 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/.
 */

#pragma once

#include "strops.hpp"
#include "vector.hpp"

namespace ox {

template<typename K, typename T>
class HashMap {

	using key_t = K;
	using value_t = T;

	private:
		struct Pair {
			K key = {};
			T value = {};
		};
		Vector<K> m_keys;
		Vector<Pair*> m_pairs;

	public:
		explicit HashMap(std::size_t size = 100);

		HashMap(HashMap &other);

		~HashMap();

		bool operator==(const HashMap &other) const;

		HashMap &operator=(HashMap &other);

		/**
		 * K is assumed to be a null terminated string.
		 */
		T &operator[](K key);

		/**
		 * K is assumed to be a null terminated string.
		 */
		T &at(K key);

		bool contains(K key);

		std::size_t size() const noexcept;

		const Vector<K> &keys() const noexcept;

	private:
		void expand();

		/**
		 * K is assumed to be a null terminated string.
		 */
		static uint64_t hash(K, int len = 0xFFFF) noexcept;

		/**
		 * K is assumed to be a null terminated string.
		 */
		Pair *&access(Vector<Pair*> &pairs, K key);

};

template<typename K, typename T>
HashMap<K, T>::HashMap(std::size_t size): m_pairs(size) {
}

template<typename K, typename T>
HashMap<K, T>::HashMap(HashMap<K, T> &other) {
	m_pairs = other.m_pairs;
}

template<typename K, typename T>
HashMap<K, T>::~HashMap() {
	for (std::size_t i = 0; i < m_pairs.size(); i++) {
		delete m_pairs[i];
	}
}

template<typename K, typename T>
bool HashMap<K, T>::operator==(const HashMap &other) const {
	if (m_keys != other.m_keys) {
		return false;
	}
	for (int i = 0; i < m_keys.size(); i++) {
		auto &k = m_keys[i];
		if (at(k) != other.at(k)) {
			return false;
		}
	}
	return true;
}

template<typename K, typename T>
HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> &other) {
	this->~HashMap<K, T>();
	m_keys = other.m_keys;
	m_pairs = other.m_pairs;
	return *this;
}

template<typename K, typename T>
T &HashMap<K, T>::operator[](K k) {
	auto &p = access(m_pairs, k);
	if (p == nullptr) {
		if (m_pairs.size() * 0.7 < m_keys.size()) {
			expand();
		}
		p = new Pair;
		p->key = k;
		m_keys.push_back(k);
	}
	return p->value;
}

template<typename K, typename T>
T &HashMap<K, T>::at(K k) {
	return operator[](k);
}

template<typename K, typename T>
bool HashMap<K, T>::contains(K k) {
	return access(m_pairs, k) != nullptr;
}

template<typename K, typename T>
std::size_t HashMap<K, T>::size() const noexcept {
	return m_keys.size();
}

template<typename K, typename T>
const Vector<K> &HashMap<K, T>::keys() const noexcept {
	return m_keys;
}

template<typename K, typename T>
void HashMap<K, T>::expand() {
	Vector<Pair*> r;
	for (std::size_t i = 0; i < m_keys.size(); i++) {
		auto k = m_keys[i];
		access(r, k) = access(m_pairs, k);
	}
	m_pairs = r;
}

template<typename K, typename T>
uint64_t HashMap<K, T>::hash(K k, int len) noexcept {
	uint64_t sum = 1;
	for (int i = 0; i < len && k[i]; i++) {
		sum += ((sum + k[i]) * 7) * sum;
	}
	return sum;
}

template<typename K, typename T>
typename HashMap<K, T>::Pair *&HashMap<K, T>::access(Vector<Pair*> &pairs, K k) {
	auto h = hash(k) % pairs.size();
	auto hashStr = reinterpret_cast<char*>(&h);
	while (1) {
		auto &p = pairs[h];
		if (p == nullptr || ox_strcmp(p->key, k) == 0) {
			return p;
		} else {
			h = hash(hashStr, 8) % pairs.size();
		}
	}
}

}
<commit_msg>[ox/std] Add const accessor to HashMap<commit_after>/*
 * Copyright 2015 - 2021 gary@drinkingtea.net
 *
 * 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/.
 */

#pragma once

#include "strops.hpp"
#include "vector.hpp"

namespace ox {

template<typename K, typename T>
class HashMap {

	using key_t = K;
	using value_t = T;

	private:
		struct Pair {
			K key = {};
			T value = {};
		};
		mutable Vector<K> m_keys;
		mutable Vector<Pair*> m_pairs;

	public:
		explicit HashMap(std::size_t size = 100);

		HashMap(HashMap &other);

		~HashMap();

		bool operator==(const HashMap &other) const;

		HashMap &operator=(HashMap &other);

		/**
		 * K is assumed to be a null terminated string.
		 */
		const T &operator[](K key) const;

		/**
		 * K is assumed to be a null terminated string.
		 */
		T &operator[](K key);

		/**
		 * K is assumed to be a null terminated string.
		 */
		T &at(K key);

		bool contains(K key) const;

		std::size_t size() const noexcept;

		const Vector<K> &keys() const noexcept;

	private:
		void expand() const;

		/**
		 * K is assumed to be a null terminated string.
		 */
		static uint64_t hash(K, int len = 0xFFFF) noexcept;

		/**
		 * K is assumed to be a null terminated string.
		 */
		Pair *&access(Vector<Pair*> &pairs, K key) const;

};

template<typename K, typename T>
HashMap<K, T>::HashMap(std::size_t size): m_pairs(size) {
}

template<typename K, typename T>
HashMap<K, T>::HashMap(HashMap<K, T> &other) {
	m_pairs = other.m_pairs;
}

template<typename K, typename T>
HashMap<K, T>::~HashMap() {
	for (std::size_t i = 0; i < m_pairs.size(); i++) {
		delete m_pairs[i];
	}
}

template<typename K, typename T>
bool HashMap<K, T>::operator==(const HashMap &other) const {
	if (m_keys != other.m_keys) {
		return false;
	}
	for (int i = 0; i < m_keys.size(); i++) {
		auto &k = m_keys[i];
		if (at(k) != other.at(k)) {
			return false;
		}
	}
	return true;
}

template<typename K, typename T>
HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> &other) {
	this->~HashMap<K, T>();
	m_keys = other.m_keys;
	m_pairs = other.m_pairs;
	return *this;
}

template<typename K, typename T>
const T &HashMap<K, T>::operator[](K k) const {
	auto &p = access(m_pairs, k);
	if (p == nullptr) {
		if (m_pairs.size() * 0.7 < m_keys.size()) {
			expand();
		}
		p = new Pair;
		p->key = k;
		m_keys.push_back(k);
	}
	return p->value;
}

template<typename K, typename T>
T &HashMap<K, T>::operator[](K k) {
	auto &p = access(m_pairs, k);
	if (p == nullptr) {
		if (m_pairs.size() * 0.7 < m_keys.size()) {
			expand();
		}
		p = new Pair;
		p->key = k;
		m_keys.push_back(k);
	}
	return p->value;
}

template<typename K, typename T>
T &HashMap<K, T>::at(K k) {
	return operator[](k);
}

template<typename K, typename T>
bool HashMap<K, T>::contains(K k) const {
	return access(m_pairs, k) != nullptr;
}

template<typename K, typename T>
std::size_t HashMap<K, T>::size() const noexcept {
	return m_keys.size();
}

template<typename K, typename T>
const Vector<K> &HashMap<K, T>::keys() const noexcept {
	return m_keys;
}

template<typename K, typename T>
void HashMap<K, T>::expand() const {
	Vector<Pair*> r;
	for (std::size_t i = 0; i < m_keys.size(); i++) {
		auto k = m_keys[i];
		access(r, k) = access(m_pairs, k);
	}
	m_pairs = r;
}

template<typename K, typename T>
uint64_t HashMap<K, T>::hash(K k, int len) noexcept {
	uint64_t sum = 1;
	for (int i = 0; i < len && k[i]; i++) {
		sum += ((sum + k[i]) * 7) * sum;
	}
	return sum;
}

template<typename K, typename T>
typename HashMap<K, T>::Pair *&HashMap<K, T>::access(Vector<Pair*> &pairs, K k) const {
	auto h = hash(k) % pairs.size();
	auto hashStr = reinterpret_cast<char*>(&h);
	while (1) {
		auto &p = pairs[h];
		if (p == nullptr || ox_strcmp(p->key, k) == 0) {
			return p;
		} else {
			h = hash(hashStr, 8) % pairs.size();
		}
	}
}

}
<|endoftext|>


<commit_before>//===- Target.cpp ---------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Target.h"
#include "Error.h"
#include "Symbols.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ELF.h"

using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;

namespace lld {
namespace elf2 {

std::unique_ptr<TargetInfo> Target;

TargetInfo::~TargetInfo() {}

bool TargetInfo::relocPointsToGot(uint32_t Type) const { return false; }

X86TargetInfo::X86TargetInfo() {
  PCRelReloc = R_386_PC32;
  GotReloc = R_386_GLOB_DAT;
  GotRefReloc = R_386_GOT32;
}

void X86TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                  uint64_t PltEntryAddr) const {
  // jmpl *val; nop; nop
  const uint8_t Inst[] = {0xff, 0x25, 0, 0, 0, 0, 0x90, 0x90};
  memcpy(Buf, Inst, sizeof(Inst));
  assert(isUInt<32>(GotEntryAddr));
  write32le(Buf + 2, GotEntryAddr);
}

bool X86TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return Type == R_386_GOT32 || relocNeedsPlt(Type, S);
}

bool X86TargetInfo::relocPointsToGot(uint32_t Type) const {
  return Type == R_386_GOTPC;
}

bool X86TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return Type == R_386_PLT32;
}

static void add32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }

void X86TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                uint64_t BaseAddr, uint64_t SymVA,
                                uint64_t GotVA) const {
  typedef ELFFile<ELF32LE>::Elf_Rel Elf_Rel;
  auto &Rel = *reinterpret_cast<const Elf_Rel *>(RelP);

  uint32_t Offset = Rel.r_offset;
  uint8_t *Location = Buf + Offset;
  switch (Type) {
  case R_386_GOT32:
    add32le(Location, SymVA - GotVA);
    break;
  case R_386_PC32:
    add32le(Location, SymVA - (BaseAddr + Offset));
    break;
  case R_386_32:
    add32le(Location, SymVA);
    break;
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

X86_64TargetInfo::X86_64TargetInfo() {
  PCRelReloc = R_X86_64_PC32;
  GotReloc = R_X86_64_GLOB_DAT;
  GotRefReloc = R_X86_64_PC32;
}

void X86_64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                     uint64_t PltEntryAddr) const {
  // jmpq *val(%rip); nop; nop
  const uint8_t Inst[] = {0xff, 0x25, 0, 0, 0, 0, 0x90, 0x90};
  memcpy(Buf, Inst, sizeof(Inst));

  uint64_t NextPC = PltEntryAddr + 6;
  int64_t Delta = GotEntryAddr - NextPC;
  assert(isInt<32>(Delta));
  write32le(Buf + 2, Delta);
}

bool X86_64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return Type == R_X86_64_GOTPCREL || relocNeedsPlt(Type, S);
}

bool X86_64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  switch (Type) {
  default:
    return false;
  case R_X86_64_PC32:
    // This relocation is defined to have a value of (S + A - P).
    // The problems start when a non PIC program calls a function in a shared
    // library.
    // In an ideal world, we could just report an error saying the relocation
    // can overflow at runtime.
    // In the real world with glibc, crt1.o has a R_X86_64_PC32 pointing to
    // libc.so.
    //
    // The general idea on how to handle such cases is to create a PLT entry
    // and use that as the function value.
    //
    // For the static linking part, we just return true and everything else
    // will use the the PLT entry as the address.
    //
    // The remaining (unimplemented) problem is making sure pointer equality
    // still works. We need the help of the dynamic linker for that. We
    // let it know that we have a direct reference to a so symbol by creating
    // an undefined symbol with a non zero st_value. Seeing that, the
    // dynamic linker resolves the symbol to the value of the symbol we created.
    // This is true even for got entries, so pointer equality is maintained.
    // To avoid an infinite loop, the only entry that points to the
    // real function is a dedicated got entry used by the plt. That is
    // identified by special relocation types (R_X86_64_JUMP_SLOT,
    // R_386_JMP_SLOT, etc).
    return S.isShared();
  case R_X86_64_PLT32:
    return true;
  }
}

void X86_64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP,
                                   uint32_t Type, uint64_t BaseAddr,
                                   uint64_t SymVA, uint64_t GotVA) const {
  typedef ELFFile<ELF64LE>::Elf_Rela Elf_Rela;
  auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP);

  uint64_t Offset = Rel.r_offset;
  uint8_t *Location = Buf + Offset;
  switch (Type) {
  case R_X86_64_PC32:
  case R_X86_64_GOTPCREL:
    write32le(Location, SymVA + Rel.r_addend - (BaseAddr + Offset));
    break;
  case R_X86_64_64:
    write64le(Location, SymVA + Rel.r_addend);
    break;
  case R_X86_64_32: {
  case R_X86_64_32S:
    uint64_t VA = SymVA + Rel.r_addend;
    if (Type == R_X86_64_32 && !isUInt<32>(VA))
      error("R_X86_64_32 out of range");
    else if (!isInt<32>(VA))
      error("R_X86_64_32S out of range");

    write32le(Location, VA);
    break;
  }
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

PPC64TargetInfo::PPC64TargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void PPC64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                    uint64_t PltEntryAddr) const {}
bool PPC64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}
bool PPC64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}
void PPC64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                  uint64_t BaseAddr, uint64_t SymVA,
                                  uint64_t GotVA) const {
  typedef ELFFile<ELF64BE>::Elf_Rela Elf_Rela;
  auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP);

  uint64_t Offset = Rel.r_offset;
  uint8_t *Location = Buf + Offset;
  switch (Type) {
  case R_PPC64_ADDR64:
    write64be(Location, SymVA + Rel.r_addend);
    break;
  case R_PPC64_TOC:
    // We don't create a TOC yet.
    break;
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

PPCTargetInfo::PPCTargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void PPCTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                  uint64_t PltEntryAddr) const {}
bool PPCTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}
bool PPCTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}
void PPCTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                uint64_t BaseAddr, uint64_t SymVA,
                                uint64_t GotVA) const {}

ARMTargetInfo::ARMTargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void ARMTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                  uint64_t PltEntryAddr) const {}
bool ARMTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}
bool ARMTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}
void ARMTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                uint64_t BaseAddr, uint64_t SymVA,
                                uint64_t GotVA) const {}

AArch64TargetInfo::AArch64TargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void AArch64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                      uint64_t PltEntryAddr) const {}
bool AArch64TargetInfo::relocNeedsGot(uint32_t Type,
                                      const SymbolBody &S) const {
  return false;
}
bool AArch64TargetInfo::relocNeedsPlt(uint32_t Type,
                                      const SymbolBody &S) const {
  return false;
}

static void AArch64UpdateAdr(uint8_t *Location, uint64_t Imm) {
  uint32_t ImmLo = (Imm & 0x3) << 29;
  uint32_t ImmHi = ((Imm & 0x1FFFFC) >> 2) << 5;
  uint64_t Mask = (0x3 << 29) | (0x7FFFF << 5);
  write32le(Location, (read32le(Location) & ~Mask) | ImmLo | ImmHi);
}

static uint64_t AArch64GetPage(uint64_t Address) {
  return (Address & (~static_cast<uint64_t>(0xFFF)));
}

static void handle_ADR_PREL_LO21(uint8_t *Location, uint64_t S, int64_t A,
                                 uint64_t P) {
  uint64_t X = S + A - P;
  if (!isInt<21>(X))
    error("Relocation R_AARCH64_ADR_PREL_LO21 out of range");
  AArch64UpdateAdr(Location, X & 0x1FFFFF);
}

static void handle_ADR_PREL_PG_HI21(uint8_t *Location, uint64_t S, int64_t A,
                                    uint64_t P) {
  uint64_t X = AArch64GetPage(S + A) - AArch64GetPage(P);
  if (!isInt<33>(X))
    error("Relocation R_AARCH64_ADR_PREL_PG_HI21 out of range");
  AArch64UpdateAdr(Location, (X >> 12) & 0x1FFFFF); // X[32:12]
}

void AArch64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP,
                                    uint32_t Type, uint64_t BaseAddr,
                                    uint64_t SymVA, uint64_t GotVA) const {
  typedef ELFFile<ELF64LE>::Elf_Rela Elf_Rela;
  auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP);

  uint8_t *Location = Buf + Rel.r_offset;
  uint64_t S = SymVA;
  int64_t A = Rel.r_addend;
  uint64_t P = BaseAddr + Rel.r_offset;
  switch (Type) {
  case R_AARCH64_ADR_PREL_LO21:
    handle_ADR_PREL_LO21(Location, S, A, P);
    break;
  case R_AARCH64_ADR_PREL_PG_HI21:
    handle_ADR_PREL_PG_HI21(Location, S, A, P);
    break;
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

MipsTargetInfo::MipsTargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
  DefaultEntry = "__start";
}

void MipsTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                   uint64_t PltEntryAddr) const {}

bool MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}

bool MipsTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}

void MipsTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                 uint64_t BaseAddr, uint64_t SymVA,
                                 uint64_t GotVA) const {}
}
}
<commit_msg>[ELF2/AArch64] Add comment and change name to match the one of the ABI.<commit_after>//===- Target.cpp ---------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Target.h"
#include "Error.h"
#include "Symbols.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ELF.h"

using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;

namespace lld {
namespace elf2 {

std::unique_ptr<TargetInfo> Target;

TargetInfo::~TargetInfo() {}

bool TargetInfo::relocPointsToGot(uint32_t Type) const { return false; }

X86TargetInfo::X86TargetInfo() {
  PCRelReloc = R_386_PC32;
  GotReloc = R_386_GLOB_DAT;
  GotRefReloc = R_386_GOT32;
}

void X86TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                  uint64_t PltEntryAddr) const {
  // jmpl *val; nop; nop
  const uint8_t Inst[] = {0xff, 0x25, 0, 0, 0, 0, 0x90, 0x90};
  memcpy(Buf, Inst, sizeof(Inst));
  assert(isUInt<32>(GotEntryAddr));
  write32le(Buf + 2, GotEntryAddr);
}

bool X86TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return Type == R_386_GOT32 || relocNeedsPlt(Type, S);
}

bool X86TargetInfo::relocPointsToGot(uint32_t Type) const {
  return Type == R_386_GOTPC;
}

bool X86TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return Type == R_386_PLT32;
}

static void add32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }

void X86TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                uint64_t BaseAddr, uint64_t SymVA,
                                uint64_t GotVA) const {
  typedef ELFFile<ELF32LE>::Elf_Rel Elf_Rel;
  auto &Rel = *reinterpret_cast<const Elf_Rel *>(RelP);

  uint32_t Offset = Rel.r_offset;
  uint8_t *Location = Buf + Offset;
  switch (Type) {
  case R_386_GOT32:
    add32le(Location, SymVA - GotVA);
    break;
  case R_386_PC32:
    add32le(Location, SymVA - (BaseAddr + Offset));
    break;
  case R_386_32:
    add32le(Location, SymVA);
    break;
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

X86_64TargetInfo::X86_64TargetInfo() {
  PCRelReloc = R_X86_64_PC32;
  GotReloc = R_X86_64_GLOB_DAT;
  GotRefReloc = R_X86_64_PC32;
}

void X86_64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                     uint64_t PltEntryAddr) const {
  // jmpq *val(%rip); nop; nop
  const uint8_t Inst[] = {0xff, 0x25, 0, 0, 0, 0, 0x90, 0x90};
  memcpy(Buf, Inst, sizeof(Inst));

  uint64_t NextPC = PltEntryAddr + 6;
  int64_t Delta = GotEntryAddr - NextPC;
  assert(isInt<32>(Delta));
  write32le(Buf + 2, Delta);
}

bool X86_64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return Type == R_X86_64_GOTPCREL || relocNeedsPlt(Type, S);
}

bool X86_64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  switch (Type) {
  default:
    return false;
  case R_X86_64_PC32:
    // This relocation is defined to have a value of (S + A - P).
    // The problems start when a non PIC program calls a function in a shared
    // library.
    // In an ideal world, we could just report an error saying the relocation
    // can overflow at runtime.
    // In the real world with glibc, crt1.o has a R_X86_64_PC32 pointing to
    // libc.so.
    //
    // The general idea on how to handle such cases is to create a PLT entry
    // and use that as the function value.
    //
    // For the static linking part, we just return true and everything else
    // will use the the PLT entry as the address.
    //
    // The remaining (unimplemented) problem is making sure pointer equality
    // still works. We need the help of the dynamic linker for that. We
    // let it know that we have a direct reference to a so symbol by creating
    // an undefined symbol with a non zero st_value. Seeing that, the
    // dynamic linker resolves the symbol to the value of the symbol we created.
    // This is true even for got entries, so pointer equality is maintained.
    // To avoid an infinite loop, the only entry that points to the
    // real function is a dedicated got entry used by the plt. That is
    // identified by special relocation types (R_X86_64_JUMP_SLOT,
    // R_386_JMP_SLOT, etc).
    return S.isShared();
  case R_X86_64_PLT32:
    return true;
  }
}

void X86_64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP,
                                   uint32_t Type, uint64_t BaseAddr,
                                   uint64_t SymVA, uint64_t GotVA) const {
  typedef ELFFile<ELF64LE>::Elf_Rela Elf_Rela;
  auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP);

  uint64_t Offset = Rel.r_offset;
  uint8_t *Location = Buf + Offset;
  switch (Type) {
  case R_X86_64_PC32:
  case R_X86_64_GOTPCREL:
    write32le(Location, SymVA + Rel.r_addend - (BaseAddr + Offset));
    break;
  case R_X86_64_64:
    write64le(Location, SymVA + Rel.r_addend);
    break;
  case R_X86_64_32: {
  case R_X86_64_32S:
    uint64_t VA = SymVA + Rel.r_addend;
    if (Type == R_X86_64_32 && !isUInt<32>(VA))
      error("R_X86_64_32 out of range");
    else if (!isInt<32>(VA))
      error("R_X86_64_32S out of range");

    write32le(Location, VA);
    break;
  }
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

PPC64TargetInfo::PPC64TargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void PPC64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                    uint64_t PltEntryAddr) const {}
bool PPC64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}
bool PPC64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}
void PPC64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                  uint64_t BaseAddr, uint64_t SymVA,
                                  uint64_t GotVA) const {
  typedef ELFFile<ELF64BE>::Elf_Rela Elf_Rela;
  auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP);

  uint64_t Offset = Rel.r_offset;
  uint8_t *Location = Buf + Offset;
  switch (Type) {
  case R_PPC64_ADDR64:
    write64be(Location, SymVA + Rel.r_addend);
    break;
  case R_PPC64_TOC:
    // We don't create a TOC yet.
    break;
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

PPCTargetInfo::PPCTargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void PPCTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                  uint64_t PltEntryAddr) const {}
bool PPCTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}
bool PPCTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}
void PPCTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                uint64_t BaseAddr, uint64_t SymVA,
                                uint64_t GotVA) const {}

ARMTargetInfo::ARMTargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void ARMTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                  uint64_t PltEntryAddr) const {}
bool ARMTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}
bool ARMTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}
void ARMTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                uint64_t BaseAddr, uint64_t SymVA,
                                uint64_t GotVA) const {}

AArch64TargetInfo::AArch64TargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
}
void AArch64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                      uint64_t PltEntryAddr) const {}
bool AArch64TargetInfo::relocNeedsGot(uint32_t Type,
                                      const SymbolBody &S) const {
  return false;
}
bool AArch64TargetInfo::relocNeedsPlt(uint32_t Type,
                                      const SymbolBody &S) const {
  return false;
}

static void AArch64UpdateAdr(uint8_t *Location, uint64_t Imm) {
  uint32_t ImmLo = (Imm & 0x3) << 29;
  uint32_t ImmHi = ((Imm & 0x1FFFFC) >> 2) << 5;
  uint64_t Mask = (0x3 << 29) | (0x7FFFF << 5);
  write32le(Location, (read32le(Location) & ~Mask) | ImmLo | ImmHi);
}

// Page(Expr) is the page address of the expression Expr, defined
// as (Expr & ~0xFFF). (This applies even if the machine page size
// supported by the platform has a differen value).
static uint64_t AArch64GetPage(uint64_t Expr) {
  return Expr & (~static_cast<uint64_t>(0xFFF));
}

static void handle_ADR_PREL_LO21(uint8_t *Location, uint64_t S, int64_t A,
                                 uint64_t P) {
  uint64_t X = S + A - P;
  if (!isInt<21>(X))
    error("Relocation R_AARCH64_ADR_PREL_LO21 out of range");
  AArch64UpdateAdr(Location, X & 0x1FFFFF);
}

static void handle_ADR_PREL_PG_HI21(uint8_t *Location, uint64_t S, int64_t A,
                                    uint64_t P) {
  uint64_t X = AArch64GetPage(S + A) - AArch64GetPage(P);
  if (!isInt<33>(X))
    error("Relocation R_AARCH64_ADR_PREL_PG_HI21 out of range");
  AArch64UpdateAdr(Location, (X >> 12) & 0x1FFFFF); // X[32:12]
}

void AArch64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP,
                                    uint32_t Type, uint64_t BaseAddr,
                                    uint64_t SymVA, uint64_t GotVA) const {
  typedef ELFFile<ELF64LE>::Elf_Rela Elf_Rela;
  auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP);

  uint8_t *Location = Buf + Rel.r_offset;
  uint64_t S = SymVA;
  int64_t A = Rel.r_addend;
  uint64_t P = BaseAddr + Rel.r_offset;
  switch (Type) {
  case R_AARCH64_ADR_PREL_LO21:
    handle_ADR_PREL_LO21(Location, S, A, P);
    break;
  case R_AARCH64_ADR_PREL_PG_HI21:
    handle_ADR_PREL_PG_HI21(Location, S, A, P);
    break;
  default:
    error(Twine("unrecognized reloc ") + Twine(Type));
    break;
  }
}

MipsTargetInfo::MipsTargetInfo() {
  // PCRelReloc = FIXME
  // GotReloc = FIXME
  DefaultEntry = "__start";
}

void MipsTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
                                   uint64_t PltEntryAddr) const {}

bool MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
  return false;
}

bool MipsTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
  return false;
}

void MipsTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type,
                                 uint64_t BaseAddr, uint64_t SymVA,
                                 uint64_t GotVA) const {}
}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011, Intel Corporation
 * 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.
 *
 * 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 HOLDER 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 "dpoCData.h"

#include "dpo_debug.h"
#include "jsfriendapi.h"
#include "dpo_debug.h"
#include "dpo_security_checks_stub.h"

#include "nsIClassInfoImpl.h"
#include "nsIProgrammingLanguage.h"
#include "nsServiceManagerUtils.h"
#include "nsIXPConnect.h"

static nsCOMPtr<nsIXPConnect> __xpc = NULL;

/*
 * implement our own macros for DPO_PROP_JS_OBJECTS and DPO_HOLD_JS_OBJECTS
 */
#define DPO_DROP_JS_OBJECTS(obj, clazz) {                                                                  \
    if ((__xpc != NULL) || (__xpc = do_GetService(nsIXPConnect::GetCID(), NULL))) {                        \
        __xpc->RemoveJSHolder(NS_CYCLE_COLLECTION_UPCAST(obj, clazz));                                     \
    }                                                                                \
}

#define DPO_HOLD_JS_OBJECTS(obj, clazz) {                                                                  \
    if ((__xpc != NULL) || (__xpc = do_GetService(nsIXPConnect::GetCID(), NULL))) {                        \
        __xpc->AddJSHolder(NS_CYCLE_COLLECTION_UPCAST(obj, clazz), &NS_CYCLE_COLLECTION_NAME(clazz));      \
    }                                                                                \
}

/*
 * Implement ClassInfo support to make this class feel more like a JavaScript class, i.e.,
 * it is autmatically casted to the right interface and all methods are available
 * without using QueryInterface.
 *
 * see https://developer.mozilla.org/en/Using_nsIClassInfo
 */
NS_IMPL_CLASSINFO( dpoCData, 0, 0, DPO_DATA_CID)
NS_IMPL_CI_INTERFACE_GETTER2(dpoCData, dpoIData, nsXPCOMCycleCollectionParticipant)

/*
 * Implement the hooks for the cycle collector
 */
NS_IMPL_CYCLE_COLLECTION_CLASS(dpoCData)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(dpoCData)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(parent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(dpoCData)
    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(theArray)
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(dpoCData)
    NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(parent)
    if (tmp->theArray) {
	DEBUG_LOG_STATUS("UNLINK!", "unlinking array " << tmp->theArray);
        DPO_DROP_JS_OBJECTS(tmp, dpoCData);
        tmp->theArray = nsnull;
    }
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(dpoCData)
    DEBUG_LOG_STATUS("COLLECT!", "entering collection");
    NS_INTERFACE_MAP_ENTRY(dpoIData)
    NS_INTERFACE_MAP_ENTRY(nsISecurityCheckedComponent)
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, dpoIData)
    NS_IMPL_QUERY_CLASSINFO(dpoCData)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(dpoCData)
NS_IMPL_CYCLE_COLLECTING_RELEASE(dpoCData)

DPO_SECURITY_CHECKS_ALL( dpoCData)

dpoCData::dpoCData(dpoIContext *aParent)
{
	DEBUG_LOG_CREATE("dpoCData", this);
	parent = aParent;
	queue = NULL;
	memObj = NULL;
	theArray = nsnull;
	theContext = NULL;
#ifdef PREALLOCATE_IN_JS_HEAP
	mapped = false;
#endif /* PREALLOCATE_IN_JS_HEAP */
}

dpoCData::~dpoCData()
{
	DEBUG_LOG_DESTROY("dpoCData", this);
	if (memObj != NULL) {
#ifdef INCREMENTAL_MEM_RELEASE
		DeferFree(memObj);
#else /* INCREMENTAL_MEM_RELEASE */
		clReleaseMemObject( memObj);
#endif /* INCREMENTAL_MEM_RELEASE */
	}
	if ((queue != NULL) && retained) {
        DEBUG_LOG_STATUS("~dpoCData", "releasing queue object");
		clReleaseCommandQueue( queue);
	}
    if (theArray) {
        DEBUG_LOG_STATUS("~dpoCData", "releasing array object");
        DPO_DROP_JS_OBJECTS(this, dpoCData);
        theArray = nsnull;
    }
}

#ifdef INCREMENTAL_MEM_RELEASE
cl_mem *dpoCData::defer_list = new cl_mem[DEFER_LIST_LENGTH];
uint dpoCData::defer_pos = 0;

void dpoCData::DeferFree(cl_mem obj) {
	if (dpoCData::defer_pos >= DEFER_LIST_LENGTH) {
		clReleaseMemObject(obj);
	} else {
		dpoCData::defer_list[dpoCData::defer_pos++] = obj;
	}
}

int dpoCData::CheckFree() {
	int freed = 0;
	while ((dpoCData::defer_pos > 0) && (freed++ < DEFER_CHUNK_SIZE)) {
		clReleaseMemObject(dpoCData::defer_list[--dpoCData::defer_pos]);
	}
	return freed;
}
#endif /* INCREMENTAL_MEM_RELEASE */

cl_int dpoCData::EnqueueReadBuffer(size_t size, void *ptr) {
#ifdef INCREMENTAL_MEM_RELEASE
	cl_int err_code;
	int freed;

	do {
		freed = dpoCData::CheckFree();
		err_code = clEnqueueReadBuffer(queue, memObj, CL_TRUE, 0, size, ptr, 0, NULL, NULL);
	} while (((err_code == CL_MEM_OBJECT_ALLOCATION_FAILURE) || (err_code == CL_OUT_OF_HOST_MEMORY)) && freed);

	return err_code;
#else /* INCREMENTAL_MEM_RELEASE */
	return clEnqueueReadBuffer(queue, memObj, CL_TRUE, 0, size, ptr, 0, NULL, NULL);
#endif /* INCREMENTAL_MEM_RELEASE */
}

nsresult dpoCData::InitCData(JSContext *cx, cl_command_queue aQueue, cl_mem aMemObj, uint32 aType, uint32 aLength, uint32 aSize, JSObject *anArray)
{
	cl_int err_code;

	type = aType;
	length = aLength;
	size = aSize;
	memObj = aMemObj;
	theContext = cx;

	if (anArray) {
		// tell the JS runtime that we hold this typed array
		DPO_HOLD_JS_OBJECTS(this, dpoCData);
		theArray = anArray;
	} else {
		theArray = nsnull;
	}

	DEBUG_LOG_STATUS("InitCData", "queue is " << aQueue << " buffer is " << aMemObj);

	err_code = clRetainCommandQueue( queue);
	if (err_code != CL_SUCCESS) {
		DEBUG_LOG_ERROR("initCData", err_code);
		// SAH: we should really fail here but a bug in the whatif OpenCL
		//      makes the above retain operation always fail :-(
		retained = false;
	} else {
		retained = true;
	}
	queue = aQueue;

	return NS_OK;
}

/* [implicit_jscontext] readonly attribute jsval value; */
NS_IMETHODIMP dpoCData::GetValue(JSContext *cx, jsval *aValue)
{
	cl_int err_code;
#ifdef PREALLOCATE_IN_JS_HEAP
	void *mem;
#endif /* PREALLOCATE_IN_JS_HEAP */

	if (theArray) {
#ifdef PREALLOCATE_IN_JS_HEAP
		if (!mapped) {
			DEBUG_LOG_STATUS("GetValue", "memory is " << theArray);
			void *mem = clEnqueueMapBuffer(queue, memObj, CL_TRUE, CL_MAP_READ, 0, size, 0, NULL, NULL, &err_code);

			if (err_code != CL_SUCCESS) {
				DEBUG_LOG_ERROR("GetValue", err_code);
				return NS_ERROR_NOT_AVAILABLE;
			}
#ifndef DEBUG_OFF
			if (!js_IsTypedArray(theArray)) {
				DEBUG_LOG_STATUS("GetValue", "Cannot access typed array");
				return NS_ERROR_NOT_AVAILABLE;
			}

			if (mem != JS_GetTypedArrayData(theArray)) {
				DEBUG_LOG_STATUS("GetValue", "EnqueueMap returned wrong pointer");
			}
#endif /* DEBUG_OFF */
			mapped = true;
		}
#endif /* PREALLOCATE_IN_JS_HEAP */
		*aValue = OBJECT_TO_JSVAL(theArray);
		return NS_OK;
	} else {
        	// tell the runtime that we cache this array object
		DPO_HOLD_JS_OBJECTS(this, dpoCData);

		switch (type) {
		case js::ArrayBufferView::TYPE_FLOAT64:
			theArray = JS_NewFloat64Array(cx, length);
			break;
		case js::ArrayBufferView::TYPE_FLOAT32:
			theArray = JS_NewFloat32Array(cx, length);
			break;
		default:
			/* we only return float our double arrays, so fail in all other cases */
			return NS_ERROR_NOT_IMPLEMENTED;
		}

		if (!theArray) {
			DEBUG_LOG_STATUS("GetValue", "Cannot create typed array");
			return NS_ERROR_OUT_OF_MEMORY;
		}

#ifdef INCREMENTAL_MEM_RELEASE
		dpoCData::CheckFree();
#endif /* INCREMENTAL_MEM_RELEASE */

		err_code = EnqueueReadBuffer(size, JS_GetArrayBufferViewData(theArray, cx));
		if (err_code != CL_SUCCESS) {
			DEBUG_LOG_ERROR("GetValue", err_code);
			return NS_ERROR_NOT_AVAILABLE;
		}

		*aValue = OBJECT_TO_JSVAL(theArray);

		DEBUG_LOG_STATUS("GetValue", "materialized typed array");

		return NS_OK;
	}
}

/* [implicit_jscontext] void writeTo (in jsval dest); */
NS_IMETHODIMP dpoCData::WriteTo(const jsval & dest, JSContext *cx)
{
	JSObject *destArray;
	cl_int err_code;

	if (JSVAL_IS_PRIMITIVE( dest)) {
		return NS_ERROR_INVALID_ARG;
	}

	destArray = JSVAL_TO_OBJECT(dest);

	if (!JS_IsTypedArrayObject(destArray, cx)) {
		return NS_ERROR_CANNOT_CONVERT_DATA;
	}

	if ((JS_GetTypedArrayType(destArray, cx) != type) || (JS_GetTypedArrayLength(destArray, cx) != length)) {
		return NS_ERROR_INVALID_ARG;
	}

	err_code = EnqueueReadBuffer(size, JS_GetArrayBufferViewData(destArray, cx));
	if (err_code != CL_SUCCESS) {
		DEBUG_LOG_ERROR("WriteTo", err_code);
		return NS_ERROR_NOT_AVAILABLE;
	}

    return NS_OK;
}

cl_mem dpoCData::GetContainedBuffer()
{
	return memObj;
}

uint32 dpoCData::GetSize()
{
	return size;
}

uint32 dpoCData::GetType()
{
	return type;
}

uint32 dpoCData::GetLength()
{
	return length;
}
<commit_msg>retired old implementation of writeTo as it does no longer apply<commit_after>/*
 * Copyright (c) 2011, Intel Corporation
 * 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.
 *
 * 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 HOLDER 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 "dpoCData.h"

#include "dpo_debug.h"
#include "jsfriendapi.h"
#include "dpo_debug.h"
#include "dpo_security_checks_stub.h"

#include "nsIClassInfoImpl.h"
#include "nsIProgrammingLanguage.h"
#include "nsServiceManagerUtils.h"
#include "nsIXPConnect.h"

static nsCOMPtr<nsIXPConnect> __xpc = NULL;

/*
 * implement our own macros for DPO_PROP_JS_OBJECTS and DPO_HOLD_JS_OBJECTS
 */
#define DPO_DROP_JS_OBJECTS(obj, clazz) {                                                                  \
    if ((__xpc != NULL) || (__xpc = do_GetService(nsIXPConnect::GetCID(), NULL))) {                        \
        __xpc->RemoveJSHolder(NS_CYCLE_COLLECTION_UPCAST(obj, clazz));                                     \
    }                                                                                \
}

#define DPO_HOLD_JS_OBJECTS(obj, clazz) {                                                                  \
    if ((__xpc != NULL) || (__xpc = do_GetService(nsIXPConnect::GetCID(), NULL))) {                        \
        __xpc->AddJSHolder(NS_CYCLE_COLLECTION_UPCAST(obj, clazz), &NS_CYCLE_COLLECTION_NAME(clazz));      \
    }                                                                                \
}

/*
 * Implement ClassInfo support to make this class feel more like a JavaScript class, i.e.,
 * it is autmatically casted to the right interface and all methods are available
 * without using QueryInterface.
 *
 * see https://developer.mozilla.org/en/Using_nsIClassInfo
 */
NS_IMPL_CLASSINFO( dpoCData, 0, 0, DPO_DATA_CID)
NS_IMPL_CI_INTERFACE_GETTER2(dpoCData, dpoIData, nsXPCOMCycleCollectionParticipant)

/*
 * Implement the hooks for the cycle collector
 */
NS_IMPL_CYCLE_COLLECTION_CLASS(dpoCData)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(dpoCData)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(parent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(dpoCData)
    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(theArray)
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(dpoCData)
    NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(parent)
    if (tmp->theArray) {
	DEBUG_LOG_STATUS("UNLINK!", "unlinking array " << tmp->theArray);
        DPO_DROP_JS_OBJECTS(tmp, dpoCData);
        tmp->theArray = nsnull;
    }
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(dpoCData)
    DEBUG_LOG_STATUS("COLLECT!", "entering collection");
    NS_INTERFACE_MAP_ENTRY(dpoIData)
    NS_INTERFACE_MAP_ENTRY(nsISecurityCheckedComponent)
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, dpoIData)
    NS_IMPL_QUERY_CLASSINFO(dpoCData)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(dpoCData)
NS_IMPL_CYCLE_COLLECTING_RELEASE(dpoCData)

DPO_SECURITY_CHECKS_ALL( dpoCData)

dpoCData::dpoCData(dpoIContext *aParent)
{
	DEBUG_LOG_CREATE("dpoCData", this);
	parent = aParent;
	queue = NULL;
	memObj = NULL;
	theArray = nsnull;
	theContext = NULL;
#ifdef PREALLOCATE_IN_JS_HEAP
	mapped = false;
#endif /* PREALLOCATE_IN_JS_HEAP */
}

dpoCData::~dpoCData()
{
	DEBUG_LOG_DESTROY("dpoCData", this);
	if (memObj != NULL) {
#ifdef INCREMENTAL_MEM_RELEASE
		DeferFree(memObj);
#else /* INCREMENTAL_MEM_RELEASE */
		clReleaseMemObject( memObj);
#endif /* INCREMENTAL_MEM_RELEASE */
	}
	if ((queue != NULL) && retained) {
        DEBUG_LOG_STATUS("~dpoCData", "releasing queue object");
		clReleaseCommandQueue( queue);
	}
    if (theArray) {
        DEBUG_LOG_STATUS("~dpoCData", "releasing array object");
        DPO_DROP_JS_OBJECTS(this, dpoCData);
        theArray = nsnull;
    }
}

#ifdef INCREMENTAL_MEM_RELEASE
cl_mem *dpoCData::defer_list = new cl_mem[DEFER_LIST_LENGTH];
uint dpoCData::defer_pos = 0;

void dpoCData::DeferFree(cl_mem obj) {
	if (dpoCData::defer_pos >= DEFER_LIST_LENGTH) {
		clReleaseMemObject(obj);
	} else {
		dpoCData::defer_list[dpoCData::defer_pos++] = obj;
	}
}

int dpoCData::CheckFree() {
	int freed = 0;
	while ((dpoCData::defer_pos > 0) && (freed++ < DEFER_CHUNK_SIZE)) {
		clReleaseMemObject(dpoCData::defer_list[--dpoCData::defer_pos]);
	}
	return freed;
}
#endif /* INCREMENTAL_MEM_RELEASE */

cl_int dpoCData::EnqueueReadBuffer(size_t size, void *ptr) {
#ifdef INCREMENTAL_MEM_RELEASE
	cl_int err_code;
	int freed;

	do {
		freed = dpoCData::CheckFree();
		err_code = clEnqueueReadBuffer(queue, memObj, CL_TRUE, 0, size, ptr, 0, NULL, NULL);
	} while (((err_code == CL_MEM_OBJECT_ALLOCATION_FAILURE) || (err_code == CL_OUT_OF_HOST_MEMORY)) && freed);

	return err_code;
#else /* INCREMENTAL_MEM_RELEASE */
	return clEnqueueReadBuffer(queue, memObj, CL_TRUE, 0, size, ptr, 0, NULL, NULL);
#endif /* INCREMENTAL_MEM_RELEASE */
}

nsresult dpoCData::InitCData(JSContext *cx, cl_command_queue aQueue, cl_mem aMemObj, uint32 aType, uint32 aLength, uint32 aSize, JSObject *anArray)
{
	cl_int err_code;

	type = aType;
	length = aLength;
	size = aSize;
	memObj = aMemObj;
	theContext = cx;

	if (anArray) {
		// tell the JS runtime that we hold this typed array
		DPO_HOLD_JS_OBJECTS(this, dpoCData);
		theArray = anArray;
	} else {
		theArray = nsnull;
	}

	DEBUG_LOG_STATUS("InitCData", "queue is " << aQueue << " buffer is " << aMemObj);

	err_code = clRetainCommandQueue( queue);
	if (err_code != CL_SUCCESS) {
		DEBUG_LOG_ERROR("initCData", err_code);
		// SAH: we should really fail here but a bug in the whatif OpenCL
		//      makes the above retain operation always fail :-(
		retained = false;
	} else {
		retained = true;
	}
	queue = aQueue;

	return NS_OK;
}

/* [implicit_jscontext] readonly attribute jsval value; */
NS_IMETHODIMP dpoCData::GetValue(JSContext *cx, jsval *aValue)
{
	cl_int err_code;
#ifdef PREALLOCATE_IN_JS_HEAP
	void *mem;
#endif /* PREALLOCATE_IN_JS_HEAP */

	if (theArray) {
#ifdef PREALLOCATE_IN_JS_HEAP
		if (!mapped) {
			DEBUG_LOG_STATUS("GetValue", "memory is " << theArray);
			void *mem = clEnqueueMapBuffer(queue, memObj, CL_TRUE, CL_MAP_READ, 0, size, 0, NULL, NULL, &err_code);

			if (err_code != CL_SUCCESS) {
				DEBUG_LOG_ERROR("GetValue", err_code);
				return NS_ERROR_NOT_AVAILABLE;
			}
#ifndef DEBUG_OFF
			if (!js_IsTypedArray(theArray)) {
				DEBUG_LOG_STATUS("GetValue", "Cannot access typed array");
				return NS_ERROR_NOT_AVAILABLE;
			}

			if (mem != JS_GetTypedArrayData(theArray)) {
				DEBUG_LOG_STATUS("GetValue", "EnqueueMap returned wrong pointer");
			}
#endif /* DEBUG_OFF */
			mapped = true;
		}
#endif /* PREALLOCATE_IN_JS_HEAP */
		*aValue = OBJECT_TO_JSVAL(theArray);
		return NS_OK;
	} else {
        	// tell the runtime that we cache this array object
		DPO_HOLD_JS_OBJECTS(this, dpoCData);

		switch (type) {
		case js::ArrayBufferView::TYPE_FLOAT64:
			theArray = JS_NewFloat64Array(cx, length);
			break;
		case js::ArrayBufferView::TYPE_FLOAT32:
			theArray = JS_NewFloat32Array(cx, length);
			break;
		default:
			/* we only return float our double arrays, so fail in all other cases */
			return NS_ERROR_NOT_IMPLEMENTED;
		}

		if (!theArray) {
			DEBUG_LOG_STATUS("GetValue", "Cannot create typed array");
			return NS_ERROR_OUT_OF_MEMORY;
		}

#ifdef INCREMENTAL_MEM_RELEASE
		dpoCData::CheckFree();
#endif /* INCREMENTAL_MEM_RELEASE */

		err_code = EnqueueReadBuffer(size, JS_GetArrayBufferViewData(theArray, cx));
		if (err_code != CL_SUCCESS) {
			DEBUG_LOG_ERROR("GetValue", err_code);
			return NS_ERROR_NOT_AVAILABLE;
		}

		*aValue = OBJECT_TO_JSVAL(theArray);

		DEBUG_LOG_STATUS("GetValue", "materialized typed array");

		return NS_OK;
	}
}

/* [implicit_jscontext] void writeTo (in jsval dest); */
NS_IMETHODIMP dpoCData::WriteTo(const jsval & dest, JSContext *cx)
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

cl_mem dpoCData::GetContainedBuffer()
{
	return memObj;
}

uint32 dpoCData::GetSize()
{
	return size;
}

uint32 dpoCData::GetType()
{
	return type;
}

uint32 dpoCData::GetLength()
{
	return length;
}
<|endoftext|>