<commit_before>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
  *
  * Condor Software Copyright Notice
  * Copyright (C) 1990-2006, Condor Team, Computer Sciences Department,
  * University of Wisconsin-Madison, WI.
  *
  * This source code is covered by the Condor Public License, which can
  * be found in the accompanying LICENSE.TXT file, or online at
  * www.condorproject.org.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  * AND THE UNIVERSITY OF WISCONSIN-MADISON "AS IS" AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY, OF SATISFACTORY QUALITY, AND FITNESS
  * FOR A PARTICULAR PURPOSE OR USE ARE DISCLAIMED. THE COPYRIGHT
  * HOLDERS AND CONTRIBUTORS AND THE UNIVERSITY OF WISCONSIN-MADISON
  * MAKE NO MAKE NO REPRESENTATION THAT THE SOFTWARE, MODIFICATIONS,
  * ENHANCEMENTS OR DERIVATIVE WORKS THEREOF, WILL NOT INFRINGE ANY
  * PATENT, COPYRIGHT, TRADEMARK, TRADE SECRET OR OTHER PROPRIETARY
  * RIGHT.
  *
  ****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "condor_common.h"
#include "condor_debug.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "daemon.h"
#include "dc_message.h"
#include "daemon_core_sock_adapter.h"

DCMsg::DCMsg(int cmd) {
	m_cmd = cmd;
	m_msg_success_debug_level = D_FULLDEBUG;
	m_msg_failure_debug_level = (D_ALWAYS|D_FAILURE);
	m_delivery_status = DELIVERY_PENDING;
}

DCMsg::~DCMsg() {
}

char const *
DCMsg::name()
{
	return getCommandString( m_cmd );
}

void
DCMsg::callMessageSendFailed( DCMessenger *messenger )
{
	deliveryStatus( DELIVERY_FAILED );
	messageSendFailed( messenger );
}

void
DCMsg::callMessageReceiveFailed( DCMessenger *messenger )
{
	deliveryStatus( DELIVERY_FAILED );
	messageReceiveFailed( messenger );
}

DCMsg::MessageClosureEnum
DCMsg::callMessageSent(
				DCMessenger *messenger, Sock *sock )
{
	deliveryStatus( DELIVERY_SUCCEEDED );
	return messageSent( messenger, sock );
}

DCMsg::MessageClosureEnum
DCMsg::callMessageReceived(
				DCMessenger *messenger, Sock *sock )
{
	deliveryStatus( DELIVERY_SUCCEEDED );
	return messageReceived( messenger, sock );
}

DCMsg::MessageClosureEnum
DCMsg::messageSent( DCMessenger *messenger, Sock *)
{
	reportSuccess( messenger );

	return MESSAGE_FINISHED;
}

DCMsg::MessageClosureEnum
DCMsg::messageReceived( DCMessenger *messenger, Sock *)
{
	reportSuccess( messenger );

	return MESSAGE_FINISHED;
}

void
DCMsg::messageSendFailed( DCMessenger *messenger )
{
	reportFailure( messenger );
}

void
DCMsg::messageReceiveFailed( DCMessenger *messenger )
{
	reportFailure( messenger );
}

void
DCMsg::reportSuccess( DCMessenger *messenger )
{
	dprintf( m_msg_success_debug_level, "Sent %s to %s\n",
			 getCommandString( m_cmd),
			 messenger->peerDescription() );
}

void
DCMsg::reportFailure( DCMessenger *messenger )
{
	dprintf( m_msg_failure_debug_level, "Failed to send %s to %s: %s\n",
			 getCommandString( m_cmd ),
			 messenger->peerDescription(),
			 m_errstack.getFullText() );
}

void
DCMsg::addError( int code, char const *format, ... )
{
	va_list args;
	va_start(args, format);

	MyString msg;
	msg.vsprintf( format,args );
	m_errstack.push( "CEDAR", code, msg.Value() );

	va_end(args);
}

void
DCMsg::sockFailed( Sock *sock )
{
		// TODO: get specific error message from sock, if possible
	if( sock->is_encode() ) {
		addError( CEDAR_ERR_PUT_FAILED, "failed writing to socket" );
	}
	else {
		addError( CEDAR_ERR_GET_FAILED, "failed reading from socket" );
	}
}

DCMessenger::DCMessenger( classy_counted_ptr<Daemon> daemon )
{
	m_daemon = daemon;
	m_sock = NULL;
	m_current_msg = NULL;
}

DCMessenger::DCMessenger( Sock *sock )
{
	m_sock = sock;
	m_current_msg = NULL;
}

DCMessenger::~DCMessenger()
{
		// should never get deleted in the middle of a pending operation
	ASSERT(!m_current_msg.get());

	if( m_sock ) {
		delete m_sock;
	}
}

char const *DCMessenger::peerDescription()
{
	if( m_daemon.get() ) {
		return m_daemon->idStr();
	}
	if( m_sock ) {
		return m_sock->get_sinful_peer();
	}
	EXCEPT("No daemon or sock object in DCMessenger::peerDescription()");
	return NULL;
}

void DCMessenger::startCommand( classy_counted_ptr<DCMsg> msg, Stream::stream_type st, int timeout )
{
	MyString error;
	if( daemonCoreSockAdapter.TooManyRegisteredSockets(-1,&error) ) {
			// Try again in a sec
			// Eventually, it would be better to queue this centrally
			// (i.e. in DaemonCore) rather than having an independent
			// timer for each case.  Then it would be possible to control
			// priority of different messages etc.
		dprintf(D_FULLDEBUG, "Delaying delivery of %s to %s, because %s\n",
				msg->name(),peerDescription(),error.Value());
		startCommandAfterDelay( 1, msg, st, timeout );
		return;
	}

		// Currently, there may be only one pending operation per messenger.
	ASSERT(!m_current_msg.get());

	m_current_msg = msg;

	incRefCount();
	m_daemon->startCommand_nonblocking (
		msg->m_cmd,
		st,
		timeout,
		&msg->m_errstack,
		&DCMessenger::connectCallback,
		this );
}

void
DCMessenger::sendBlockingMsg( classy_counted_ptr<DCMsg> msg, Stream::stream_type st, int timeout )
{
	Sock *sock = m_daemon->startCommand (
		msg->m_cmd,
		st,
		timeout,
		&msg->m_errstack);

	if( !sock ) {
		msg->callMessageSendFailed( this );
		return;
	}

	writeMsg( msg, sock );
}

void
DCMessenger::doneWithSock(Stream *sock)
{
		// If sock == m_sock, it will be cleaned up when the messenger
		// is deleted.  Otherwise, do it now.
	if( sock != m_sock ) {
		if( sock ) {
			delete sock;
		}
	}
}

void
DCMessenger::connectCallback(bool success, Sock *sock, CondorError *, void *misc_data)
{
	ASSERT(misc_data);

	DCMessenger *self = (DCMessenger *)misc_data;
	classy_counted_ptr<DCMsg> msg = self->m_current_msg;
	self->m_current_msg = NULL;

	if(!success) {
		msg->callMessageSendFailed( self );
		self->doneWithSock(sock);
	}
	else {
		ASSERT(sock);
		self->writeMsg( msg, sock );
	}

	self->decRefCount();
}

void DCMessenger::writeMsg( classy_counted_ptr<DCMsg> msg, Sock *sock )
{
	ASSERT( msg.get() );
	ASSERT( sock );

	incRefCount();

		/* Some day, we may send message asynchronously and call
		   messageSent() later, after the delivery.  For now, we do it
		   all synchronously, right here. */

	sock->encode();

	if( !msg->writeMsg( this, sock ) ) {
		msg->callMessageSendFailed( this );
	}
	else if( !sock->eom() ) {
		msg->addError( CEDAR_ERR_EOM_FAILED, "failed to send EOM" );
		msg->callMessageSendFailed( this );
	}
	else {
			// Success
		DCMsg::MessageClosureEnum closure = msg->callMessageSent( this, sock );

		switch( closure ) {
		case DCMsg::MESSAGE_FINISHED:
			doneWithSock(sock);
			break;
		case DCMsg::MESSAGE_CONTINUING:
			break;
		}
	}

	decRefCount();
}

void DCMessenger::startReceiveMsg( classy_counted_ptr<DCMsg> msg, Sock *sock )
{
		// Currently, only one pending message per messenger.
	ASSERT( !m_current_msg.get() );

	MyString name;
	name.sprintf("<%s>", msg->name());

	incRefCount();

	int reg_rc = daemonCoreSockAdapter.
		Register_Socket( sock, peerDescription(),
						 (SocketHandlercpp)&DCMessenger::receiveMsgCallback,
						 name.Value(), this, ALLOW );
	if(reg_rc < 0) {
		msg->addError(
			CEDAR_ERR_REGISTER_SOCK_FAILED,
			"failed to register socket (Register_Socket returned %d)",
			reg_rc );
		msg->callMessageSendFailed( this );
		doneWithSock(sock);
		decRefCount();
		return;
	}

	m_current_msg = msg; // prevent msg from going out of reference
	ASSERT(daemonCoreSockAdapter.Register_DataPtr( msg.get() ));
}

int
DCMessenger::receiveMsgCallback(Stream *sock)
{
	classy_counted_ptr<DCMsg> msg = (DCMsg *) daemonCoreSockAdapter.GetDataPtr();
	ASSERT(msg.get());
	DCMessenger *self = msg->m_messenger_callback_ref;
	ASSERT(self);

	m_current_msg = NULL;

	daemonCoreSockAdapter.Cancel_Socket( sock );

	if( !msg->readMsg( this, (Sock *)sock ) ) {
		msg->callMessageReceiveFailed( this );
		doneWithSock(sock);
	}
	else {
		ASSERT( sock );
		readMsg( msg, (Sock *)sock );
	}

	decRefCount();
	return KEEP_STREAM;
}

void
DCMessenger::readMsg( classy_counted_ptr<DCMsg> msg, Sock *sock )
{
	ASSERT( msg.get() );
	ASSERT( sock );

	incRefCount();

	sock->decode();

	if( !msg->readMsg( this, sock ) ) {
		msg->callMessageReceiveFailed( this );
	}
	else if( !sock->eom() ) {
		msg->addError( CEDAR_ERR_EOM_FAILED, "failed to read EOM" );
		msg->callMessageReceiveFailed( this );
	}
	else {
			// Success
		DCMsg::MessageClosureEnum closure = msg->callMessageReceived( this, sock );

		switch( closure ) {
		case DCMsg::MESSAGE_FINISHED:
			doneWithSock(sock);
			break;
		case DCMsg::MESSAGE_CONTINUING:
			break;
		}
	}

	decRefCount();
}

struct QueuedCommand {
	classy_counted_ptr<DCMsg> msg;
	Stream::stream_type stream_type;
	int timeout;
	int timer_handle;
};

void
DCMessenger::startCommandAfterDelay( unsigned int delay, classy_counted_ptr<DCMsg> msg, Stream::stream_type st, int timeout )
{
	QueuedCommand *qc = new QueuedCommand;
	qc->msg = msg;
	qc->stream_type = st;
	qc->timeout = timeout;

	incRefCount();
	qc->timer_handle = daemonCoreSockAdapter.Register_Timer(
		delay,
		(Eventcpp)&DCMessenger::startCommandAfterDelay_alarm,
		"DCMessenger::startCommandAfterDelay",
		this );
	ASSERT(qc->timer_handle != -1);
	daemonCoreSockAdapter.Register_DataPtr( qc );
}

int DCMessenger::startCommandAfterDelay_alarm()
{
	QueuedCommand *qc = (QueuedCommand *)daemonCoreSockAdapter.GetDataPtr();
	ASSERT(qc);

	startCommand(qc->msg,qc->stream_type,qc->timeout);

	delete qc;
	decRefCount();

	return TRUE;
}

DCStringMsg::DCStringMsg( int cmd, char const *str ):
	DCMsg( cmd )
{
	m_str = str;
}

bool DCStringMsg::writeMsg( DCMessenger *, Sock *sock )
{
	if( !sock->put( (char *)m_str.Value() ) ) {
		sockFailed( sock );
		return false;
	}
	return true;
}

bool DCStringMsg::readMsg( DCMessenger *, Sock *sock )
{
	char *str = NULL;
	if( !sock->get( str ) ){
		sockFailed( sock );
		return false;
	}
	m_str = str;
	free(str);

	return true;
}
<commit_msg>just got rid of some control characters (ctrl-m's) that somehow got into the source file<commit_after>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
  *
  * Condor Software Copyright Notice
  * Copyright (C) 1990-2006, Condor Team, Computer Sciences Department,
  * University of Wisconsin-Madison, WI.
  *
  * This source code is covered by the Condor Public License, which can
  * be found in the accompanying LICENSE.TXT file, or online at
  * www.condorproject.org.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  * AND THE UNIVERSITY OF WISCONSIN-MADISON "AS IS" AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY, OF SATISFACTORY QUALITY, AND FITNESS
  * FOR A PARTICULAR PURPOSE OR USE ARE DISCLAIMED. THE COPYRIGHT
  * HOLDERS AND CONTRIBUTORS AND THE UNIVERSITY OF WISCONSIN-MADISON
  * MAKE NO MAKE NO REPRESENTATION THAT THE SOFTWARE, MODIFICATIONS,
  * ENHANCEMENTS OR DERIVATIVE WORKS THEREOF, WILL NOT INFRINGE ANY
  * PATENT, COPYRIGHT, TRADEMARK, TRADE SECRET OR OTHER PROPRIETARY
  * RIGHT.
  *
  ****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "condor_common.h"
#include "condor_debug.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "daemon.h"
#include "dc_message.h"
#include "daemon_core_sock_adapter.h"

DCMsg::DCMsg(int cmd) {
	m_cmd = cmd;
	m_msg_success_debug_level = D_FULLDEBUG;
	m_msg_failure_debug_level = (D_ALWAYS|D_FAILURE);
	m_delivery_status = DELIVERY_PENDING;
}

DCMsg::~DCMsg() {
}

char const *
DCMsg::name()
{
	return getCommandString( m_cmd );
}

void
DCMsg::callMessageSendFailed( DCMessenger *messenger )
{
	deliveryStatus( DELIVERY_FAILED );
	messageSendFailed( messenger );
}

void
DCMsg::callMessageReceiveFailed( DCMessenger *messenger )
{
	deliveryStatus( DELIVERY_FAILED );
	messageReceiveFailed( messenger );
}

DCMsg::MessageClosureEnum
DCMsg::callMessageSent(
				DCMessenger *messenger, Sock *sock )
{
	deliveryStatus( DELIVERY_SUCCEEDED );
	return messageSent( messenger, sock );
}

DCMsg::MessageClosureEnum
DCMsg::callMessageReceived(
				DCMessenger *messenger, Sock *sock )
{
	deliveryStatus( DELIVERY_SUCCEEDED );
	return messageReceived( messenger, sock );
}

DCMsg::MessageClosureEnum
DCMsg::messageSent( DCMessenger *messenger, Sock *)
{
	reportSuccess( messenger );

	return MESSAGE_FINISHED;
}

DCMsg::MessageClosureEnum
DCMsg::messageReceived( DCMessenger *messenger, Sock *)
{
	reportSuccess( messenger );

	return MESSAGE_FINISHED;
}

void
DCMsg::messageSendFailed( DCMessenger *messenger )
{
	reportFailure( messenger );
}

void
DCMsg::messageReceiveFailed( DCMessenger *messenger )
{
	reportFailure( messenger );
}

void
DCMsg::reportSuccess( DCMessenger *messenger )
{
	dprintf( m_msg_success_debug_level, "Sent %s to %s\n",
			 getCommandString( m_cmd),
			 messenger->peerDescription() );
}

void
DCMsg::reportFailure( DCMessenger *messenger )
{
	dprintf( m_msg_failure_debug_level, "Failed to send %s to %s: %s\n",
			 getCommandString( m_cmd ),
			 messenger->peerDescription(),
			 m_errstack.getFullText() );
}

void
DCMsg::addError( int code, char const *format, ... )
{
	va_list args;
	va_start(args, format);

	MyString msg;
	msg.vsprintf( format,args );
	m_errstack.push( "CEDAR", code, msg.Value() );

	va_end(args);
}

void
DCMsg::sockFailed( Sock *sock )
{
		// TODO: get specific error message from sock, if possible
	if( sock->is_encode() ) {
		addError( CEDAR_ERR_PUT_FAILED, "failed writing to socket" );
	}
	else {
		addError( CEDAR_ERR_GET_FAILED, "failed reading from socket" );
	}
}

DCMessenger::DCMessenger( classy_counted_ptr<Daemon> daemon )
{
	m_daemon = daemon;
	m_sock = NULL;
	m_current_msg = NULL;
}

DCMessenger::DCMessenger( Sock *sock )
{
	m_sock = sock;
	m_current_msg = NULL;
}

DCMessenger::~DCMessenger()
{
		// should never get deleted in the middle of a pending operation
	ASSERT(!m_current_msg.get());

	if( m_sock ) {
		delete m_sock;
	}
}

char const *DCMessenger::peerDescription()
{
	if( m_daemon.get() ) {
		return m_daemon->idStr();
	}
	if( m_sock ) {
		return m_sock->get_sinful_peer();
	}
	EXCEPT("No daemon or sock object in DCMessenger::peerDescription()");
	return NULL;
}

void DCMessenger::startCommand( classy_counted_ptr<DCMsg> msg, Stream::stream_type st, int timeout )
{
	MyString error;
	if( daemonCoreSockAdapter.TooManyRegisteredSockets(-1,&error) ) {
			// Try again in a sec
			// Eventually, it would be better to queue this centrally
			// (i.e. in DaemonCore) rather than having an independent
			// timer for each case.  Then it would be possible to control
			// priority of different messages etc.
		dprintf(D_FULLDEBUG, "Delaying delivery of %s to %s, because %s\n",
				msg->name(),peerDescription(),error.Value());
		startCommandAfterDelay( 1, msg, st, timeout );
		return;
	}

		// Currently, there may be only one pending operation per messenger.
	ASSERT(!m_current_msg.get());

	m_current_msg = msg;

	incRefCount();
	m_daemon->startCommand_nonblocking (
		msg->m_cmd,
		st,
		timeout,
		&msg->m_errstack,
		&DCMessenger::connectCallback,
		this );
}

void
DCMessenger::sendBlockingMsg( classy_counted_ptr<DCMsg> msg, Stream::stream_type st, int timeout )
{
	Sock *sock = m_daemon->startCommand (
		msg->m_cmd,
		st,
		timeout,
		&msg->m_errstack);

	if( !sock ) {
		msg->callMessageSendFailed( this );
		return;
	}

	writeMsg( msg, sock );
}

void
DCMessenger::doneWithSock(Stream *sock)
{
		// If sock == m_sock, it will be cleaned up when the messenger
		// is deleted.  Otherwise, do it now.
	if( sock != m_sock ) {
		if( sock ) {
			delete sock;
		}
	}
}

void
DCMessenger::connectCallback(bool success, Sock *sock, CondorError *, void *misc_data)
{
	ASSERT(misc_data);

	DCMessenger *self = (DCMessenger *)misc_data;
	classy_counted_ptr<DCMsg> msg = self->m_current_msg;
	self->m_current_msg = NULL;

	if(!success) {
		msg->callMessageSendFailed( self );
		self->doneWithSock(sock);
	}
	else {
		ASSERT(sock);
		self->writeMsg( msg, sock );
	}

	self->decRefCount();
}

void DCMessenger::writeMsg( classy_counted_ptr<DCMsg> msg, Sock *sock )
{
	ASSERT( msg.get() );
	ASSERT( sock );

	incRefCount();

		/* Some day, we may send message asynchronously and call
		   messageSent() later, after the delivery.  For now, we do it
		   all synchronously, right here. */

	sock->encode();

	if( !msg->writeMsg( this, sock ) ) {
		msg->callMessageSendFailed( this );
	}
	else if( !sock->eom() ) {
		msg->addError( CEDAR_ERR_EOM_FAILED, "failed to send EOM" );
		msg->callMessageSendFailed( this );
	}
	else {
			// Success
		DCMsg::MessageClosureEnum closure = msg->callMessageSent( this, sock );

		switch( closure ) {
		case DCMsg::MESSAGE_FINISHED:
			doneWithSock(sock);
			break;
		case DCMsg::MESSAGE_CONTINUING:
			break;
		}
	}

	decRefCount();
}

void DCMessenger::startReceiveMsg( classy_counted_ptr<DCMsg> msg, Sock *sock )
{
		// Currently, only one pending message per messenger.
	ASSERT( !m_current_msg.get() );

	MyString name;
	name.sprintf("<%s>", msg->name());

	incRefCount();

	int reg_rc = daemonCoreSockAdapter.
		Register_Socket( sock, peerDescription(),
						 (SocketHandlercpp)&DCMessenger::receiveMsgCallback,
						 name.Value(), this, ALLOW );
	if(reg_rc < 0) {
		msg->addError(
			CEDAR_ERR_REGISTER_SOCK_FAILED,
			"failed to register socket (Register_Socket returned %d)",
			reg_rc );
		msg->callMessageSendFailed( this );
		doneWithSock(sock);
		decRefCount();
		return;
	}

	m_current_msg = msg; // prevent msg from going out of reference
	ASSERT(daemonCoreSockAdapter.Register_DataPtr( msg.get() ));
}

int
DCMessenger::receiveMsgCallback(Stream *sock)
{
	classy_counted_ptr<DCMsg> msg = (DCMsg *) daemonCoreSockAdapter.GetDataPtr();
	ASSERT(msg.get());
	DCMessenger *self = msg->m_messenger_callback_ref;
	ASSERT(self);

	m_current_msg = NULL;

	daemonCoreSockAdapter.Cancel_Socket( sock );

	if( !msg->readMsg( this, (Sock *)sock ) ) {
		msg->callMessageReceiveFailed( this );
		doneWithSock(sock);
	}
	else {
		ASSERT( sock );
		readMsg( msg, (Sock *)sock );
	}

	decRefCount();
	return KEEP_STREAM;
}

void
DCMessenger::readMsg( classy_counted_ptr<DCMsg> msg, Sock *sock )
{
	ASSERT( msg.get() );
	ASSERT( sock );

	incRefCount();

	sock->decode();

	if( !msg->readMsg( this, sock ) ) {
		msg->callMessageReceiveFailed( this );
	}
	else if( !sock->eom() ) {
		msg->addError( CEDAR_ERR_EOM_FAILED, "failed to read EOM" );
		msg->callMessageReceiveFailed( this );
	}
	else {
			// Success
		DCMsg::MessageClosureEnum closure = msg->callMessageReceived( this, sock );

		switch( closure ) {
		case DCMsg::MESSAGE_FINISHED:
			doneWithSock(sock);
			break;
		case DCMsg::MESSAGE_CONTINUING:
			break;
		}
	}

	decRefCount();
}

struct QueuedCommand {
	classy_counted_ptr<DCMsg> msg;
	Stream::stream_type stream_type;
	int timeout;
	int timer_handle;
};

void
DCMessenger::startCommandAfterDelay( unsigned int delay, classy_counted_ptr<DCMsg> msg, Stream::stream_type st, int timeout )
{
	QueuedCommand *qc = new QueuedCommand;
	qc->msg = msg;
	qc->stream_type = st;
	qc->timeout = timeout;

	incRefCount();
	qc->timer_handle = daemonCoreSockAdapter.Register_Timer(
		delay,
		(Eventcpp)&DCMessenger::startCommandAfterDelay_alarm,
		"DCMessenger::startCommandAfterDelay",
		this );
	ASSERT(qc->timer_handle != -1);
	daemonCoreSockAdapter.Register_DataPtr( qc );
}

int DCMessenger::startCommandAfterDelay_alarm()
{
	QueuedCommand *qc = (QueuedCommand *)daemonCoreSockAdapter.GetDataPtr();
	ASSERT(qc);

	startCommand(qc->msg,qc->stream_type,qc->timeout);

	delete qc;
	decRefCount();

	return TRUE;
}

DCStringMsg::DCStringMsg( int cmd, char const *str ):
	DCMsg( cmd )
{
	m_str = str;
}

bool DCStringMsg::writeMsg( DCMessenger *, Sock *sock )
{
	if( !sock->put( (char *)m_str.Value() ) ) {
		sockFailed( sock );
		return false;
	}
	return true;
}

bool DCStringMsg::readMsg( DCMessenger *, Sock *sock )
{
	char *str = NULL;
	if( !sock->get( str ) ){
		sockFailed( sock );
		return false;
	}
	m_str = str;
	free(str);

	return true;
}
<|endoftext|>


<commit_before>#ifndef __Data_general__
#define __Data_general__

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>    // has the for_each

using namespace std;

enum GenEd {
	// Foundation Studies
	FYW,   // First-Year Writing
	WRI,   // Writing in Context
	FOL,   // Foreign Language
	ORC,   // Oral Communication
	AQR,   // Abstract and Quantitative Reasoning
	SPM,   // Studies in Physical Movement

	// Core Studies
	HWC,   // Historical Studies in Western Culture
	MCD,   // Multicultural Studies: Domestic
	MCG,   // Multicultural Studies: Global
	ALSA,  // Artistic and Literary Studies: Artistic Studies
	ALSL,  // Artistic and Literary Studies: Literary Studies
	BTSB,  // Biblical and Theological Studies: Bible
	BTST,  // Biblical and Theological Studies: Theology
	SED,   // Studies in Natural Science: Scientific Exploration and Discovery
	IST,   // Studies in Natural Science: Integrated Scientific Topics
	HBS,   // Studies in Human Behavior and Society

	// Integrative Studies
	EIN    // Ethical Issues and Normative Perspectives
};

enum dept_t {
	AFAM,  //0 Africa and the Americas
	ALSO,  //1 Alternate Language Study Option
	AMCON, //2 American Conversation
	AMST,  //3 American Studies
	ARMS,  //4 American Racial and Multicultural Studies
	ART,   //5 Art and Art History
	ASIAN, //6 Asian Studies (AS)
	BIO,   //7 Biology (BI)
	BMOLS, //8 Biomolecular Science
	CHEM,  //9 Chemistry (CH)
	CHIN,  //10 Chinese
	CLASS, //11 Classics
	CSCI,  //12 Computer Science
	DANCE, //13 Dance
	ECON,  //14 Economics
	EDUC,  //15 Education
	ENGL,  //16 English
	ENVST, //17 Environmental Studies (ES)
	ESAC,  //18 Exercise Science Activity
	ESTH,  //19 Exercise Science Theory
	FAMST, //20 Family Studies
	FILM,  //21 Film Studies
	FREN,  //22 French
	GCON,  //23 Great Conversation
	GERM,  //24 German
	GREEK, //25 Greek
	HIST,  //26 History (HI)
	HSPST, //27 Hispanic Studies
	// Note: INTER used to be ID, but there was a system conflict.
	IDFA,  //28
	INTD,  //29 Interdepartmental
	INTER, //30 Interdisciplinary
	IS,    //31 Integrative Studies
	JAPAN, //32 Japanese
	LATIN, //33 Latin
	MATH,  //34 Mathematics
	MEDIA, //35 Media Studies
	MEDVL, //36 Medieval Studies
	MGMT,  //37 Management Studies
	MUSIC, //38 Music
	MUSPF, //39 Music Performance
	NEURO, //40 Neuroscience
	NORW,  //41 Norwegian
	NURS,  //42 Nursing
	PHIL,  //43 Philosphy
	PHYS,  //44 Physics
	PSCI,  //45 Political Science
	PSYCH, //46 Psychology
	REL,   //47 Religion (RE)
	RUSSN, //48 Russian
	SCICN, //49 Science Conversation
	SOAN,  //50 Sociology and Anthropology
	SPAN,  //51 Spanish
	STAT,  //52 Statistics
	SWRK,  //53 Social Work
	THEAT, //54 Theater
	WMGST, //55 Women's & Gender Studies
	WRIT,  //56 Writing
	NONE,
};

string tostring(int i);
int stringToInt(string const& str);
float stringToFloat(string const& str);

vector<string> &split(const string &s, char delim, vector<string> &elems);
vector<string> split(const string &s, char delim);

string removeAllQuotes(string s);
string removeTrailingSlashes(string s);
string removeTrailingText(string s, string toRemove);
string removeStartingText(string s, string toRemove);

string deDoubleString(string s);
void printEntireRecord(vector<string> record);

#endif
<commit_msg>Remove #include <algorithm> as we didn't use it<commit_after>#ifndef __Data_general__
#define __Data_general__

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>

using namespace std;

enum GenEd {
	// Foundation Studies
	FYW,   // First-Year Writing
	WRI,   // Writing in Context
	FOL,   // Foreign Language
	ORC,   // Oral Communication
	AQR,   // Abstract and Quantitative Reasoning
	SPM,   // Studies in Physical Movement

	// Core Studies
	HWC,   // Historical Studies in Western Culture
	MCD,   // Multicultural Studies: Domestic
	MCG,   // Multicultural Studies: Global
	ALSA,  // Artistic and Literary Studies: Artistic Studies
	ALSL,  // Artistic and Literary Studies: Literary Studies
	BTSB,  // Biblical and Theological Studies: Bible
	BTST,  // Biblical and Theological Studies: Theology
	SED,   // Studies in Natural Science: Scientific Exploration and Discovery
	IST,   // Studies in Natural Science: Integrated Scientific Topics
	HBS,   // Studies in Human Behavior and Society

	// Integrative Studies
	EIN    // Ethical Issues and Normative Perspectives
};

enum dept_t {
	AFAM,  //0 Africa and the Americas
	ALSO,  //1 Alternate Language Study Option
	AMCON, //2 American Conversation
	AMST,  //3 American Studies
	ARMS,  //4 American Racial and Multicultural Studies
	ART,   //5 Art and Art History
	ASIAN, //6 Asian Studies (AS)
	BIO,   //7 Biology (BI)
	BMOLS, //8 Biomolecular Science
	CHEM,  //9 Chemistry (CH)
	CHIN,  //10 Chinese
	CLASS, //11 Classics
	CSCI,  //12 Computer Science
	DANCE, //13 Dance
	ECON,  //14 Economics
	EDUC,  //15 Education
	ENGL,  //16 English
	ENVST, //17 Environmental Studies (ES)
	ESAC,  //18 Exercise Science Activity
	ESTH,  //19 Exercise Science Theory
	FAMST, //20 Family Studies
	FILM,  //21 Film Studies
	FREN,  //22 French
	GCON,  //23 Great Conversation
	GERM,  //24 German
	GREEK, //25 Greek
	HIST,  //26 History (HI)
	HSPST, //27 Hispanic Studies
	// Note: INTER used to be ID, but there was a system conflict.
	IDFA,  //28
	INTD,  //29 Interdepartmental
	INTER, //30 Interdisciplinary
	IS,    //31 Integrative Studies
	JAPAN, //32 Japanese
	LATIN, //33 Latin
	MATH,  //34 Mathematics
	MEDIA, //35 Media Studies
	MEDVL, //36 Medieval Studies
	MGMT,  //37 Management Studies
	MUSIC, //38 Music
	MUSPF, //39 Music Performance
	NEURO, //40 Neuroscience
	NORW,  //41 Norwegian
	NURS,  //42 Nursing
	PHIL,  //43 Philosphy
	PHYS,  //44 Physics
	PSCI,  //45 Political Science
	PSYCH, //46 Psychology
	REL,   //47 Religion (RE)
	RUSSN, //48 Russian
	SCICN, //49 Science Conversation
	SOAN,  //50 Sociology and Anthropology
	SPAN,  //51 Spanish
	STAT,  //52 Statistics
	SWRK,  //53 Social Work
	THEAT, //54 Theater
	WMGST, //55 Women's & Gender Studies
	WRIT,  //56 Writing
	NONE,
};

string tostring(int i);
int stringToInt(string const& str);
float stringToFloat(string const& str);

vector<string> &split(const string &s, char delim, vector<string> &elems);
vector<string> split(const string &s, char delim);

string removeAllQuotes(string s);
string removeTrailingSlashes(string s);
string removeTrailingText(string s, string toRemove);
string removeStartingText(string s, string toRemove);

string deDoubleString(string s);
void printEntireRecord(vector<string> record);

#endif
<|endoftext|>


<commit_before>// material_tests.cpp
#include "material_tests.h"

#include <cmath>
#include <gtest/gtest.h>

#include "cyc_limits.h"
#include "error.h"
#include "mat_query.h"

using cyclus::Iso;
using cyclus::CompMap;
using cyclus::Composition;
using cyclus::Material;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, Constructors) {
  EXPECT_EQ(default_mat_->units(), "kg");
  EXPECT_EQ(default_mat_->type(), cyclus::Material::kType);
  EXPECT_GE(default_mat_->id(), 0);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, Clone) {
  cyclus::Resource::Ptr clone_mat;
  ASSERT_NO_THROW(clone_mat = test_mat_->Clone());

  EXPECT_EQ(test_mat_->type(), clone_mat->type());
  EXPECT_EQ(test_mat_->units(), clone_mat->units());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), clone_mat->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractRes) {
  EXPECT_DOUBLE_EQ(test_size_, test_mat_->quantity());
  double other_size = test_size_ / 3;
  cyclus::Resource::Ptr other;
  ASSERT_NO_THROW(other = test_mat_->ExtractRes(other_size));
  EXPECT_DOUBLE_EQ(test_size_ - other_size, test_mat_->quantity());
  EXPECT_DOUBLE_EQ(other_size, other->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, SimpleAbsorb) {
  double val = 1.5 * units::kg;
  cyclus::Material::Ptr m1 = cyclus::Material::CreateUntracked(val, test_comp_);
  cyclus::Material::Ptr m2 = cyclus::Material::CreateUntracked(val, test_comp_);
  ASSERT_EQ(m1->comp(), m2->comp());
  ASSERT_EQ(m1->quantity(), m2->quantity());

  m2->Absorb(m1);

  ///ASSERT_EQ(m1->comp(), m2->comp());
  ///EXPECT_EQ(m1->quantity(), 0);
  ///EXPECT_EQ(m2->quantity(), 2 * val);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbLikeMaterial) {
  cyclus::Material::Ptr mat1;
  cyclus::Material::Ptr mat2;
  cyclus::Material::Ptr mat10;
  mat1 = cyclus::Material::CreateUntracked(1 * test_size_, test_comp_);
  mat2 = cyclus::Material::CreateUntracked(2 * test_size_, test_comp_);
  mat10 = cyclus::Material::CreateUntracked(10 * test_size_, test_comp_);

  // see that two materials with the same composition do the right thing
  double orig = test_mat_->quantity();
  int factor = 2;
  ASSERT_NO_THROW(test_mat_->Absorb(mat1));
  ASSERT_EQ(test_mat_->comp(), mat1->comp());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), factor * orig);

  factor += 2;
  ASSERT_NO_THROW(test_mat_->Absorb(mat2));
  ASSERT_EQ(test_mat_->comp(), mat2->comp());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), factor * orig);

  factor += 10;
  ASSERT_NO_THROW(test_mat_->Absorb(mat10));
  ASSERT_EQ(test_mat_->comp(), mat10->comp());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), factor * orig);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbUnLikeMaterial) {
  // make a number of materials masses 1, 2, and 10
  cyclus::Material::Ptr same_as_orig_test_mat = cyclus::Material::CreateUntracked(0,
                                                test_comp_);

  CompMap v;
  v[pb208_] = 1.0 * units::g;
  v[am241_] = 1.0 * units::g;
  v[th228_] = 1.0 * units::g;
  cyclus::Composition::Ptr diff_test_comp = cyclus::Composition::CreateFromMass(
                                              v);
  cyclus::Material::Ptr diff_test_mat = cyclus::Material::CreateUntracked(
                                        test_size_ / 2,
                                        diff_test_comp);

  double orig = test_mat_->quantity();
  double origdiff = diff_test_mat->quantity();

  // see that materials with different compositions do the right thing
  ASSERT_NO_THROW(test_mat_->Absorb(diff_test_mat));
  EXPECT_NE(test_mat_->comp(), diff_test_mat->comp());
  EXPECT_NE(test_mat_->comp(), same_as_orig_test_mat->comp());
  EXPECT_DOUBLE_EQ(orig + origdiff, test_mat_->quantity());
  EXPECT_TRUE(std::abs(same_as_orig_test_mat->quantity() -
                       test_mat_->quantity()) > cyclus::eps_rsrc());

  // see that an empty material appropriately absorbs a not empty material.
  ASSERT_NO_THROW(default_mat_->Absorb(test_mat_));
  EXPECT_DOUBLE_EQ(orig + origdiff, default_mat_->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbZeroMaterial) {
  Material::Ptr same_as_test_mat = Material::CreateUntracked(0, test_comp_);
  EXPECT_NO_THROW(test_mat_->Absorb(same_as_test_mat));
  EXPECT_FLOAT_EQ(test_size_, test_mat_->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbIntoZeroMaterial) {
  Material::Ptr same_as_test_mat = Material::CreateUntracked(0, test_comp_);
  EXPECT_NO_THROW(same_as_test_mat->Absorb(test_mat_));
  EXPECT_FLOAT_EQ(test_size_, same_as_test_mat->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractMass) {
  double amt = test_size_ / 3;
  double diff = test_size_ - amt;
  cyclus::Material::Ptr extracted;
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), test_size_); // we expect this amt
  EXPECT_NO_THROW(extracted = test_mat_->ExtractQty(amt)); // extract an amt
  EXPECT_DOUBLE_EQ(extracted->quantity(), amt); // check correctness
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), diff); // check correctness
  EXPECT_EQ(test_mat_->comp(), extracted->comp());
  EXPECT_THROW(two_test_mat_->ExtractQty(2 * two_test_mat_->quantity()),
               cyclus::Error);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractComplete) {
  cyclus::Material::Ptr m1;
  EXPECT_NO_THROW(m1 = test_mat_->ExtractComp(test_size_, test_comp_));
  EXPECT_EQ(m1->comp(), test_comp_);
  EXPECT_DOUBLE_EQ(0, test_mat_->quantity());
  EXPECT_DOUBLE_EQ(test_size_, m1->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractOverQty) {
  EXPECT_THROW(diff_mat_->ExtractComp(2 * test_size_, test_comp_),
               cyclus::ValueError);
  EXPECT_THROW(test_mat_->ExtractComp(2 * test_size_, test_comp_),
               cyclus::ValueError);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractOverComp) {
  Material::Ptr m;

  CompMap inexact = diff_comp_->mass();
  inexact[am241_] *= 2;
  Composition::Ptr inexact_comp = Composition::CreateFromMass(inexact);

  EXPECT_THROW(diff_mat_->ExtractComp(test_size_, inexact_comp),
               cyclus::ValueError);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractCompleteInexactComp) {
  // Complete extraction
  // this should succeed even if inexact, within eps.
  Material::Ptr m1;
  // make an inexact composition

  CompMap inexact = diff_comp_->mass();
  inexact[am241_] *= (1 - cyclus::eps_rsrc() / test_size_);
  Composition::Ptr inexact_comp = Composition::CreateFromMass(inexact);

  m1 = diff_mat_->ExtractComp(test_size_, inexact_comp);
  EXPECT_EQ(m1->comp(), inexact_comp);
  EXPECT_NEAR(test_size_, m1->quantity(), cyclus::eps_rsrc());

  cyclus::MatQuery mq(diff_mat_);
  EXPECT_NEAR(0, mq.mass(am241_), cyclus::eps_rsrc());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractHalf) {
  cyclus::Material::Ptr m1 = two_test_mat_->ExtractComp(test_size_, test_comp_);
  EXPECT_DOUBLE_EQ(test_size_, m1->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractDiffComp) {
  // differing comp minus one element equals old comp minus new
  cyclus::Material::Ptr m1;
  double orig = diff_mat_->quantity();
  CompMap v = diff_mat_->comp()->atom();

  cyclus::MatQuery mq(diff_mat_);
  const double orig_u235 = mq.mass(u235_);
  const double orig_am241 = mq.mass(am241_);
  const double orig_pb208 = mq.mass(pb208_);

  EXPECT_NO_THROW(m1 = diff_mat_->ExtractComp(mq.mass(u235_), test_comp_));
  EXPECT_DOUBLE_EQ(orig - m1->quantity(), diff_mat_->quantity());
  EXPECT_EQ(m1->comp(), test_comp_);
  EXPECT_NE(diff_mat_->comp(), test_comp_);
  EXPECT_DOUBLE_EQ(orig_am241, mq.mass(am241_));
  EXPECT_DOUBLE_EQ(orig_pb208, mq.mass(pb208_));
  EXPECT_NE(orig_u235, mq.mass(u235_));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbThenExtract) {
  cyclus::Composition::Ptr comp_to_rem = cyclus::Composition::Ptr(test_comp_);
  double kg_to_rem = 0.25 * test_size_;

  // if you start with an empty material
  EXPECT_DOUBLE_EQ(0, default_mat_->quantity());
  // then you absorb another material, they should be identical
  EXPECT_NO_THROW(default_mat_->Absorb(test_mat_));
  EXPECT_EQ(default_mat_->comp(), test_mat_->comp());
  EXPECT_DOUBLE_EQ(test_size_, default_mat_->quantity());
  // and it should be okay to extract a fraction of the original composiiton
  EXPECT_NO_THROW(default_mat_->ExtractComp(kg_to_rem, comp_to_rem));
  EXPECT_DOUBLE_EQ(test_size_ - kg_to_rem, default_mat_->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractInGrams) {
  cyclus::Composition::Ptr comp_to_rem = cyclus::Composition::Ptr(test_comp_);
  double kg_to_rem = 0.25 * test_size_;
  double g_to_rem = 1000 * kg_to_rem;

  // if you start with an empty material
  EXPECT_DOUBLE_EQ(0, default_mat_->quantity());
  // then you absorb another material, they should be identical
  EXPECT_NO_THROW(default_mat_->Absorb(test_mat_));
  EXPECT_EQ(default_mat_->comp(), test_mat_->comp());
  EXPECT_DOUBLE_EQ(test_size_, default_mat_->quantity());
  // and it should be okay to extract part of the original composiiton IN GRAMS
  EXPECT_NO_THROW(default_mat_->ExtractComp(g_to_rem * units::g, comp_to_rem));
  EXPECT_DOUBLE_EQ(test_size_ - kg_to_rem, default_mat_->quantity());
}

<commit_msg>added test that decay shortcut works<commit_after>// material_tests.cpp
#include "material_tests.h"

#include <cmath>
#include <gtest/gtest.h>

#include "cyc_limits.h"
#include "error.h"
#include "mat_query.h"

using cyclus::Iso;
using cyclus::CompMap;
using cyclus::Composition;
using cyclus::Material;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, Constructors) {
  EXPECT_EQ(default_mat_->units(), "kg");
  EXPECT_EQ(default_mat_->type(), cyclus::Material::kType);
  EXPECT_GE(default_mat_->id(), 0);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, Clone) {
  cyclus::Resource::Ptr clone_mat;
  ASSERT_NO_THROW(clone_mat = test_mat_->Clone());

  EXPECT_EQ(test_mat_->type(), clone_mat->type());
  EXPECT_EQ(test_mat_->units(), clone_mat->units());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), clone_mat->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractRes) {
  EXPECT_DOUBLE_EQ(test_size_, test_mat_->quantity());
  double other_size = test_size_ / 3;
  cyclus::Resource::Ptr other;
  ASSERT_NO_THROW(other = test_mat_->ExtractRes(other_size));
  EXPECT_DOUBLE_EQ(test_size_ - other_size, test_mat_->quantity());
  EXPECT_DOUBLE_EQ(other_size, other->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, SimpleAbsorb) {
  double val = 1.5 * units::kg;
  cyclus::Material::Ptr m1 = cyclus::Material::CreateUntracked(val, test_comp_);
  cyclus::Material::Ptr m2 = cyclus::Material::CreateUntracked(val, test_comp_);
  ASSERT_EQ(m1->comp(), m2->comp());
  ASSERT_EQ(m1->quantity(), m2->quantity());

  m2->Absorb(m1);

  ///ASSERT_EQ(m1->comp(), m2->comp());
  ///EXPECT_EQ(m1->quantity(), 0);
  ///EXPECT_EQ(m2->quantity(), 2 * val);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbLikeMaterial) {
  cyclus::Material::Ptr mat1;
  cyclus::Material::Ptr mat2;
  cyclus::Material::Ptr mat10;
  mat1 = cyclus::Material::CreateUntracked(1 * test_size_, test_comp_);
  mat2 = cyclus::Material::CreateUntracked(2 * test_size_, test_comp_);
  mat10 = cyclus::Material::CreateUntracked(10 * test_size_, test_comp_);

  // see that two materials with the same composition do the right thing
  double orig = test_mat_->quantity();
  int factor = 2;
  ASSERT_NO_THROW(test_mat_->Absorb(mat1));
  ASSERT_EQ(test_mat_->comp(), mat1->comp());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), factor * orig);

  factor += 2;
  ASSERT_NO_THROW(test_mat_->Absorb(mat2));
  ASSERT_EQ(test_mat_->comp(), mat2->comp());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), factor * orig);

  factor += 10;
  ASSERT_NO_THROW(test_mat_->Absorb(mat10));
  ASSERT_EQ(test_mat_->comp(), mat10->comp());
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), factor * orig);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbUnLikeMaterial) {
  // make a number of materials masses 1, 2, and 10
  cyclus::Material::Ptr same_as_orig_test_mat = cyclus::Material::CreateUntracked(0,
                                                test_comp_);

  CompMap v;
  v[pb208_] = 1.0 * units::g;
  v[am241_] = 1.0 * units::g;
  v[th228_] = 1.0 * units::g;
  cyclus::Composition::Ptr diff_test_comp = cyclus::Composition::CreateFromMass(
                                              v);
  cyclus::Material::Ptr diff_test_mat = cyclus::Material::CreateUntracked(
                                        test_size_ / 2,
                                        diff_test_comp);

  double orig = test_mat_->quantity();
  double origdiff = diff_test_mat->quantity();

  // see that materials with different compositions do the right thing
  ASSERT_NO_THROW(test_mat_->Absorb(diff_test_mat));
  EXPECT_NE(test_mat_->comp(), diff_test_mat->comp());
  EXPECT_NE(test_mat_->comp(), same_as_orig_test_mat->comp());
  EXPECT_DOUBLE_EQ(orig + origdiff, test_mat_->quantity());
  EXPECT_TRUE(std::abs(same_as_orig_test_mat->quantity() -
                       test_mat_->quantity()) > cyclus::eps_rsrc());

  // see that an empty material appropriately absorbs a not empty material.
  ASSERT_NO_THROW(default_mat_->Absorb(test_mat_));
  EXPECT_DOUBLE_EQ(orig + origdiff, default_mat_->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbZeroMaterial) {
  Material::Ptr same_as_test_mat = Material::CreateUntracked(0, test_comp_);
  EXPECT_NO_THROW(test_mat_->Absorb(same_as_test_mat));
  EXPECT_FLOAT_EQ(test_size_, test_mat_->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbIntoZeroMaterial) {
  Material::Ptr same_as_test_mat = Material::CreateUntracked(0, test_comp_);
  EXPECT_NO_THROW(same_as_test_mat->Absorb(test_mat_));
  EXPECT_FLOAT_EQ(test_size_, same_as_test_mat->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractMass) {
  double amt = test_size_ / 3;
  double diff = test_size_ - amt;
  cyclus::Material::Ptr extracted;
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), test_size_); // we expect this amt
  EXPECT_NO_THROW(extracted = test_mat_->ExtractQty(amt)); // extract an amt
  EXPECT_DOUBLE_EQ(extracted->quantity(), amt); // check correctness
  EXPECT_DOUBLE_EQ(test_mat_->quantity(), diff); // check correctness
  EXPECT_EQ(test_mat_->comp(), extracted->comp());
  EXPECT_THROW(two_test_mat_->ExtractQty(2 * two_test_mat_->quantity()),
               cyclus::Error);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractComplete) {
  cyclus::Material::Ptr m1;
  EXPECT_NO_THROW(m1 = test_mat_->ExtractComp(test_size_, test_comp_));
  EXPECT_EQ(m1->comp(), test_comp_);
  EXPECT_DOUBLE_EQ(0, test_mat_->quantity());
  EXPECT_DOUBLE_EQ(test_size_, m1->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractOverQty) {
  EXPECT_THROW(diff_mat_->ExtractComp(2 * test_size_, test_comp_),
               cyclus::ValueError);
  EXPECT_THROW(test_mat_->ExtractComp(2 * test_size_, test_comp_),
               cyclus::ValueError);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractOverComp) {
  Material::Ptr m;

  CompMap inexact = diff_comp_->mass();
  inexact[am241_] *= 2;
  Composition::Ptr inexact_comp = Composition::CreateFromMass(inexact);

  EXPECT_THROW(diff_mat_->ExtractComp(test_size_, inexact_comp),
               cyclus::ValueError);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractCompleteInexactComp) {
  // Complete extraction
  // this should succeed even if inexact, within eps.
  Material::Ptr m1;
  // make an inexact composition

  CompMap inexact = diff_comp_->mass();
  inexact[am241_] *= (1 - cyclus::eps_rsrc() / test_size_);
  Composition::Ptr inexact_comp = Composition::CreateFromMass(inexact);

  m1 = diff_mat_->ExtractComp(test_size_, inexact_comp);
  EXPECT_EQ(m1->comp(), inexact_comp);
  EXPECT_NEAR(test_size_, m1->quantity(), cyclus::eps_rsrc());

  cyclus::MatQuery mq(diff_mat_);
  EXPECT_NEAR(0, mq.mass(am241_), cyclus::eps_rsrc());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractHalf) {
  cyclus::Material::Ptr m1 = two_test_mat_->ExtractComp(test_size_, test_comp_);
  EXPECT_DOUBLE_EQ(test_size_, m1->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractDiffComp) {
  // differing comp minus one element equals old comp minus new
  cyclus::Material::Ptr m1;
  double orig = diff_mat_->quantity();
  CompMap v = diff_mat_->comp()->atom();

  cyclus::MatQuery mq(diff_mat_);
  const double orig_u235 = mq.mass(u235_);
  const double orig_am241 = mq.mass(am241_);
  const double orig_pb208 = mq.mass(pb208_);

  EXPECT_NO_THROW(m1 = diff_mat_->ExtractComp(mq.mass(u235_), test_comp_));
  EXPECT_DOUBLE_EQ(orig - m1->quantity(), diff_mat_->quantity());
  EXPECT_EQ(m1->comp(), test_comp_);
  EXPECT_NE(diff_mat_->comp(), test_comp_);
  EXPECT_DOUBLE_EQ(orig_am241, mq.mass(am241_));
  EXPECT_DOUBLE_EQ(orig_pb208, mq.mass(pb208_));
  EXPECT_NE(orig_u235, mq.mass(u235_));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, AbsorbThenExtract) {
  cyclus::Composition::Ptr comp_to_rem = cyclus::Composition::Ptr(test_comp_);
  double kg_to_rem = 0.25 * test_size_;

  // if you start with an empty material
  EXPECT_DOUBLE_EQ(0, default_mat_->quantity());
  // then you absorb another material, they should be identical
  EXPECT_NO_THROW(default_mat_->Absorb(test_mat_));
  EXPECT_EQ(default_mat_->comp(), test_mat_->comp());
  EXPECT_DOUBLE_EQ(test_size_, default_mat_->quantity());
  // and it should be okay to extract a fraction of the original composiiton
  EXPECT_NO_THROW(default_mat_->ExtractComp(kg_to_rem, comp_to_rem));
  EXPECT_DOUBLE_EQ(test_size_ - kg_to_rem, default_mat_->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, ExtractInGrams) {
  cyclus::Composition::Ptr comp_to_rem = cyclus::Composition::Ptr(test_comp_);
  double kg_to_rem = 0.25 * test_size_;
  double g_to_rem = 1000 * kg_to_rem;

  // if you start with an empty material
  EXPECT_DOUBLE_EQ(0, default_mat_->quantity());
  // then you absorb another material, they should be identical
  EXPECT_NO_THROW(default_mat_->Absorb(test_mat_));
  EXPECT_EQ(default_mat_->comp(), test_mat_->comp());
  EXPECT_DOUBLE_EQ(test_size_, default_mat_->quantity());
  // and it should be okay to extract part of the original composiiton IN GRAMS
  EXPECT_NO_THROW(default_mat_->ExtractComp(g_to_rem * units::g, comp_to_rem));
  EXPECT_DOUBLE_EQ(test_size_ - kg_to_rem, default_mat_->quantity());
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TEST_F(MaterialTest, DecayShortcut) {
  using cyclus::Composition;
  cyclus::CompMap mp;
  mp[92235] = 1;
  Composition::Ptr c = Composition::CreateFromAtom(mp);
  cyclus::Material::Ptr m = Material::CreateUntracked(1.0, c);

  double u235_decay_const = 8.087e-11; // per month
  double eps = 1e-3;
  double threshold = -1 * std::log(1-eps) / u235_decay_const;
  m->Decay(threshold * 0.9);
  EXPECT_EQ(c, m->comp());
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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 "code_generator.h"

#include "code_generator_arm.h"
#include "code_generator_x86.h"
#include "dex/verified_method.h"
#include "driver/dex_compilation_unit.h"
#include "gc_map_builder.h"
#include "leb128.h"
#include "mapping_table.h"
#include "utils/assembler.h"
#include "verifier/dex_gc_map.h"
#include "vmap_table.h"

namespace art {

void CodeGenerator::Compile(CodeAllocator* allocator) {
  const GrowableArray<HBasicBlock*>* blocks = GetGraph()->GetBlocks();
  DCHECK(blocks->Get(0) == GetGraph()->GetEntryBlock());
  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks->Get(1)));
  GenerateFrameEntry();
  for (size_t i = 0; i < blocks->Size(); i++) {
    CompileBlock(blocks->Get(i));
  }
  size_t code_size = GetAssembler()->CodeSize();
  uint8_t* buffer = allocator->Allocate(code_size);
  MemoryRegion code(buffer, code_size);
  GetAssembler()->FinalizeInstructions(code);
}

void CodeGenerator::CompileBlock(HBasicBlock* block) {
  Bind(GetLabelOf(block));
  HGraphVisitor* location_builder = GetLocationBuilder();
  HGraphVisitor* instruction_visitor = GetInstructionVisitor();
  for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
    HInstruction* current = it.Current();
    current->Accept(location_builder);
    InitLocations(current);
    current->Accept(instruction_visitor);
  }
}

size_t CodeGenerator::AllocateFreeRegisterInternal(
    bool* blocked_registers, size_t number_of_registers) const {
  for (size_t regno = 0; regno < number_of_registers; regno++) {
    if (!blocked_registers[regno]) {
      blocked_registers[regno] = true;
      return regno;
    }
  }
  LOG(FATAL) << "Unreachable";
  return -1;
}

void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const {
  LocationSummary* locations = instruction->GetLocations();
  if (locations == nullptr) return;

  for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) {
    blocked_registers_[i] = false;
  }

  // Mark all fixed input, temp and output registers as used.
  for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
    Location loc = locations->InAt(i);
    if (loc.IsRegister()) {
      // Check that a register is not specified twice in the summary.
      DCHECK(!blocked_registers_[loc.GetEncoding()]);
      blocked_registers_[loc.GetEncoding()] = true;
    }
  }

  for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) {
    Location loc = locations->GetTemp(i);
    if (loc.IsRegister()) {
      // Check that a register is not specified twice in the summary.
      DCHECK(!blocked_registers_[loc.GetEncoding()]);
      blocked_registers_[loc.GetEncoding()] = true;
    }
  }

  SetupBlockedRegisters(blocked_registers_);

  // Allocate all unallocated input locations.
  for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
    Location loc = locations->InAt(i);
    HInstruction* input = instruction->InputAt(i);
    if (loc.IsUnallocated()) {
      if (loc.GetPolicy() == Location::kRequiresRegister) {
        loc = Location::RegisterLocation(
            AllocateFreeRegister(input->GetType(), blocked_registers_));
      } else {
        DCHECK_EQ(loc.GetPolicy(), Location::kAny);
        HLoadLocal* load = input->AsLoadLocal();
        if (load != nullptr) {
          loc = GetStackLocation(load);
        } else {
          loc = Location::RegisterLocation(
              AllocateFreeRegister(input->GetType(), blocked_registers_));
        }
      }
      locations->SetInAt(i, loc);
    }
  }

  // Allocate all unallocated temp locations.
  for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) {
    Location loc = locations->GetTemp(i);
    if (loc.IsUnallocated()) {
      DCHECK_EQ(loc.GetPolicy(), Location::kRequiresRegister);
      // TODO: Adjust handling of temps. We currently consider temps to use
      // core registers. They may also use floating point registers at some point.
      loc = Location::RegisterLocation(static_cast<ManagedRegister>(
          AllocateFreeRegister(Primitive::kPrimInt, blocked_registers_)));
      locations->SetTempAt(i, loc);
    }
  }

  Location result_location = locations->Out();
  if (result_location.IsUnallocated()) {
    switch (result_location.GetPolicy()) {
      case Location::kAny:
      case Location::kRequiresRegister:
        result_location = Location::RegisterLocation(
            AllocateFreeRegister(instruction->GetType(), blocked_registers_));
        break;
      case Location::kSameAsFirstInput:
        result_location = locations->InAt(0);
        break;
    }
    locations->SetOut(result_location);
  }
}

void CodeGenerator::InitLocations(HInstruction* instruction) {
  if (instruction->GetLocations() == nullptr) {
    return;
  }
  AllocateRegistersLocally(instruction);
  for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
    Location location = instruction->GetLocations()->InAt(i);
    if (location.IsValid()) {
      // Move the input to the desired location.
      Move(instruction->InputAt(i), location, instruction);
    }
  }
}

bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
  // We currently iterate over the block in insertion order.
  return current->GetBlockId() + 1 == next->GetBlockId();
}

Label* CodeGenerator::GetLabelOf(HBasicBlock* block) const {
  return block_labels_.GetRawStorage() + block->GetBlockId();
}

CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator,
                                     HGraph* graph,
                                     InstructionSet instruction_set) {
  switch (instruction_set) {
    case kArm:
    case kThumb2: {
      return new (allocator) arm::CodeGeneratorARM(graph);
    }
    case kMips:
      return nullptr;
    case kX86: {
      return new (allocator) x86::CodeGeneratorX86(graph);
    }
    case kX86_64: {
      return new (allocator) x86::CodeGeneratorX86(graph);
    }
    default:
      return nullptr;
  }
}

void CodeGenerator::BuildNativeGCMap(
    std::vector<uint8_t>* data, const DexCompilationUnit& dex_compilation_unit) const {
  const std::vector<uint8_t>& gc_map_raw =
      dex_compilation_unit.GetVerifiedMethod()->GetDexGcMap();
  verifier::DexPcToReferenceMap dex_gc_map(&(gc_map_raw)[0]);

  uint32_t max_native_offset = 0;
  for (size_t i = 0; i < pc_infos_.Size(); i++) {
    uint32_t native_offset = pc_infos_.Get(i).native_pc;
    if (native_offset > max_native_offset) {
      max_native_offset = native_offset;
    }
  }

  GcMapBuilder builder(data, pc_infos_.Size(), max_native_offset, dex_gc_map.RegWidth());
  for (size_t i = 0; i < pc_infos_.Size(); i++) {
    struct PcInfo pc_info = pc_infos_.Get(i);
    uint32_t native_offset = pc_info.native_pc;
    uint32_t dex_pc = pc_info.dex_pc;
    const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
    CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
    builder.AddEntry(native_offset, references);
  }
}

void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data) const {
  uint32_t pc2dex_data_size = 0u;
  uint32_t pc2dex_entries = pc_infos_.Size();
  uint32_t pc2dex_offset = 0u;
  int32_t pc2dex_dalvik_offset = 0;
  uint32_t dex2pc_data_size = 0u;
  uint32_t dex2pc_entries = 0u;

  // We currently only have pc2dex entries.
  for (size_t i = 0; i < pc2dex_entries; i++) {
    struct PcInfo pc_info = pc_infos_.Get(i);
    pc2dex_data_size += UnsignedLeb128Size(pc_info.native_pc - pc2dex_offset);
    pc2dex_data_size += SignedLeb128Size(pc_info.dex_pc - pc2dex_dalvik_offset);
    pc2dex_offset = pc_info.native_pc;
    pc2dex_dalvik_offset = pc_info.dex_pc;
  }

  uint32_t total_entries = pc2dex_entries + dex2pc_entries;
  uint32_t hdr_data_size = UnsignedLeb128Size(total_entries) + UnsignedLeb128Size(pc2dex_entries);
  uint32_t data_size = hdr_data_size + pc2dex_data_size + dex2pc_data_size;
  data->resize(data_size);

  uint8_t* data_ptr = &(*data)[0];
  uint8_t* write_pos = data_ptr;
  write_pos = EncodeUnsignedLeb128(write_pos, total_entries);
  write_pos = EncodeUnsignedLeb128(write_pos, pc2dex_entries);
  DCHECK_EQ(static_cast<size_t>(write_pos - data_ptr), hdr_data_size);
  uint8_t* write_pos2 = write_pos + pc2dex_data_size;

  pc2dex_offset = 0u;
  pc2dex_dalvik_offset = 0u;
  for (size_t i = 0; i < pc2dex_entries; i++) {
    struct PcInfo pc_info = pc_infos_.Get(i);
    DCHECK(pc2dex_offset <= pc_info.native_pc);
    write_pos = EncodeUnsignedLeb128(write_pos, pc_info.native_pc - pc2dex_offset);
    write_pos = EncodeSignedLeb128(write_pos, pc_info.dex_pc - pc2dex_dalvik_offset);
    pc2dex_offset = pc_info.native_pc;
    pc2dex_dalvik_offset = pc_info.dex_pc;
  }
  DCHECK_EQ(static_cast<size_t>(write_pos - data_ptr), hdr_data_size + pc2dex_data_size);
  DCHECK_EQ(static_cast<size_t>(write_pos2 - data_ptr), data_size);

  if (kIsDebugBuild) {
    // Verify the encoded table holds the expected data.
    MappingTable table(data_ptr);
    CHECK_EQ(table.TotalSize(), total_entries);
    CHECK_EQ(table.PcToDexSize(), pc2dex_entries);
    auto it = table.PcToDexBegin();
    auto it2 = table.DexToPcBegin();
    for (size_t i = 0; i < pc2dex_entries; i++) {
      struct PcInfo pc_info = pc_infos_.Get(i);
      CHECK_EQ(pc_info.native_pc, it.NativePcOffset());
      CHECK_EQ(pc_info.dex_pc, it.DexPc());
      ++it;
    }
    CHECK(it == table.PcToDexEnd());
    CHECK(it2 == table.DexToPcEnd());
  }
}

void CodeGenerator::BuildVMapTable(std::vector<uint8_t>* data) const {
  Leb128EncodingVector vmap_encoder;
  // We currently don't use callee-saved registers.
  size_t size = 0 + 1 /* marker */ + 0;
  vmap_encoder.Reserve(size + 1u);  // All values are likely to be one byte in ULEB128 (<128).
  vmap_encoder.PushBackUnsigned(size);
  vmap_encoder.PushBackUnsigned(VmapTable::kAdjustedFpMarker);

  *data = vmap_encoder.GetData();
}

}  // namespace art
<commit_msg>am ac2918a8: am f2f8252a: am 6ab0d9fe: Merge "Make all registers available when allocating an output register."<commit_after>/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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 "code_generator.h"

#include "code_generator_arm.h"
#include "code_generator_x86.h"
#include "dex/verified_method.h"
#include "driver/dex_compilation_unit.h"
#include "gc_map_builder.h"
#include "leb128.h"
#include "mapping_table.h"
#include "utils/assembler.h"
#include "verifier/dex_gc_map.h"
#include "vmap_table.h"

namespace art {

void CodeGenerator::Compile(CodeAllocator* allocator) {
  const GrowableArray<HBasicBlock*>* blocks = GetGraph()->GetBlocks();
  DCHECK(blocks->Get(0) == GetGraph()->GetEntryBlock());
  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks->Get(1)));
  GenerateFrameEntry();
  for (size_t i = 0; i < blocks->Size(); i++) {
    CompileBlock(blocks->Get(i));
  }
  size_t code_size = GetAssembler()->CodeSize();
  uint8_t* buffer = allocator->Allocate(code_size);
  MemoryRegion code(buffer, code_size);
  GetAssembler()->FinalizeInstructions(code);
}

void CodeGenerator::CompileBlock(HBasicBlock* block) {
  Bind(GetLabelOf(block));
  HGraphVisitor* location_builder = GetLocationBuilder();
  HGraphVisitor* instruction_visitor = GetInstructionVisitor();
  for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
    HInstruction* current = it.Current();
    current->Accept(location_builder);
    InitLocations(current);
    current->Accept(instruction_visitor);
  }
}

size_t CodeGenerator::AllocateFreeRegisterInternal(
    bool* blocked_registers, size_t number_of_registers) const {
  for (size_t regno = 0; regno < number_of_registers; regno++) {
    if (!blocked_registers[regno]) {
      blocked_registers[regno] = true;
      return regno;
    }
  }
  LOG(FATAL) << "Unreachable";
  return -1;
}

void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const {
  LocationSummary* locations = instruction->GetLocations();
  if (locations == nullptr) return;

  for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) {
    blocked_registers_[i] = false;
  }

  // Mark all fixed input, temp and output registers as used.
  for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
    Location loc = locations->InAt(i);
    if (loc.IsRegister()) {
      // Check that a register is not specified twice in the summary.
      DCHECK(!blocked_registers_[loc.GetEncoding()]);
      blocked_registers_[loc.GetEncoding()] = true;
    }
  }

  for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) {
    Location loc = locations->GetTemp(i);
    if (loc.IsRegister()) {
      // Check that a register is not specified twice in the summary.
      DCHECK(!blocked_registers_[loc.GetEncoding()]);
      blocked_registers_[loc.GetEncoding()] = true;
    }
  }

  SetupBlockedRegisters(blocked_registers_);

  // Allocate all unallocated input locations.
  for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
    Location loc = locations->InAt(i);
    HInstruction* input = instruction->InputAt(i);
    if (loc.IsUnallocated()) {
      if (loc.GetPolicy() == Location::kRequiresRegister) {
        loc = Location::RegisterLocation(
            AllocateFreeRegister(input->GetType(), blocked_registers_));
      } else {
        DCHECK_EQ(loc.GetPolicy(), Location::kAny);
        HLoadLocal* load = input->AsLoadLocal();
        if (load != nullptr) {
          loc = GetStackLocation(load);
        } else {
          loc = Location::RegisterLocation(
              AllocateFreeRegister(input->GetType(), blocked_registers_));
        }
      }
      locations->SetInAt(i, loc);
    }
  }

  // Allocate all unallocated temp locations.
  for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) {
    Location loc = locations->GetTemp(i);
    if (loc.IsUnallocated()) {
      DCHECK_EQ(loc.GetPolicy(), Location::kRequiresRegister);
      // TODO: Adjust handling of temps. We currently consider temps to use
      // core registers. They may also use floating point registers at some point.
      loc = Location::RegisterLocation(static_cast<ManagedRegister>(
          AllocateFreeRegister(Primitive::kPrimInt, blocked_registers_)));
      locations->SetTempAt(i, loc);
    }
  }

  // Make all registers available for the return value.
  for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) {
    blocked_registers_[i] = false;
  }
  SetupBlockedRegisters(blocked_registers_);

  Location result_location = locations->Out();
  if (result_location.IsUnallocated()) {
    switch (result_location.GetPolicy()) {
      case Location::kAny:
      case Location::kRequiresRegister:
        result_location = Location::RegisterLocation(
            AllocateFreeRegister(instruction->GetType(), blocked_registers_));
        break;
      case Location::kSameAsFirstInput:
        result_location = locations->InAt(0);
        break;
    }
    locations->SetOut(result_location);
  }
}

void CodeGenerator::InitLocations(HInstruction* instruction) {
  if (instruction->GetLocations() == nullptr) {
    return;
  }
  AllocateRegistersLocally(instruction);
  for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
    Location location = instruction->GetLocations()->InAt(i);
    if (location.IsValid()) {
      // Move the input to the desired location.
      Move(instruction->InputAt(i), location, instruction);
    }
  }
}

bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
  // We currently iterate over the block in insertion order.
  return current->GetBlockId() + 1 == next->GetBlockId();
}

Label* CodeGenerator::GetLabelOf(HBasicBlock* block) const {
  return block_labels_.GetRawStorage() + block->GetBlockId();
}

CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator,
                                     HGraph* graph,
                                     InstructionSet instruction_set) {
  switch (instruction_set) {
    case kArm:
    case kThumb2: {
      return new (allocator) arm::CodeGeneratorARM(graph);
    }
    case kMips:
      return nullptr;
    case kX86: {
      return new (allocator) x86::CodeGeneratorX86(graph);
    }
    case kX86_64: {
      return new (allocator) x86::CodeGeneratorX86(graph);
    }
    default:
      return nullptr;
  }
}

void CodeGenerator::BuildNativeGCMap(
    std::vector<uint8_t>* data, const DexCompilationUnit& dex_compilation_unit) const {
  const std::vector<uint8_t>& gc_map_raw =
      dex_compilation_unit.GetVerifiedMethod()->GetDexGcMap();
  verifier::DexPcToReferenceMap dex_gc_map(&(gc_map_raw)[0]);

  uint32_t max_native_offset = 0;
  for (size_t i = 0; i < pc_infos_.Size(); i++) {
    uint32_t native_offset = pc_infos_.Get(i).native_pc;
    if (native_offset > max_native_offset) {
      max_native_offset = native_offset;
    }
  }

  GcMapBuilder builder(data, pc_infos_.Size(), max_native_offset, dex_gc_map.RegWidth());
  for (size_t i = 0; i < pc_infos_.Size(); i++) {
    struct PcInfo pc_info = pc_infos_.Get(i);
    uint32_t native_offset = pc_info.native_pc;
    uint32_t dex_pc = pc_info.dex_pc;
    const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
    CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
    builder.AddEntry(native_offset, references);
  }
}

void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data) const {
  uint32_t pc2dex_data_size = 0u;
  uint32_t pc2dex_entries = pc_infos_.Size();
  uint32_t pc2dex_offset = 0u;
  int32_t pc2dex_dalvik_offset = 0;
  uint32_t dex2pc_data_size = 0u;
  uint32_t dex2pc_entries = 0u;

  // We currently only have pc2dex entries.
  for (size_t i = 0; i < pc2dex_entries; i++) {
    struct PcInfo pc_info = pc_infos_.Get(i);
    pc2dex_data_size += UnsignedLeb128Size(pc_info.native_pc - pc2dex_offset);
    pc2dex_data_size += SignedLeb128Size(pc_info.dex_pc - pc2dex_dalvik_offset);
    pc2dex_offset = pc_info.native_pc;
    pc2dex_dalvik_offset = pc_info.dex_pc;
  }

  uint32_t total_entries = pc2dex_entries + dex2pc_entries;
  uint32_t hdr_data_size = UnsignedLeb128Size(total_entries) + UnsignedLeb128Size(pc2dex_entries);
  uint32_t data_size = hdr_data_size + pc2dex_data_size + dex2pc_data_size;
  data->resize(data_size);

  uint8_t* data_ptr = &(*data)[0];
  uint8_t* write_pos = data_ptr;
  write_pos = EncodeUnsignedLeb128(write_pos, total_entries);
  write_pos = EncodeUnsignedLeb128(write_pos, pc2dex_entries);
  DCHECK_EQ(static_cast<size_t>(write_pos - data_ptr), hdr_data_size);
  uint8_t* write_pos2 = write_pos + pc2dex_data_size;

  pc2dex_offset = 0u;
  pc2dex_dalvik_offset = 0u;
  for (size_t i = 0; i < pc2dex_entries; i++) {
    struct PcInfo pc_info = pc_infos_.Get(i);
    DCHECK(pc2dex_offset <= pc_info.native_pc);
    write_pos = EncodeUnsignedLeb128(write_pos, pc_info.native_pc - pc2dex_offset);
    write_pos = EncodeSignedLeb128(write_pos, pc_info.dex_pc - pc2dex_dalvik_offset);
    pc2dex_offset = pc_info.native_pc;
    pc2dex_dalvik_offset = pc_info.dex_pc;
  }
  DCHECK_EQ(static_cast<size_t>(write_pos - data_ptr), hdr_data_size + pc2dex_data_size);
  DCHECK_EQ(static_cast<size_t>(write_pos2 - data_ptr), data_size);

  if (kIsDebugBuild) {
    // Verify the encoded table holds the expected data.
    MappingTable table(data_ptr);
    CHECK_EQ(table.TotalSize(), total_entries);
    CHECK_EQ(table.PcToDexSize(), pc2dex_entries);
    auto it = table.PcToDexBegin();
    auto it2 = table.DexToPcBegin();
    for (size_t i = 0; i < pc2dex_entries; i++) {
      struct PcInfo pc_info = pc_infos_.Get(i);
      CHECK_EQ(pc_info.native_pc, it.NativePcOffset());
      CHECK_EQ(pc_info.dex_pc, it.DexPc());
      ++it;
    }
    CHECK(it == table.PcToDexEnd());
    CHECK(it2 == table.DexToPcEnd());
  }
}

void CodeGenerator::BuildVMapTable(std::vector<uint8_t>* data) const {
  Leb128EncodingVector vmap_encoder;
  // We currently don't use callee-saved registers.
  size_t size = 0 + 1 /* marker */ + 0;
  vmap_encoder.Reserve(size + 1u);  // All values are likely to be one byte in ULEB128 (<128).
  vmap_encoder.PushBackUnsigned(size);
  vmap_encoder.PushBackUnsigned(VmapTable::kAdjustedFpMarker);

  *data = vmap_encoder.GetData();
}

}  // namespace art
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Facebook, 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 "folly/Bits.h"

#include "folly/CpuId.h"

// None of this is necessary if we're compiling for a target that supports
// popcnt
#ifndef __POPCNT__

namespace {

int popcount_inst(unsigned int x) {
  asm ("popcntl %0, %0" : "=r" (x) : "0" (x));
  return x;
}

int popcount_builtin(unsigned int x) {
  return __builtin_popcount(x);
}

int popcountll_inst(unsigned long long x) {
  asm ("popcntq %0, %0" : "=r" (x) : "0" (x));
  return x;
}

int popcountll_builtin(unsigned long long x) {
  return __builtin_popcountll(x);
}

typedef decltype(popcount_builtin) Type_popcount;
typedef decltype(popcountll_builtin) Type_popcountll;

}  // namespace

// This function is called on startup to resolve folly::detail::popcount
extern "C" Type_popcount* folly_popcount_ifunc() {
  return folly::CpuId().popcnt() ?  popcount_inst : popcount_builtin;
}

// This function is called on startup to resolve folly::detail::popcountll
extern "C" Type_popcountll* folly_popcountll_ifunc() {
  return folly::CpuId().popcnt() ?  popcountll_inst : popcountll_builtin;
}

namespace folly {
namespace detail {

// Call folly_popcount_ifunc on startup to resolve to either popcount_inst
// or popcount_builtin
int popcount(unsigned int x)
  __attribute__((ifunc("folly_popcount_ifunc")));

// Call folly_popcount_ifunc on startup to resolve to either popcountll_inst
// or popcountll_builtin
int popcountll(unsigned long long x)
  __attribute__((ifunc("folly_popcountll_ifunc")));

}  // namespace detail
}  // namespace folly

#endif  /* !__POPCNT__ */

<commit_msg>Remove 1 instruction from popcount<commit_after>/*
 * Copyright 2012 Facebook, 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 "folly/Bits.h"

#include "folly/CpuId.h"

// None of this is necessary if we're compiling for a target that supports
// popcnt
#ifndef __POPCNT__

namespace {

int popcount_inst(unsigned int x) {
  int n;
  asm ("popcntl %1, %0" : "=r" (n) : "r" (x));
  return n;
}

int popcount_builtin(unsigned int x) {
  return __builtin_popcount(x);
}

int popcountll_inst(unsigned long long x) {
  unsigned long long n;
  asm ("popcntq %1, %0" : "=r" (n) : "r" (x));
  return n;
}

int popcountll_builtin(unsigned long long x) {
  return __builtin_popcountll(x);
}

typedef decltype(popcount_builtin) Type_popcount;
typedef decltype(popcountll_builtin) Type_popcountll;

}  // namespace

// This function is called on startup to resolve folly::detail::popcount
extern "C" Type_popcount* folly_popcount_ifunc() {
  return folly::CpuId().popcnt() ?  popcount_inst : popcount_builtin;
}

// This function is called on startup to resolve folly::detail::popcountll
extern "C" Type_popcountll* folly_popcountll_ifunc() {
  return folly::CpuId().popcnt() ?  popcountll_inst : popcountll_builtin;
}

namespace folly {
namespace detail {

// Call folly_popcount_ifunc on startup to resolve to either popcount_inst
// or popcount_builtin
int popcount(unsigned int x)
  __attribute__((ifunc("folly_popcount_ifunc")));

// Call folly_popcount_ifunc on startup to resolve to either popcountll_inst
// or popcountll_builtin
int popcountll(unsigned long long x)
  __attribute__((ifunc("folly_popcountll_ifunc")));

}  // namespace detail
}  // namespace folly

#endif  /* !__POPCNT__ */

<|endoftext|>


<commit_before>#include "country_status_display.hpp"
#include "framework.hpp"

#include "../gui/controller.hpp"
#include "../gui/button.hpp"
#include "../gui/text_view.hpp"

#include "../graphics/overlay_renderer.hpp"
#include "../graphics/display_list.hpp"

#include "../platform/platform.hpp"

#include "../base/thread.hpp"

#include "../base/string_format.hpp"

#include "../std/bind.hpp"
#include "../std/sstream.hpp"

using namespace storage;

CountryStatusDisplay::CountryStatusDisplay(Params const & p)
  : gui::Element(p)
  , m_activeMaps(p.m_activeMaps)
{
  m_activeMapsSlotID = m_activeMaps.AddListener(this);
  gui::Button::Params bp;

  bp.m_depth = depth();
  bp.m_minWidth = 200;
  bp.m_minHeight = 40;
  bp.m_position = graphics::EPosCenter;

  auto createButtonFn = [this](gui::Button::Params const & params)
  {
    gui::Button * result = new gui::Button(params);
    result->setIsVisible(false);
    result->setOnClickListener(bind(&CountryStatusDisplay::OnButtonClicked, this, _1));

    result->setFont(EActive, graphics::FontDesc(16, graphics::Color(255, 255, 255, 255)));
    result->setFont(EPressed, graphics::FontDesc(16, graphics::Color(255, 255, 255, 255)));

    result->setColor(EActive, graphics::Color(0, 0, 0, 0.6 * 255));
    result->setColor(EPressed, graphics::Color(0, 0, 0, 0.4 * 255));

    return result;
  };

  m_primaryButton.reset(createButtonFn(bp));
  m_secondaryButton.reset(createButtonFn(bp));

  gui::TextView::Params tp;
  tp.m_depth = depth();
  tp.m_position = graphics::EPosCenter;

  m_label.reset(new gui::TextView(tp));
  m_label->setIsVisible(false);
  m_label->setFont(gui::Element::EActive, graphics::FontDesc(18));

  setIsVisible(false);
}

CountryStatusDisplay::~CountryStatusDisplay()
{
  m_activeMaps.RemoveListener(m_activeMapsSlotID);
}

void CountryStatusDisplay::SetCountryIndex(TIndex const & idx)
{
  if (m_countryIdx != idx)
  {
    m_countryIdx = idx;

    if (m_countryIdx.IsValid())
    {
      m_countryStatus = m_activeMaps.GetCountryStatus(m_countryIdx);
      m_displayMapName = m_activeMaps.GetFormatedCountryName(m_countryIdx);
    }

    Repaint();
  }
}

void CountryStatusDisplay::DownloadCurrentCountry(int options)
{
  // I don't know why this work, but it's terrible
  // This method call only on android, from "AndroidUIThread"
  DownloadCountry(options, false);
}

void CountryStatusDisplay::setIsVisible(bool isVisible) const
{
  if (isVisible && isVisible != TBase::isVisible())
    Repaint();

  TBase::setIsVisible(isVisible);
}

void CountryStatusDisplay::setIsDirtyLayout(bool isDirty) const
{
  TBase::setIsDirtyLayout(isDirty);
  m_label->setIsDirtyLayout(isDirty);
  m_primaryButton->setIsDirtyLayout(isDirty);
  m_secondaryButton->setIsDirtyLayout(isDirty);

  if (isDirty)
    SetVisibilityForState();
}

void CountryStatusDisplay::draw(graphics::OverlayRenderer * r,
                                math::Matrix<double, 3, 3> const & m) const
{
  if (isVisible())
  {
    Lock();
    checkDirtyLayout();

    m_label->draw(r, m);
    m_primaryButton->draw(r, m);
    m_secondaryButton->draw(r, m);
    Unlock();
  }
}

void CountryStatusDisplay::layout()
{
  if (!isVisible())
    return;
  SetContentForState();

  auto layoutFn = [] (gui::Element * e)
  {
    if (e->isVisible())
      e->layout();
  };

  layoutFn(m_label.get());
  layoutFn(m_primaryButton.get());
  layoutFn(m_secondaryButton.get());

  ComposeElementsForState();

  // !!!! Hack !!!!
  // ComposeElementsForState modify pivot point of elements.
  // After setPivot all elements must be relayouted.
  // For reduce "cache" operations we call layout secondary
  layoutFn(m_label.get());
  layoutFn(m_primaryButton.get());
  layoutFn(m_secondaryButton.get());
}

void CountryStatusDisplay::purge()
{
  m_label->purge();
  m_primaryButton->purge();
  m_secondaryButton->purge();
}

void CountryStatusDisplay::cache()
{
  auto cacheFn = [](gui::Element * e)
  {
    if (e->isVisible())
      e->cache();
  };

  cacheFn(m_label.get());
  cacheFn(m_primaryButton.get());
  cacheFn(m_secondaryButton.get());
}

m2::RectD CountryStatusDisplay::GetBoundRect() const
{
  ASSERT(isVisible(), ());
  m2::RectD r(pivot(), pivot());

  if (m_primaryButton->isVisible())
    r.Add(m_primaryButton->GetBoundRect());
  if (m_secondaryButton->isVisible())
    r.Add(m_secondaryButton->GetBoundRect());

  return r;
}

void CountryStatusDisplay::setController(gui::Controller * controller)
{
  Element::setController(controller);
  m_label->setController(controller);
  m_primaryButton->setController(controller);
  m_secondaryButton->setController(controller);
}

bool CountryStatusDisplay::onTapStarted(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapStarted, _1, _2), pt);
}

bool CountryStatusDisplay::onTapMoved(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapMoved, _1, _2), pt);
}

bool CountryStatusDisplay::onTapEnded(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapEnded, _1, _2), pt);
}

bool CountryStatusDisplay::onTapCancelled(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapCancelled, _1, _2), pt);
}

void CountryStatusDisplay::CountryStatusChanged(ActiveMapsLayout::TGroup const & group, int position)
{
  TIndex index = m_activeMaps.GetCoreIndex(group, position);
  if (m_countryIdx == index)
  {
    Lock();
    m_countryStatus = m_activeMaps.GetCountryStatus(index);
    Repaint();
    Unlock();
  }
}

void CountryStatusDisplay::DownloadingProgressUpdate(ActiveMapsLayout::TGroup const & group, int position, LocalAndRemoteSizeT const & progress)
{
  TIndex index = m_activeMaps.GetCoreIndex(group, position);
  if (m_countryIdx == index)
  {
    Lock();
    m_countryStatus = m_activeMaps.GetCountryStatus(index);
    m_progressSize = progress;
    Repaint();
    Unlock();
  }
}

template <class T1, class T2>
string CountryStatusDisplay::FormatStatusMessage(string const & msgID, T1 const * t1, T2 const * t2)
{
  string msg = m_controller->GetStringsBundle()->GetString(msgID);
  if (t1)
  {
    if (t2)
      msg = strings::Format(msg, *t1, *t2);
    else
    {
      msg = strings::Format(msg, *t1);

      size_t const count = msg.size();
      if (count > 0)
      {
        if (msg[count-1] == '\n')
          msg.erase(count-1, 1);
      }
    }
  }

  return msg;
}

void CountryStatusDisplay::SetVisibilityForState() const
{
  uint8_t visibilityFlags = 0;
  uint8_t const labelVisibility = 0x1;
  uint8_t const primeVisibility = 0x2;
  uint8_t const secondaryVisibility = 0x4;

  if (m_countryIdx.IsValid())
  {
    switch (m_countryStatus)
    {
    case TStatus::EDownloadFailed:
      visibilityFlags |= labelVisibility;
      visibilityFlags |= primeVisibility;
      break;
    case TStatus::EDownloading:
    case TStatus::EInQueue:
      visibilityFlags |= labelVisibility;
      break;
    case TStatus::ENotDownloaded:
      visibilityFlags |= labelVisibility;
      visibilityFlags |= primeVisibility;
      visibilityFlags |= secondaryVisibility;
      break;
    default:
      break;
    }
  }

  m_label->setIsVisible(visibilityFlags & labelVisibility);
  m_primaryButton->setIsVisible(visibilityFlags & primeVisibility);
  m_secondaryButton->setIsVisible(visibilityFlags & secondaryVisibility);

  TBase::setIsVisible(m_label->isVisible() || m_primaryButton->isVisible() || m_secondaryButton->isVisible());
}

void CountryStatusDisplay::SetContentForState()
{
  if (!isVisible())
    return;

  switch (m_countryStatus)
  {
  case TStatus::EDownloadFailed:
  case TStatus::EOutOfMemFailed:
    SetContentForError();
    break;
  case TStatus::ENotDownloaded:
    SetContentForDownloadPropose();
    break;
  case TStatus::EDownloading:
    SetContentForProgress();
    break;
  case TStatus::EInQueue:
    SetContentForInQueue();
    break;
  default:
    break;
  }
}

void CountryStatusDisplay::SetContentForDownloadPropose()
{
  ASSERT(m_label->isVisible(), ());
  ASSERT(m_primaryButton->isVisible(), ());
  ASSERT(m_secondaryButton->isVisible(), ());

  LocalAndRemoteSizeT mapOnlySize = m_activeMaps.GetCountrySize(m_countryIdx, TMapOptions::EMapOnly);
  LocalAndRemoteSizeT withRouting = m_activeMaps.GetCountrySize(m_countryIdx, TMapOptions::EMapWithCarRouting);

  m_label->setText(m_displayMapName);
  int mbToDownload = mapOnlySize.second / (1024 * 1024);
  m_primaryButton->setText(FormatStatusMessage<int, int>("country_status_download", &mbToDownload));
  mbToDownload = withRouting.second / (1024 * 1024);
  m_secondaryButton->setText(FormatStatusMessage<int, int>("country_status_download_routing", &mbToDownload));
}

void CountryStatusDisplay::SetContentForProgress()
{
  ASSERT(m_label->isVisible(), ());
  int const percent = m_progressSize.first * 100 / m_progressSize.second;
  m_label->setText(FormatStatusMessage<string, int>("country_status_downloading", &m_displayMapName, &percent));
}

void CountryStatusDisplay::SetContentForInQueue()
{
  ASSERT(m_label->isVisible(), ());
  m_label->setText(FormatStatusMessage<string, int>("country_status_added_to_queue", &m_displayMapName));
}

void CountryStatusDisplay::SetContentForError()
{
  ASSERT(m_label->isVisible(), ());
  ASSERT(m_primaryButton->isVisible(), ());

  if (m_countryStatus == TStatus::EDownloadFailed)
    m_label->setText(FormatStatusMessage<string, int>("country_status_download_failed", &m_displayMapName));
  else
    m_label->setText(FormatStatusMessage<int, int>("not_enough_free_space_on_sdcard"));

  m_primaryButton->setText(m_controller->GetStringsBundle()->GetString("try_again"));
}

void CountryStatusDisplay::ComposeElementsForState()
{
  ASSERT(isVisible(), ());
  int visibleCount = 0;
  auto visibleCheckFn = [&visibleCount](gui::Element const * e)
  {
    if (e->isVisible())
      ++visibleCount;
  };

  visibleCheckFn(m_label.get());
  visibleCheckFn(m_primaryButton.get());
  visibleCheckFn(m_secondaryButton.get());

  ASSERT(visibleCount > 0, ());

  m2::PointD const & pv = pivot();
  if (visibleCount == 1)
    m_label->setPivot(pv);
  else if (visibleCount == 2)
  {
    size_t const labelHeight = m_label->GetBoundRect().SizeY();
    size_t const buttonHeight = m_primaryButton->GetBoundRect().SizeY();
    size_t const commonHeight = buttonHeight + labelHeight + 10 * visualScale();

    m_label->setPivot(m2::PointD(pv.x, pv.y - commonHeight / 2 + labelHeight / 2));
    m_primaryButton->setPivot(m2::PointD(pv.x, pv.y + commonHeight / 2 - buttonHeight / 2));
  }
  else
  {
    size_t const labelHeight = m_label->GetBoundRect().SizeY();
    size_t const primButtonHeight = m_primaryButton->GetBoundRect().SizeY();
    size_t const secButtonHeight = m_secondaryButton->GetBoundRect().SizeY();
    size_t const emptySpace = 10 * visualScale();

    double const offsetFromCenter = (primButtonHeight / 2 + emptySpace);

    m_label->setPivot(m2::PointD(pv.x, pv.y - offsetFromCenter - labelHeight / 2));
    m_primaryButton->setPivot(pv);
    m_secondaryButton->setPivot(m2::PointD(pv.x, pv.y + offsetFromCenter + secButtonHeight / 2.0));
  }
}

bool CountryStatusDisplay::OnTapAction(TTapActionFn const & action, const m2::PointD & pt)
{
  bool result = false;
  if (m_primaryButton->isVisible() && m_primaryButton->hitTest(pt))
    result |= action(m_primaryButton, pt);
  else if (m_secondaryButton->isVisible() && m_secondaryButton->hitTest(pt))
    result |= action(m_secondaryButton, pt);

  return result;
}

void CountryStatusDisplay::OnButtonClicked(gui::Element const * button)
{
  ASSERT(m_countryIdx.IsValid(), ());

  TMapOptions options = TMapOptions::EMapOnly;
  if (button == m_secondaryButton.get())
    options |= TMapOptions::ECarRouting;

  DownloadCountry(static_cast<int>(options));
}

void CountryStatusDisplay::DownloadCountry(int options, bool checkCallback)
{
  ASSERT(m_countryIdx.IsValid(), ());
  if (checkCallback && m_downloadCallback)
    m_downloadCallback(options);
  else
  {
    if (IsStatusFailed())
    {
      m_progressSize = m_activeMaps.GetDownloadableCountrySize(m_countryIdx);
      m_activeMaps.RetryDownloading(m_countryIdx);
    }
    else
    {
      TMapOptions mapOptions = static_cast<TMapOptions>(options);
      m_progressSize = m_activeMaps.GetCountrySize(m_countryIdx, mapOptions);
      m_activeMaps.DownloadMap(m_countryIdx, mapOptions);
    }
  }
}

void CountryStatusDisplay::Repaint() const
{
  setIsDirtyLayout(true);
  const_cast<CountryStatusDisplay *>(this)->invalidate();
}

bool CountryStatusDisplay::IsStatusFailed() const
{
  return m_countryStatus == TStatus::EOutOfMemFailed || m_countryStatus == TStatus::EDownloadFailed;
}

void CountryStatusDisplay::Lock() const
{
#ifdef OMIM_OS_ANDROID
  m_mutex.Lock();
#endif
}

void CountryStatusDisplay::Unlock() const
{
#ifdef OMIM_OS_ANDROID
  m_mutex.Unlock();
#endif
}
<commit_msg>[core] correct format map size<commit_after>#include "country_status_display.hpp"
#include "framework.hpp"

#include "../gui/controller.hpp"
#include "../gui/button.hpp"
#include "../gui/text_view.hpp"

#include "../graphics/overlay_renderer.hpp"
#include "../graphics/display_list.hpp"

#include "../platform/platform.hpp"

#include "../base/thread.hpp"

#include "../base/string_format.hpp"

#include "../std/bind.hpp"
#include "../std/sstream.hpp"

using namespace storage;

CountryStatusDisplay::CountryStatusDisplay(Params const & p)
  : gui::Element(p)
  , m_activeMaps(p.m_activeMaps)
{
  m_activeMapsSlotID = m_activeMaps.AddListener(this);
  gui::Button::Params bp;

  bp.m_depth = depth();
  bp.m_minWidth = 200;
  bp.m_minHeight = 40;
  bp.m_position = graphics::EPosCenter;

  auto createButtonFn = [this](gui::Button::Params const & params)
  {
    gui::Button * result = new gui::Button(params);
    result->setIsVisible(false);
    result->setOnClickListener(bind(&CountryStatusDisplay::OnButtonClicked, this, _1));

    result->setFont(EActive, graphics::FontDesc(16, graphics::Color(255, 255, 255, 255)));
    result->setFont(EPressed, graphics::FontDesc(16, graphics::Color(255, 255, 255, 255)));

    result->setColor(EActive, graphics::Color(0, 0, 0, 0.6 * 255));
    result->setColor(EPressed, graphics::Color(0, 0, 0, 0.4 * 255));

    return result;
  };

  m_primaryButton.reset(createButtonFn(bp));
  m_secondaryButton.reset(createButtonFn(bp));

  gui::TextView::Params tp;
  tp.m_depth = depth();
  tp.m_position = graphics::EPosCenter;

  m_label.reset(new gui::TextView(tp));
  m_label->setIsVisible(false);
  m_label->setFont(gui::Element::EActive, graphics::FontDesc(18));

  setIsVisible(false);
}

CountryStatusDisplay::~CountryStatusDisplay()
{
  m_activeMaps.RemoveListener(m_activeMapsSlotID);
}

void CountryStatusDisplay::SetCountryIndex(TIndex const & idx)
{
  if (m_countryIdx != idx)
  {
    m_countryIdx = idx;

    if (m_countryIdx.IsValid())
    {
      m_countryStatus = m_activeMaps.GetCountryStatus(m_countryIdx);
      m_displayMapName = m_activeMaps.GetFormatedCountryName(m_countryIdx);
    }

    Repaint();
  }
}

void CountryStatusDisplay::DownloadCurrentCountry(int options)
{
  // I don't know why this work, but it's terrible
  // This method call only on android, from "AndroidUIThread"
  DownloadCountry(options, false);
}

void CountryStatusDisplay::setIsVisible(bool isVisible) const
{
  if (isVisible && isVisible != TBase::isVisible())
    Repaint();

  TBase::setIsVisible(isVisible);
}

void CountryStatusDisplay::setIsDirtyLayout(bool isDirty) const
{
  TBase::setIsDirtyLayout(isDirty);
  m_label->setIsDirtyLayout(isDirty);
  m_primaryButton->setIsDirtyLayout(isDirty);
  m_secondaryButton->setIsDirtyLayout(isDirty);

  if (isDirty)
    SetVisibilityForState();
}

void CountryStatusDisplay::draw(graphics::OverlayRenderer * r,
                                math::Matrix<double, 3, 3> const & m) const
{
  if (isVisible())
  {
    Lock();
    checkDirtyLayout();

    m_label->draw(r, m);
    m_primaryButton->draw(r, m);
    m_secondaryButton->draw(r, m);
    Unlock();
  }
}

void CountryStatusDisplay::layout()
{
  if (!isVisible())
    return;
  SetContentForState();

  auto layoutFn = [] (gui::Element * e)
  {
    if (e->isVisible())
      e->layout();
  };

  layoutFn(m_label.get());
  layoutFn(m_primaryButton.get());
  layoutFn(m_secondaryButton.get());

  ComposeElementsForState();

  // !!!! Hack !!!!
  // ComposeElementsForState modify pivot point of elements.
  // After setPivot all elements must be relayouted.
  // For reduce "cache" operations we call layout secondary
  layoutFn(m_label.get());
  layoutFn(m_primaryButton.get());
  layoutFn(m_secondaryButton.get());
}

void CountryStatusDisplay::purge()
{
  m_label->purge();
  m_primaryButton->purge();
  m_secondaryButton->purge();
}

void CountryStatusDisplay::cache()
{
  auto cacheFn = [](gui::Element * e)
  {
    if (e->isVisible())
      e->cache();
  };

  cacheFn(m_label.get());
  cacheFn(m_primaryButton.get());
  cacheFn(m_secondaryButton.get());
}

m2::RectD CountryStatusDisplay::GetBoundRect() const
{
  ASSERT(isVisible(), ());
  m2::RectD r(pivot(), pivot());

  if (m_primaryButton->isVisible())
    r.Add(m_primaryButton->GetBoundRect());
  if (m_secondaryButton->isVisible())
    r.Add(m_secondaryButton->GetBoundRect());

  return r;
}

void CountryStatusDisplay::setController(gui::Controller * controller)
{
  Element::setController(controller);
  m_label->setController(controller);
  m_primaryButton->setController(controller);
  m_secondaryButton->setController(controller);
}

bool CountryStatusDisplay::onTapStarted(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapStarted, _1, _2), pt);
}

bool CountryStatusDisplay::onTapMoved(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapMoved, _1, _2), pt);
}

bool CountryStatusDisplay::onTapEnded(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapEnded, _1, _2), pt);
}

bool CountryStatusDisplay::onTapCancelled(m2::PointD const & pt)
{
  return OnTapAction(bind(&gui::Button::onTapCancelled, _1, _2), pt);
}

void CountryStatusDisplay::CountryStatusChanged(ActiveMapsLayout::TGroup const & group, int position)
{
  TIndex index = m_activeMaps.GetCoreIndex(group, position);
  if (m_countryIdx == index)
  {
    Lock();
    m_countryStatus = m_activeMaps.GetCountryStatus(index);
    Repaint();
    Unlock();
  }
}

void CountryStatusDisplay::DownloadingProgressUpdate(ActiveMapsLayout::TGroup const & group, int position, LocalAndRemoteSizeT const & progress)
{
  TIndex index = m_activeMaps.GetCoreIndex(group, position);
  if (m_countryIdx == index)
  {
    Lock();
    m_countryStatus = m_activeMaps.GetCountryStatus(index);
    m_progressSize = progress;
    Repaint();
    Unlock();
  }
}

template <class T1, class T2>
string CountryStatusDisplay::FormatStatusMessage(string const & msgID, T1 const * t1, T2 const * t2)
{
  string msg = m_controller->GetStringsBundle()->GetString(msgID);
  if (t1)
  {
    if (t2)
      msg = strings::Format(msg, *t1, *t2);
    else
    {
      msg = strings::Format(msg, *t1);

      size_t const count = msg.size();
      if (count > 0)
      {
        if (msg[count-1] == '\n')
          msg.erase(count-1, 1);
      }
    }
  }

  return msg;
}

void CountryStatusDisplay::SetVisibilityForState() const
{
  uint8_t visibilityFlags = 0;
  uint8_t const labelVisibility = 0x1;
  uint8_t const primeVisibility = 0x2;
  uint8_t const secondaryVisibility = 0x4;

  if (m_countryIdx.IsValid())
  {
    switch (m_countryStatus)
    {
    case TStatus::EDownloadFailed:
      visibilityFlags |= labelVisibility;
      visibilityFlags |= primeVisibility;
      break;
    case TStatus::EDownloading:
    case TStatus::EInQueue:
      visibilityFlags |= labelVisibility;
      break;
    case TStatus::ENotDownloaded:
      visibilityFlags |= labelVisibility;
      visibilityFlags |= primeVisibility;
      visibilityFlags |= secondaryVisibility;
      break;
    default:
      break;
    }
  }

  m_label->setIsVisible(visibilityFlags & labelVisibility);
  m_primaryButton->setIsVisible(visibilityFlags & primeVisibility);
  m_secondaryButton->setIsVisible(visibilityFlags & secondaryVisibility);

  TBase::setIsVisible(m_label->isVisible() || m_primaryButton->isVisible() || m_secondaryButton->isVisible());
}

void CountryStatusDisplay::SetContentForState()
{
  if (!isVisible())
    return;

  switch (m_countryStatus)
  {
  case TStatus::EDownloadFailed:
  case TStatus::EOutOfMemFailed:
    SetContentForError();
    break;
  case TStatus::ENotDownloaded:
    SetContentForDownloadPropose();
    break;
  case TStatus::EDownloading:
    SetContentForProgress();
    break;
  case TStatus::EInQueue:
    SetContentForInQueue();
    break;
  default:
    break;
  }
}

namespace
{
  void FormatMapSize(int sizeInBytes, string & units, int & sizeToDownload)
  {
    int const mbInBytes = 1024 * 1024;
    int const kbInBytes = 1024;
    if (sizeInBytes < mbInBytes)
    {
      sizeToDownload = sizeInBytes / kbInBytes;
      units = "KB";
    }
    else
    {
      sizeToDownload = sizeInBytes / mbInBytes;
      units = "MB";
    }
  }
}

void CountryStatusDisplay::SetContentForDownloadPropose()
{
  ASSERT(m_label->isVisible(), ());
  ASSERT(m_primaryButton->isVisible(), ());
  ASSERT(m_secondaryButton->isVisible(), ());

  LocalAndRemoteSizeT mapOnlySize = m_activeMaps.GetCountrySize(m_countryIdx, TMapOptions::EMapOnly);
  LocalAndRemoteSizeT withRouting = m_activeMaps.GetCountrySize(m_countryIdx, TMapOptions::EMapWithCarRouting);

  m_label->setText(m_displayMapName);
  int sizeToDownload;
  string units;
  FormatMapSize(mapOnlySize.second, units, sizeToDownload);
  m_primaryButton->setText(FormatStatusMessage<int, string>("country_status_download", &sizeToDownload, &units));
  FormatMapSize(withRouting.second, units, sizeToDownload);
  m_secondaryButton->setText(FormatStatusMessage<int, string>("country_status_download_routing", &sizeToDownload));
}

void CountryStatusDisplay::SetContentForProgress()
{
  ASSERT(m_label->isVisible(), ());
  int const percent = m_progressSize.first * 100 / m_progressSize.second;
  m_label->setText(FormatStatusMessage<string, int>("country_status_downloading", &m_displayMapName, &percent));
}

void CountryStatusDisplay::SetContentForInQueue()
{
  ASSERT(m_label->isVisible(), ());
  m_label->setText(FormatStatusMessage<string, int>("country_status_added_to_queue", &m_displayMapName));
}

void CountryStatusDisplay::SetContentForError()
{
  ASSERT(m_label->isVisible(), ());
  ASSERT(m_primaryButton->isVisible(), ());

  if (m_countryStatus == TStatus::EDownloadFailed)
    m_label->setText(FormatStatusMessage<string, int>("country_status_download_failed", &m_displayMapName));
  else
    m_label->setText(FormatStatusMessage<int, int>("not_enough_free_space_on_sdcard"));

  m_primaryButton->setText(m_controller->GetStringsBundle()->GetString("try_again"));
}

void CountryStatusDisplay::ComposeElementsForState()
{
  ASSERT(isVisible(), ());
  int visibleCount = 0;
  auto visibleCheckFn = [&visibleCount](gui::Element const * e)
  {
    if (e->isVisible())
      ++visibleCount;
  };

  visibleCheckFn(m_label.get());
  visibleCheckFn(m_primaryButton.get());
  visibleCheckFn(m_secondaryButton.get());

  ASSERT(visibleCount > 0, ());

  m2::PointD const & pv = pivot();
  if (visibleCount == 1)
    m_label->setPivot(pv);
  else if (visibleCount == 2)
  {
    size_t const labelHeight = m_label->GetBoundRect().SizeY();
    size_t const buttonHeight = m_primaryButton->GetBoundRect().SizeY();
    size_t const commonHeight = buttonHeight + labelHeight + 10 * visualScale();

    m_label->setPivot(m2::PointD(pv.x, pv.y - commonHeight / 2 + labelHeight / 2));
    m_primaryButton->setPivot(m2::PointD(pv.x, pv.y + commonHeight / 2 - buttonHeight / 2));
  }
  else
  {
    size_t const labelHeight = m_label->GetBoundRect().SizeY();
    size_t const primButtonHeight = m_primaryButton->GetBoundRect().SizeY();
    size_t const secButtonHeight = m_secondaryButton->GetBoundRect().SizeY();
    size_t const emptySpace = 10 * visualScale();

    double const offsetFromCenter = (primButtonHeight / 2 + emptySpace);

    m_label->setPivot(m2::PointD(pv.x, pv.y - offsetFromCenter - labelHeight / 2));
    m_primaryButton->setPivot(pv);
    m_secondaryButton->setPivot(m2::PointD(pv.x, pv.y + offsetFromCenter + secButtonHeight / 2.0));
  }
}

bool CountryStatusDisplay::OnTapAction(TTapActionFn const & action, const m2::PointD & pt)
{
  bool result = false;
  if (m_primaryButton->isVisible() && m_primaryButton->hitTest(pt))
    result |= action(m_primaryButton, pt);
  else if (m_secondaryButton->isVisible() && m_secondaryButton->hitTest(pt))
    result |= action(m_secondaryButton, pt);

  return result;
}

void CountryStatusDisplay::OnButtonClicked(gui::Element const * button)
{
  ASSERT(m_countryIdx.IsValid(), ());

  TMapOptions options = TMapOptions::EMapOnly;
  if (button == m_secondaryButton.get())
    options |= TMapOptions::ECarRouting;

  DownloadCountry(static_cast<int>(options));
}

void CountryStatusDisplay::DownloadCountry(int options, bool checkCallback)
{
  ASSERT(m_countryIdx.IsValid(), ());
  if (checkCallback && m_downloadCallback)
    m_downloadCallback(options);
  else
  {
    if (IsStatusFailed())
    {
      m_progressSize = m_activeMaps.GetDownloadableCountrySize(m_countryIdx);
      m_activeMaps.RetryDownloading(m_countryIdx);
    }
    else
    {
      TMapOptions mapOptions = static_cast<TMapOptions>(options);
      m_progressSize = m_activeMaps.GetCountrySize(m_countryIdx, mapOptions);
      m_activeMaps.DownloadMap(m_countryIdx, mapOptions);
    }
  }
}

void CountryStatusDisplay::Repaint() const
{
  setIsDirtyLayout(true);
  const_cast<CountryStatusDisplay *>(this)->invalidate();
}

bool CountryStatusDisplay::IsStatusFailed() const
{
  return m_countryStatus == TStatus::EOutOfMemFailed || m_countryStatus == TStatus::EDownloadFailed;
}

void CountryStatusDisplay::Lock() const
{
#ifdef OMIM_OS_ANDROID
  m_mutex.Lock();
#endif
}

void CountryStatusDisplay::Unlock() const
{
#ifdef OMIM_OS_ANDROID
  m_mutex.Unlock();
#endif
}
<|endoftext|>