<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of TeapotNet.                                     *
 *                                                                       *
 *   TeapotNet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   TeapotNet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with TeapotNet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "user.h"
#include "config.h"
#include "file.h"
#include "directory.h"
#include "sha512.h"
#include "html.h"

namespace tpot
{

Map<String, User*>	User::UsersByName;
Map<Identifier, User*>	User::UsersByAuth;
Mutex			User::UsersMutex;

unsigned User::Count(void)
{
	 UsersMutex.lock();
	 unsigned count = UsersByName.size();
	 UsersMutex.unlock();
	 return count;
}

bool User::Exist(const String &name)
{
	return (User::Get(name) != NULL);
}

User *User::Get(const String &name)
{
	User *user = NULL;
	UsersMutex.lock();
	if(UsersByName.get(name, user))
	{
	  	UsersMutex.unlock();
		return user;
	}
	UsersMutex.unlock();
	return NULL;
}

User *User::Authenticate(const String &name, const String &password)
{
	Identifier hash;
	Sha512::Hash(name + ':' + password, hash, Sha512::CryptRounds);

	User *user = NULL;
	UsersMutex.lock();
	if(UsersByAuth.get(hash, user))
	{
	  	UsersMutex.unlock();
		return user;
	}
	UsersMutex.unlock();
	Log("User::Authenticate", "Authentication failed for \""+name+"\"");
	return NULL;
}

User::User(const String &name, const String &password) :
	mName(name),
	mAddressBook(new AddressBook(this)),
	mStore(new Store(this))
{
	if(password.empty())
	{
		File file(profilePath()+"password", File::Read);
		file.read(mHash);
		file.close();
	}
	else {
		Sha512::Hash(name + ':' + password, mHash, Sha512::CryptRounds);

		File file(profilePath()+"password", File::Write);
		file.write(mHash);
		file.close();
	}

	UsersMutex.lock();
	UsersByName.insert(mName, this);
	UsersByAuth.insert(mHash, this);
	UsersMutex.unlock();

	Interface::Instance->add("/"+mName, this);
}

User::~User(void)
{
  	UsersMutex.lock();
	UsersByName.erase(mName);
  	UsersByAuth.erase(mHash);
	UsersMutex.unlock();

	Interface::Instance->remove("/"+mName);

	delete mAddressBook;
	delete mStore;
}

const String &User::name(void) const
{
	return mName;
}

String User::profilePath(void) const
{
	if(!Directory::Exist(Config::Get("profiles_dir"))) Directory::Create(Config::Get("profiles_dir"));
	String path = Config::Get("profiles_dir") + Directory::Separator + mName;
	if(!Directory::Exist(path)) Directory::Create(path);
	return path + Directory::Separator;
}

AddressBook *User::addressBook(void) const
{
	return mAddressBook;
}

Store *User::store(void) const
{
	return mStore;
}

void User::http(const String &prefix, Http::Request &request)
{
	Synchronize(this);

	try {
		String url = request.url;
		if(url.empty() || url[0] != '/') throw 404;

		if(url == "/")
		{
			Http::Response response(request,200);
			response.send();

			Html page(response.sock);
			page.header(APPNAME, true);

			page.open("div", "mainheader");
			page.openLink("/"); page.image("/logo.png", APPNAME, "logo"); page.closeLink();

			page.open("div", ".search");
			page.openForm(prefix + "/search", "post", "searchForm");
			page.input("text","query");
			page.button("search","Search");
			page.closeForm();
			page.javascript("document.searchForm.query.focus();");
			page.br();
			page.close("div");

			page.close("div");

			page.open("h1");
			page.text(String("Welcome, ")+mName+" !");
			page.close("h1");

			/*
			int msgcount = mAddressBook->unreadMessagesCount();
			if(msgcount)
			{
				page.span(String("You have ")+String::number(msgcount)+String(" new messages"), ".important");
				page.br();
			}
			*/

			page.open("div","contacts.box");
			page.open("h2");
			page.text("Contacts - ");
			page.link(prefix+"/contacts/","Edit");
			page.close("h2");

			Array<AddressBook::Contact*> contacts;
			mAddressBook->getContacts(contacts);
			if(contacts.empty()) page.link(prefix+"/contacts/","Add a contact !");
			else {
				page.open("table",".contacts");
				for(int i=0; i<contacts.size(); ++i)
				{
					AddressBook::Contact *contact = contacts[i];

					page.open("tr", String("contact_")+contact->uniqueName());
					page.open("td");
					page.open("span",".contact");
					page.link(contact->urlPrefix(), contact->name() + "@" + contact->tracker());
					page.close("span");
					page.close("td");

					page.open("td",".status");
					page.span(contact->status().capitalized(), String(".") + contact->status());
					page.close("td");

					page.open("td",".files");
					page.link(contact->urlPrefix()+"/files/", "files");
					page.close("td");

					page.open("td",".chat");
					page.openLink(contact->urlPrefix()+"/chat/");
					page.text("chat");
					page.open("span",".messagescount");
					int msgcount = contact->unreadMessagesCount();
					if(msgcount) page.text(String("(")+String::number(msgcount)+String(")"));
					page.close("span");
					page.closeLink();
					page.close("td");

					page.close("tr");
				}
				page.close("table");
			}
			page.close("div");

			unsigned refreshPeriod = 5000;
			page.javascript("function updateContacts() {\n\
				$.getJSON('"+prefix+"/contacts/?json', function(data) {\n\
  					$.each(data, function(uname, info) {\n\
			  			transition($('#contact_'+uname+' .status'),\n\
							''+info.status.capitalize()+'\\n');\n\
						var msg = '';\n\
						if(info.messages != 0) msg = ' ('+info.messages+')';\n\
						transition($('#contact_'+uname+' .messagescount'), msg);\n\
  					});\n\
  					setTimeout('updateContacts()',"+String::number(refreshPeriod)+");\n\
				});\n\
			}\n\
			updateContacts();");

			page.open("div","files.box");
			page.open("h2");
			page.text("Shared folders - ");
			page.link(prefix+"/files/","Edit");
			if(mAddressBook->getSelf())
			{
				page.text(" - ");
				page.link(prefix+"/myself/","Show all my files");
			}
			page.close("h2");

			Array<String> directories;
			mStore->getDirectories(directories);
			if(directories.empty()) page.link(prefix+"/files/","Add a shared personal folder !");
			else {
				page.open("table",".files");
				for(int i=0; i<directories.size(); ++i)
				{
					const String &directory = directories[i];
					String directoryUrl = prefix + "/files/" + directory + "/";

					page.open("tr");
					page.open("td");
					page.open("span",".file");
					page.link(directoryUrl, directory);
					page.close("span");
					page.close("td");
				}
				page.close("table");
			}
			page.close("div");

			directories.clear();
			Store::GlobalInstance->getDirectories(directories);
			if(!directories.empty())
			{
				page.open("div","otherfiles.box");
				page.open("h2");
				page.text("Other shared folders");
				page.close("h2");

				page.open("table",".files");
				for(int i=0; i<directories.size(); ++i)
				{
					const String &directory = directories[i];
					String directoryUrl = prefix + "/files/" + directory + "/";

					page.open("tr");
					page.open("td");
					page.open("span",".file");
					page.link(directoryUrl, directory);
					page.close("span");
					page.close("td");
				}
				page.close("table");

				page.close("div");
			}

			page.open("div", "footer");
			page.link(SOURCELINK, "Source code", "", true);
			page.close("div");

			page.footer();
			return;
		}

		if(url == "/search" || url == "/search/")
		{
			String query;
			if(request.post.contains("query"))
			{
				query = request.post.get("query");
				query.trim();
			}

			Http::Response response(request,200);
			response.send();

			Html page(response.sock);
			page.header("Search");

			page.openForm(prefix + "/search", "post", "searchform");
			page.input("text","query",query);
			page.button("search","Search");
			page.closeForm();
			page.br();

			if(query.empty())
			{
				page.footer();
				return;
			}

			Store::Query squery;
			squery.setMatch(query);

			int count = 0;
			page.open("div",".box");
			page.open("table",".files");

			List<Store::Entry> list;
			if(mStore->queryList(squery, list))
			{

				for(List<Store::Entry>::iterator it = list.begin();
					it != list.end();
					++it)
				{
					Store::Entry &entry = *it;
					if(entry.url.empty()) continue;

					page.open("tr");
					page.open("td");
					page.text("(" + mName + ")");
					page.close("td");
					page.open("td",".filename");
					page.link("/" + mName + "/files" + entry.url, entry.name);
					page.close("td");
					page.open("td",".size");
					if(!entry.type) page.text("directory");
					else page.text(String::hrSize(entry.size));
					page.close("td");
					page.close("tr");

					++count;
				}
			}

			Desynchronize(this);
			Request trequest("search:"+query, false);	// no data
			Synchronize(&trequest);

			const unsigned timeout = Config::Get("request_timeout").toInt();
			trequest.submit();
			trequest.wait(timeout);

			if(trequest.isSuccessful())
			try {
				for(int i=0; i<trequest.responsesCount(); ++i)
				{
					Request::Response *tresponse = trequest.response(i);
					if(tresponse->error()) continue;

					// Check contact
					const AddressBook::Contact *contact = mAddressBook->getContact(tresponse->peering());
					if(!contact) continue;

					// Check info
					StringMap map = tresponse->parameters();
					if(!map.contains("type")) continue;
					if(!map.contains("path")) continue;
					if(!map.contains("hash")) map["hash"] = "";
					if(!map.contains("name")) map["name"] = map["path"].afterLast('/');

					page.open("tr");
					page.open("td");
					page.text("("); page.link(contact->urlPrefix()+"/", contact->name()); page.text(")");
					page.close("td");
					page.open("td",".filename");
					if(map.get("type") == "directory") page.link(contact->urlPrefix() + "/files" + map.get("path"), map.get("name"));
					else if(!map.get("hash").empty()) page.link("/" + map.get("hash"), map.get("name"));
					else page.link(contact->urlPrefix() + "/files" + map.get("path") + "?instance=" + tresponse->instance().urlEncode() + "&file=1", map.get("name"));
					page.close("td");
					page.open("td",".size");
					if(map.get("type") == "directory") page.text("directory");
					else if(map.contains("size")) page.text(String::hrSize(map.get("size")));
					page.close("td");
					page.close("tr");

					++count;
				}

			}
			catch(const Exception &e)
			{
				Log("User::http", String("Unable to list files: ") + e.what());
			}

			page.close("table");

			if(!count) page.text("No files found");

			page.close("div");
			page.footer();
			return;
		}

		url.ignore();
		String urlLeft = String("/") + url.cut('/');
		url = String("/") + url;

		if(url == "/myself")
		{
			AddressBook::Contact *self = mAddressBook->getSelf();
			if(!self)
			{
				Http::Response response(request, 303);
				response.headers["Location"] = prefix + "/files/";
				response.send();
				return;
			}

			request.url = String("/files") + urlLeft;
			String newPrefix = prefix + "/contacts/" + self->uniqueName();

			Desynchronize(this);
			self->http(newPrefix, request);
			return;
		}
	}
	catch(const Exception &e)
	{
		Log("User::http",e.what());
		throw 404;	// Httpd handles integer exceptions
	}

	throw 404;
}

void User::run(void)
{
	while(true)
	{
		wait(5*60*1000);
		if(!mAddressBook->isRunning()) mAddressBook->start();
		if(!mStore->isRunning()) mStore->start();
	}
}

}
<commit_msg>Display version number<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of TeapotNet.                                     *
 *                                                                       *
 *   TeapotNet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   TeapotNet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with TeapotNet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "user.h"
#include "config.h"
#include "file.h"
#include "directory.h"
#include "sha512.h"
#include "html.h"

namespace tpot
{

Map<String, User*>	User::UsersByName;
Map<Identifier, User*>	User::UsersByAuth;
Mutex			User::UsersMutex;

unsigned User::Count(void)
{
	 UsersMutex.lock();
	 unsigned count = UsersByName.size();
	 UsersMutex.unlock();
	 return count;
}

bool User::Exist(const String &name)
{
	return (User::Get(name) != NULL);
}

User *User::Get(const String &name)
{
	User *user = NULL;
	UsersMutex.lock();
	if(UsersByName.get(name, user))
	{
	  	UsersMutex.unlock();
		return user;
	}
	UsersMutex.unlock();
	return NULL;
}

User *User::Authenticate(const String &name, const String &password)
{
	Identifier hash;
	Sha512::Hash(name + ':' + password, hash, Sha512::CryptRounds);

	User *user = NULL;
	UsersMutex.lock();
	if(UsersByAuth.get(hash, user))
	{
	  	UsersMutex.unlock();
		return user;
	}
	UsersMutex.unlock();
	Log("User::Authenticate", "Authentication failed for \""+name+"\"");
	return NULL;
}

User::User(const String &name, const String &password) :
	mName(name),
	mAddressBook(new AddressBook(this)),
	mStore(new Store(this))
{
	if(password.empty())
	{
		File file(profilePath()+"password", File::Read);
		file.read(mHash);
		file.close();
	}
	else {
		Sha512::Hash(name + ':' + password, mHash, Sha512::CryptRounds);

		File file(profilePath()+"password", File::Write);
		file.write(mHash);
		file.close();
	}

	UsersMutex.lock();
	UsersByName.insert(mName, this);
	UsersByAuth.insert(mHash, this);
	UsersMutex.unlock();

	Interface::Instance->add("/"+mName, this);
}

User::~User(void)
{
  	UsersMutex.lock();
	UsersByName.erase(mName);
  	UsersByAuth.erase(mHash);
	UsersMutex.unlock();

	Interface::Instance->remove("/"+mName);

	delete mAddressBook;
	delete mStore;
}

const String &User::name(void) const
{
	return mName;
}

String User::profilePath(void) const
{
	if(!Directory::Exist(Config::Get("profiles_dir"))) Directory::Create(Config::Get("profiles_dir"));
	String path = Config::Get("profiles_dir") + Directory::Separator + mName;
	if(!Directory::Exist(path)) Directory::Create(path);
	return path + Directory::Separator;
}

AddressBook *User::addressBook(void) const
{
	return mAddressBook;
}

Store *User::store(void) const
{
	return mStore;
}

void User::http(const String &prefix, Http::Request &request)
{
	Synchronize(this);

	try {
		String url = request.url;
		if(url.empty() || url[0] != '/') throw 404;

		if(url == "/")
		{
			Http::Response response(request,200);
			response.send();

			Html page(response.sock);
			page.header(APPNAME, true);

			page.open("div", "mainheader");
			page.openLink("/"); page.image("/logo.png", APPNAME, "logo"); page.closeLink();

			page.open("div", ".search");
			page.openForm(prefix + "/search", "post", "searchForm");
			page.input("text","query");
			page.button("search","Search");
			page.closeForm();
			page.javascript("document.searchForm.query.focus();");
			page.br();
			page.close("div");

			page.close("div");

			page.open("h1");
			page.text(String("Welcome, ")+mName+" !");
			page.close("h1");

			/*
			int msgcount = mAddressBook->unreadMessagesCount();
			if(msgcount)
			{
				page.span(String("You have ")+String::number(msgcount)+String(" new messages"), ".important");
				page.br();
			}
			*/

			page.open("div","contacts.box");
			page.open("h2");
			page.text("Contacts - ");
			page.link(prefix+"/contacts/","Edit");
			page.close("h2");

			Array<AddressBook::Contact*> contacts;
			mAddressBook->getContacts(contacts);
			if(contacts.empty()) page.link(prefix+"/contacts/","Add a contact !");
			else {
				page.open("table",".contacts");
				for(int i=0; i<contacts.size(); ++i)
				{
					AddressBook::Contact *contact = contacts[i];

					page.open("tr", String("contact_")+contact->uniqueName());
					page.open("td");
					page.open("span",".contact");
					page.link(contact->urlPrefix(), contact->name() + "@" + contact->tracker());
					page.close("span");
					page.close("td");

					page.open("td",".status");
					page.span(contact->status().capitalized(), String(".") + contact->status());
					page.close("td");

					page.open("td",".files");
					page.link(contact->urlPrefix()+"/files/", "files");
					page.close("td");

					page.open("td",".chat");
					page.openLink(contact->urlPrefix()+"/chat/");
					page.text("chat");
					page.open("span",".messagescount");
					int msgcount = contact->unreadMessagesCount();
					if(msgcount) page.text(String("(")+String::number(msgcount)+String(")"));
					page.close("span");
					page.closeLink();
					page.close("td");

					page.close("tr");
				}
				page.close("table");
			}
			page.close("div");

			unsigned refreshPeriod = 5000;
			page.javascript("function updateContacts() {\n\
				$.getJSON('"+prefix+"/contacts/?json', function(data) {\n\
  					$.each(data, function(uname, info) {\n\
			  			transition($('#contact_'+uname+' .status'),\n\
							''+info.status.capitalize()+'\\n');\n\
						var msg = '';\n\
						if(info.messages != 0) msg = ' ('+info.messages+')';\n\
						transition($('#contact_'+uname+' .messagescount'), msg);\n\
  					});\n\
  					setTimeout('updateContacts()',"+String::number(refreshPeriod)+");\n\
				});\n\
			}\n\
			updateContacts();");

			page.open("div","files.box");
			page.open("h2");
			page.text("Shared folders - ");
			page.link(prefix+"/files/","Edit");
			if(mAddressBook->getSelf())
			{
				page.text(" - ");
				page.link(prefix+"/myself/","Show all my files");
			}
			page.close("h2");

			Array<String> directories;
			mStore->getDirectories(directories);
			if(directories.empty()) page.link(prefix+"/files/","Add a shared personal folder !");
			else {
				page.open("table",".files");
				for(int i=0; i<directories.size(); ++i)
				{
					const String &directory = directories[i];
					String directoryUrl = prefix + "/files/" + directory + "/";

					page.open("tr");
					page.open("td");
					page.open("span",".file");
					page.link(directoryUrl, directory);
					page.close("span");
					page.close("td");
				}
				page.close("table");
			}
			page.close("div");

			directories.clear();
			Store::GlobalInstance->getDirectories(directories);
			if(!directories.empty())
			{
				page.open("div","otherfiles.box");
				page.open("h2");
				page.text("Other shared folders");
				page.close("h2");

				page.open("table",".files");
				for(int i=0; i<directories.size(); ++i)
				{
					const String &directory = directories[i];
					String directoryUrl = prefix + "/files/" + directory + "/";

					page.open("tr");
					page.open("td");
					page.open("span",".file");
					page.link(directoryUrl, directory);
					page.close("span");
					page.close("td");
				}
				page.close("table");

				page.close("div");
			}

			page.open("div", "footer");
			page.text(String("Version ") + APPVERSION + " - ");
			page.link(SOURCELINK, "Source code", "", true);
			page.close("div");

			page.footer();
			return;
		}

		if(url == "/search" || url == "/search/")
		{
			String query;
			if(request.post.contains("query"))
			{
				query = request.post.get("query");
				query.trim();
			}

			Http::Response response(request,200);
			response.send();

			Html page(response.sock);
			page.header("Search");

			page.openForm(prefix + "/search", "post", "searchform");
			page.input("text","query",query);
			page.button("search","Search");
			page.closeForm();
			page.br();

			if(query.empty())
			{
				page.footer();
				return;
			}

			Store::Query squery;
			squery.setMatch(query);

			int count = 0;
			page.open("div",".box");
			page.open("table",".files");

			List<Store::Entry> list;
			if(mStore->queryList(squery, list))
			{

				for(List<Store::Entry>::iterator it = list.begin();
					it != list.end();
					++it)
				{
					Store::Entry &entry = *it;
					if(entry.url.empty()) continue;

					page.open("tr");
					page.open("td");
					page.text("(" + mName + ")");
					page.close("td");
					page.open("td",".filename");
					page.link("/" + mName + "/files" + entry.url, entry.name);
					page.close("td");
					page.open("td",".size");
					if(!entry.type) page.text("directory");
					else page.text(String::hrSize(entry.size));
					page.close("td");
					page.close("tr");

					++count;
				}
			}

			Desynchronize(this);
			Request trequest("search:"+query, false);	// no data
			Synchronize(&trequest);

			const unsigned timeout = Config::Get("request_timeout").toInt();
			trequest.submit();
			trequest.wait(timeout);

			if(trequest.isSuccessful())
			try {
				for(int i=0; i<trequest.responsesCount(); ++i)
				{
					Request::Response *tresponse = trequest.response(i);
					if(tresponse->error()) continue;

					// Check contact
					const AddressBook::Contact *contact = mAddressBook->getContact(tresponse->peering());
					if(!contact) continue;

					// Check info
					StringMap map = tresponse->parameters();
					if(!map.contains("type")) continue;
					if(!map.contains("path")) continue;
					if(!map.contains("hash")) map["hash"] = "";
					if(!map.contains("name")) map["name"] = map["path"].afterLast('/');

					page.open("tr");
					page.open("td");
					page.text("("); page.link(contact->urlPrefix()+"/", contact->name()); page.text(")");
					page.close("td");
					page.open("td",".filename");
					if(map.get("type") == "directory") page.link(contact->urlPrefix() + "/files" + map.get("path"), map.get("name"));
					else if(!map.get("hash").empty()) page.link("/" + map.get("hash"), map.get("name"));
					else page.link(contact->urlPrefix() + "/files" + map.get("path") + "?instance=" + tresponse->instance().urlEncode() + "&file=1", map.get("name"));
					page.close("td");
					page.open("td",".size");
					if(map.get("type") == "directory") page.text("directory");
					else if(map.contains("size")) page.text(String::hrSize(map.get("size")));
					page.close("td");
					page.close("tr");

					++count;
				}

			}
			catch(const Exception &e)
			{
				Log("User::http", String("Unable to list files: ") + e.what());
			}

			page.close("table");

			if(!count) page.text("No files found");

			page.close("div");
			page.footer();
			return;
		}

		url.ignore();
		String urlLeft = String("/") + url.cut('/');
		url = String("/") + url;

		if(url == "/myself")
		{
			AddressBook::Contact *self = mAddressBook->getSelf();
			if(!self)
			{
				Http::Response response(request, 303);
				response.headers["Location"] = prefix + "/files/";
				response.send();
				return;
			}

			request.url = String("/files") + urlLeft;
			String newPrefix = prefix + "/contacts/" + self->uniqueName();

			Desynchronize(this);
			self->http(newPrefix, request);
			return;
		}
	}
	catch(const Exception &e)
	{
		Log("User::http",e.what());
		throw 404;	// Httpd handles integer exceptions
	}

	throw 404;
}

void User::run(void)
{
	while(true)
	{
		wait(5*60*1000);
		if(!mAddressBook->isRunning()) mAddressBook->start();
		if(!mStore->isRunning()) mStore->start();
	}
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/surface_texture_transport_client_android.h"

#include <android/native_window_jni.h>

#include "base/bind.h"
#include "cc/layers/video_layer.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
#include "ui/gl/android/surface_texture_bridge.h"
#include "webkit/compositor_bindings/web_compositor_support_impl.h"

namespace content {

namespace {

static const uint32 kGLTextureExternalOES = 0x8D65;

class SurfaceRefAndroid : public GpuSurfaceTracker::SurfaceRef {
 public:
  SurfaceRefAndroid(
      const scoped_refptr<gfx::SurfaceTextureBridge>& surface,
      ANativeWindow* window)
      : surface_(surface),
        window_(window) {
    ANativeWindow_acquire(window_);
  }

 private:
  virtual ~SurfaceRefAndroid() {
    DCHECK(window_);
    ANativeWindow_release(window_);
  }

  scoped_refptr<gfx::SurfaceTextureBridge> surface_;
  ANativeWindow* window_;
};

} // anonymous namespace

SurfaceTextureTransportClient::SurfaceTextureTransportClient()
    : window_(NULL),
      texture_id_(0),
      surface_id_(0),
      weak_factory_(this) {
}

SurfaceTextureTransportClient::~SurfaceTextureTransportClient() {
}

scoped_refptr<cc::Layer> SurfaceTextureTransportClient::Initialize() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Use a SurfaceTexture to stream frames to the UI thread.
  video_layer_ = cc::VideoLayer::Create(this);

  surface_texture_ = new gfx::SurfaceTextureBridge(0);
  surface_texture_->SetFrameAvailableCallback(
    base::Bind(
        &SurfaceTextureTransportClient::OnSurfaceTextureFrameAvailable,
        weak_factory_.GetWeakPtr()));
  surface_texture_->DetachFromGLContext();
  return video_layer_.get();
}

gfx::GLSurfaceHandle
SurfaceTextureTransportClient::GetCompositingSurface(int surface_id) {
  DCHECK(surface_id);
  surface_id_ = surface_id;

  if (!window_) {
    window_ = surface_texture_->CreateSurface();

    GpuSurfaceTracker::Get()->SetNativeWidget(
        surface_id, window_, new SurfaceRefAndroid(surface_texture_, window_));
    // SurfaceRefAndroid took ownership (and an extra ref to) window_.
    ANativeWindow_release(window_);
  }

  return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_DIRECT);
}

void SurfaceTextureTransportClient::SetSize(const gfx::Size& size) {
  if (size.width() > 0 && size.height() > 0) {
    surface_texture_->SetDefaultBufferSize(size.width(), size.height());
  }
  video_layer_->SetBounds(size);
  video_frame_ = NULL;
}

scoped_refptr<media::VideoFrame> SurfaceTextureTransportClient::
    GetCurrentFrame() {
  if (!texture_id_) {
    WebKit::WebGraphicsContext3D* context =
        ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
    context->makeContextCurrent();
    texture_id_ = context->createTexture();
    surface_texture_->AttachToGLContext(texture_id_);
  }
  if (!video_frame_) {
    const gfx::Size size = video_layer_->bounds();
    video_frame_ = media::VideoFrame::WrapNativeTexture(
        texture_id_, kGLTextureExternalOES,
        size,
        gfx::Rect(gfx::Point(), size),
        size,
        base::TimeDelta(),
        media::VideoFrame::ReadPixelsCB(),
        base::Closure());
  }
  surface_texture_->UpdateTexImage();

  return video_frame_;
}

void SurfaceTextureTransportClient::PutCurrentFrame(
    const scoped_refptr<media::VideoFrame>& frame) {
}

void SurfaceTextureTransportClient::OnSurfaceTextureFrameAvailable() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  video_layer_->SetNeedsDisplay();
}

} // namespace content
<commit_msg>Remove unused webkit/compositor_bindings include<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/surface_texture_transport_client_android.h"

#include <android/native_window_jni.h>

#include "base/bind.h"
#include "cc/layers/video_layer.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
#include "ui/gl/android/surface_texture_bridge.h"

namespace content {

namespace {

static const uint32 kGLTextureExternalOES = 0x8D65;

class SurfaceRefAndroid : public GpuSurfaceTracker::SurfaceRef {
 public:
  SurfaceRefAndroid(
      const scoped_refptr<gfx::SurfaceTextureBridge>& surface,
      ANativeWindow* window)
      : surface_(surface),
        window_(window) {
    ANativeWindow_acquire(window_);
  }

 private:
  virtual ~SurfaceRefAndroid() {
    DCHECK(window_);
    ANativeWindow_release(window_);
  }

  scoped_refptr<gfx::SurfaceTextureBridge> surface_;
  ANativeWindow* window_;
};

} // anonymous namespace

SurfaceTextureTransportClient::SurfaceTextureTransportClient()
    : window_(NULL),
      texture_id_(0),
      surface_id_(0),
      weak_factory_(this) {
}

SurfaceTextureTransportClient::~SurfaceTextureTransportClient() {
}

scoped_refptr<cc::Layer> SurfaceTextureTransportClient::Initialize() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Use a SurfaceTexture to stream frames to the UI thread.
  video_layer_ = cc::VideoLayer::Create(this);

  surface_texture_ = new gfx::SurfaceTextureBridge(0);
  surface_texture_->SetFrameAvailableCallback(
    base::Bind(
        &SurfaceTextureTransportClient::OnSurfaceTextureFrameAvailable,
        weak_factory_.GetWeakPtr()));
  surface_texture_->DetachFromGLContext();
  return video_layer_.get();
}

gfx::GLSurfaceHandle
SurfaceTextureTransportClient::GetCompositingSurface(int surface_id) {
  DCHECK(surface_id);
  surface_id_ = surface_id;

  if (!window_) {
    window_ = surface_texture_->CreateSurface();

    GpuSurfaceTracker::Get()->SetNativeWidget(
        surface_id, window_, new SurfaceRefAndroid(surface_texture_, window_));
    // SurfaceRefAndroid took ownership (and an extra ref to) window_.
    ANativeWindow_release(window_);
  }

  return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_DIRECT);
}

void SurfaceTextureTransportClient::SetSize(const gfx::Size& size) {
  if (size.width() > 0 && size.height() > 0) {
    surface_texture_->SetDefaultBufferSize(size.width(), size.height());
  }
  video_layer_->SetBounds(size);
  video_frame_ = NULL;
}

scoped_refptr<media::VideoFrame> SurfaceTextureTransportClient::
    GetCurrentFrame() {
  if (!texture_id_) {
    WebKit::WebGraphicsContext3D* context =
        ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
    context->makeContextCurrent();
    texture_id_ = context->createTexture();
    surface_texture_->AttachToGLContext(texture_id_);
  }
  if (!video_frame_) {
    const gfx::Size size = video_layer_->bounds();
    video_frame_ = media::VideoFrame::WrapNativeTexture(
        texture_id_, kGLTextureExternalOES,
        size,
        gfx::Rect(gfx::Point(), size),
        size,
        base::TimeDelta(),
        media::VideoFrame::ReadPixelsCB(),
        base::Closure());
  }
  surface_texture_->UpdateTexImage();

  return video_frame_;
}

void SurfaceTextureTransportClient::PutCurrentFrame(
    const scoped_refptr<media::VideoFrame>& frame) {
}

void SurfaceTextureTransportClient::OnSurfaceTextureFrameAvailable() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  video_layer_->SetNeedsDisplay();
}

} // namespace content
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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.

#ifndef SERVICE_STATISTICS_HPP
#define SERVICE_STATISTICS_HPP

/**
 *
 */
struct Statistics {

  /**
   *
   */
  Statistics& bump_connection_count() noexcept
  { ++number_of_connections_; return *this; }

  /**
   *
   */
  Statistics& bump_data_received(const size_t n) noexcept
  { data_received_ += n; return *this; }

  /**
   *
   */
  Statistics& bump_data_sent(const size_t n) noexcept
  { data_sent_ += n; return *this; }

  /**
   *
   */
  Statistics& bump_request_received() noexcept
  { ++request_received_; return *this; }

  /**
   *
   */
  Statistics& bump_response_sent() noexcept
  { ++response_sent_; return *this; }

  /**
   *
   */
  Statistics& set_active_clients(const size_t n) noexcept
  { active_clients_ = n; return *this; }

  /**
   *
   */
  Statistics& set_memory_usage(const size_t n) noexcept
  { memory_usage_ = n; return *this; }

  /**
   *
   */
  template<typename Writer>
  void serialize(Writer& writer) const;

private:
  uint64_t data_received_         {0U};
  uint64_t data_sent_             {0U};

  uint64_t request_received_      {0U};
  uint64_t response_sent_         {0U};

  uint64_t number_of_connections_ {0U};

  uint64_t active_clients_        {0U};
  uint64_t memory_usage_          {0U};
}; //< struct Statistics

/**--v----------- Implementation Details -----------v--**/

template<typename Writer>
void Statistics::serialize(Writer& writer) const {
  writer.StartObject();

  writer.Key("DATA_RECV");
  writer.Uint64(data_received_);

  writer.Key("DATA_SENT");
  writer.Uint64(data_sent_);

  writer.Key("REQ_RECV");
  writer.Uint64(request_received_);

  writer.Key("RES_SENT");
  writer.Uint64(response_sent_);

  writer.Key("NO_CONN");
  writer.Uint64(number_of_connections_);

  writer.Key("ACTIVE_CONN");
  writer.Uint(active_clients_);

  writer.Key("MEM_USAGE");
  writer.Uint(memory_usage_);

  writer.EndObject();
}

/**--^----------- Implementation Details -----------^--**/

#endif //< SERVICE_STATISTICS_HPP
<commit_msg>Removed deprecated component<commit_after><|endoftext|>


<commit_before>#include "../../base/stdinc.h"
#include "../../base/logger.h"
#include "../../base/dctcp.h"
#include "../../base/dcutils.hpp"
#include "../../base/dcseqnum.hpp"
#include "../../base/msg_proto.hpp"
#include "../../base/msg_buffer.hpp"
/////////////////////////////////////////////////
#include "../share/dcrpc.pb.h"
#include "../share/dcrpc.h"
#include "dccrpc.h"
NS_BEGIN(dcrpc)
//using namespace google::protobuf;
using dcrpc::RpcValues;
typedef dcsutil::sequence_number_t<2, 30>    rpcall_transaction_sn;
typedef msgproto_t<RpcMsg>  dcrpc_msg_t;

////////////////////////////////////////////////////////////////////
struct  RpcClientImpl {
    dctcp_t * cli{ nullptr };
    int fd{ -1 };
    std::unordered_map<uint64_t, RpcClient::RpcCallNotify>  rpcall_cbs;
    std::unordered_map<string, RpcClient::RpcCallNotify>    notify_cbs;
    //todo timer queue
    msg_buffer_t send_msg_buff;
    std::vector<std::string> rpc_server_addrs;
    int  connect_server_retry{ 3 };
    std::queue<dcrpc_msg_t>  sending_queue;
    int  queue_max_size{ 0 };
public:
    const string select_server(){
        return rpc_server_addrs[rand() % rpc_server_addrs.size()];
    }
    RpcClientImpl(){
        send_msg_buff.create(dcrpc::MAX_RPC_MSG_BUFF_SIZE);
    }

    ~RpcClientImpl(){
        send_msg_buff.destroy();
    }

public:
    uint64_t append_callback(RpcClient::RpcCallNotify cb, int expired = 0){
        uint64_t sn = rpcall_transaction_sn::next();
        rpcall_cbs[sn] = cb;
		UNUSED(expired);
        return sn;
    }
    RpcClient::RpcCallNotify remove_callback(uint64_t transid){
        auto it = rpcall_cbs.find(transid);
        if (it == rpcall_cbs.end()){
            return nullptr;
        }
        auto ret = it->second;
        rpcall_cbs.erase(it);
        return ret;
    }
};

RpcClient::RpcClient(){
    impl = nullptr;
}
RpcClient::~RpcClient(){
    destroy();
}

static inline void
_distpach_remote_service_smsg(RpcClientImpl * impl, const char * buff, int ibuff){
    dcrpc_msg_t rpc_msg;
    if (!rpc_msg.Unpack(msg_buffer_t(buff, ibuff))){
        GLOG_ERR("unpack rpc msg error ! buff:%64s len:%d", buff, ibuff);
        return;
    }
    GLOG_TRA("recv [%s]", rpc_msg.Debug());
    uint64_t transid = rpc_msg.cookie().transaction();
    if (transid > 0){ //call back
        auto cb = impl->remove_callback(transid);
        if (cb == nullptr){
            GLOG_ERR("not found callback trnasid:%lu", transid);
            return;
        }
        if (rpc_msg.status() == RpcMsg::StatusCode::RpcMsg_StatusCode_RPC_STATUS_SUCCESS){
            auto & response = rpc_msg.response();
            const RpcValuesImpl *data = (const RpcValuesImpl *)&response.result();
            RpcValues vals(*data);
            cb(response.status(), vals);
            if (response.status() != 0){
                GLOG_WAR("service :%s return error:%d transid:%lu", rpc_msg.path().c_str()
                    , response.status(), transid);
            }
        }
        else {
            GLOG_ERR("rpc call error svc:%s error:%d", rpc_msg.path().c_str(), rpc_msg.status());
        }
    }
    else { //notify
        auto it = impl->notify_cbs.find(rpc_msg.path());
        if (it == impl->notify_cbs.end()){
            GLOG_IFO("notify msg not register :%s", rpc_msg.path().c_str());
        }
        else {
            const RpcValuesImpl *data = (const RpcValuesImpl *)&rpc_msg.notify().result();
            RpcValues vals(*data);
            it->second(0, vals);
        }
    }
}
int RpcClient::init(const std::string & svraddrs, int queue_size){
    if (impl){
        return -1;
    }
    impl = new RpcClientImpl;
    dctcp_config_t dctcp;
    impl->cli = dctcp_create(dctcp);
    if (!impl->cli){
        return -2;
    }
    impl->queue_max_size = queue_size;
    dctcp_event_cb(impl->cli, [](dctcp_t* dc, const dctcp_event_t & ev, void * ud)->int {
        RpcClientImpl * impl = (RpcClientImpl *)ud;
        switch (ev.type){
        case DCTCP_CONNECTED:
            impl->fd = ev.fd;
            break;
        case DCTCP_CLOSED:
            impl->fd = -1;
            GLOG_TRA("connection closed [reason:%d] ! reconnecting ...", ev.reason);
            dctcp_connect(dc, impl->select_server(), impl->connect_server_retry);
            break;
        case DCTCP_READ:
            _distpach_remote_service_smsg(impl, ev.msg->buff, ev.msg->buff_sz);
            break;
        default:
            return -1;
        }
		return 0;
    }, impl);
    dcsutil::strsplit(svraddrs, ",", impl->rpc_server_addrs);
    return dctcp_connect(impl->cli, impl->select_server(), impl->connect_server_retry);
}
static inline int _send_directly(RpcClientImpl * impl, const dcrpc_msg_t & tosend_msg){
	if (!tosend_msg.Pack(impl->send_msg_buff)){
		GLOG_ERR("pack msg error when send msg to svc:%s", tosend_msg.path().c_str());
		return -1;
	}
	int ret = dctcp_send(impl->cli, impl->fd, dctcp_msg_t(impl->send_msg_buff.buffer, impl->send_msg_buff.valid_size));
	GLOG_TRA("send [%d] [%s] [%s]", ret, tosend_msg.path().c_str(), tosend_msg.Debug());
	if (ret){
		GLOG_SER("tcp send error = %d when send msg to svc:%s buff len:%d",
			ret, tosend_msg.path().c_str(), impl->send_msg_buff.valid_size);
		return -2;
	}
	return 0;
}
static inline int _check_sending_queue(RpcClientImpl * impl){
	int ret = 0;
	while (impl->fd != -1 && !impl->sending_queue.empty()){
		const dcrpc_msg_t & tosend_msg = impl->sending_queue.front();
		ret = _send_directly(impl, tosend_msg);
		if (ret == 0 || ret == -1){ //directly or pack error
			impl->sending_queue.pop();
		}
		else {
			return ret;
		}
	}
	return ret;
}
int RpcClient::update(){
	_check_sending_queue(impl);
	int nproc = dctcp_poll(impl->cli, 1000);
	_check_sending_queue(impl);
	return nproc;
}
int RpcClient::destroy(){
    if (impl){
        if (impl->cli){
            dctcp_destroy(impl->cli);
            impl->cli = nullptr;
        }
        delete impl;
        impl = nullptr;
    }
	return 0;
}
bool RpcClient::ready() const {
    return impl->fd != -1;
}

int RpcClient::notify(const string & svc, RpcCallNotify cb){
    impl->notify_cbs[svc]=cb;
    return 0;
}
static inline int _send_msg(RpcClientImpl * impl, const dcrpc_msg_t & rpc_msg){
    if (impl->fd == -1 && (int)impl->sending_queue.size() >= impl->queue_max_size){
        GLOG_ERR("connection not ready error send msg to svc:%s", rpc_msg.path().c_str());
        return -1;
    }
	_check_sending_queue(impl); //checking queue
	if (!impl->sending_queue.empty() || impl->fd == -1){ //not send all
        impl->sending_queue.push(rpc_msg);
    }
	else { //send all
		int ret = _send_directly(impl, rpc_msg);
		if (ret && ret != -1){ //ret == -2 , send error
			impl->sending_queue.push(rpc_msg);
		}
    }
	return 0;
}
int RpcClient::push(const std::string & svc, const dcrpc::RpcValues & args){
    dcrpc_msg_t rpc_msg;
    rpc_msg.set_path(svc);
    auto margs = (decltype(rpc_msg.mutable_request()->mutable_args()))const_cast<RpcValues&>(args).data();
    rpc_msg.mutable_request()->mutable_args()->CopyFrom(*margs);
    return _send_msg(impl, rpc_msg);
}
int RpcClient::call(const string & svc, const RpcValues & args, RpcCallNotify result_cb){
    dcrpc_msg_t rpc_msg;
    RpcMsg::Cookie & ck = *rpc_msg.mutable_cookie();
    rpc_msg.set_path(svc);
    auto margs = (decltype(rpc_msg.mutable_request()->mutable_args()))const_cast<RpcValues&>(args).data();
    rpc_msg.mutable_request()->mutable_args()->CopyFrom(*margs);
    ck.set_transaction(impl->append_callback(result_cb));
    return _send_msg(impl, rpc_msg);
}
//int call(const std::string & svc, const char * buff, int ibuff, callback_result_t result_cb);
//int call(const std::string & svc, const ::google::protobuf::Message & msg, callback_result_t result_cb);

NS_END()<commit_msg>rpc check timer<commit_after>#include "../../base/stdinc.h"
#include "../../base/logger.h"
#include "../../base/dctcp.h"
#include "../../base/dcutils.hpp"
#include "../../base/dcseqnum.hpp"
#include "../../base/msg_proto.hpp"
#include "../../base/msg_buffer.hpp"
/////////////////////////////////////////////////
#include "../share/dcrpc.pb.h"
#include "../share/dcrpc.h"
#include "dccrpc.h"
NS_BEGIN(dcrpc)
//using namespace google::protobuf;
using dcrpc::RpcValues;
typedef dcsutil::sequence_number_t<2, 30>    rpcall_transaction_sn;
typedef msgproto_t<RpcMsg>  dcrpc_msg_t;

////////////////////////////////////////////////////////////////////
struct  RpcClientImpl {
    dctcp_t * cli{ nullptr };
    int fd{ -1 };
    std::unordered_map<uint64_t, RpcClient::RpcCallNotify>  rpcall_cbs;
    std::unordered_map<string, RpcClient::RpcCallNotify>    notify_cbs;
    //todo timer queue
    msg_buffer_t send_msg_buff;
    std::vector<std::string> rpc_server_addrs;
    int  connect_server_retry{ 3 };
    std::queue<dcrpc_msg_t>  sending_queue;
    int  queue_max_size{ 0 };
public:
    const string select_server(){
        return rpc_server_addrs[rand() % rpc_server_addrs.size()];
    }
    RpcClientImpl(){
        send_msg_buff.create(dcrpc::MAX_RPC_MSG_BUFF_SIZE);
    }

    ~RpcClientImpl(){
        send_msg_buff.destroy();
    }

public:
    uint64_t append_callback(RpcClient::RpcCallNotify cb, int expired = 0){
        uint64_t sn = rpcall_transaction_sn::next();
        rpcall_cbs[sn] = cb;
		UNUSED(expired);
        return sn;
    }
    RpcClient::RpcCallNotify remove_callback(uint64_t transid){
        auto it = rpcall_cbs.find(transid);
        if (it == rpcall_cbs.end()){
            return nullptr;
        }
        auto ret = it->second;
        rpcall_cbs.erase(it);
        return ret;
    }
};

RpcClient::RpcClient(){
    impl = nullptr;
}
RpcClient::~RpcClient(){
    destroy();
}

static inline void
_distpach_remote_service_smsg(RpcClientImpl * impl, const char * buff, int ibuff){
    dcrpc_msg_t rpc_msg;
    if (!rpc_msg.Unpack(msg_buffer_t(buff, ibuff))){
        GLOG_ERR("unpack rpc msg error ! buff:%64s len:%d", buff, ibuff);
        return;
    }
    GLOG_TRA("recv [%s]", rpc_msg.Debug());
    uint64_t transid = rpc_msg.cookie().transaction();
    if (transid > 0){ //call back
        auto cb = impl->remove_callback(transid);
        if (cb == nullptr){
            GLOG_ERR("not found callback trnasid:%lu", transid);
            return;
        }
        if (rpc_msg.status() == RpcMsg::StatusCode::RpcMsg_StatusCode_RPC_STATUS_SUCCESS){
            auto & response = rpc_msg.response();
            const RpcValuesImpl *data = (const RpcValuesImpl *)&response.result();
            RpcValues vals(*data);
            cb(response.status(), vals);
            if (response.status() != 0){
                GLOG_WAR("service :%s return error:%d transid:%lu", rpc_msg.path().c_str()
                    , response.status(), transid);
            }
        }
        else {
            GLOG_ERR("rpc call error svc:%s error:%d", rpc_msg.path().c_str(), rpc_msg.status());
        }
    }
    else { //notify
        auto it = impl->notify_cbs.find(rpc_msg.path());
        if (it == impl->notify_cbs.end()){
            GLOG_IFO("notify msg not register :%s", rpc_msg.path().c_str());
        }
        else {
            const RpcValuesImpl *data = (const RpcValuesImpl *)&rpc_msg.notify().result();
            RpcValues vals(*data);
            it->second(0, vals);
        }
    }
}
int RpcClient::init(const std::string & svraddrs, int queue_size){
    if (impl){
        return -1;
    }
    impl = new RpcClientImpl;
    dctcp_config_t dctcp;
    impl->cli = dctcp_create(dctcp);
    if (!impl->cli){
        return -2;
    }
    impl->queue_max_size = queue_size;
    dctcp_event_cb(impl->cli, [](dctcp_t* dc, const dctcp_event_t & ev, void * ud)->int {
        RpcClientImpl * impl = (RpcClientImpl *)ud;
        switch (ev.type){
        case DCTCP_CONNECTED:
            impl->fd = ev.fd;
            break;
        case DCTCP_CLOSED:
            impl->fd = -1;
            GLOG_TRA("connection closed [reason:%d] ! reconnecting ...", ev.reason);
            dctcp_connect(dc, impl->select_server(), impl->connect_server_retry);
            break;
        case DCTCP_READ:
            _distpach_remote_service_smsg(impl, ev.msg->buff, ev.msg->buff_sz);
            break;
        default:
            return -1;
        }
		return 0;
    }, impl);
    dcsutil::strsplit(svraddrs, ",", impl->rpc_server_addrs);
    return dctcp_connect(impl->cli, impl->select_server(), impl->connect_server_retry);
}
static inline int _send_directly(RpcClientImpl * impl, const dcrpc_msg_t & tosend_msg){
	if (!tosend_msg.Pack(impl->send_msg_buff)){
		GLOG_ERR("pack msg error when send msg to svc:%s", tosend_msg.path().c_str());
		return -1;
	}
	int ret = dctcp_send(impl->cli, impl->fd, dctcp_msg_t(impl->send_msg_buff.buffer, impl->send_msg_buff.valid_size));
	GLOG_TRA("send [%d] [%s] [%s]", ret, tosend_msg.path().c_str(), tosend_msg.Debug());
	if (ret){
		GLOG_SER("tcp send error = %d when send msg to svc:%s buff len:%d",
			ret, tosend_msg.path().c_str(), impl->send_msg_buff.valid_size);
		return -2;
	}
	return 0;
}
static inline int _check_sending_queue(RpcClientImpl * impl){
	int ret = 0;
	while (impl->fd != -1 && !impl->sending_queue.empty()){
		const dcrpc_msg_t & tosend_msg = impl->sending_queue.front();
		ret = _send_directly(impl, tosend_msg);
		if (ret == 0 || ret == -1){ //directly or pack error
			impl->sending_queue.pop();
		}
		else {
			return ret;
		}
	}
	return ret;
}
int RpcClient::update(){
	_check_sending_queue(impl);
	int nproc = dctcp_poll(impl->cli, 100);
	_check_sending_queue(impl);
	return nproc;
}
int RpcClient::destroy(){
    if (impl){
        if (impl->cli){
            dctcp_destroy(impl->cli);
            impl->cli = nullptr;
        }
        delete impl;
        impl = nullptr;
    }
	return 0;
}
bool RpcClient::ready() const {
    return impl->fd != -1;
}

int RpcClient::notify(const string & svc, RpcCallNotify cb){
    impl->notify_cbs[svc]=cb;
    return 0;
}
static inline int _send_msg(RpcClientImpl * impl, const dcrpc_msg_t & rpc_msg){
    if (impl->fd == -1 && (int)impl->sending_queue.size() >= impl->queue_max_size){
        GLOG_ERR("connection not ready error send msg to svc:%s", rpc_msg.path().c_str());
        return -1;
    }
	_check_sending_queue(impl); //checking queue
	if (!impl->sending_queue.empty() || impl->fd == -1){ //not send all
        impl->sending_queue.push(rpc_msg);
    }
	else { //send all
		int ret = _send_directly(impl, rpc_msg);
		if (ret && ret != -1){ //ret == -2 , send error
			impl->sending_queue.push(rpc_msg);
		}
    }
	return 0;
}
int RpcClient::push(const std::string & svc, const dcrpc::RpcValues & args){
    dcrpc_msg_t rpc_msg;
    rpc_msg.set_path(svc);
    auto margs = (decltype(rpc_msg.mutable_request()->mutable_args()))const_cast<RpcValues&>(args).data();
    rpc_msg.mutable_request()->mutable_args()->CopyFrom(*margs);
    return _send_msg(impl, rpc_msg);
}
int RpcClient::call(const string & svc, const RpcValues & args, RpcCallNotify result_cb){
    dcrpc_msg_t rpc_msg;
    RpcMsg::Cookie & ck = *rpc_msg.mutable_cookie();
    rpc_msg.set_path(svc);
    auto margs = (decltype(rpc_msg.mutable_request()->mutable_args()))const_cast<RpcValues&>(args).data();
    rpc_msg.mutable_request()->mutable_args()->CopyFrom(*margs);
    ck.set_transaction(impl->append_callback(result_cb));
    return _send_msg(impl, rpc_msg);
}
//int call(const std::string & svc, const char * buff, int ibuff, callback_result_t result_cb);
//int call(const std::string & svc, const ::google::protobuf::Message & msg, callback_result_t result_cb);

NS_END()<|endoftext|>


<commit_before>#ifndef ITER_ENUMERATE_H_
#define ITER_ENUMERATE_H_

#include "iterbase.hpp"

#include <utility>
#include <iterator>
#include <functional>
#include <initializer_list>
#include <type_traits>

namespace iter {

    //Forward declarations of Enumerable and enumerate
    template <typename Container>
    class Enumerable;

    template <typename Container>
    Enumerable<Container> enumerate(Container&&);

    template <typename T>
    Enumerable<std::initializer_list<T>> enumerate(std::initializer_list<T>);

    template <typename Container>
    class Enumerable {
        private:
            Container container;

            // The only thing allowed to directly instantiate an Enumerable is
            // the enumerate function
            friend Enumerable enumerate<Container>(Container&&);
            template <typename T>
            friend Enumerable<std::initializer_list<T>> enumerate(
                    std::initializer_list<T>);

            // for IterYield
            using BasePair = std::pair<std::size_t, iterator_deref<Container>>;

            // Value constructor for use only in the enumerate function
            Enumerable(Container&& in_container)
                : container(std::forward<Container>(in_container))
            { }

        public:
            // "yielded" by the Enumerable::Iterator.  Has a .index, and a
            // .element referencing the value yielded by the subiterator
            class IterYield : public BasePair {
                public:
                    using BasePair::BasePair;
                    decltype(BasePair::first)& index = BasePair::first;
                    decltype(BasePair::second)& element = BasePair::second;
            };

            //  Holds an iterator of the contained type and a size_t for the
            //  index.  Each call to ++ increments both of these data members.
            //  Each dereference returns an IterYield.
            class Iterator :
                public std::iterator<std::input_iterator_tag, IterYield>
            {
                private:
                    iterator_type<Container> sub_iter;
                    std::size_t index;
                public:
                    Iterator(iterator_type<Container>&& si)
                        : sub_iter{std::move(si)},
                        index{0}
                    { }

                    IterYield operator*() {
                        return {this->index, *this->sub_iter};
                    }

                    ArrowProxy<IterYield> operator->() {
                        return {**this};
                    }

                    Iterator& operator++() {
                        ++this->sub_iter;
                        ++this->index;
                        return *this;
                    }

                    Iterator operator++(int) {
                        auto ret = *this;
                        ++*this;
                        return ret;
                    }

                    bool operator!=(const Iterator& other) const {
                        return this->sub_iter != other.sub_iter;
                    }

                    bool operator==(const Iterator& other) const {
                        return !(*this != other);
                    }
            };

            Iterator begin() {
                return {std::begin(this->container)};
            }

            Iterator end() {
                return {std::end(this->container)};
            }

    };

    template <typename Container>
    Enumerable<Container> enumerate(Container&& container) {
        return {std::forward<Container>(container)};
    }

    template <typename T>
    Enumerable<std::initializer_list<T>> enumerate(
            std::initializer_list<T> il)
    {
        return {std::move(il)};
    }
}

#endif
<commit_msg>uses first_type instead of decltype(first)<commit_after>#ifndef ITER_ENUMERATE_H_
#define ITER_ENUMERATE_H_

#include "iterbase.hpp"

#include <utility>
#include <iterator>
#include <functional>
#include <initializer_list>
#include <type_traits>

namespace iter {

    //Forward declarations of Enumerable and enumerate
    template <typename Container>
    class Enumerable;

    template <typename Container>
    Enumerable<Container> enumerate(Container&&);

    template <typename T>
    Enumerable<std::initializer_list<T>> enumerate(std::initializer_list<T>);

    template <typename Container>
    class Enumerable {
        private:
            Container container;

            // The only thing allowed to directly instantiate an Enumerable is
            // the enumerate function
            friend Enumerable enumerate<Container>(Container&&);
            template <typename T>
            friend Enumerable<std::initializer_list<T>> enumerate(
                    std::initializer_list<T>);

            // for IterYield
            using BasePair = std::pair<std::size_t, iterator_deref<Container>>;

            // Value constructor for use only in the enumerate function
            Enumerable(Container&& in_container)
                : container(std::forward<Container>(in_container))
            { }

        public:
            // "yielded" by the Enumerable::Iterator.  Has a .index, and a
            // .element referencing the value yielded by the subiterator
            class IterYield : public BasePair {
                public:
                    using BasePair::BasePair;
                    typename BasePair::first_type& index = BasePair::first;
                    typename BasePair::second_type& element = BasePair::second;
            };

            //  Holds an iterator of the contained type and a size_t for the
            //  index.  Each call to ++ increments both of these data members.
            //  Each dereference returns an IterYield.
            class Iterator :
                public std::iterator<std::input_iterator_tag, IterYield>
            {
                private:
                    iterator_type<Container> sub_iter;
                    std::size_t index;
                public:
                    Iterator(iterator_type<Container>&& si)
                        : sub_iter{std::move(si)},
                        index{0}
                    { }

                    IterYield operator*() {
                        return {this->index, *this->sub_iter};
                    }

                    ArrowProxy<IterYield> operator->() {
                        return {**this};
                    }

                    Iterator& operator++() {
                        ++this->sub_iter;
                        ++this->index;
                        return *this;
                    }

                    Iterator operator++(int) {
                        auto ret = *this;
                        ++*this;
                        return ret;
                    }

                    bool operator!=(const Iterator& other) const {
                        return this->sub_iter != other.sub_iter;
                    }

                    bool operator==(const Iterator& other) const {
                        return !(*this != other);
                    }
            };

            Iterator begin() {
                return {std::begin(this->container)};
            }

            Iterator end() {
                return {std::end(this->container)};
            }

    };

    template <typename Container>
    Enumerable<Container> enumerate(Container&& container) {
        return {std::forward<Container>(container)};
    }

    template <typename T>
    Enumerable<std::initializer_list<T>> enumerate(
            std::initializer_list<T> il)
    {
        return {std::move(il)};
    }
}

#endif
<|endoftext|>


<commit_before>#include "bandit/bandit.h"
#include "cstr.h"

using namespace bandit;

go_bandit([](){
    describe("Compile Time String", [](){
        it("counts msg length", [&]()
        {
            static_assert(
                cstr("Hello world.").length() == 12,
                "msg's length should be 8.");
        });
        it("counts symbol count", [&]()
        {
            static_assert(
                char_count(cstr("Hello %"), '%') == 1 ,
                "msg should have one #.");
        });

        describe("args seqeunce", [&](){
            it("has no args", [&]()
            {
                static_assert(arg_seq(cstr("Hello world."), '%') == 0,
                "should return (0000) to represent no args");
            });
            it("has 1 str arg", [&]()
            {
                static_assert(arg_seq(cstr("Hello %s."), '%') == 1,
                "should return (0001) to represent 1 arg");
            });
            it("has 2 str arg", [&]()
            {
                static_assert(arg_seq(cstr("%s %s."), '%') == 2+1,
                "should return (0011) to represent 2 args");
            });
            it("has 3 str args", [&]()
            {
                static_assert(arg_seq(cstr("Hey %s %s, I'm %s."), '%') == 4+2+1,
                "should return (0111) to represent 3 args");
            });
            it("has 4 str args", [&]()
            {
                static_assert(arg_seq(cstr("Hey %s %s, I'm %s %s."), '%') == 8+4+2+1,
                "should return (1111) to represent 4 args");
            });
        });

        describe("str args sequence", []()
        {
            it("has 0 str", []()
            {
                static_assert(str_arg_seq(cstr("%d %d %d %d")) == 0,
                    "should return 0000 to represent 0 str");
            });
            it("has 1 str", []()
            {
                static_assert(str_arg_seq(cstr("%s %d %d %d")) == 1,
                    "should return 0001 to represent 1 str at 1th arg");

                static_assert(str_arg_seq(cstr("%d %s %d %d")) == 2,
                    "should return 0010 to represent 1 str at 2th arg");

                static_assert(str_arg_seq(cstr("%d %d %s %d")) == 4,
                    "should return 0100 to represent 1 str at 3th arg");

                static_assert(str_arg_seq(cstr("%d %d %d %s")) == 8,
                    "should return 1000 to represent 1 str at 4th arg");

                static_assert(str_arg_seq(cstr("%s %s %d %d")) == 1 + 2, "");

                static_assert(str_arg_seq(cstr("%s %d %s %d")) == 1 + 4, "");

                static_assert(str_arg_seq(cstr("%s %d %d %s")) == 1 + 8, "");

                static_assert(str_arg_seq(cstr("%d %s %s %d")) == 2 + 4, "");

                static_assert(str_arg_seq(cstr("%d %s %d %s")) == 2 + 8, "");

                static_assert(str_arg_seq(cstr("%s %d %s %d")) == 1 + 4, "");

                static_assert(str_arg_seq(cstr("%s %s %s %d")) == 1 + 2 + 4, "");

                static_assert(str_arg_seq(cstr("%s %s %s %s")) == 1 + 2 + 4 + 8, "");
            });
        });
    });
    describe("compile time bit helper", []()
    {
        it("nth_bit", []()
        {
            static_assert(nth_bit(0) == 0,  "0th should be 00000000");
            static_assert(nth_bit(1) == 1,  "1th should be 00000001");
            static_assert(nth_bit(2) == 2,  "2th should be 00000010");
            static_assert(nth_bit(3) == 4,  "3th should be 00000100");
            static_assert(nth_bit(4) == 8,  "4th should be 00001000");
            static_assert(nth_bit(5) == 16, "5th should be 00010000");
            static_assert(nth_bit(6) == 32, "6th should be 00100000");
            static_assert(nth_bit(7) == 64, "7th should be 01000000");
            static_assert(nth_bit(8) == 128,"8th should be 10000000");

            static_assert(nth_bit(9) == 256,     "9th should be 00000001 00000000");
            static_assert(nth_bit(10) == 512,   "10th should be 00000010 00000000");
            static_assert(nth_bit(11) == 1024,  "11th should be 00000100 00000000");
            static_assert(nth_bit(12) == 2048,  "12th should be 00001000 00000000");
            static_assert(nth_bit(13) == 4096,  "13th should be 00010000 00000000");
            static_assert(nth_bit(14) == 8192,  "14th should be 00100000 00000000");
            static_assert(nth_bit(15) == 16384, "15th should be 01000000 00000000");
            static_assert(nth_bit(16) == 32768, "16th should be 10000000 00000000");
        });
    });
});
<commit_msg>refactor the test<commit_after>#include "bandit/bandit.h"
#include "cstr.h"

using namespace bandit;

go_bandit([](){
    describe("Compile Time String", [](){
        it("counts msg length", [&]()
        {
            static_assert(
                cstr("Hello world.").length() == 12,
                "msg's length should be 8.");
        });
        it("counts symbol count", [&]()
        {
            static_assert(
                char_count(cstr("Hello %"), '%') == 1 ,
                "msg should have one #.");
        });

        describe("args seqeunce", [&](){
            it("has no args", [&]()
            {
                static_assert(arg_seq(cstr("Hello world."), '%') == 0,
                "should return (0000) to represent no args");
            });
            it("has 1 str arg", [&]()
            {
                static_assert(arg_seq(cstr("Hello %s."), '%') == 1,
                "should return (0001) to represent 1 arg");
            });
            it("has 2 str arg", [&]()
            {
                static_assert(arg_seq(cstr("%s %s."), '%') == 2+1,
                "should return (0011) to represent 2 args");
            });
            it("has 3 str args", [&]()
            {
                static_assert(arg_seq(cstr("Hey %s %s, I'm %s."), '%') == 4+2+1,
                "should return (0111) to represent 3 args");
            });
            it("has 4 str args", [&]()
            {
                static_assert(arg_seq(cstr("Hey %s %s, I'm %s %s."), '%') == 8+4+2+1,
                "should return (1111) to represent 4 args");
            });
        });

        describe("str args sequence", []()
        {
            static_assert(str_arg_seq(cstr("%s %d %d %d")) == 1, "");
            static_assert(str_arg_seq(cstr("%d %s %d %d")) == 2, "");
            static_assert(str_arg_seq(cstr("%d %d %s %d")) == 4, "");
            static_assert(str_arg_seq(cstr("%d %d %d %s")) == 8, "");
            static_assert(str_arg_seq(cstr("%s %s %d %d")) == 1 + 2, "");
            static_assert(str_arg_seq(cstr("%s %d %s %d")) == 1 + 4, "");
            static_assert(str_arg_seq(cstr("%s %d %d %s")) == 1 + 8, "");
            static_assert(str_arg_seq(cstr("%d %s %s %d")) == 2 + 4, "");
            static_assert(str_arg_seq(cstr("%d %s %d %s")) == 2 + 8, "");
            static_assert(str_arg_seq(cstr("%s %d %s %d")) == 1 + 4, "");
            static_assert(str_arg_seq(cstr("%s %s %s %d")) == 1 + 2 + 4, "");
            static_assert(str_arg_seq(cstr("%s %s %s %s")) == 1 + 2 + 4 + 8, "");
        });
    });
    describe("compile time bit helper", []()
    {
        it("nth_bit", []()
        {
            static_assert(nth_bit(0) == 0,  "0th should be 00000000");
            static_assert(nth_bit(1) == 1,  "1th should be 00000001");
            static_assert(nth_bit(2) == 2,  "2th should be 00000010");
            static_assert(nth_bit(3) == 4,  "3th should be 00000100");
            static_assert(nth_bit(4) == 8,  "4th should be 00001000");
            static_assert(nth_bit(5) == 16, "5th should be 00010000");
            static_assert(nth_bit(6) == 32, "6th should be 00100000");
            static_assert(nth_bit(7) == 64, "7th should be 01000000");
            static_assert(nth_bit(8) == 128,"8th should be 10000000");

            static_assert(nth_bit(9) == 256,     "9th should be 00000001 00000000");
            static_assert(nth_bit(10) == 512,   "10th should be 00000010 00000000");
            static_assert(nth_bit(11) == 1024,  "11th should be 00000100 00000000");
            static_assert(nth_bit(12) == 2048,  "12th should be 00001000 00000000");
            static_assert(nth_bit(13) == 4096,  "13th should be 00010000 00000000");
            static_assert(nth_bit(14) == 8192,  "14th should be 00100000 00000000");
            static_assert(nth_bit(15) == 16384, "15th should be 01000000 00000000");
            static_assert(nth_bit(16) == 32768, "16th should be 10000000 00000000");
        });
    });
});
<|endoftext|>


<commit_before><commit_msg>Update navigation_lane.cc<commit_after><|endoftext|>


<commit_before>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkDebugger.h"
#include "SkString.h"

SkDebugger::SkDebugger() {
    // Create this some other dynamic way?
    fDebugCanvas = new SkDebugCanvas(100, 100);
    fPicture = NULL;
    fPictureWidth = 0;
    fPictureHeight = 0;
    fIndex = 0;
}

SkDebugger::~SkDebugger() {
    // Need to inherit from SkRef object in order for following to work
    SkSafeUnref(fDebugCanvas);
    SkSafeUnref(fPicture);
}

void SkDebugger::loadPicture(SkPicture* picture) {
    fPictureWidth = picture->width();
    fPictureHeight = picture->height();
    delete fDebugCanvas;
    fDebugCanvas = new SkDebugCanvas(fPictureWidth, fPictureHeight);
    fDebugCanvas->setBounds(fPictureWidth, fPictureHeight);
    picture->draw(fDebugCanvas);
    fIndex = fDebugCanvas->getSize() - 1;
    SkRefCnt_SafeAssign(fPicture, picture);
}

SkPicture* SkDebugger::copyPicture() {
    // We can't just call clone here since we want to removed the "deleted"
    // commands. Playing back will strip those out.
    SkPicture* newPicture = new SkPicture;
    SkCanvas* canvas = newPicture->beginRecording(fPictureWidth, fPictureHeight);
    fDebugCanvas->draw(canvas);
    newPicture->endRecording();
    return newPicture;
}

void SkDebugger::getOverviewText(const SkTDArray<double>* typeTimes,
                                 double totTime,
                                 SkString* overview,
                                 int numRuns) {
    const SkTDArray<SkDrawCommand*>& commands = this->getDrawCommands();

    SkTDArray<int> counts;
    counts.setCount(LAST_DRAWTYPE_ENUM+1);
    for (int i = 0; i < LAST_DRAWTYPE_ENUM+1; ++i) {
        counts[i] = 0;
    }

    for (int i = 0; i < commands.count(); i++) {
        counts[commands[i]->getType()]++;
    }

    overview->reset();
    int total = 0;
#ifdef SK_DEBUG
    double totPercent = 0, tempSum = 0;
#endif
    for (int i = 0; i < LAST_DRAWTYPE_ENUM+1; ++i) {
        if (0 == counts[i]) {
            // if there were no commands of this type then they should've consumed no time
            SkASSERT(NULL == typeTimes || 0.0 == (*typeTimes)[i]);
            continue;
        }

        overview->append(SkDrawCommand::GetCommandString((DrawType) i));
        overview->append(": ");
        overview->appendS32(counts[i]);
        if (NULL != typeTimes) {
            overview->append(" - ");
            overview->appendf("%.2f", (*typeTimes)[i]/(float)numRuns);
            overview->append("ms");
            overview->append(" - ");
            double percent = 100.0*(*typeTimes)[i]/totTime;
            overview->appendf("%.2f", percent);
            overview->append("%");
#ifdef SK_DEBUG
            totPercent += percent;
            tempSum += (*typeTimes)[i];
#endif
        }
        overview->append("
");
        total += counts[i];
    }
#ifdef SK_DEBUG
    if (NULL != typeTimes) {
        SkASSERT(SkScalarNearlyEqual(SkDoubleToScalar(totPercent),
                                     SkDoubleToScalar(100.0)));
        SkASSERT(SkScalarNearlyEqual(SkDoubleToScalar(tempSum),
                                     SkDoubleToScalar(totTime)));
    }
#endif

    if (totTime > 0.0) {
        overview->append("Total Time: ");
        overview->appendf("%.2f", totTime/(float)numRuns);
        overview->append("ms");
#ifdef SK_DEBUG
        overview->append(" ");
        overview->appendScalar(SkDoubleToScalar(totPercent));
        overview->append("% ");
#endif
        overview->append("
");
    }

    SkString totalStr;
    totalStr.append("Total Draw Commands: ");
    totalStr.appendScalar(SkDoubleToScalar(total));
    totalStr.append("
");
    overview->insert(0, totalStr);

    overview->append("
");
    overview->append("SkPicture Width: ");
    overview->appendS32(pictureWidth());
    overview->append("px
");
    overview->append("SkPicture Height: ");
    overview->appendS32(pictureHeight());
    overview->append("px");
}

#include "SkImageDecoder.h"

void forceLinking();
void forceLinking() {
    // This function leaks, but that is okay because it is not intended
    // to be called. It is only here so that the linker will include the
    // decoders.
    SkDEBUGCODE(SkImageDecoder *creator = ) CreateJPEGImageDecoder();
    SkASSERT(creator);
#ifdef SK_BUILD_FOR_UNIX
    SkDEBUGCODE(creator = ) CreateGIFImageDecoder();
    SkASSERT(creator);
#endif
}
<commit_msg>Fix NaCl build.<commit_after>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkDebugger.h"
#include "SkString.h"

SkDebugger::SkDebugger() {
    // Create this some other dynamic way?
    fDebugCanvas = new SkDebugCanvas(100, 100);
    fPicture = NULL;
    fPictureWidth = 0;
    fPictureHeight = 0;
    fIndex = 0;
}

SkDebugger::~SkDebugger() {
    // Need to inherit from SkRef object in order for following to work
    SkSafeUnref(fDebugCanvas);
    SkSafeUnref(fPicture);
}

void SkDebugger::loadPicture(SkPicture* picture) {
    fPictureWidth = picture->width();
    fPictureHeight = picture->height();
    delete fDebugCanvas;
    fDebugCanvas = new SkDebugCanvas(fPictureWidth, fPictureHeight);
    fDebugCanvas->setBounds(fPictureWidth, fPictureHeight);
    picture->draw(fDebugCanvas);
    fIndex = fDebugCanvas->getSize() - 1;
    SkRefCnt_SafeAssign(fPicture, picture);
}

SkPicture* SkDebugger::copyPicture() {
    // We can't just call clone here since we want to removed the "deleted"
    // commands. Playing back will strip those out.
    SkPicture* newPicture = new SkPicture;
    SkCanvas* canvas = newPicture->beginRecording(fPictureWidth, fPictureHeight);
    fDebugCanvas->draw(canvas);
    newPicture->endRecording();
    return newPicture;
}

void SkDebugger::getOverviewText(const SkTDArray<double>* typeTimes,
                                 double totTime,
                                 SkString* overview,
                                 int numRuns) {
    const SkTDArray<SkDrawCommand*>& commands = this->getDrawCommands();

    SkTDArray<int> counts;
    counts.setCount(LAST_DRAWTYPE_ENUM+1);
    for (int i = 0; i < LAST_DRAWTYPE_ENUM+1; ++i) {
        counts[i] = 0;
    }

    for (int i = 0; i < commands.count(); i++) {
        counts[commands[i]->getType()]++;
    }

    overview->reset();
    int total = 0;
#ifdef SK_DEBUG
    double totPercent = 0, tempSum = 0;
#endif
    for (int i = 0; i < LAST_DRAWTYPE_ENUM+1; ++i) {
        if (0 == counts[i]) {
            // if there were no commands of this type then they should've consumed no time
            SkASSERT(NULL == typeTimes || 0.0 == (*typeTimes)[i]);
            continue;
        }

        overview->append(SkDrawCommand::GetCommandString((DrawType) i));
        overview->append(": ");
        overview->appendS32(counts[i]);
        if (NULL != typeTimes) {
            overview->append(" - ");
            overview->appendf("%.2f", (*typeTimes)[i]/(float)numRuns);
            overview->append("ms");
            overview->append(" - ");
            double percent = 100.0*(*typeTimes)[i]/totTime;
            overview->appendf("%.2f", percent);
            overview->append("%");
#ifdef SK_DEBUG
            totPercent += percent;
            tempSum += (*typeTimes)[i];
#endif
        }
        overview->append("
");
        total += counts[i];
    }
#ifdef SK_DEBUG
    if (NULL != typeTimes) {
        SkASSERT(SkScalarNearlyEqual(SkDoubleToScalar(totPercent),
                                     SkDoubleToScalar(100.0)));
        SkASSERT(SkScalarNearlyEqual(SkDoubleToScalar(tempSum),
                                     SkDoubleToScalar(totTime)));
    }
#endif

    if (totTime > 0.0) {
        overview->append("Total Time: ");
        overview->appendf("%.2f", totTime/(float)numRuns);
        overview->append("ms");
#ifdef SK_DEBUG
        overview->append(" ");
        overview->appendScalar(SkDoubleToScalar(totPercent));
        overview->append("% ");
#endif
        overview->append("
");
    }

    SkString totalStr;
    totalStr.append("Total Draw Commands: ");
    totalStr.appendScalar(SkDoubleToScalar(total));
    totalStr.append("
");
    overview->insert(0, totalStr);

    overview->append("
");
    overview->append("SkPicture Width: ");
    overview->appendS32(pictureWidth());
    overview->append("px
");
    overview->append("SkPicture Height: ");
    overview->appendS32(pictureHeight());
    overview->append("px");
}

#include "SkImageDecoder.h"

void forceLinking();
void forceLinking() {
    // This function leaks, but that is okay because it is not intended
    // to be called. It is only here so that the linker will include the
    // decoders.
    SkDEBUGCODE(SkImageDecoder *creator = ) CreateJPEGImageDecoder();
    SkASSERT(creator);
#if defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_NACL)
    SkDEBUGCODE(creator = ) CreateGIFImageDecoder();
    SkASSERT(creator);
#endif
}
<|endoftext|>


<commit_before>#define BOOST_TEST_DYN_LINK
//#define BOOST_TEST_MODULE balance
#include <boost/test/unit_test.hpp>

#include <system.hh>

#include "balance.h"

using namespace ledger;

struct balance_fixture {
  balance_fixture() {
  times_initialize();
  amount_t::initialize();

  // Cause the display precision for dollars to be initialized to 2.
  amount_t x1("$1.00");
  BOOST_CHECK(x1);

  amount_t::stream_fullstrings = true; // make reports from UnitTests accurate
  }

  ~balance_fixture()
  {
  amount_t::stream_fullstrings = false;
  amount_t::shutdown();
  times_shutdown();
  }
};

BOOST_FIXTURE_TEST_SUITE(balance, balance_fixture)

BOOST_AUTO_TEST_CASE(testConstructors)
{
  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(123456UL);
  balance_t b3(12345L);
  balance_t b4(string ("EUR 123"));
  balance_t b5("$ 456");
  balance_t b6;
  balance_t b7(amount_t("$ 1.00"));
  balance_t b8(b7);

  BOOST_CHECK_EQUAL(balance_t(), b0);
  BOOST_CHECK_NE(balance_t("0"), b0);
  BOOST_CHECK_NE(balance_t("0.0"), b0);
  BOOST_CHECK_EQUAL(b2, 123456UL);
  BOOST_CHECK_EQUAL(b3, 12345L);
  BOOST_CHECK_EQUAL(b4, "EUR 123");
  BOOST_CHECK_EQUAL(b5, string("$ 456"));
  BOOST_CHECK_EQUAL(b7, b8);
  BOOST_CHECK_EQUAL(b8, amount_t("$ 1.00"));

  b5 = "euro 2345";
  b6 = string("DM -34532");
  b7 = amount_t("$ 1.00");

  b8 = b5;
  BOOST_CHECK_EQUAL(b5, b8);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
  BOOST_CHECK(b7.valid());
  BOOST_CHECK(b8.valid());
}

BOOST_AUTO_TEST_CASE(testAddition)
{
  amount_t a0;
  amount_t a1("$1");
  amount_t a2("2 EUR");
  amount_t a3("0.00 CAD");
  amount_t a4("$2");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3(2L);
  balance_t b4;
  balance_t b5;

  b0 += b1;
  b2 += b3;
  b3 += a1;
  b3 += a2;
  b4 += b3;
  b5 += a1;
  b5 += a4;

  BOOST_CHECK_EQUAL(balance_t(1.00), b0);
  BOOST_CHECK_EQUAL(b3 += a3, b4);
  BOOST_CHECK_EQUAL(balance_t(4L), b2);
  BOOST_CHECK_EQUAL(balance_t() += amount_t("$3"), b5);

  BOOST_CHECK_THROW(b3 += a0, balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
}

BOOST_AUTO_TEST_CASE(testSubtraction)
{
  amount_t a0;
  amount_t a1("$1");
  amount_t a2("2 EUR");
  amount_t a3("0.00 CAD");
  amount_t a4("$2");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3(2L);
  balance_t b4;
  balance_t b5;

  b0 -= b1;
  b2 -= b3;
  b3 -= a1;
  b3 -= a2;
  b4 = b3;
  b5 -= a1;
  b5 -= a4;

  BOOST_CHECK_EQUAL(balance_t(-1.00), b0);
  BOOST_CHECK_EQUAL(b3 -= a3, b4);
  BOOST_CHECK_EQUAL(balance_t(), b2);
  BOOST_CHECK_EQUAL(b3 -= b2, b3);
  BOOST_CHECK_EQUAL(balance_t() -= amount_t("$3"), b5);

  BOOST_CHECK_THROW(b3 -= a0, balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
}

BOOST_AUTO_TEST_CASE(testEqaulity)
{
  amount_t a0;
  amount_t a1("$1");
  amount_t a2("2 EUR");
  amount_t a3("0.00 CAD");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3(2L);
  balance_t b4("EUR 2");
  balance_t b5("$-1");
  balance_t b6("0.00");
  balance_t b7("0.00");

  BOOST_CHECK(b2 == b3);
  BOOST_CHECK(b4 == a2);
  BOOST_CHECK(b1 == "1.00");
  BOOST_CHECK(b5 == amount_t("-$1"));
  BOOST_CHECK(!(b6 == "0"));
  BOOST_CHECK(!(b6 == a3));
  BOOST_CHECK(!(b6 == "0.00"));
  BOOST_CHECK(b6 == b7);

  b4 += b5;
  b5 += a2;

  BOOST_CHECK(b4 == b5);

  BOOST_CHECK_THROW(b0 == a0, balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
  BOOST_CHECK(b7.valid());
}

BOOST_AUTO_TEST_CASE(testMultiplication)
{
  amount_t a0;
  amount_t a1("0.00");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3("CAD -3");
  balance_t b4("EUR 4.99999");
  balance_t b5("$1");
  balance_t b6;

  BOOST_CHECK_EQUAL(b1 *= 2.00, amount_t(2.00));
  BOOST_CHECK_EQUAL(b2 *= 2L, amount_t(4L));
  BOOST_CHECK_EQUAL(b2 *= 2UL, amount_t(8UL));
  BOOST_CHECK_EQUAL(b3 *= amount_t("-8 CAD"), amount_t("CAD 24"));
  BOOST_CHECK_EQUAL(b0 *= 2UL, b0);
  BOOST_CHECK_EQUAL(b0 *= a1, a1);

  b6 += b3;
  b3 += b4;
  b3 += b5;
  b3 *= 2L;
  b6 *= 2L;
  b4 *= 2L;
  b5 *= 2L;
  b6 += b4;
  b6 += b5;

  BOOST_CHECK_EQUAL(b3, b6);

  BOOST_CHECK_THROW(b1 *= a0 , balance_error);
  BOOST_CHECK_THROW(b4 *= amount_t("1 CAD") , balance_error);
  BOOST_CHECK_THROW(b3 *= amount_t("1 CAD") , balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
}

BOOST_AUTO_TEST_CASE(testDivision)
{
  amount_t a0;
  amount_t a1("0.00");

  balance_t b0;
  balance_t b1(4.00);
  balance_t b2(4UL);
  balance_t b3("CAD -24");
  balance_t b4("EUR 4");
  balance_t b5("$2");
  balance_t b6;

  BOOST_CHECK_EQUAL(b1 /= 2.00, amount_t(2.00));
  BOOST_CHECK_EQUAL(b2 /= 2L, amount_t(2L));
  BOOST_CHECK_EQUAL(b2 /= 2UL, amount_t(1UL));
  BOOST_CHECK_EQUAL(b3 /= amount_t("-3 CAD"), amount_t("CAD 8"));
  BOOST_CHECK_EQUAL(b0 /= 2UL, b0);

  b6 += b3;
  b3 += b4;
  b3 += b5;
  b3 /= 2L;
  b6 /= 2L;
  b4 /= 2L;
  b5 /= 2L;
  b6 += b4;
  b6 += b5;

  BOOST_CHECK_EQUAL(b3, b6);

  BOOST_CHECK_THROW(b1 /= a0 , balance_error);
  BOOST_CHECK_THROW(b1 /= a1 , balance_error);
  BOOST_CHECK_THROW(b4 /= amount_t("1 CAD") , balance_error);
  BOOST_CHECK_THROW(b3 /= amount_t("1 CAD") , balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
}

BOOST_AUTO_TEST_CASE(testNegation)
{
  amount_t a1("0.00");
  amount_t a2("$ 123");
  amount_t a3("EUR 456");

  balance_t b0;
  balance_t b1;
  balance_t b2;
  balance_t b3;

  b1 += a1;
  b1 += a2;
  b1 += a3;
  b2 += -a1;
  b2 += -a2;
  b2 += -a3;
  b3 = -b1;

  BOOST_CHECK_EQUAL(b0.negated(), b0);
  BOOST_CHECK_EQUAL(b2, b3);
  BOOST_CHECK_EQUAL(b2, -b1);
  BOOST_CHECK_EQUAL(b2.negated(), b1);

  b2.in_place_negate();

  BOOST_CHECK_EQUAL(b2, b1);
  BOOST_CHECK_EQUAL(b1, -b3);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>add unit test Absolute for balance_t<commit_after>#define BOOST_TEST_DYN_LINK
//#define BOOST_TEST_MODULE balance
#include <boost/test/unit_test.hpp>

#include <system.hh>

#include "balance.h"

using namespace ledger;

struct balance_fixture {
  balance_fixture() {
  times_initialize();
  amount_t::initialize();

  // Cause the display precision for dollars to be initialized to 2.
  amount_t x1("$1.00");
  BOOST_CHECK(x1);

  amount_t::stream_fullstrings = true; // make reports from UnitTests accurate
  }

  ~balance_fixture()
  {
  amount_t::stream_fullstrings = false;
  amount_t::shutdown();
  times_shutdown();
  }
};

BOOST_FIXTURE_TEST_SUITE(balance, balance_fixture)

BOOST_AUTO_TEST_CASE(testConstructors)
{
  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(123456UL);
  balance_t b3(12345L);
  balance_t b4(string ("EUR 123"));
  balance_t b5("$ 456");
  balance_t b6;
  balance_t b7(amount_t("$ 1.00"));
  balance_t b8(b7);

  BOOST_CHECK_EQUAL(balance_t(), b0);
  BOOST_CHECK_NE(balance_t("0"), b0);
  BOOST_CHECK_NE(balance_t("0.0"), b0);
  BOOST_CHECK_EQUAL(b2, 123456UL);
  BOOST_CHECK_EQUAL(b3, 12345L);
  BOOST_CHECK_EQUAL(b4, "EUR 123");
  BOOST_CHECK_EQUAL(b5, string("$ 456"));
  BOOST_CHECK_EQUAL(b7, b8);
  BOOST_CHECK_EQUAL(b8, amount_t("$ 1.00"));

  b5 = "euro 2345";
  b6 = string("DM -34532");
  b7 = amount_t("$ 1.00");

  b8 = b5;
  BOOST_CHECK_EQUAL(b5, b8);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
  BOOST_CHECK(b7.valid());
  BOOST_CHECK(b8.valid());
}

BOOST_AUTO_TEST_CASE(testAddition)
{
  amount_t a0;
  amount_t a1("$1");
  amount_t a2("2 EUR");
  amount_t a3("0.00 CAD");
  amount_t a4("$2");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3(2L);
  balance_t b4;
  balance_t b5;

  b0 += b1;
  b2 += b3;
  b3 += a1;
  b3 += a2;
  b4 += b3;
  b5 += a1;
  b5 += a4;

  BOOST_CHECK_EQUAL(balance_t(1.00), b0);
  BOOST_CHECK_EQUAL(b3 += a3, b4);
  BOOST_CHECK_EQUAL(balance_t(4L), b2);
  BOOST_CHECK_EQUAL(balance_t() += amount_t("$3"), b5);

  BOOST_CHECK_THROW(b3 += a0, balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
}

BOOST_AUTO_TEST_CASE(testSubtraction)
{
  amount_t a0;
  amount_t a1("$1");
  amount_t a2("2 EUR");
  amount_t a3("0.00 CAD");
  amount_t a4("$2");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3(2L);
  balance_t b4;
  balance_t b5;

  b0 -= b1;
  b2 -= b3;
  b3 -= a1;
  b3 -= a2;
  b4 = b3;
  b5 -= a1;
  b5 -= a4;

  BOOST_CHECK_EQUAL(balance_t(-1.00), b0);
  BOOST_CHECK_EQUAL(b3 -= a3, b4);
  BOOST_CHECK_EQUAL(balance_t(), b2);
  BOOST_CHECK_EQUAL(b3 -= b2, b3);
  BOOST_CHECK_EQUAL(balance_t() -= amount_t("$3"), b5);

  BOOST_CHECK_THROW(b3 -= a0, balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
}

BOOST_AUTO_TEST_CASE(testEqaulity)
{
  amount_t a0;
  amount_t a1("$1");
  amount_t a2("2 EUR");
  amount_t a3("0.00 CAD");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3(2L);
  balance_t b4("EUR 2");
  balance_t b5("$-1");
  balance_t b6("0.00");
  balance_t b7("0.00");

  BOOST_CHECK(b2 == b3);
  BOOST_CHECK(b4 == a2);
  BOOST_CHECK(b1 == "1.00");
  BOOST_CHECK(b5 == amount_t("-$1"));
  BOOST_CHECK(!(b6 == "0"));
  BOOST_CHECK(!(b6 == a3));
  BOOST_CHECK(!(b6 == "0.00"));
  BOOST_CHECK(b6 == b7);

  b4 += b5;
  b5 += a2;

  BOOST_CHECK(b4 == b5);

  BOOST_CHECK_THROW(b0 == a0, balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
  BOOST_CHECK(b7.valid());
}

BOOST_AUTO_TEST_CASE(testMultiplication)
{
  amount_t a0;
  amount_t a1("0.00");

  balance_t b0;
  balance_t b1(1.00);
  balance_t b2(2UL);
  balance_t b3("CAD -3");
  balance_t b4("EUR 4.99999");
  balance_t b5("$1");
  balance_t b6;

  BOOST_CHECK_EQUAL(b1 *= 2.00, amount_t(2.00));
  BOOST_CHECK_EQUAL(b2 *= 2L, amount_t(4L));
  BOOST_CHECK_EQUAL(b2 *= 2UL, amount_t(8UL));
  BOOST_CHECK_EQUAL(b3 *= amount_t("-8 CAD"), amount_t("CAD 24"));
  BOOST_CHECK_EQUAL(b0 *= 2UL, b0);
  BOOST_CHECK_EQUAL(b0 *= a1, a1);

  b6 += b3;
  b3 += b4;
  b3 += b5;
  b3 *= 2L;
  b6 *= 2L;
  b4 *= 2L;
  b5 *= 2L;
  b6 += b4;
  b6 += b5;

  BOOST_CHECK_EQUAL(b3, b6);

  BOOST_CHECK_THROW(b1 *= a0 , balance_error);
  BOOST_CHECK_THROW(b4 *= amount_t("1 CAD") , balance_error);
  BOOST_CHECK_THROW(b3 *= amount_t("1 CAD") , balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
}

BOOST_AUTO_TEST_CASE(testDivision)
{
  amount_t a0;
  amount_t a1("0.00");

  balance_t b0;
  balance_t b1(4.00);
  balance_t b2(4UL);
  balance_t b3("CAD -24");
  balance_t b4("EUR 4");
  balance_t b5("$2");
  balance_t b6;

  BOOST_CHECK_EQUAL(b1 /= 2.00, amount_t(2.00));
  BOOST_CHECK_EQUAL(b2 /= 2L, amount_t(2L));
  BOOST_CHECK_EQUAL(b2 /= 2UL, amount_t(1UL));
  BOOST_CHECK_EQUAL(b3 /= amount_t("-3 CAD"), amount_t("CAD 8"));
  BOOST_CHECK_EQUAL(b0 /= 2UL, b0);

  b6 += b3;
  b3 += b4;
  b3 += b5;
  b3 /= 2L;
  b6 /= 2L;
  b4 /= 2L;
  b5 /= 2L;
  b6 += b4;
  b6 += b5;

  BOOST_CHECK_EQUAL(b3, b6);

  BOOST_CHECK_THROW(b1 /= a0 , balance_error);
  BOOST_CHECK_THROW(b1 /= a1 , balance_error);
  BOOST_CHECK_THROW(b4 /= amount_t("1 CAD") , balance_error);
  BOOST_CHECK_THROW(b3 /= amount_t("1 CAD") , balance_error);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
  BOOST_CHECK(b4.valid());
  BOOST_CHECK(b5.valid());
  BOOST_CHECK(b6.valid());
}

BOOST_AUTO_TEST_CASE(testNegation)
{
  amount_t a1("0.00");
  amount_t a2("$ 123");
  amount_t a3("EUR 456");

  balance_t b0;
  balance_t b1;
  balance_t b2;
  balance_t b3;

  b1 += a1;
  b1 += a2;
  b1 += a3;
  b2 += -a1;
  b2 += -a2;
  b2 += -a3;
  b3 = -b1;

  BOOST_CHECK_EQUAL(b0.negated(), b0);
  BOOST_CHECK_EQUAL(b2, b3);
  BOOST_CHECK_EQUAL(b2, -b1);
  BOOST_CHECK_EQUAL(b2.negated(), b1);

  b2.in_place_negate();

  BOOST_CHECK_EQUAL(b2, b1);
  BOOST_CHECK_EQUAL(b1, -b3);

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
  BOOST_CHECK(b3.valid());
}

BOOST_AUTO_TEST_CASE(testAbs)
{
  amount_t a1("0.00");
  amount_t a2("$ 123");
  amount_t a3("EUR 456");

  balance_t b0;
  balance_t b1;
  balance_t b2;

  b1 += a1;
  b1 += a2;
  b1 += a3;
  b2 += -a1;
  b2 += -a2;
  b2 += -a3;

  BOOST_CHECK_EQUAL(b0.abs(), b0);
  BOOST_CHECK_EQUAL(b2.abs(), b1.abs());

  BOOST_CHECK(b0.valid());
  BOOST_CHECK(b1.valid());
  BOOST_CHECK(b2.valid());
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*
  This file is part of the Grantlee template system.

  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either version
  2 of the Licence, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "regroup.h"

#include "exception.h"
#include "parser.h"
#include "util.h"

RegroupNodeFactory::RegroupNodeFactory()
{

}

Node* RegroupNodeFactory::getNode( const QString &tagContent, Parser *p ) const
{
  QStringList expr = tagContent.split( QLatin1Char( ' ' ) );

  if ( expr.size() != 6 ) {
    throw Grantlee::Exception( TagSyntaxError, QLatin1String( "widthratio takes five arguments" ) );
  }
  FilterExpression target( expr.at( 1 ), p );
  if ( expr.at( 2 ) != QLatin1String( "by" ) ) {
    throw Grantlee::Exception( TagSyntaxError, QLatin1String( "second argument must be 'by'" ) );
  }

  if ( expr.at( 4 ) != QLatin1String( "as" ) ) {
    throw Grantlee::Exception( TagSyntaxError, QLatin1String( "fourth argument must be 'as'" ) );
  }

  FilterExpression expression( QLatin1String( "\"" ) + expr.at( 3 ) + QLatin1String( "\"" ), p );

  QString name = expr.at( 5 );

  return new RegroupNode( target, expression, name, p );
}

RegroupNode::RegroupNode( FilterExpression target, FilterExpression expression, const QString &varName, QObject *parent )
    : Node( parent ), m_target( target ), m_expression( expression ), m_varName( varName )
{

}

void RegroupNode::render( OutputStream *stream, Context *c )
{
  Q_UNUSED( stream )
  QVariantList objList = m_target.resolve( c ).toList();
  if ( objList.isEmpty() ) {
    c->insert( m_varName, QVariantHash() );
    return;
  }

  // What's going on?
  //
  // objList is a flat list of objects with a common parameter. For example, Person objects with
  // a name parameter. The list is already sorted.
  // Say the objList contains ["David Beckham", "David Blain", "Keira Nightly"] etc.
  // We want to regroup the list into separate lists of people with the same first name.
  // ie objHash should be: {"David": ["David Beckham", "David Blain"], "Keira": ["Keira Nightly"]}
  //
  // We then insert the objHash into the Context ready for rendering later in a for loop.

  QVariantList list;
  QVariantHash objHash;
  QVariantList contextList;
  QString hashKey;
  QString lastKey;
  QString keyName = getSafeString( m_expression.resolve( c ) );
  QListIterator<QVariant> i( objList );
  while ( i.hasNext() ) {
    QVariant var = i.next();
    c->push();
    c->insert( QLatin1String( "var" ), var );
    QString key = getSafeString( FilterExpression( QLatin1String( "var." ) + keyName, 0 ).resolve( c ) );
    c->pop();
    QVariantHash hash;
    if ( contextList.size() > 0 ) {
      QVariant hashVar = contextList.last();
      hash = hashVar.toHash();
    }
    if ( !hash.contains( QLatin1String( "grouper" ) ) || hash.value( QLatin1String( "grouper" ) ) != key ) {
      QVariantHash newHash;
      hash.insert( QLatin1String( "grouper" ), key );
      hash.insert( QLatin1String( "list" ), QVariantList() );
      contextList.append( newHash );
    }

    QVariantList list = hash.value( QLatin1String( "list" ) ).toList();
    list.append( var );
    hash.insert( QLatin1String( "list" ), list );
    contextList[contextList.size() - 1] = hash;
  }
  c->insert( m_varName, contextList );
}

<commit_msg>Clean the regroup filter implementation up a bit.<commit_after>/*
  This file is part of the Grantlee template system.

  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either version
  2 of the Licence, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "regroup.h"

#include "exception.h"
#include "parser.h"
#include "util.h"

RegroupNodeFactory::RegroupNodeFactory()
{

}

Node* RegroupNodeFactory::getNode( const QString &tagContent, Parser *p ) const
{
  QStringList expr = tagContent.split( QLatin1Char( ' ' ) );

  if ( expr.size() != 6 ) {
    throw Grantlee::Exception( TagSyntaxError, QLatin1String( "widthratio takes five arguments" ) );
  }
  FilterExpression target( expr.at( 1 ), p );
  if ( expr.at( 2 ) != QLatin1String( "by" ) ) {
    throw Grantlee::Exception( TagSyntaxError, QLatin1String( "second argument must be 'by'" ) );
  }

  if ( expr.at( 4 ) != QLatin1String( "as" ) ) {
    throw Grantlee::Exception( TagSyntaxError, QLatin1String( "fourth argument must be 'as'" ) );
  }

  FilterExpression expression( QLatin1String( "\"" ) + expr.at( 3 ) + QLatin1String( "\"" ), p );

  QString name = expr.at( 5 );

  return new RegroupNode( target, expression, name, p );
}

RegroupNode::RegroupNode( FilterExpression target, FilterExpression expression, const QString &varName, QObject *parent )
    : Node( parent ), m_target( target ), m_expression( expression ), m_varName( varName )
{

}

void RegroupNode::render( OutputStream *stream, Context *c )
{
  Q_UNUSED( stream )
  QVariantList objList = m_target.resolve( c ).toList();
  if ( objList.isEmpty() ) {
    c->insert( m_varName, QVariantHash() );
    return;
  }

  // What's going on?
  //
  // objList is a flat list of objects with a common parameter. For example, Person objects with
  // a name parameter. The list is already sorted.
  // Say the objList contains ["David Beckham", "David Blain", "Keira Nightly"] etc.
  // We want to regroup the list into separate lists of people with the same first name.
  // ie objHash should be: {"David": ["David Beckham", "David Blain"], "Keira": ["Keira Nightly"]}
  //
  // We then insert the objHash into the Context ready for rendering later in a for loop.

  QVariantList contextList;
  const QString keyName = getSafeString( m_expression.resolve( c ) );
  QListIterator<QVariant> i( objList );
  while ( i.hasNext() ) {
    const QVariant var = i.next();
    c->push();
    c->insert( QLatin1String( "var" ), var );
    const QString key = getSafeString( FilterExpression( QLatin1String( "var." ) + keyName, 0 ).resolve( c ) );
    c->pop();
    QVariantHash hash;
    if ( contextList.size() > 0 ) {
      QVariant hashVar = contextList.last();
      hash = hashVar.toHash();
    }
    if ( !hash.contains( QLatin1String( "grouper" ) ) || hash.value( QLatin1String( "grouper" ) ) != key ) {
      QVariantHash newHash;
      hash.insert( QLatin1String( "grouper" ), key );
      hash.insert( QLatin1String( "list" ), QVariantList() );
      contextList.append( newHash );
    }

    QVariantList list = hash.value( QLatin1String( "list" ) ).toList();
    list.append( var );
    hash.insert( QLatin1String( "list" ), list );
    contextList[contextList.size() - 1] = hash;
  }
  c->insert( m_varName, contextList );
}

<|endoftext|>