<commit_before>#include "util/bitmap.h"
#include "network.h"
#include "chat_server.h"
#include "chat.h"
#include "util/bitmap.h"
#include "util/funcs.h"
#include "util/keyboard.h"
#include "util/font.h"
#include "globals.h"
#include "init.h"
#include <iostream>

#include <signal.h>

using namespace std;

static const char * DEFAULT_FONT = "/fonts/arial.ttf";

Client::Client( Network::Socket socket, ChatServer * server, unsigned int id ):
socket( socket ),
server( server ),
id( id ),
alive( true ){
	pthread_mutex_init( &lock, NULL );
}

Client::~Client(){
}

string Client::getName(){
	string s;
	pthread_mutex_lock( &lock );
	s = name;
	pthread_mutex_unlock( &lock );
	return s;
}

void Client::kill(){
	pthread_mutex_lock( &lock );
	alive = false;
	pthread_mutex_unlock( &lock );
}

bool Client::isAlive(){
	bool b;
	pthread_mutex_lock( &lock );
	b = alive;
	pthread_mutex_unlock( &lock );
	return b;
}

void Client::setName( const std::string & s ){
	pthread_mutex_lock( &lock );
	name = s;
	pthread_mutex_unlock( &lock );
}

static void * clientInput( void * client_ ){
	Client * client = (Client *) client_;
	bool done = false;
	while ( ! done ){
		try{
			Network::Message message( client->getSocket() );
			Global::debug( 1 ) << "Got a message: '" << message.path << "'" << endl;
			int type;
			message >> type;
			switch ( type ){
				case CHANGE_NAME : {
					message << client->getId();
					client->getServer()->sendMessage( message, client->getId() );
					client->setName( message.path );
					client->getServer()->needUpdate();
					break;
				}
				case ADD_MESSAGE : {
					client->getServer()->addMessage( client->getName() + ":" + message.path, client->getId() );
					break;
				}
			}
			done = ! client->isAlive();
		} catch ( const Network::NetworkException & e ){
			Global::debug( 0 ) << "Client input " << client->getId() << " died" << endl;
			done = true;
		}
	}

	if ( client->isAlive() ){
		client->getServer()->killClient( client );
	}

	return NULL;
}

static void * clientOutput( void * client_ ){
	Client * client = (Client *) client_;
	bool done = false;
	while ( ! done ){
		Network::Message message;
		done = ! client->isAlive();
		if ( client->getOutgoing( message ) != false ){
			Global::debug( 1 ) << "Sending a message to " << client->getId() << endl;
			try{
				/*
				Network::Message net;
				net.path = message;
				net.send( client->getSocket() );
				*/
				message.send( client->getSocket() );
			} catch ( const Network::NetworkException & e ){
				Global::debug( 0 ) << "Client output " << client->getId() << " died" << endl;
				done = true;
			}
		} else {
			Util::rest( 1 );
		}
	}

	if ( client->isAlive() ){
		client->getServer()->killClient( client );
	}

	return NULL;
}

bool Client::getOutgoing( Network::Message & m ){
	bool has;
	pthread_mutex_lock( &lock );
	has = ! outgoing.empty();
	if ( has ){
		m = outgoing.front();
		outgoing.erase( outgoing.begin() );
	}
	pthread_mutex_unlock( &lock );
	return has;
}

void Client::addOutputMessage( const Network::Message & s ){
	pthread_mutex_lock( &lock );
	outgoing.push_back( s );
	pthread_mutex_unlock( &lock );
}

void Client::startThreads(){
	pthread_create( &inputThread, NULL, clientInput, this );
	pthread_create( &outputThread, NULL, clientOutput, this );
}

static void * acceptConnections( void * server_ ){
	bool done = false;
	ChatServer * server = (ChatServer *) server_;
	Network::Socket socket = server->getSocket();
	while ( ! done ){
		try{
			server->addConnection( Network::accept( socket ) );
		} catch ( const Network::NoConnectionsPendingException & e ){
		} catch ( const Network::NetworkException & e ){
			Global::debug( 0 ) << "Error accepting connections: " << e.getMessage() << endl;
		}
		Util::rest( 1 );
	}

	return NULL;
}

ChatServer::ChatServer( const string & name, Network::Socket socket ):
need_update( true ),
socket( socket ),
messages( 400, 350 ),
focus( INPUT_BOX ),
client_id( 1 ),
name( name ){
	background = new Bitmap( Util::getDataPath() + "/paintown-title.png" );

	Network::listen( socket );
	pthread_mutex_init( &lock, NULL );
	pthread_create( &acceptThread, NULL, acceptConnections, this );
}

void ChatServer::addConnection( Network::Socket s ){
	Client * client = new Client( s, this, clientId() );

	{
		Network::Message message;
		message << ADD_BUDDY;
		message << 0;
		message.path = getName();
		client->addOutputMessage( message );
	}

	pthread_mutex_lock( &lock );
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * c = *it;
		Network::Message message;
		message << ADD_BUDDY;
		message << c->getId();
		message.path = c->getName();
		client->addOutputMessage( message );
	}
	pthread_mutex_unlock( &lock );

	Global::debug( 1 ) << "Adding client " << client->getId() << endl;

	Network::Message message;
	message << ADD_BUDDY;
	message << client->getId();
	sendMessage( message, 0 );

	pthread_mutex_lock( &lock );
	clients.push_back( client );
	pthread_mutex_unlock( &lock );
	client->startThreads();
}

static char lowerCase( const char * x ){
	if ( x[0] >= 'A' && x[0] <= 'Z' ){
		return x[0] - 'A' + 'a';
	}
	return x[0];
}

void ChatServer::sendMessage( const Network::Message & message, unsigned int id ){
	pthread_mutex_lock( &lock );
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * c = *it;
		if ( c->getId() != id ){
			c->addOutputMessage( message );
		}
	}
	pthread_mutex_unlock( &lock );
}

void ChatServer::addMessage( const string & s, unsigned int id ){
	Network::Message message;
	message << ADD_MESSAGE;
	message.path = s;
	pthread_mutex_lock( &lock );
	messages.addMessage( s );
	needUpdate();
	pthread_mutex_unlock( &lock );

	sendMessage( message, id );
	/*
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * c = *it;
		if ( c->getId() != id ){
			c->addOutputMessage( message );
		}
	}
	*/
}

void ChatServer::handleInput( Keyboard & keyboard ){
	vector< int > keys;
	keyboard.readKeys( keys );

	for ( vector< int >::iterator it = keys.begin(); it != keys.end(); it++ ){
		int key = *it;
		if ( key == Keyboard::Key_BACKSPACE ){
			if ( input != "" ){
				input = input.substr( 0, input.length() - 1 );
				needUpdate();
			}
		} else if ( Keyboard::isAlpha( key ) ){
			input += lowerCase( Keyboard::keyToName( key ) );
			needUpdate();
		} else if ( key == Keyboard::Key_SPACE ){
			input += " ";
			needUpdate();
		} else if ( key == Keyboard::Key_ENTER ){
			addMessage( name + ": " + input, 0 );
			input = "";
			needUpdate();
		}
	}
}

void ChatServer::killClient( Client * c ){
	int id = c->getId();
	string name = c->getName();
	pthread_mutex_lock( &lock );
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); ){
		Client * client = *it;
		if ( client == c ){
			Global::debug( 0 ) << "Killing socket" << endl;
			c->kill();
			Network::close( c->getSocket() );
			Global::debug( 0 ) << "Waiting for input thread to die" << endl;
			pthread_join( c->getInputThread(), NULL );
			Global::debug( 0 ) << "Waiting for output thread to die" << endl;
			pthread_join( c->getOutputThread(), NULL );
			Global::debug( 0 ) << "Deleting client" << endl;
			delete client;
			it = clients.erase( it );
		} else {
			it++;
		}
	}
	needUpdate();
	pthread_mutex_unlock( &lock );
	addMessage( "** " + name + " quit", 0 );
	Network::Message remove;
	remove << REMOVE_BUDDY;
	remove << id;
	sendMessage( remove, 0 );
}

void ChatServer::logic( Keyboard & keyboard ){
	if ( keyboard[ Keyboard::Key_TAB ] ){
		focus = nextFocus( focus );
		needUpdate();
	}

	switch ( focus ){
		case INPUT_BOX : {
			handleInput( keyboard );
			break;
		}
		case QUIT : {
			break;
		}
	}
}

void ChatServer::needUpdate(){
	need_update = true;
}

Focus ChatServer::nextFocus( Focus f ){
	switch ( f ){
		case INPUT_BOX : return QUIT;
		case QUIT : return INPUT_BOX;
		default : return INPUT_BOX;
	}
}

bool ChatServer::needToDraw(){
	return need_update;
}

void ChatServer::drawInputBox( int x, int y, const Bitmap & work ){
	const Font & font = Font::getFont( Util::getDataPath() + DEFAULT_FONT, 20, 20 );

	work.drawingMode( Bitmap::MODE_TRANS );
	Bitmap::transBlender( 0, 0, 0, 128 );
	work.rectangleFill( x, y, x + messages.getWidth(), y + font.getHeight() + 1, Bitmap::makeColor( 0, 0, 0 ) );
	work.drawingMode( Bitmap::MODE_SOLID );
	int color = Bitmap::makeColor( 255, 255, 255 );
	if ( focus == INPUT_BOX ){
		color = Bitmap::makeColor( 255, 255, 0 );
	}
	work.rectangle( x, y, x + messages.getWidth(), y + font.getHeight(), color );
	Bitmap input_box( work, x + 1, y, messages.getWidth(), font.getHeight() );
	// font.printf( x + 1, y, Bitmap::makeColor( 255, 255, 255 ), work, input, 0 );
	font.printf( 0, 0, Bitmap::makeColor( 255, 255, 255 ), input_box, input, 0 );

}

void ChatServer::drawBuddyList( int x, int y, const Bitmap & work, const Font & font ){
	Bitmap buddyList( work, x, y, GFX_X - x - 5, 200 );
	buddyList.drawingMode( Bitmap::MODE_TRANS );
	Bitmap::transBlender( 0, 0, 0, 128 );
	buddyList.rectangleFill( 0, 0, buddyList.getWidth(), buddyList.getHeight(), Bitmap::makeColor( 0, 0, 0 ) );
	buddyList.drawingMode( Bitmap::MODE_SOLID );
	buddyList.rectangle( 0, 0, buddyList.getWidth() -1, buddyList.getHeight() - 1, Bitmap::makeColor( 255, 255, 255 ) );
	int fy = 1;
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * client = *it;
		const string & name = client->getName();
		font.printf( 1, fy, Bitmap::makeColor( 255, 255, 255 ), buddyList, name, 0 );
		fy += font.getHeight();
	}
}

void ChatServer::draw( const Bitmap & work ){
	int start_x = 20;
	int start_y = 20;
	const Font & font = Font::getFont( Util::getDataPath() + DEFAULT_FONT, 20, 20 );
	background->Blit( work );
	messages.draw( start_x, start_y, work, font );

	drawInputBox( start_x, start_y + messages.getHeight() + 5, work );

	drawBuddyList( start_x + messages.getWidth() + 10, start_y, work, font );

	need_update = false;
}

void ChatServer::run(){
	Global::speed_counter = 0;
	Bitmap work( GFX_X, GFX_Y );
	Keyboard keyboard;

	keyboard.setAllDelay( 200 );
	keyboard.setDelay( Keyboard::Key_TAB, 200 );
	keyboard.setDelay( Keyboard::Key_ESC, 0 );

	bool done = false;
	while ( ! done ){
		int think = Global::speed_counter;
		while ( think > 0 ){
			keyboard.poll();
			logic( keyboard );
			think -= 1;
			Global::speed_counter = 0;
			done = keyboard[ Keyboard::Key_ESC ];
		}

		if ( needToDraw() ){
			draw( work );
			work.BlitToScreen();
			work.clear();
		}

		while ( Global::speed_counter == 0 ){
			Util::rest( 1 );
			keyboard.poll();
		}
	}
}

ChatServer::~ChatServer(){
	delete background;
}
<commit_msg>add some comments<commit_after>#include "util/bitmap.h"
#include "network.h"
#include "chat_server.h"
#include "chat.h"
#include "util/bitmap.h"
#include "util/funcs.h"
#include "util/keyboard.h"
#include "util/font.h"
#include "globals.h"
#include "init.h"
#include <iostream>

#include <signal.h>

using namespace std;

static const char * DEFAULT_FONT = "/fonts/arial.ttf";

Client::Client( Network::Socket socket, ChatServer * server, unsigned int id ):
socket( socket ),
server( server ),
id( id ),
alive( true ){
	pthread_mutex_init( &lock, NULL );
}

Client::~Client(){
}

string Client::getName(){
	string s;
	pthread_mutex_lock( &lock );
	s = name;
	pthread_mutex_unlock( &lock );
	return s;
}

void Client::kill(){
	pthread_mutex_lock( &lock );
	alive = false;
	pthread_mutex_unlock( &lock );
}

bool Client::isAlive(){
	bool b;
	pthread_mutex_lock( &lock );
	b = alive;
	pthread_mutex_unlock( &lock );
	return b;
}

void Client::setName( const std::string & s ){
	pthread_mutex_lock( &lock );
	name = s;
	pthread_mutex_unlock( &lock );
}

static void * clientInput( void * client_ ){
	Client * client = (Client *) client_;
	bool done = false;
	while ( ! done ){
		try{
			Network::Message message( client->getSocket() );
			Global::debug( 1 ) << "Got a message: '" << message.path << "'" << endl;
			int type;
			message >> type;
			switch ( type ){
				case CHANGE_NAME : {
					message << client->getId();
					client->getServer()->sendMessage( message, client->getId() );
					client->setName( message.path );
					client->getServer()->needUpdate();
					break;
				}
				case ADD_MESSAGE : {
					client->getServer()->addMessage( client->getName() + ":" + message.path, client->getId() );
					break;
				}
			}
			done = ! client->isAlive();
		} catch ( const Network::NetworkException & e ){
			Global::debug( 0 ) << "Client input " << client->getId() << " died" << endl;
			done = true;
		}
	}

	/* this is not thread safe with the output client thread */
	if ( client->isAlive() ){
		Global::debug( 0 ) << "Input thread killing client" << endl;
		client->getServer()->killClient( client );
	}

	return NULL;
}

static void * clientOutput( void * client_ ){
	Client * client = (Client *) client_;
	bool done = false;
	while ( ! done ){
		Network::Message message;
		done = ! client->isAlive();
		if ( client->getOutgoing( message ) != false ){
			Global::debug( 1 ) << "Sending a message to " << client->getId() << endl;
			try{
				/*
				Network::Message net;
				net.path = message;
				net.send( client->getSocket() );
				*/
				message.send( client->getSocket() );
			} catch ( const Network::NetworkException & e ){
				Global::debug( 0 ) << "Client output " << client->getId() << " died" << endl;
				done = true;
			}
		} else {
			Util::rest( 1 );
		}
	}

	/* this is not thread safe with the input client thread */
	if ( client->isAlive() ){
		Global::debug( 0 ) << "Output thread killing client" << endl;
		client->getServer()->killClient( client );
	}

	return NULL;
}

bool Client::getOutgoing( Network::Message & m ){
	bool has;
	pthread_mutex_lock( &lock );
	has = ! outgoing.empty();
	if ( has ){
		m = outgoing.front();
		outgoing.erase( outgoing.begin() );
	}
	pthread_mutex_unlock( &lock );
	return has;
}

void Client::addOutputMessage( const Network::Message & s ){
	pthread_mutex_lock( &lock );
	outgoing.push_back( s );
	pthread_mutex_unlock( &lock );
}

void Client::startThreads(){
	pthread_create( &inputThread, NULL, clientInput, this );
	pthread_create( &outputThread, NULL, clientOutput, this );
}

static void * acceptConnections( void * server_ ){
	bool done = false;
	ChatServer * server = (ChatServer *) server_;
	Network::Socket socket = server->getSocket();
	while ( ! done ){
		try{
			server->addConnection( Network::accept( socket ) );
		} catch ( const Network::NoConnectionsPendingException & e ){
		} catch ( const Network::NetworkException & e ){
			Global::debug( 0 ) << "Error accepting connections: " << e.getMessage() << endl;
		}
		Util::rest( 1 );
	}

	return NULL;
}

ChatServer::ChatServer( const string & name, Network::Socket socket ):
need_update( true ),
socket( socket ),
messages( 400, 350 ),
focus( INPUT_BOX ),
client_id( 1 ),
name( name ){
	background = new Bitmap( Util::getDataPath() + "/paintown-title.png" );

	Network::listen( socket );
	pthread_mutex_init( &lock, NULL );
	pthread_create( &acceptThread, NULL, acceptConnections, this );
}

void ChatServer::addConnection( Network::Socket s ){
	Client * client = new Client( s, this, clientId() );

	{
		Network::Message message;
		message << ADD_BUDDY;
		message << 0;
		message.path = getName();
		client->addOutputMessage( message );
	}

	pthread_mutex_lock( &lock );
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * c = *it;
		Network::Message message;
		message << ADD_BUDDY;
		message << c->getId();
		message.path = c->getName();
		client->addOutputMessage( message );
	}
	pthread_mutex_unlock( &lock );

	Global::debug( 1 ) << "Adding client " << client->getId() << endl;

	Network::Message message;
	message << ADD_BUDDY;
	message << client->getId();
	sendMessage( message, 0 );

	pthread_mutex_lock( &lock );
	clients.push_back( client );
	pthread_mutex_unlock( &lock );
	client->startThreads();
}

static char lowerCase( const char * x ){
	if ( x[0] >= 'A' && x[0] <= 'Z' ){
		return x[0] - 'A' + 'a';
	}
	return x[0];
}

void ChatServer::sendMessage( const Network::Message & message, unsigned int id ){
	pthread_mutex_lock( &lock );
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * c = *it;
		if ( c->getId() != id ){
			c->addOutputMessage( message );
		}
	}
	pthread_mutex_unlock( &lock );
}

void ChatServer::addMessage( const string & s, unsigned int id ){
	Network::Message message;
	message << ADD_MESSAGE;
	message.path = s;
	pthread_mutex_lock( &lock );
	messages.addMessage( s );
	needUpdate();
	pthread_mutex_unlock( &lock );

	sendMessage( message, id );
	/*
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * c = *it;
		if ( c->getId() != id ){
			c->addOutputMessage( message );
		}
	}
	*/
}

void ChatServer::handleInput( Keyboard & keyboard ){
	vector< int > keys;
	keyboard.readKeys( keys );

	for ( vector< int >::iterator it = keys.begin(); it != keys.end(); it++ ){
		int key = *it;
		if ( key == Keyboard::Key_BACKSPACE ){
			if ( input != "" ){
				input = input.substr( 0, input.length() - 1 );
				needUpdate();
			}
		} else if ( Keyboard::isAlpha( key ) ){
			input += lowerCase( Keyboard::keyToName( key ) );
			needUpdate();
		} else if ( key == Keyboard::Key_SPACE ){
			input += " ";
			needUpdate();
		} else if ( key == Keyboard::Key_ENTER ){
			addMessage( name + ": " + input, 0 );
			input = "";
			needUpdate();
		}
	}
}

void ChatServer::killClient( Client * c ){
	int id = c->getId();
	string name = c->getName();
	pthread_mutex_lock( &lock );
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); ){
		Client * client = *it;
		if ( client == c ){
			Global::debug( 0 ) << "Killing socket" << endl;
			c->kill();
			Network::close( c->getSocket() );
			/* the client thread that called killClient will wait for
			 * itself to die, but pthreads won't deadlock on join
			 */
			Global::debug( 0 ) << "Waiting for input thread to die " << c->getInputThread() << endl;
			pthread_join( c->getInputThread(), NULL );
			Global::debug( 0 ) << "Waiting for output thread to die" << endl;
			pthread_join( c->getOutputThread(), NULL );
			Global::debug( 0 ) << "Deleting client" << endl;
			delete client;
			it = clients.erase( it );
		} else {
			it++;
		}
	}
	needUpdate();
	pthread_mutex_unlock( &lock );
	addMessage( "** " + name + " quit", 0 );
	Network::Message remove;
	remove << REMOVE_BUDDY;
	remove << id;
	sendMessage( remove, 0 );
}

void ChatServer::logic( Keyboard & keyboard ){
	if ( keyboard[ Keyboard::Key_TAB ] ){
		focus = nextFocus( focus );
		needUpdate();
	}

	switch ( focus ){
		case INPUT_BOX : {
			handleInput( keyboard );
			break;
		}
		case QUIT : {
			break;
		}
	}
}

void ChatServer::needUpdate(){
	need_update = true;
}

Focus ChatServer::nextFocus( Focus f ){
	switch ( f ){
		case INPUT_BOX : return QUIT;
		case QUIT : return INPUT_BOX;
		default : return INPUT_BOX;
	}
}

bool ChatServer::needToDraw(){
	return need_update;
}

void ChatServer::drawInputBox( int x, int y, const Bitmap & work ){
	const Font & font = Font::getFont( Util::getDataPath() + DEFAULT_FONT, 20, 20 );

	work.drawingMode( Bitmap::MODE_TRANS );
	Bitmap::transBlender( 0, 0, 0, 128 );
	work.rectangleFill( x, y, x + messages.getWidth(), y + font.getHeight() + 1, Bitmap::makeColor( 0, 0, 0 ) );
	work.drawingMode( Bitmap::MODE_SOLID );
	int color = Bitmap::makeColor( 255, 255, 255 );
	if ( focus == INPUT_BOX ){
		color = Bitmap::makeColor( 255, 255, 0 );
	}
	work.rectangle( x, y, x + messages.getWidth(), y + font.getHeight(), color );
	Bitmap input_box( work, x + 1, y, messages.getWidth(), font.getHeight() );
	// font.printf( x + 1, y, Bitmap::makeColor( 255, 255, 255 ), work, input, 0 );
	font.printf( 0, 0, Bitmap::makeColor( 255, 255, 255 ), input_box, input, 0 );

}

void ChatServer::drawBuddyList( int x, int y, const Bitmap & work, const Font & font ){
	Bitmap buddyList( work, x, y, GFX_X - x - 5, 200 );
	buddyList.drawingMode( Bitmap::MODE_TRANS );
	Bitmap::transBlender( 0, 0, 0, 128 );
	buddyList.rectangleFill( 0, 0, buddyList.getWidth(), buddyList.getHeight(), Bitmap::makeColor( 0, 0, 0 ) );
	buddyList.drawingMode( Bitmap::MODE_SOLID );
	buddyList.rectangle( 0, 0, buddyList.getWidth() -1, buddyList.getHeight() - 1, Bitmap::makeColor( 255, 255, 255 ) );
	int fy = 1;
	for ( vector< Client * >::iterator it = clients.begin(); it != clients.end(); it++ ){
		Client * client = *it;
		const string & name = client->getName();
		font.printf( 1, fy, Bitmap::makeColor( 255, 255, 255 ), buddyList, name, 0 );
		fy += font.getHeight();
	}
}

void ChatServer::draw( const Bitmap & work ){
	int start_x = 20;
	int start_y = 20;
	const Font & font = Font::getFont( Util::getDataPath() + DEFAULT_FONT, 20, 20 );
	background->Blit( work );
	messages.draw( start_x, start_y, work, font );

	drawInputBox( start_x, start_y + messages.getHeight() + 5, work );

	drawBuddyList( start_x + messages.getWidth() + 10, start_y, work, font );

	need_update = false;
}

void ChatServer::run(){
	Global::speed_counter = 0;
	Bitmap work( GFX_X, GFX_Y );
	Keyboard keyboard;

	keyboard.setAllDelay( 200 );
	keyboard.setDelay( Keyboard::Key_TAB, 200 );
	keyboard.setDelay( Keyboard::Key_ESC, 0 );

	bool done = false;
	while ( ! done ){
		int think = Global::speed_counter;
		while ( think > 0 ){
			keyboard.poll();
			logic( keyboard );
			think -= 1;
			Global::speed_counter = 0;
			done = keyboard[ Keyboard::Key_ESC ];
		}

		if ( needToDraw() ){
			draw( work );
			work.BlitToScreen();
			work.clear();
		}

		while ( Global::speed_counter == 0 ){
			Util::rest( 1 );
			keyboard.poll();
		}
	}
}

ChatServer::~ChatServer(){
	delete background;
}
<|endoftext|>


<commit_before>#include "object/jsbasicobject.h"
#include "object/function.h"
#include "object/argument.h"
#include "object/jsobject.h"

namespace grok {
namespace obj {

std::string JSObject::AsString() const
{
    std::string buff = "";
    buff += "{ ";
    for (const auto &it : object_) {
        auto prop = it.second->as<JSObject>();
        if (!prop->IsEnumerable())
            continue;
        buff += it.first + ": ";
        buff += it.second->as<JSObject>()->ToString() += ", ";
    }
    buff.pop_back(); // " "
    buff += " }";
    return buff;
}

std::string JSObject::ToString() const
{
    return "[ object Object ]";
}

std::shared_ptr<Object> toString(std::shared_ptr<Argument> Args)
{
    auto This = Args->GetProperty("this");

    auto result = CreateJSString(This->as<JSObject>()->ToString());
    return result;
}

std::shared_ptr<Object> hasOwnProperty(std::shared_ptr<Argument> Args)
{
    auto This = Args->GetProperty("this");

    auto Prop = Args->GetProperty("prop");
    auto Name = Prop->as<JSObject>()->ToString();
    // not according to ECMAScript standard
    return CreateJSNumber(This->as<JSObject>()->HasProperty(Name));
}

void DefineInternalObjectProperties(JSObject *obj)
{
    auto F = std::make_shared<Function>(toString);
    F->SetNonWritable();
    F->SetNonEnumerable();
    obj->AddProperty(std::string("toString"), std::make_shared<Object>(F));

    F = std::make_shared<Function>(hasOwnProperty);
    F->SetNonWritable();
    F->SetNonEnumerable();
    F->SetParams({ "prop" });
    obj->AddProperty(std::string("hasOwnProperty"),
        std::make_shared<Object>(F));

    // add the prototype property default is undefined
    auto P = CreateUndefinedObject();
    F->SetNonEnumerable();
    obj->AddProperty(std::string("prototype"), std::make_shared<Object>(P));
}

std::shared_ptr<Object> CreateJSObject()
{
    auto O = std::make_shared<JSObject>();
    DefineInternalObjectProperties(O.get());
    return std::make_shared<Object>(O);
}

std::shared_ptr<Object> ObjectConstructor(std::shared_ptr<Argument> Args)
{
    if (Args->Size() < 1) {
        return CreateJSObject();
    }
    return Args->At(0);
}

std::shared_ptr<Object> CreateCopy(std::shared_ptr<Object> obj)
{
    if (IsJSNumber(obj)) {
        return CreateJSNumber(obj->as<JSNumber>()->GetNumber());
    } else if (IsJSString(obj)) {
        return CreateJSString(obj->as<JSString>()->ToString());
    }
    // else return reference
    return obj;
}

} // obj
} // grok
<commit_msg>Typo: JSNumber ==> JSDouble<commit_after>#include "object/jsbasicobject.h"
#include "object/function.h"
#include "object/argument.h"
#include "object/jsobject.h"

namespace grok {
namespace obj {

std::string JSObject::AsString() const
{
    std::string buff = "";
    buff += "{ ";
    for (const auto &it : object_) {
        auto prop = it.second->as<JSObject>();
        if (!prop->IsEnumerable())
            continue;
        buff += it.first + ": ";
        buff += it.second->as<JSObject>()->ToString() += ", ";
    }
    buff.pop_back(); // " "
    buff += " }";
    return buff;
}

std::string JSObject::ToString() const
{
    return "[ object Object ]";
}

std::shared_ptr<Object> toString(std::shared_ptr<Argument> Args)
{
    auto This = Args->GetProperty("this");

    auto result = CreateJSString(This->as<JSObject>()->ToString());
    return result;
}

std::shared_ptr<Object> hasOwnProperty(std::shared_ptr<Argument> Args)
{
    auto This = Args->GetProperty("this");

    auto Prop = Args->GetProperty("prop");
    auto Name = Prop->as<JSObject>()->ToString();
    // not according to ECMAScript standard
    return CreateJSNumber(This->as<JSObject>()->HasProperty(Name));
}

void DefineInternalObjectProperties(JSObject *obj)
{
    auto F = std::make_shared<Function>(toString);
    F->SetNonWritable();
    F->SetNonEnumerable();
    obj->AddProperty(std::string("toString"), std::make_shared<Object>(F));

    F = std::make_shared<Function>(hasOwnProperty);
    F->SetNonWritable();
    F->SetNonEnumerable();
    F->SetParams({ "prop" });
    obj->AddProperty(std::string("hasOwnProperty"),
        std::make_shared<Object>(F));

    // add the prototype property default is undefined
    auto P = CreateUndefinedObject();
    F->SetNonEnumerable();
    obj->AddProperty(std::string("prototype"), std::make_shared<Object>(P));
}

std::shared_ptr<Object> CreateJSObject()
{
    auto O = std::make_shared<JSObject>();
    DefineInternalObjectProperties(O.get());
    return std::make_shared<Object>(O);
}

std::shared_ptr<Object> ObjectConstructor(std::shared_ptr<Argument> Args)
{
    if (Args->Size() < 1) {
        return CreateJSObject();
    }
    return Args->At(0);
}

std::shared_ptr<Object> CreateCopy(std::shared_ptr<Object> obj)
{
    if (IsJSNumber(obj)) {
        return CreateJSNumber(obj->as<JSDouble>()->GetNumber());
    } else if (IsJSString(obj)) {
        return CreateJSString(obj->as<JSString>()->ToString());
    }
    // else return reference
    return obj;
}

} // obj
} // grok
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// BSD 3-Clause License
//
// Copyright (c) 2019, Nefelus Inc
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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 "dbRegion.h"

#include "db.h"
#include "dbBlock.h"
#include "dbBlockCallBackObj.h"
#include "dbBox.h"
#include "dbBoxItr.h"
#include "dbDatabase.h"
#include "dbInst.h"
#include "dbRegion.h"
#include "dbRegionInstItr.h"
#include "dbRegionItr.h"
#include "dbTable.h"
#include "dbTable.hpp"

namespace odb {

template class dbTable<_dbRegion>;

_dbRegion::_dbRegion(_dbDatabase*)
{
  _flags._type = dbRegionType::INCLUSIVE;
  _flags._invalid = false;
  _name = NULL;
}

_dbRegion::_dbRegion(_dbDatabase*, const _dbRegion& r)
    : _flags(r._flags),
      _name(NULL),
      _insts(r._insts),
      _boxes(r._boxes),
      _parent(r._parent),
      _children(r._children),
      _next_child(r._next_child)
{
  if (r._name)
    _name = strdup(r._name);
}

_dbRegion::~_dbRegion()
{
  if (_name)
    free((void*) _name);
}

bool _dbRegion::operator==(const _dbRegion& rhs) const
{
  if (_flags._type != rhs._flags._type)
    return false;

  if (_flags._invalid != rhs._flags._invalid)
    return false;

  if (_name && rhs._name) {
    if (strcmp(_name, rhs._name) != 0)
      return false;
  } else if (_name || rhs._name)
    return false;

  if (_insts != rhs._insts)
    return false;

  if (_parent != rhs._parent)
    return false;

  if (_children != rhs._children)
    return false;

  if (_next_child != rhs._next_child)
    return false;

  return true;
}

bool _dbRegion::operator<(const _dbRegion& rhs) const
{
  if (_flags._type < rhs._flags._type)
    return false;

  if (_flags._type > rhs._flags._type)
    return true;

  if (_flags._invalid < rhs._flags._invalid)
    return false;

  if (_flags._invalid > rhs._flags._invalid)
    return true;

  if (_insts < rhs._insts)
    return true;

  if (_insts > rhs._insts)
    return false;

  if (_parent < rhs._parent)
    return true;

  if (_parent > rhs._parent)
    return false;

  if (_children < rhs._children)
    return true;

  if (_children > rhs._children)
    return false;

  if (_next_child < rhs._next_child)
    return true;

  if (_next_child > rhs._next_child)
    return false;

  return _boxes < rhs._boxes;
}

void _dbRegion::differences(dbDiff& diff,
                            const char* field,
                            const _dbRegion& rhs) const
{
  if (diff.deepDiff())
    return;

  DIFF_BEGIN
  DIFF_FIELD(_flags._type);
  DIFF_FIELD(_flags._invalid);
  DIFF_FIELD(_name);
  DIFF_FIELD(_insts);
  DIFF_FIELD(_boxes);
  DIFF_FIELD(_parent);
  DIFF_FIELD(_children);
  DIFF_FIELD(_next_child);
  DIFF_END
}

void _dbRegion::out(dbDiff& diff, char side, const char* field) const
{
  if (diff.deepDiff())
    return;

  DIFF_OUT_BEGIN
  DIFF_OUT_FIELD(_flags._type);
  DIFF_OUT_FIELD(_flags._invalid);
  DIFF_OUT_FIELD(_name);
  DIFF_OUT_FIELD(_insts);
  DIFF_OUT_FIELD(_boxes);
  DIFF_OUT_FIELD(_parent);
  DIFF_OUT_FIELD(_children);
  DIFF_OUT_FIELD(_next_child);
  DIFF_END
}

dbOStream& operator<<(dbOStream& stream, const _dbRegion& r)
{
  uint* bit_field = (uint*) &r._flags;
  stream << *bit_field;
  stream << r._name;
  stream << r._insts;
  stream << r._boxes;
  stream << r._parent;
  stream << r._children;
  stream << r._next_child;
  return stream;
}

dbIStream& operator>>(dbIStream& stream, _dbRegion& r)
{
  uint* bit_field = (uint*) &r._flags;
  stream >> *bit_field;
  stream >> r._name;
  stream >> r._insts;
  stream >> r._boxes;
  stream >> r._parent;
  stream >> r._children;
  stream >> r._next_child;

  return stream;
}

std::string dbRegion::getName()
{
  _dbRegion* region = (_dbRegion*) this;
  return region->_name;
}

dbRegionType dbRegion::getRegionType()
{
  _dbRegion* region = (_dbRegion*) this;
  dbRegionType t(region->_flags._type);
  return t;
}

void dbRegion::setInvalid(bool v)
{
  _dbRegion* region = (_dbRegion*) this;
  region->_flags._invalid = v;
}

bool dbRegion::isInvalid()
{
  _dbRegion* region = (_dbRegion*) this;
  return region->_flags._invalid == 1;
}

void dbRegion::setRegionType(dbRegionType type)
{
  _dbRegion* region = (_dbRegion*) this;
  region->_flags._type = type;
}

dbSet<dbInst> dbRegion::getRegionInsts()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();
  return dbSet<dbInst>(region, block->_region_inst_itr);
}

dbSet<dbBox> dbRegion::getBoundaries()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();
  return dbSet<dbBox>(region, block->_box_itr);
}

void dbRegion::addInst(dbInst* inst_)
{
  _dbRegion* region = (_dbRegion*) this;
  _dbInst* inst = (_dbInst*) inst_;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  if (inst->_region != 0) {
    dbRegion* r = dbRegion::getRegion((dbBlock*) block, inst->_region);
    r->removeInst(inst_);
  }

  inst->_region = region->getOID();

  if (region->_insts != 0) {
    _dbInst* tail = block->_inst_tbl->getPtr(region->_insts);
    inst->_region_next = region->_insts;
    inst->_region_prev = 0;
    tail->_region_prev = inst->getOID();
  } else {
    inst->_region_next = 0;
    inst->_region_prev = 0;
  }

  region->_insts = inst->getOID();
}

void dbRegion::removeInst(dbInst* inst_)
{
  _dbRegion* region = (_dbRegion*) this;
  _dbInst* inst = (_dbInst*) inst_;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  uint id = inst->getOID();

  if (region->_insts == id) {
    region->_insts = inst->_region_next;

    if (region->_insts != 0) {
      _dbInst* t = block->_inst_tbl->getPtr(region->_insts);
      t->_region_prev = 0;
    }
  } else {
    if (inst->_region_next != 0) {
      _dbInst* next = block->_inst_tbl->getPtr(inst->_region_next);
      next->_region_prev = inst->_region_prev;
    }

    if (inst->_region_prev != 0) {
      _dbInst* prev = block->_inst_tbl->getPtr(inst->_region_prev);
      prev->_region_next = inst->_region_next;
    }
  }

  inst->_region = 0;
}

dbRegion* dbRegion::getParent()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  if (region->_parent == 0)
    return NULL;

  _dbRegion* p = block->_region_tbl->getPtr(region->_parent);
  return (dbRegion*) p;
}

dbSet<dbRegion> dbRegion::getChildren()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  dbSet<dbRegion> children(region, block->_region_itr);
  return children;
}

void dbRegion::addChild(dbRegion* child_)
{
  _dbRegion* child = (_dbRegion*) child_;
  _dbRegion* parent = (_dbRegion*) this;
  //_dbBlock * block = (_dbBlock *) getOwner();

  if (child->_parent || (parent == child))
    return;

  child->_parent = parent->getOID();
  child->_next_child = parent->_children;
  parent->_children = child->getOID();
}

dbBlock* dbRegion::getBlock()
{
  return (dbBlock*) getImpl()->getOwner();
}

dbRegion* dbRegion::create(dbBlock* block_, const char* name)
{
  _dbBlock* block = (_dbBlock*) block_;

  if (block_->findRegion(name))
    return NULL;

  _dbRegion* region = block->_region_tbl->create();
  region->_name = strdup(name);
  ZALLOCATED(region->_name);
  for (auto callback : block->_callbacks)
    callback->inDbRegionCreate((dbRegion*) region);
  return (dbRegion*) region;
}

dbRegion* dbRegion::create(dbRegion* parent_, const char* name)
{
  _dbRegion* parent = (_dbRegion*) parent_;
  _dbBlock* block = (_dbBlock*) parent->getOwner();

  if (((dbBlock*) block)->findRegion(name))
    return NULL;

  _dbRegion* region = block->_region_tbl->create();
  region->_name = strdup(name);
  ZALLOCATED(region->_name);
  region->_parent = parent->getOID();
  region->_next_child = parent->_children;
  parent->_children = region->getOID();
  for (auto callback : block->_callbacks)
    callback->inDbRegionCreate((dbRegion*) region);
  return (dbRegion*) region;
}

void dbRegion::destroy(dbRegion* region_)
{
  _dbRegion* region = (_dbRegion*) region_;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  dbSet<dbRegion> children = region_->getChildren();
  dbSet<dbRegion>::iterator childItr;
  for (childItr = children.begin(); childItr != children.end();
       childItr = children.begin()) {
    dbRegion* child = *childItr;
    child->destroy(child);
  }
  for (auto callback : block->_callbacks)
    callback->inDbRegionDestroy((dbRegion*) region);

  dbSet<dbInst> insts = region_->getRegionInsts();
  dbSet<dbInst>::iterator iitr;

  for (iitr = insts.begin(); iitr != insts.end(); iitr = insts.begin()) {
    dbInst* inst = *iitr;
    region_->removeInst(inst);
  }

  dbSet<dbBox> boxes = region_->getBoundaries();
  dbSet<dbBox>::iterator bitr;

  for (bitr = boxes.begin(); bitr != boxes.end();) {
    dbSet<dbBox>::iterator next = ++bitr;
    dbBox* box = *bitr;
    dbProperty::destroyProperties(box);
    block->_box_tbl->destroy((_dbBox*) box);
    bitr = next;
  }

  if (region->_parent) {
    _dbRegion* parent = block->_region_tbl->getPtr(region->_parent);
    _dbRegion* prev = NULL;
    _dbRegion* cur = block->_region_tbl->getPtr(parent->_children);

    while (cur) {
      if (cur == region)
        break;

      prev = cur;
      cur = block->_region_tbl->getPtr(cur->_next_child);
    }

    if (prev == NULL)
      parent->_children = region->_next_child;
    else
      prev->_next_child = region->_next_child;
  }

  dbProperty::destroyProperties(region);
  block->_region_tbl->destroy(region);
}

dbSet<dbRegion>::iterator dbRegion::destroy(dbSet<dbRegion>::iterator& itr)
{
  dbRegion* r = *itr;
  dbSet<dbRegion>::iterator next = ++itr;
  destroy(r);
  return next;
}

dbRegion* dbRegion::getRegion(dbBlock* block_, uint dbid_)
{
  _dbBlock* block = (_dbBlock*) block_;
  return (dbRegion*) block->_region_tbl->getPtr(dbid_);
}

}  // namespace odb
<commit_msg>odb: fix bug in dbRegion::destory<commit_after>///////////////////////////////////////////////////////////////////////////////
// BSD 3-Clause License
//
// Copyright (c) 2019, Nefelus Inc
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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 "dbRegion.h"

#include "db.h"
#include "dbBlock.h"
#include "dbBlockCallBackObj.h"
#include "dbBox.h"
#include "dbBoxItr.h"
#include "dbDatabase.h"
#include "dbInst.h"
#include "dbRegion.h"
#include "dbRegionInstItr.h"
#include "dbRegionItr.h"
#include "dbTable.h"
#include "dbTable.hpp"

namespace odb {

template class dbTable<_dbRegion>;

_dbRegion::_dbRegion(_dbDatabase*)
{
  _flags._type = dbRegionType::INCLUSIVE;
  _flags._invalid = false;
  _name = NULL;
}

_dbRegion::_dbRegion(_dbDatabase*, const _dbRegion& r)
    : _flags(r._flags),
      _name(NULL),
      _insts(r._insts),
      _boxes(r._boxes),
      _parent(r._parent),
      _children(r._children),
      _next_child(r._next_child)
{
  if (r._name)
    _name = strdup(r._name);
}

_dbRegion::~_dbRegion()
{
  if (_name)
    free((void*) _name);
}

bool _dbRegion::operator==(const _dbRegion& rhs) const
{
  if (_flags._type != rhs._flags._type)
    return false;

  if (_flags._invalid != rhs._flags._invalid)
    return false;

  if (_name && rhs._name) {
    if (strcmp(_name, rhs._name) != 0)
      return false;
  } else if (_name || rhs._name)
    return false;

  if (_insts != rhs._insts)
    return false;

  if (_parent != rhs._parent)
    return false;

  if (_children != rhs._children)
    return false;

  if (_next_child != rhs._next_child)
    return false;

  return true;
}

bool _dbRegion::operator<(const _dbRegion& rhs) const
{
  if (_flags._type < rhs._flags._type)
    return false;

  if (_flags._type > rhs._flags._type)
    return true;

  if (_flags._invalid < rhs._flags._invalid)
    return false;

  if (_flags._invalid > rhs._flags._invalid)
    return true;

  if (_insts < rhs._insts)
    return true;

  if (_insts > rhs._insts)
    return false;

  if (_parent < rhs._parent)
    return true;

  if (_parent > rhs._parent)
    return false;

  if (_children < rhs._children)
    return true;

  if (_children > rhs._children)
    return false;

  if (_next_child < rhs._next_child)
    return true;

  if (_next_child > rhs._next_child)
    return false;

  return _boxes < rhs._boxes;
}

void _dbRegion::differences(dbDiff& diff,
                            const char* field,
                            const _dbRegion& rhs) const
{
  if (diff.deepDiff())
    return;

  DIFF_BEGIN
  DIFF_FIELD(_flags._type);
  DIFF_FIELD(_flags._invalid);
  DIFF_FIELD(_name);
  DIFF_FIELD(_insts);
  DIFF_FIELD(_boxes);
  DIFF_FIELD(_parent);
  DIFF_FIELD(_children);
  DIFF_FIELD(_next_child);
  DIFF_END
}

void _dbRegion::out(dbDiff& diff, char side, const char* field) const
{
  if (diff.deepDiff())
    return;

  DIFF_OUT_BEGIN
  DIFF_OUT_FIELD(_flags._type);
  DIFF_OUT_FIELD(_flags._invalid);
  DIFF_OUT_FIELD(_name);
  DIFF_OUT_FIELD(_insts);
  DIFF_OUT_FIELD(_boxes);
  DIFF_OUT_FIELD(_parent);
  DIFF_OUT_FIELD(_children);
  DIFF_OUT_FIELD(_next_child);
  DIFF_END
}

dbOStream& operator<<(dbOStream& stream, const _dbRegion& r)
{
  uint* bit_field = (uint*) &r._flags;
  stream << *bit_field;
  stream << r._name;
  stream << r._insts;
  stream << r._boxes;
  stream << r._parent;
  stream << r._children;
  stream << r._next_child;
  return stream;
}

dbIStream& operator>>(dbIStream& stream, _dbRegion& r)
{
  uint* bit_field = (uint*) &r._flags;
  stream >> *bit_field;
  stream >> r._name;
  stream >> r._insts;
  stream >> r._boxes;
  stream >> r._parent;
  stream >> r._children;
  stream >> r._next_child;

  return stream;
}

std::string dbRegion::getName()
{
  _dbRegion* region = (_dbRegion*) this;
  return region->_name;
}

dbRegionType dbRegion::getRegionType()
{
  _dbRegion* region = (_dbRegion*) this;
  dbRegionType t(region->_flags._type);
  return t;
}

void dbRegion::setInvalid(bool v)
{
  _dbRegion* region = (_dbRegion*) this;
  region->_flags._invalid = v;
}

bool dbRegion::isInvalid()
{
  _dbRegion* region = (_dbRegion*) this;
  return region->_flags._invalid == 1;
}

void dbRegion::setRegionType(dbRegionType type)
{
  _dbRegion* region = (_dbRegion*) this;
  region->_flags._type = type;
}

dbSet<dbInst> dbRegion::getRegionInsts()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();
  return dbSet<dbInst>(region, block->_region_inst_itr);
}

dbSet<dbBox> dbRegion::getBoundaries()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();
  return dbSet<dbBox>(region, block->_box_itr);
}

void dbRegion::addInst(dbInst* inst_)
{
  _dbRegion* region = (_dbRegion*) this;
  _dbInst* inst = (_dbInst*) inst_;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  if (inst->_region != 0) {
    dbRegion* r = dbRegion::getRegion((dbBlock*) block, inst->_region);
    r->removeInst(inst_);
  }

  inst->_region = region->getOID();

  if (region->_insts != 0) {
    _dbInst* tail = block->_inst_tbl->getPtr(region->_insts);
    inst->_region_next = region->_insts;
    inst->_region_prev = 0;
    tail->_region_prev = inst->getOID();
  } else {
    inst->_region_next = 0;
    inst->_region_prev = 0;
  }

  region->_insts = inst->getOID();
}

void dbRegion::removeInst(dbInst* inst_)
{
  _dbRegion* region = (_dbRegion*) this;
  _dbInst* inst = (_dbInst*) inst_;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  uint id = inst->getOID();

  if (region->_insts == id) {
    region->_insts = inst->_region_next;

    if (region->_insts != 0) {
      _dbInst* t = block->_inst_tbl->getPtr(region->_insts);
      t->_region_prev = 0;
    }
  } else {
    if (inst->_region_next != 0) {
      _dbInst* next = block->_inst_tbl->getPtr(inst->_region_next);
      next->_region_prev = inst->_region_prev;
    }

    if (inst->_region_prev != 0) {
      _dbInst* prev = block->_inst_tbl->getPtr(inst->_region_prev);
      prev->_region_next = inst->_region_next;
    }
  }

  inst->_region = 0;
}

dbRegion* dbRegion::getParent()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  if (region->_parent == 0)
    return NULL;

  _dbRegion* p = block->_region_tbl->getPtr(region->_parent);
  return (dbRegion*) p;
}

dbSet<dbRegion> dbRegion::getChildren()
{
  _dbRegion* region = (_dbRegion*) this;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  dbSet<dbRegion> children(region, block->_region_itr);
  return children;
}

void dbRegion::addChild(dbRegion* child_)
{
  _dbRegion* child = (_dbRegion*) child_;
  _dbRegion* parent = (_dbRegion*) this;
  //_dbBlock * block = (_dbBlock *) getOwner();

  if (child->_parent || (parent == child))
    return;

  child->_parent = parent->getOID();
  child->_next_child = parent->_children;
  parent->_children = child->getOID();
}

dbBlock* dbRegion::getBlock()
{
  return (dbBlock*) getImpl()->getOwner();
}

dbRegion* dbRegion::create(dbBlock* block_, const char* name)
{
  _dbBlock* block = (_dbBlock*) block_;

  if (block_->findRegion(name))
    return NULL;

  _dbRegion* region = block->_region_tbl->create();
  region->_name = strdup(name);
  ZALLOCATED(region->_name);
  for (auto callback : block->_callbacks)
    callback->inDbRegionCreate((dbRegion*) region);
  return (dbRegion*) region;
}

dbRegion* dbRegion::create(dbRegion* parent_, const char* name)
{
  _dbRegion* parent = (_dbRegion*) parent_;
  _dbBlock* block = (_dbBlock*) parent->getOwner();

  if (((dbBlock*) block)->findRegion(name))
    return NULL;

  _dbRegion* region = block->_region_tbl->create();
  region->_name = strdup(name);
  ZALLOCATED(region->_name);
  region->_parent = parent->getOID();
  region->_next_child = parent->_children;
  parent->_children = region->getOID();
  for (auto callback : block->_callbacks)
    callback->inDbRegionCreate((dbRegion*) region);
  return (dbRegion*) region;
}

void dbRegion::destroy(dbRegion* region_)
{
  _dbRegion* region = (_dbRegion*) region_;
  _dbBlock* block = (_dbBlock*) region->getOwner();

  dbSet<dbRegion> children = region_->getChildren();
  dbSet<dbRegion>::iterator childItr;
  for (childItr = children.begin(); childItr != children.end();
       childItr = children.begin()) {
    dbRegion* child = *childItr;
    child->destroy(child);
  }
  for (auto callback : block->_callbacks)
    callback->inDbRegionDestroy((dbRegion*) region);

  dbSet<dbInst> insts = region_->getRegionInsts();
  dbSet<dbInst>::iterator iitr;

  for (iitr = insts.begin(); iitr != insts.end(); iitr = insts.begin()) {
    dbInst* inst = *iitr;
    region_->removeInst(inst);
  }

  dbSet<dbBox> boxes = region_->getBoundaries();
  dbSet<dbBox>::iterator bitr;

  for (bitr = boxes.begin(); bitr != boxes.end();) {
    dbBox* box = *bitr;
    dbSet<dbBox>::iterator next = ++bitr;
    dbProperty::destroyProperties(box);
    block->_box_tbl->destroy((_dbBox*) box);
    bitr = next;
  }

  if (region->_parent) {
    _dbRegion* parent = block->_region_tbl->getPtr(region->_parent);
    _dbRegion* prev = NULL;
    _dbRegion* cur = block->_region_tbl->getPtr(parent->_children);

    while (cur) {
      if (cur == region)
        break;

      prev = cur;
      cur = block->_region_tbl->getPtr(cur->_next_child);
    }

    if (prev == NULL)
      parent->_children = region->_next_child;
    else
      prev->_next_child = region->_next_child;
  }

  dbProperty::destroyProperties(region);
  block->_region_tbl->destroy(region);
}

dbSet<dbRegion>::iterator dbRegion::destroy(dbSet<dbRegion>::iterator& itr)
{
  dbRegion* r = *itr;
  dbSet<dbRegion>::iterator next = ++itr;
  destroy(r);
  return next;
}

dbRegion* dbRegion::getRegion(dbBlock* block_, uint dbid_)
{
  _dbBlock* block = (_dbBlock*) block_;
  return (dbRegion*) block->_region_tbl->getPtr(dbid_);
}

}  // namespace odb
<|endoftext|>


<commit_before>#include <PCU.h>
#include <MeshSim.h>
#include <SimPartitionedMesh.h>
#include <SimUtil.h>
#include <SimParasolidKrnl.h>
#include <apfSIM.h>
#include <apfMDS.h>
#include <gmi_sim.h>
#include <apf.h>
#include <apfConvert.h>
#include <apfMesh2.h>

int main(int argc, char** argv)
{
  MPI_Init(&argc, &argv);
  PCU_Comm_Init();
  if (argc != 4) {
    if(0==PCU_Comm_Self())
      std::cerr << "usage: " << argv[0] << " <model file> <simmetrix mesh> <scorec mesh>\n";
    return 0;
  }
  Sim_readLicenseFile(NULL);
  SimPartitionedMesh_start(&argc,&argv);
  SimModel_start();
  pProgress progress = Progress_new();
  Progress_setDefaultCallback(progress);

  pGModel simModel;
  simModel = GM_load(argv[1], NULL, progress);

  pParMesh sim_mesh = PM_load(argv[2],sthreadNone,0,progress);
  apf::Mesh* simApfMesh = apf::createMesh(sim_mesh);

  gmi_register_sim();
  gmi_model* mdl = gmi_import_sim(simModel);
  apf::Mesh2* mesh = apf::createMdsMesh(mdl, simApfMesh);
  apf::destroyMesh(simApfMesh);
  M_release(sim_mesh);
  if (mesh->hasMatching()) {
    if (apf::alignMdsMatches(mesh))
      printf("fixed misaligned matches\n");
    else
      printf("matches were aligned\n");
    assert( ! apf::alignMdsMatches(mesh));
  }
  mesh->verify();
  mesh->writeNative(argv[3]);

  mesh->destroyNative();
  apf::destroyMesh(mesh);
  GM_release(simModel);

  Progress_delete(progress);
  SimModel_stop();
  SimPartitionedMesh_stop();
  Sim_unregisterAllKeys();
  PCU_Comm_Free();
  MPI_Finalize();
}
<commit_msg>add layer cleanup to convert<commit_after>#include <PCU.h>
#include <MeshSim.h>
#include <SimPartitionedMesh.h>
#include <SimUtil.h>
#include <SimParasolidKrnl.h>
#include <apfSIM.h>
#include <apfMDS.h>
#include <gmi_sim.h>
#include <apf.h>
#include <apfConvert.h>
#include <apfMesh2.h>

static void fixMatches(apf::Mesh2* m)
{
  if (m->hasMatching()) {
    if (apf::alignMdsMatches(m))
      printf("fixed misaligned matches\n");
    else
      printf("matches were aligned\n");
    assert( ! apf::alignMdsMatches(m));
  }
}

static void fixPyramids(apf::Mesh2* m)
{
  ma::Input* in = ma::configureIdentity(m);
  in->shouldCleanupLayer = true;
  ma::adapt(in);
}

static void postConvert(apf::Mesh2* m)
{
  fixMatches(m);
  fixPyramids(m);
  m->verify();
}

int main(int argc, char** argv)
{
  MPI_Init(&argc, &argv);
  PCU_Comm_Init();
  if (argc != 4) {
    if(0==PCU_Comm_Self())
      std::cerr << "usage: " << argv[0] << " <model file> <simmetrix mesh> <scorec mesh>\n";
    return EXIT_FAILURE;
  }
  Sim_readLicenseFile(NULL);
  SimPartitionedMesh_start(&argc,&argv);
  SimModel_start();
  pProgress progress = Progress_new();
  Progress_setDefaultCallback(progress);

  pGModel simModel;
  simModel = GM_load(argv[1], NULL, progress);

  pParMesh sim_mesh = PM_load(argv[2],sthreadNone,0,progress);
  apf::Mesh* simApfMesh = apf::createMesh(sim_mesh);

  gmi_register_sim();
  gmi_model* mdl = gmi_import_sim(simModel);
  apf::Mesh2* mesh = apf::createMdsMesh(mdl, simApfMesh);
  apf::destroyMesh(simApfMesh);
  M_release(sim_mesh);
  postConvert(mesh);
  mesh->writeNative(argv[3]);

  mesh->destroyNative();
  apf::destroyMesh(mesh);
  GM_release(simModel);

  Progress_delete(progress);
  SimModel_stop();
  SimPartitionedMesh_stop();
  Sim_unregisterAllKeys();
  PCU_Comm_Free();
  MPI_Finalize();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: OOXMLTestService.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: os $ $Date: 2007-06-19 05:33:42 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

/**
  Copyright 2005 Sun Microsystems, Inc.
  */

#ifndef _CPPUHELPER_IMPLBASE1_HXX_
#include <cppuhelper/implbase1.hxx>
#endif

#ifndef _COM_SUN_STAR_LANG_XMAIN_HPP_
#include <com/sun/star/lang/XMain.hpp>
#endif

#ifndef _COM_SUN_STAR_UNO_XCOMPONENTCONTEXT_HPP_
#include <com/sun/star/uno/XComponentContext.hpp>
#endif

#ifndef _COMPHELPER_PROCESSFACTORY_HXX_
#include <comphelper/processfactory.hxx>
#endif

#include "OOXMLTestService.hxx"
#include <stdio.h>
#include <wchar.h>
#include <com/sun/star/io/XStream.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/io/XTruncate.hpp>
#include <com/sun/star/task/XStatusIndicator.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <ucbhelper/contentbroker.hxx>
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#include <osl/process.h>
#include <rtl/string.hxx>
#include <hash_set>
#include <assert.h>
#include <cppuhelper/implbase2.hxx>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <comphelper/storagehelper.hxx>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <comphelper/seqstream.hxx>
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/container/XNameContainer.hpp>
#include <ooxml/OOXMLDocument.hxx>

#include <ctype.h>

using namespace ::com::sun::star;

namespace writerfilter { namespace ooxmltest  {

const sal_Char ScannerTestService::SERVICE_NAME[40] = "debugservices.ooxml.ScannerTestService";
const sal_Char ScannerTestService::IMPLEMENTATION_NAME[40] = "debugservices.ooxml.ScannerTestService";

ScannerTestService::ScannerTestService(const uno::Reference< uno::XComponentContext > &xContext_) :
xContext( xContext_ )
{
}

sal_Int32 SAL_CALL ScannerTestService::run( const uno::Sequence< rtl::OUString >& aArguments ) throw (uno::RuntimeException)
{
    uno::Sequence<uno::Any> aUcbInitSequence(2);
    aUcbInitSequence[0] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Local"));
    aUcbInitSequence[1] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Office"));
    uno::Reference<lang::XMultiServiceFactory> xServiceFactory(xContext->getServiceManager(), uno::UNO_QUERY_THROW);
    uno::Reference<lang::XMultiComponentFactory> xFactory(xContext->getServiceManager(), uno::UNO_QUERY_THROW );
    if (::ucbhelper::ContentBroker::initialize(xServiceFactory, aUcbInitSequence))
    {
            rtl::OUString arg=aArguments[0];

            ::comphelper::setProcessServiceFactory(xServiceFactory);

            uno::Reference<com::sun::star::ucb::XSimpleFileAccess> xFileAccess(
            xFactory->createInstanceWithContext(
                ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.ucb.SimpleFileAccess")),
                xContext), uno::UNO_QUERY_THROW );

            rtl_uString *dir=NULL;
            osl_getProcessWorkingDir(&dir);
            rtl::OUString absFileUrl;
            osl_getAbsoluteFileURL(dir, arg.pData, &absFileUrl.pData);
            rtl_uString_release(dir);

            uno::Reference<io::XInputStream> xInputStream = xFileAccess->openFileRead(absFileUrl);
            ooxml::OOXMLStream::Pointer_t pDocStream = ooxml::OOXMLDocumentFactory::createStream(xContext, xInputStream);

            ooxml::OOXMLDocument::Pointer_t pDocument(ooxml::OOXMLDocumentFactory::createDocument(pDocStream));

#if 0
        TimeValue t1; osl_getSystemTime(&t1);
#endif

        doctok::Stream::Pointer_t pStream = doctok::createStreamHandler();
        pDocument->resolve(*pStream);

#if 0
        TimeValue t2; osl_getSystemTime(&t2);
        printf("time=%is\n", t2.Seconds-t1.Seconds);
#endif

        ::ucbhelper::ContentBroker::deinitialize();
    }
    else
    {
        fprintf(stderr, "can't initialize UCB");
    }
    return 0;
}

::rtl::OUString ScannerTestService_getImplementationName ()
{
    return rtl::OUString::createFromAscii ( ScannerTestService::IMPLEMENTATION_NAME );
}

sal_Bool SAL_CALL ScannerTestService_supportsService( const ::rtl::OUString& ServiceName )
{
    return ServiceName.equals( rtl::OUString::createFromAscii( ScannerTestService::SERVICE_NAME ) );
}
uno::Sequence< rtl::OUString > SAL_CALL ScannerTestService_getSupportedServiceNames(  ) throw (uno::RuntimeException)
{
    uno::Sequence < rtl::OUString > aRet(1);
    rtl::OUString* pArray = aRet.getArray();
    pArray[0] =  rtl::OUString::createFromAscii ( ScannerTestService::SERVICE_NAME );
    return aRet;
}

uno::Reference< uno::XInterface > SAL_CALL ScannerTestService_createInstance( const uno::Reference< uno::XComponentContext > & xContext) throw( uno::Exception )
{
    return (cppu::OWeakObject*) new ScannerTestService( xContext );
}

} } /* end namespace writerfilter::ooxml */
<commit_msg>INTEGRATION: CWS xmlfilter02 (1.4.12); FILE MERGED 2007/11/29 15:50:13 hbrinkm 1.4.12.2: clean up 2007/11/14 13:01:09 hbrinkm 1.4.12.1: namespace clean up<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: OOXMLTestService.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: obo $ $Date: 2008-01-10 12:19:23 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

/**
  Copyright 2005 Sun Microsystems, Inc.
  */

#ifndef _CPPUHELPER_IMPLBASE1_HXX_
#include <cppuhelper/implbase1.hxx>
#endif

#ifndef _COM_SUN_STAR_LANG_XMAIN_HPP_
#include <com/sun/star/lang/XMain.hpp>
#endif

#ifndef _COM_SUN_STAR_UNO_XCOMPONENTCONTEXT_HPP_
#include <com/sun/star/uno/XComponentContext.hpp>
#endif

#ifndef _COMPHELPER_PROCESSFACTORY_HXX_
#include <comphelper/processfactory.hxx>
#endif

#include "OOXMLTestService.hxx"
#include <stdio.h>
#include <wchar.h>
#include <com/sun/star/io/XStream.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/io/XTruncate.hpp>
#include <com/sun/star/task/XStatusIndicator.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <ucbhelper/contentbroker.hxx>
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#include <osl/process.h>
#include <rtl/string.hxx>
#include <hash_set>
#include <assert.h>
#include <cppuhelper/implbase2.hxx>
#include <cppuhelper/bootstrap.hxx>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <comphelper/storagehelper.hxx>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <comphelper/seqstream.hxx>
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <ooxml/OOXMLDocument.hxx>

#include <ctype.h>

using namespace ::com::sun::star;

namespace writerfilter { namespace ooxmltest  {

const sal_Char ScannerTestService::SERVICE_NAME[40] = "debugservices.ooxml.ScannerTestService";
const sal_Char ScannerTestService::IMPLEMENTATION_NAME[40] = "debugservices.ooxml.ScannerTestService";

ScannerTestService::ScannerTestService(const uno::Reference< uno::XComponentContext > &xContext_) :
xContext( xContext_ )
{
}

sal_Int32 SAL_CALL ScannerTestService::run( const uno::Sequence< rtl::OUString >& aArguments ) throw (uno::RuntimeException)
{
    uno::Sequence<uno::Any> aUcbInitSequence(2);
    aUcbInitSequence[0] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Local"));
    aUcbInitSequence[1] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Office"));
    uno::Reference<lang::XMultiServiceFactory> xServiceFactory(xContext->getServiceManager(), uno::UNO_QUERY_THROW);
    uno::Reference<lang::XMultiComponentFactory> xFactory(xContext->getServiceManager(), uno::UNO_QUERY_THROW );
    if (::ucbhelper::ContentBroker::initialize(xServiceFactory, aUcbInitSequence))
    {
        rtl::OUString arg=aArguments[0];

        ::comphelper::setProcessServiceFactory(xServiceFactory);

        uno::Reference<com::sun::star::ucb::XSimpleFileAccess> xFileAccess
            (xFactory->createInstanceWithContext
             (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM
                              ("com.sun.star.ucb.SimpleFileAccess")),
              xContext), uno::UNO_QUERY_THROW );

        rtl_uString *dir=NULL;
        osl_getProcessWorkingDir(&dir);
        rtl::OUString absFileUrl;
        osl_getAbsoluteFileURL(dir, arg.pData, &absFileUrl.pData);
        rtl_uString_release(dir);

        uno::Reference<io::XInputStream> xInputStream =
            xFileAccess->openFileRead(absFileUrl);
        ooxml::OOXMLStream::Pointer_t pDocStream =
            ooxml::OOXMLDocumentFactory::createStream(xContext, xInputStream);

        ooxml::OOXMLDocument::Pointer_t pDocument
            (ooxml::OOXMLDocumentFactory::createDocument(pDocStream));

#if 0
        uno::Reference<text::XTextDocument> xDocument
            (xFactory->createInstanceWithContext
             (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM
                              ("com.sun.star.text.TextDocument")),
              xContext), uno::UNO_QUERY_THROW );
        uno::Reference<frame::XModel> xModel
            (xDocument, uno::UNO_QUERY_THROW);

        uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier
            (xDocument, uno::UNO_QUERY_THROW);
        uno::Reference<drawing::XShapes> xShapes
            (xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW);

        pDocument->setModel(xModel);
        pDocument->setShapes(xShapes);
#endif

        Stream::Pointer_t pStream = createStreamHandler();
        pDocument->resolve(*pStream);

        ::ucbhelper::ContentBroker::deinitialize();
    }
    else
    {
        fprintf(stderr, "can't initialize UCB");
    }
    return 0;
}

::rtl::OUString ScannerTestService_getImplementationName ()
{
    return rtl::OUString::createFromAscii ( ScannerTestService::IMPLEMENTATION_NAME );
}

sal_Bool SAL_CALL ScannerTestService_supportsService( const ::rtl::OUString& ServiceName )
{
    return ServiceName.equals( rtl::OUString::createFromAscii( ScannerTestService::SERVICE_NAME ) );
}
uno::Sequence< rtl::OUString > SAL_CALL ScannerTestService_getSupportedServiceNames(  ) throw (uno::RuntimeException)
{
    uno::Sequence < rtl::OUString > aRet(1);
    rtl::OUString* pArray = aRet.getArray();
    pArray[0] =  rtl::OUString::createFromAscii ( ScannerTestService::SERVICE_NAME );
    return aRet;
}

uno::Reference< uno::XInterface > SAL_CALL ScannerTestService_createInstance( const uno::Reference< uno::XComponentContext > & xContext) throw( uno::Exception )
{
    return (cppu::OWeakObject*) new ScannerTestService( xContext );
}

} } /* end namespace writerfilter::ooxml */
<|endoftext|>


<commit_before>#include "xchainer/native/native_device.h"

#include <cstddef>
#include <memory>
#include <thread>
#include <vector>

#include <gtest/gtest.h>

#include "xchainer/context.h"
#include "xchainer/native/native_backend.h"

namespace xchainer {
namespace native {
namespace {

template <typename T>
void ExpectDataEqual(const std::shared_ptr<void>& expected, const std::shared_ptr<void>& actual, size_t size) {
    auto expected_raw_ptr = static_cast<const T*>(expected.get());
    auto actual_raw_ptr = static_cast<const T*>(actual.get());
    for (size_t i = 0; i < size; ++i) {
        EXPECT_EQ(expected_raw_ptr[i], actual_raw_ptr[i]);
    }
}

NativeDevice& GetNativeDevice(Context& ctx, int device_index) {
    // Using dynamic_cast to ensure it's actually NativeDevice
    return dynamic_cast<NativeDevice&>(ctx.GetDevice({"native", device_index}));
}

TEST(NativeDeviceTest, Allocate) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    size_t bytesize = 3;
    std::shared_ptr<void> ptr = device.Allocate(bytesize);
    EXPECT_NE(nullptr, ptr);
}

TEST(NativeDeviceTest, AllocateZero) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    std::shared_ptr<void> ptr = device.Allocate(size_t{0});
    EXPECT_NE(ptr, nullptr);
}

TEST(NativeDeviceTest, AllocateFreeThreadSafe) {
    static constexpr size_t kNumThreads = 2;
    static constexpr size_t kNumLoopsPerThread = 1;
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    // Allocate-and-free loop
    auto func = [&device](size_t size) {
        for (size_t j = 0; j < kNumLoopsPerThread; ++j) {
            std::shared_ptr<void> ptr = device.Allocate(size);
            (void)ptr;  // unused
        }
    };

    // Launch threads
    std::vector<std::thread> threads;
    threads.reserve(kNumThreads);
    for (size_t i = 0; i < kNumThreads; ++i) {
        threads.emplace_back(func, i);
    }

    // Join threads
    for (size_t i = 0; i < kNumThreads; ++i) {
        threads[i].join();
    }
}

TEST(NativeDeviceTest, MakeDataFromForeignPointer) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    size_t bytesize = 3;
    std::shared_ptr<void> data = device.Allocate(bytesize);
    EXPECT_EQ(data.get(), device.MakeDataFromForeignPointer(data).get());
}

TEST(NativeDeviceTest, FromHostMemory) {
    size_t size = 3;
    size_t bytesize = size * sizeof(float);
    float raw_data[] = {0, 1, 2};
    std::shared_ptr<void> src(raw_data, [](const float* ptr) {
        (void)ptr;  // unused
    });

    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    std::shared_ptr<void> dst = device.FromHostMemory(src, bytesize);
    EXPECT_EQ(src.get(), dst.get());
}

TEST(NativeDeviceTest, Synchronize) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);
    device.Synchronize();  // no throw
}

}  // namespace
}  // namespace native
}  // namespace xchainer
<commit_msg>Add xchainer::NativeDevice thread safety test<commit_after>#include "xchainer/native/native_device.h"

#include <cstddef>
#include <memory>
#include <string>
#include <thread>
#include <vector>

#include <gtest/gtest.h>

#include "xchainer/context.h"
#include "xchainer/native/native_backend.h"
#include "xchainer/testing/threading.h"

namespace xchainer {
namespace native {
namespace {

template <typename T>
void ExpectDataEqual(const std::shared_ptr<void>& expected, const std::shared_ptr<void>& actual, size_t size) {
    auto expected_raw_ptr = static_cast<const T*>(expected.get());
    auto actual_raw_ptr = static_cast<const T*>(actual.get());
    for (size_t i = 0; i < size; ++i) {
        EXPECT_EQ(expected_raw_ptr[i], actual_raw_ptr[i]);
    }
}

NativeDevice& GetNativeDevice(Context& ctx, int device_index) {
    // Using dynamic_cast to ensure it's actually NativeDevice
    return dynamic_cast<NativeDevice&>(ctx.GetDevice({"native", device_index}));
}

void RunThreads(const std::function<void(void)>& func) {
    testing::RunThreads(2, [&func](size_t /*thread_index*/) {
        func();
        return nullptr;
    });
}

TEST(NativeDeviceTest, Allocate) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    size_t bytesize = 3;
    std::shared_ptr<void> ptr = device.Allocate(bytesize);
    EXPECT_NE(nullptr, ptr);
}

TEST(NativeDeviceTest, AllocateZero) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    std::shared_ptr<void> ptr = device.Allocate(size_t{0});
    EXPECT_NE(ptr, nullptr);
}

TEST(NativeDeviceTest, AllocateFreeThreadSafe) {
    static constexpr size_t kNumThreads = 2;
    static constexpr size_t kNumLoopsPerThread = 1;
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    // Allocate-and-free loop
    auto func = [&device](size_t size) {
        for (size_t j = 0; j < kNumLoopsPerThread; ++j) {
            std::shared_ptr<void> ptr = device.Allocate(size);
            (void)ptr;  // unused
        }
    };

    // Launch threads
    std::vector<std::thread> threads;
    threads.reserve(kNumThreads);
    for (size_t i = 0; i < kNumThreads; ++i) {
        threads.emplace_back(func, i);
    }

    // Join threads
    for (size_t i = 0; i < kNumThreads; ++i) {
        threads[i].join();
    }
}

TEST(NativeDeviceTest, MakeDataFromForeignPointer) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    size_t bytesize = 3;
    std::shared_ptr<void> data = device.Allocate(bytesize);
    EXPECT_EQ(data.get(), device.MakeDataFromForeignPointer(data).get());
}

TEST(NativeDeviceTest, FromHostMemory) {
    size_t size = 3;
    size_t bytesize = size * sizeof(float);
    float raw_data[] = {0, 1, 2};
    std::shared_ptr<void> src(raw_data, [](const float* ptr) {
        (void)ptr;  // unused
    });

    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    std::shared_ptr<void> dst = device.FromHostMemory(src, bytesize);
    EXPECT_EQ(src.get(), dst.get());
}

TEST(NativeDeviceTest, Synchronize) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);
    device.Synchronize();  // no throw
}

TEST(NativeDeviceTest, GetDeviceMultiThread) {
    Context ctx;

    RunThreads([&ctx]() {
        NativeDevice& device = GetNativeDevice(ctx, 0);
        (void)device;
    });
}

TEST(NativeDeviceTest, GetBackendMultiThread) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);
    Backend& expected_backend = device.backend();

    RunThreads([&device, &expected_backend]() {
        Backend& backend = device.backend();
        EXPECT_EQ(&expected_backend, &backend);
    });
}

TEST(NativeDeviceTest, GetIndexMultiThread) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);
    int expected_index = device.index();

    RunThreads([&device, &expected_index]() {
        int index = device.index();
        EXPECT_EQ(expected_index, index);
    });
}

TEST(NativeDeviceTest, GetNameMultiThread) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);
    std::string expected_name = device.name();

    RunThreads([&device, &expected_name]() {
        std::string name = device.name();
        EXPECT_EQ(expected_name, name);
    });
}

TEST(NativeDeviceTest, GetContextMultiThread) {
    Context ctx;
    NativeDevice& device = GetNativeDevice(ctx, 0);

    RunThreads([&ctx, &device]() {
        Context& context = device.context();
        EXPECT_EQ(&ctx, &context);
    });
}

}  // namespace
}  // namespace native
}  // namespace xchainer
<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE "test_serialize_file"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <iostream>
#include <fstream>

BOOST_AUTO_TEST_CASE(test_example)
{
    const auto data = toml::parse("toml/tests/example.toml");
    {
        std::ofstream ofs("tmp1.toml");
        ofs << std::setw(80) << data;
    }

    auto serialized = toml::parse("tmp1.toml");
    {
        auto& owner = toml::find<toml::table>(serialized, "owner");
        auto& bio   = toml::get<std::string>(owner.at("bio"));
        const auto CR = std::find(bio.begin(), bio.end(), '\r');
        if(CR != bio.end())
        {
            bio.erase(CR);
        }
    }
    BOOST_CHECK(data == serialized);
}

BOOST_AUTO_TEST_CASE(test_fruit)
{
    const auto data = toml::parse("toml/tests/fruit.toml");
    {
        std::ofstream ofs("tmp2.toml");
        ofs << std::setw(80) << data;
    }
    const auto serialized = toml::parse("tmp2.toml");
    BOOST_CHECK(data == serialized);
}

BOOST_AUTO_TEST_CASE(test_hard_example)
{
    const auto data = toml::parse("toml/tests/hard_example.toml");
    {
        std::ofstream ofs("tmp3.toml");
        ofs << std::setw(80) << data;
    }
    const auto serialized = toml::parse("tmp3.toml");
    BOOST_CHECK(data == serialized);
}
<commit_msg>test: check serialization keeps comments<commit_after>#define BOOST_TEST_MODULE "test_serialize_file"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <iostream>
#include <fstream>

BOOST_AUTO_TEST_CASE(test_example)
{
    const auto data = toml::parse("toml/tests/example.toml");
    {
        std::ofstream ofs("tmp1.toml");
        ofs << std::setw(80) << data;
    }

    auto serialized = toml::parse("tmp1.toml");
    {
        auto& owner = toml::find<toml::table>(serialized, "owner");
        auto& bio   = toml::get<std::string>(owner.at("bio"));
        const auto CR = std::find(bio.begin(), bio.end(), '\r');
        if(CR != bio.end())
        {
            bio.erase(CR);
        }
    }
    BOOST_CHECK(data == serialized);
}

BOOST_AUTO_TEST_CASE(test_example_with_comment)
{
    const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
    {
        std::ofstream ofs("tmp1_com.toml");
        ofs << std::setw(80) << data;
    }

    auto serialized = toml::parse<toml::preserve_comments>("tmp1_com.toml");
    {
        auto& owner = toml::find<typename decltype(serialized)::table_type>(serialized, "owner");
        auto& bio   = toml::get<std::string>(owner.at("bio"));
        const auto CR = std::find(bio.begin(), bio.end(), '\r');
        if(CR != bio.end())
        {
            bio.erase(CR);
        }
    }
    BOOST_CHECK(data == serialized);
    {
        std::ofstream ofs("tmp1_com1.toml");
        ofs << std::setw(80) << serialized;
    }
}

BOOST_AUTO_TEST_CASE(test_fruit)
{
    const auto data = toml::parse("toml/tests/fruit.toml");
    {
        std::ofstream ofs("tmp2.toml");
        ofs << std::setw(80) << data;
    }
    const auto serialized = toml::parse("tmp2.toml");
    BOOST_CHECK(data == serialized);
}

BOOST_AUTO_TEST_CASE(test_fruit_with_comments)
{
    const auto data = toml::parse<toml::preserve_comments>("toml/tests/fruit.toml");
    {
        std::ofstream ofs("tmp2_com.toml");
        ofs << std::setw(80) << data;
    }
    const auto serialized = toml::parse<toml::preserve_comments>("tmp2_com.toml");
    BOOST_CHECK(data == serialized);
}

BOOST_AUTO_TEST_CASE(test_hard_example)
{
    const auto data = toml::parse("toml/tests/hard_example.toml");
    {
        std::ofstream ofs("tmp3.toml");
        ofs << std::setw(80) << data;
    }
    const auto serialized = toml::parse("tmp3.toml");
    BOOST_CHECK(data == serialized);
}

BOOST_AUTO_TEST_CASE(test_hard_example_with_comment)
{
    const auto data = toml::parse<toml::preserve_comments>("toml/tests/hard_example.toml");
    {
        std::ofstream ofs("tmp3_com.toml");
        ofs << std::setw(80) << data;
    }
    const auto serialized = toml::parse<toml::preserve_comments>("tmp3_com.toml");
    {
        std::ofstream ofs("tmp3_com1.toml");
        ofs << std::setw(80) << serialized;
    }
    BOOST_CHECK(data == serialized);
}
<|endoftext|>


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

#include "node.h"
#include <folly/portability/Unistd.h>
#include <nlohmann/json.hpp>
#include <platform/dirutils.h>
#include <platform/strerror.h>
#include <protocol/connection/client_connection_map.h>
#ifndef WIN32
#include <signal.h>
#include <sys/wait.h>
#endif
#include <chrono>
#include <fstream>
#include <iostream>
#include <thread>

namespace cb::test {

Node::~Node() = default;
Node::Node(std::string directory) : directory(std::move(directory)) {
}

class NodeImpl : public Node {
public:
    NodeImpl(std::string directory, std::string id);
    ~NodeImpl() override;
    void startMemcachedServer();

    std::unique_ptr<MemcachedConnection> getConnection() override;

protected:
    void parsePortnumberFile();

    std::string configfile;
    nlohmann::json config;
    ConnectionMap connectionMap;
    const std::string id;
};

NodeImpl::NodeImpl(std::string directory, std::string id)
    : Node(std::move(directory)), id(std::move(id)) {
    const auto errmaps =
            cb::io::sanitizePath(SOURCE_ROOT "/etc/couchbase/kv/error_maps");
    const auto rbac = cb::io::sanitizePath(SOURCE_ROOT
                                           "/tests/testapp_cluster/rbac.json");
    const auto log_filename =
            cb::io::sanitizePath(NodeImpl::directory + "/memcached_log");
    const auto portnumber_file =
            cb::io::sanitizePath(NodeImpl::directory + "/memcached.ports.json");
    const auto minidump_dir =
            cb::io::sanitizePath(NodeImpl::directory + "/crash");
    cb::io::mkdirp(minidump_dir);

    config = {{"max_connections", 1000},
              {"system_connections", 250},
              {"stdin_listener", false},
              {"datatype_json", true},
              {"datatype_snappy", true},
              {"xattr_enabled", true},
              {"dedupe_nmvb_maps", false},
              {"active_external_users_push_interval", "30 m"},
              {"error_maps_dir", errmaps},
              {"external_auth_service", true},
              {"rbac_file", rbac},
              {"ssl_cipher_list", "HIGH"},
              {"ssl_minimum_protocol", "tlsv1"},
              {"opcode_attributes_override",
               {{"version", 1}, {"EWB_CTL", {{"slow", 50}}}}},
              {"logger",
               {{"unit_test", true},
                {"console", false},
                {"filename", log_filename}}},
              {"breakpad",
               {{"enabled", true},
                {"minidump_dir", minidump_dir},
                {"content", "default"}}},
              {"portnumber_file", portnumber_file},
              {"parent_identifier", (int)getpid()}};
    config["interfaces"][0] = {{"tag", "plain"},
                               {"system", true},
                               {"port", 0},
                               {"ipv4", "required"},
                               {"host", "*"}};
    configfile = cb::io::sanitizePath(NodeImpl::directory + "/memcached.json");
    std::ofstream out(configfile);
    out << config.dump(2);
    out.close();
}

void NodeImpl::startMemcachedServer() {
#ifdef WIN32
    STARTUPINFO sinfo{};
    PROCESS_INFORMATION pinfo{};
    sinfo.cb = sizeof(sinfo);

    char commandline[1024];
    sprintf(commandline, "memcached.exe -C %s", configfile.c_str());
    if (!CreateProcess("memcached.exe", // lpApplicationName
                       commandline, // lpCommandLine
                       nullptr, // lpProcessAttributes
                       nullptr, // lpThreadAttributes
                       false, // bInheritHandles
                       0, // dwCreationFlags
                       nullptr, // lpEnvironment
                       nullptr, // lpCurrentDirectory
                       &sinfo, // lpStartupInfo
                       &pinfo)) { // lpProcessInfoqrmation
        throw std::system_error(GetLastError(),
                                std::system_category(),
                                "Failed to execute memcached");
    }

    child = pinfo.hProcess;
#else
    child = fork();
    if (child == -1) {
        throw std::system_error(
                errno, std::system_category(), "Failed to start client");
    }

    if (child == 0) {
        std::string binary(OBJECT_ROOT);
        binary.append("/memcached");

        const char* argv[20];
        int arg = 0;

        argv[arg++] = binary.c_str();
        argv[arg++] = "-C";
        argv[arg++] = configfile.c_str();

        argv[arg++] = nullptr;
        execvp(argv[0], const_cast<char**>(argv));
        throw std::system_error(
                errno, std::system_category(), "Failed to execute memcached");
    }
#endif

    // wait and read the portnumber file
    parsePortnumberFile();
}

NodeImpl::~NodeImpl() {
    if (isRunning()) {
#ifdef WIN32
        // @todo This should be made a bit more robust
        TerminateProcess(child, 0);
        WaitForSingleObject(child, 60000);
        DWORD status;
        GetExitCodeProcess(child, &status);
#else
        // Start by giving it a slow and easy start...
        const auto timeout =
                std::chrono::steady_clock::now() + std::chrono::seconds(15);
        kill(child, SIGTERM);

        do {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        } while (isRunning() && std::chrono::steady_clock::now() < timeout);

        if (isRunning()) {
            // no mercy!
            kill(child, SIGKILL);

            int status;
            pid_t ret;
            while (true) {
                ret = waitpid(child, &status, 0);
                if (ret == reinterpret_cast<pid_t>(-1) && errno == EINTR) {
                    // Just loop again
                    continue;
                }
                break;
            }
        }
#endif
    }

    if (!configfile.empty()) {
        try {
            cb::io::rmrf(configfile);
        } catch (const std::exception& e) {
            std::cerr << "WARNING: Failed to remove \"" << configfile
                      << "\": " << e.what() << std::endl;
        }
    }
}

void NodeImpl::parsePortnumberFile() {
    connectionMap.initialize(nlohmann::json::parse(cb::io::loadFile(
            config["portnumber_file"], std::chrono::minutes{5})));
    cb::io::rmrf(config["portnumber_file"]);
}

#ifdef WIN32
bool Node::isRunning() const {
    if (child != INVALID_HANDLE_VALUE) {
        DWORD status;

        if (!GetExitCodeProcess(child, &status)) {
            throw std::system_error(
                    GetLastError(),
                    std::system_category(),
                    "NodeImpl::isRunning: GetExitCodeProcess failed");

            std::cerr << "GetExitCodeProcess: failed: " << cb_strerror()
                      << std::endl;
            exit(EXIT_FAILURE);
        }

        if (status == STILL_ACTIVE) {
            return true;
        }

        CloseHandle(child);
        child = INVALID_HANDLE_VALUE;
    }
    return false;
}
#else
bool Node::isRunning() const {
    if (child != 0) {
        int status;
        auto next = waitpid(child, &status, WNOHANG);
        if (next == static_cast<pid_t>(-1)) {
            throw std::system_error(errno,
                                    std::system_category(),
                                    "NodeImpl::isRunning: waitpid failed");
        }

        if (next == child) {
            child = 0;
            return false;
        }

        return true;
    }

    return false;
}
#endif

std::unique_ptr<MemcachedConnection> NodeImpl::getConnection() {
    auto ret = connectionMap.getConnection().clone();
    ret->setAutoRetryTmpfail(true);
    std::vector<cb::mcbp::Feature> features = {
            {cb::mcbp::Feature::MUTATION_SEQNO,
             cb::mcbp::Feature::XATTR,
             cb::mcbp::Feature::XERROR,
             cb::mcbp::Feature::JSON}};
    ret->setFeatures("cluster_testapp", features);
    return ret;
}

std::unique_ptr<Node> Node::create(const std::string& directory,
                                   const std::string& id) {
    auto ret = std::make_unique<NodeImpl>(directory, id);
    ret->startMemcachedServer();
    return ret;
}

} // namespace cb::test
<commit_msg>[cluster_test] Add error handling in node shutdown WIN32<commit_after>/*
 *     Copyright 2019 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "node.h"
#include <folly/portability/Unistd.h>
#include <nlohmann/json.hpp>
#include <platform/dirutils.h>
#include <platform/strerror.h>
#include <protocol/connection/client_connection_map.h>
#ifndef WIN32
#include <signal.h>
#include <sys/wait.h>
#endif
#include <chrono>
#include <fstream>
#include <iostream>
#include <thread>

namespace cb::test {

Node::~Node() = default;
Node::Node(std::string directory) : directory(std::move(directory)) {
}

class NodeImpl : public Node {
public:
    NodeImpl(std::string directory, std::string id);
    ~NodeImpl() override;
    void startMemcachedServer();

    std::unique_ptr<MemcachedConnection> getConnection() override;

protected:
    void parsePortnumberFile();

    std::string configfile;
    nlohmann::json config;
    ConnectionMap connectionMap;
    const std::string id;
};

NodeImpl::NodeImpl(std::string directory, std::string id)
    : Node(std::move(directory)), id(std::move(id)) {
    const auto errmaps =
            cb::io::sanitizePath(SOURCE_ROOT "/etc/couchbase/kv/error_maps");
    const auto rbac = cb::io::sanitizePath(SOURCE_ROOT
                                           "/tests/testapp_cluster/rbac.json");
    const auto log_filename =
            cb::io::sanitizePath(NodeImpl::directory + "/memcached_log");
    const auto portnumber_file =
            cb::io::sanitizePath(NodeImpl::directory + "/memcached.ports.json");
    const auto minidump_dir =
            cb::io::sanitizePath(NodeImpl::directory + "/crash");
    cb::io::mkdirp(minidump_dir);

    config = {{"max_connections", 1000},
              {"system_connections", 250},
              {"stdin_listener", false},
              {"datatype_json", true},
              {"datatype_snappy", true},
              {"xattr_enabled", true},
              {"dedupe_nmvb_maps", false},
              {"active_external_users_push_interval", "30 m"},
              {"error_maps_dir", errmaps},
              {"external_auth_service", true},
              {"rbac_file", rbac},
              {"ssl_cipher_list", "HIGH"},
              {"ssl_minimum_protocol", "tlsv1"},
              {"opcode_attributes_override",
               {{"version", 1}, {"EWB_CTL", {{"slow", 50}}}}},
              {"logger",
               {{"unit_test", true},
                {"console", false},
                {"filename", log_filename}}},
              {"breakpad",
               {{"enabled", true},
                {"minidump_dir", minidump_dir},
                {"content", "default"}}},
              {"portnumber_file", portnumber_file},
              {"parent_identifier", (int)getpid()}};
    config["interfaces"][0] = {{"tag", "plain"},
                               {"system", true},
                               {"port", 0},
                               {"ipv4", "required"},
                               {"host", "*"}};
    configfile = cb::io::sanitizePath(NodeImpl::directory + "/memcached.json");
    std::ofstream out(configfile);
    out << config.dump(2);
    out.close();
}

void NodeImpl::startMemcachedServer() {
#ifdef WIN32
    STARTUPINFO sinfo{};
    PROCESS_INFORMATION pinfo{};
    sinfo.cb = sizeof(sinfo);

    char commandline[1024];
    sprintf(commandline, "memcached.exe -C %s", configfile.c_str());
    if (!CreateProcess("memcached.exe", // lpApplicationName
                       commandline, // lpCommandLine
                       nullptr, // lpProcessAttributes
                       nullptr, // lpThreadAttributes
                       false, // bInheritHandles
                       0, // dwCreationFlags
                       nullptr, // lpEnvironment
                       nullptr, // lpCurrentDirectory
                       &sinfo, // lpStartupInfo
                       &pinfo)) { // lpProcessInfoqrmation
        throw std::system_error(GetLastError(),
                                std::system_category(),
                                "Failed to execute memcached");
    }

    child = pinfo.hProcess;
#else
    child = fork();
    if (child == -1) {
        throw std::system_error(
                errno, std::system_category(), "Failed to start client");
    }

    if (child == 0) {
        std::string binary(OBJECT_ROOT);
        binary.append("/memcached");

        const char* argv[20];
        int arg = 0;

        argv[arg++] = binary.c_str();
        argv[arg++] = "-C";
        argv[arg++] = configfile.c_str();

        argv[arg++] = nullptr;
        execvp(argv[0], const_cast<char**>(argv));
        throw std::system_error(
                errno, std::system_category(), "Failed to execute memcached");
    }
#endif

    // wait and read the portnumber file
    parsePortnumberFile();
}

NodeImpl::~NodeImpl() {
    if (isRunning()) {
#ifdef WIN32
        // @todo This should be made a bit more robust
        if (!TerminateProcess(child, 0)) {
            std::cerr << "TerminateProcess failed!" << std::endl;
            std::cerr.flush();
            _exit(EXIT_FAILURE);
        }
        DWORD status;
        if ((status = WaitForSingleObject(child, 60000)) != WAIT_OBJECT_0) {
            std::cerr << "Unexpected return value from WaitForSingleObject: "
                      << status << std::endl;
            std::cerr.flush();
            _exit(EXIT_FAILURE);
        }
        if (!GetExitCodeProcess(child, &status)) {
            std::cerr << "GetExitCodeProcess failed: " << GetLastError()
                      << std::endl;
            std::cerr.flush();
            _exit(EXIT_FAILURE);
        }
#else
        // Start by giving it a slow and easy start...
        const auto timeout =
                std::chrono::steady_clock::now() + std::chrono::seconds(15);
        kill(child, SIGTERM);

        do {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        } while (isRunning() && std::chrono::steady_clock::now() < timeout);

        if (isRunning()) {
            // no mercy!
            kill(child, SIGKILL);

            int status;
            pid_t ret;
            while (true) {
                ret = waitpid(child, &status, 0);
                if (ret == reinterpret_cast<pid_t>(-1) && errno == EINTR) {
                    // Just loop again
                    continue;
                }
                break;
            }
        }
#endif
    }

    if (!configfile.empty()) {
        try {
            cb::io::rmrf(configfile);
        } catch (const std::exception& e) {
            std::cerr << "WARNING: Failed to remove \"" << configfile
                      << "\": " << e.what() << std::endl;
        }
    }
}

void NodeImpl::parsePortnumberFile() {
    connectionMap.initialize(nlohmann::json::parse(cb::io::loadFile(
            config["portnumber_file"], std::chrono::minutes{5})));
    cb::io::rmrf(config["portnumber_file"]);
}

#ifdef WIN32
bool Node::isRunning() const {
    if (child != INVALID_HANDLE_VALUE) {
        DWORD status;

        if (!GetExitCodeProcess(child, &status)) {
            throw std::system_error(
                    GetLastError(),
                    std::system_category(),
                    "NodeImpl::isRunning: GetExitCodeProcess failed");

            std::cerr << "GetExitCodeProcess: failed: " << cb_strerror()
                      << std::endl;
            exit(EXIT_FAILURE);
        }

        if (status == STILL_ACTIVE) {
            return true;
        }

        CloseHandle(child);
        child = INVALID_HANDLE_VALUE;
    }
    return false;
}
#else
bool Node::isRunning() const {
    if (child != 0) {
        int status;
        auto next = waitpid(child, &status, WNOHANG);
        if (next == static_cast<pid_t>(-1)) {
            throw std::system_error(errno,
                                    std::system_category(),
                                    "NodeImpl::isRunning: waitpid failed");
        }

        if (next == child) {
            child = 0;
            return false;
        }

        return true;
    }

    return false;
}
#endif

std::unique_ptr<MemcachedConnection> NodeImpl::getConnection() {
    auto ret = connectionMap.getConnection().clone();
    ret->setAutoRetryTmpfail(true);
    std::vector<cb::mcbp::Feature> features = {
            {cb::mcbp::Feature::MUTATION_SEQNO,
             cb::mcbp::Feature::XATTR,
             cb::mcbp::Feature::XERROR,
             cb::mcbp::Feature::JSON}};
    ret->setFeatures("cluster_testapp", features);
    return ret;
}

std::unique_ptr<Node> Node::create(const std::string& directory,
                                   const std::string& id) {
    auto ret = std::make_unique<NodeImpl>(directory, id);
    ret->startMemcachedServer();
    return ret;
}

} // namespace cb::test
<|endoftext|>